Kmemo

Cache semantica con undici controlli lessicali, perché il coseno confonde 100 e 250 USD.

Gradleimplementation("io.github.nacode-studios:kmemo-core:2.1.0")
kmemo.kt
val cache = SemanticCache( embedder = Embedder { text -> openAi.embed(text) }, store = InMemoryStore(maxEntries = 10_000, ttl = 1.hours),)val answer = cache.getOrPut(prompt) { llm.complete(it) }// Ogni miss dice perché: soglia o controllo.when (val r = cache.lookup(prompt)) { is CacheLookup.Hit -> r.response is CacheLookup.Miss -> r.reason}
Il prodotto

Cache semantica per chiamate LLM. Riconosce le richieste già elaborate anche quando sono riformulate e scarta i casi in cui la similarità è alta ma la risposta corretta è diversa. Il core compila per JVM, iOS, macOS, Linux, Windows, JS e WasmJS.

Cache semanticaIn attesa
in cacheConverti 100 USD in EUR
in arrivoConverti 250 USD in EUR
similarità coseno · soglia 0,950,000
Cache a sola soglia
HIT

—

Kmemoverifica lessicale…
numeriunità di misuraentitàriferimenti temporalinegazioniantonimiconfronti invertiti
numeri · 100 ≠ 250
MISS· numeri diversichiamata al modello

Un rifiuto sbagliato costa una chiamata API. Un'accettazione sbagliata costa una risposta sbagliata.

il caso che rompe le cache semantiche

Una cache a corrispondenza esatta non riconosce due formulazioni diverse della stessa domanda. Una cache semantica calcola l’embedding della richiesta, cerca la più vicina tra quelle già elaborate e riutilizza la risposta associata, riducendo chiamate al modello e latenza. Il limite noto di questo approccio è il falso positivo. Le richieste «Converti 100 USD in EUR» e «Converti 250 USD in EUR» hanno una similarità intorno a 0,99 su tutti i principali modelli di embedding: nessuna soglia le separa, perché su quell’asse risultano più vicine di molte riformulazioni legittime. Una cache basata sulla sola soglia restituirebbe la risposta sbagliata senza generare alcun errore. Kmemo affronta questo caso come requisito primario. La similarità è solo il primo filtro: i candidati che la superano vengono analizzati come testo da una catena di undici controlli lessicali, che verifica differenze concrete su numeri, unità di misura, entità, riferimenti temporali, negazioni, antonimi, confronti invertiti e clausole che restringono la domanda. Un dodicesimo controllo, opzionale, legge la risposta già in cache invece dei soli due testi: è l’unico modo per riconoscere due riformulazioni oneste la cui differenza sta nella risposta e non nella domanda. I costi di errore sono asimmetrici: un rifiuto errato costa una chiamata API, un’accettazione errata costa una risposta sbagliata. I valori di default seguono questo criterio e i controlli si astengono in caso di incertezza. La soglia di similarità va misurata sul modello di embedding effettivamente in uso: la libreria include un calibratore dedicato. I numeri sono misurati su corpora ciechi, scritti prima dei controlli e mai usati per calibrarli. Una cache basata sulla sola soglia serve tutti i falsi positivi che le vengono mostrati; i controlli di Kmemo li riducono a circa un terzo, mantenendo l’88% delle riformulazioni legittime. Il confronto con GPTCache è pubblicato insieme al suo costo: il cross-encoder dell’alternativa ne serve meno, e per farlo rifiuta più di metà delle riformulazioni corrette. Quei tre corpora hanno però un limite che nessun processo elimina: li ha scritti la stessa persona che ha scritto i controlli, quindi coprono i casi limite a cui qualcuno ha pensato, non quelli che esistono. Dalla versione 2.1 c’è un quarto corpus che questo progetto non ha scritto: PAWS, costruito da Google Research nel 2019 per misurare se un modello sa distinguere una riformulazione da una quasi-riformulazione quando le parole coincidono quasi tutte. Su quel corpus i controlli rifiutano il 14% dei falsi positivi e mantengono il 79% delle riformulazioni, molto peggio dei numeri interni. Pesa in due sensi opposti e nessuno dei due lo migliora: un corpus costruito apposta per battere la sovrapposizione lessicale è più duro del traffico reale, e le sue frasi sono enunciati enciclopedici mentre i controlli leggono domande. Resta l’unica cifra della pagina che nessuno qui avrebbe potuto calibrare, ed è per questo che è pubblicata accanto alle altre. Dalla versione 2.0 il core non è più legato alla JVM. Compila per iOS, macOS, Linux, Windows, JS e WasmJS senza aggiungere una sola dipendenza. Una app mobile paga ogni chiamata sulla rete cellulare e uno strumento nel browser non ha un server su cui mettere la cache: sono i casi che una libreria solo JVM non copre. Gli store Redis e PostgreSQL e le integrazioni con i framework restano lato JVM, dove vivono i driver che incapsulano.

Ambito
Open sourceKotlinKotlin MultiplatformLLMCaching
Approccio

Cosa abbiamo fatto

01Design della catena di controlli e del modello di astensione
02Migrazione del core a Kotlin Multiplatform
03Calibratore della soglia sul modello di embedding del cliente
04Store in memoria, Redis, PostgreSQL e HNSW
05Integrazioni Spring Boot, Spring AI, LangChain4j e Ktor
Punti di forza
+Undici controlli lessicali a difesa dai falsi positivi della soglia
+Un controllo opzionale legge la risposta in cache, non solo le due domande
+Ogni miss riporta la causa: similarità sotto soglia o rifiuto di un controllo
+Calibratore della soglia sul modello di embedding in uso
+Core multipiattaforma: JVM, iOS, macOS, Linux, Windows, JS e WasmJS
+kmemo-core dipende solo da kotlinx-coroutines-core, su ogni piattaforma
Risultati ottenuti
Falsi positivi ridotti da tutti a circa un terzo, con l’88% delle riformulazioni mantenute
Misurati anche su PAWS, corpus esterno del 2019: 14% di falsi positivi rifiutati, 79% di riformulazioni mantenute
Confronto con GPTCache pubblicato insieme al costo che comporta
Controlli verificati su corpora ciechi a ogni build
Dodici moduli distribuiti su Maven Central
API pubblica stabile secondo SemVer, con guida di migrazione da 1.x
Risorse
Apri su GitHubVisita il sitoRepository

Ti piace quello che facciamo?

Parliamo del tuo prossimo progetto insieme