Kmemo
Cache semantica con undici controlli lessicali, perché il coseno confonde 100 e 250 USD.
implementation("io.github.nacode-studios:kmemo-core:2.1.0")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}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.
—
Un rifiuto sbagliato costa una chiamata API. Un'accettazione sbagliata costa una risposta sbagliata.
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.