Home Nieuws Waarom uw LLM-rekening explodeert – en hoe semantische caching deze met 73%...

Waarom uw LLM-rekening explodeert – en hoe semantische caching deze met 73% kan verlagen

4
0
Waarom uw LLM-rekening explodeert – en hoe semantische caching deze met 73% kan verlagen

Onze LLM API-factuur groeide maand-op-maand met 30%. Het verkeer nam toe, maar niet zo snel. Toen ik onze zoekopdrachtlogboeken analyseerde, ontdekte ik het echte probleem: gebruikers stellen dezelfde vragen op verschillende manieren.

“Wat is uw retourbeleid?”, “Hoe kan ik iets retourneren?” en “Kan ik mijn geld terugkrijgen?” bereikten allemaal afzonderlijk onze LLM en genereerden vrijwel identieke reacties, waarbij elk de volledige API-kosten met zich meebracht.

Exact-match caching, de voor de hand liggende eerste oplossing, kon slechts 18% van deze redundante oproepen opvangen. Dezelfde semantische vraag, anders geformuleerd, ging volledig voorbij aan de cache.

Daarom heb ik semantische caching geïmplementeerd op basis van wat zoekopdrachten betekenen, niet hoe ze zijn geformuleerd. Na de implementatie ervan steeg ons cachetrefferpercentage naar 67%, waardoor de LLM API-kosten met 73% daalden. Maar om daar te komen, zijn problemen nodig die naïeve implementaties over het hoofd zien.

Waarom exacte match-caching tekortschiet

Bij traditionele caching wordt querytekst gebruikt als cachesleutel. Dit werkt als zoekopdrachten identiek zijn:

# Caching met exacte overeenkomsten

cache_key = hash(query_tekst)

als cache_key in cache:

retour cache(cache_key)

Maar gebruikers formuleren vragen niet op dezelfde manier. Mijn analyse van 100.000 productiequery’s vond het volgende:

  • Slechts 18% waren exacte duplicaten van eerdere zoekopdrachten

  • 47% waren semantisch vergelijkbaar met eerdere zoekopdrachten (dezelfde intentie, andere bewoording)

  • 35% waren werkelijk nieuwe vragen

Die 47% vertegenwoordigde enorme kostenbesparingen die we misten. Elke semantisch vergelijkbare zoekopdracht veroorzaakte een volledige LLM-aanroep, waardoor een antwoord ontstond dat vrijwel identiek was aan het antwoord dat we al hadden berekend.

Semantische caching-architectuur

Semantische caching vervangt op tekst gebaseerde sleutels door op insluitingen gebaseerde overeenkomsten op te zoeken:

klasse SemantischeCache:

def __init__(zelf, embedding_model, gelijkenisdrempel=0,92):

self.embedding_model = embedding_model

self.threshold = gelijkenis_drempel

self.vector_store = VectorWinkel() # FAISS, Dennenappel, enz.

self.response_store = ResponseStore() # Redis, DynamoDB, enz.

def get(self, query: str) -> Optioneel(str):

“””Retourneer het in de cache opgeslagen antwoord als er een semantisch vergelijkbare zoekopdracht bestaat.”””

query_embedding = self.embedding_model.encode(query)

# Vind de meest vergelijkbare in de cache opgeslagen zoekopdracht

komt overeen met = self.vector_store.search(query_embedding, top_k=1)

if komt overeen met en komt overeen met(0).similarity >= self.threshold:

cache_id = komt overeen met(0).id

return self.response_store.get(cache_id)

retour Geen

def set(zelf, vraag: str, antwoord: str):

“””Cachequery-antwoordpaar.”””

query_embedding = self.embedding_model.encode(query)

cache_id = genereer_id()

self.vector_store.add(cache_id, query_embedding)

self.response_store.set(cache_id, {

‘vraag’: vraag,

‘reactie’: reactie,

’tijdstempel’: datetime.utcnow()

})

Het belangrijkste inzicht: in plaats van de tekst van de zoekopdracht te hashen, sluit ik zoekopdrachten in de vectorruimte in en vind ik in de cache opgeslagen zoekopdrachten binnen een gelijkenisdrempel.

Het drempelprobleem

De gelijkenisdrempel is de kritische parameter. Als u deze te hoog instelt, mist u geldige cachehits. Als u het te laag instelt, krijgt u verkeerde antwoorden.

Onze aanvankelijke drempel van 0,85 leek redelijk; 85% vergelijkbaar zou “dezelfde vraag” moeten zijn, toch?

Fout. Bij 0,85 kregen we cachehits zoals:

Dit zijn verschillende vragen met verschillende antwoorden. Het retourneren van het in de cache opgeslagen antwoord zou onjuist zijn.

Ik ontdekte dat optimale drempels variëren per querytype:

Zoektype

Optimale drempel

Reden

Vragen in FAQ-stijl

0,94

Hoge precisie nodig; Verkeerde antwoorden schaden het vertrouwen

Zoeken naar producten

0,88

Meer tolerantie voor bijna-matches

Ondersteuningsvragen

0,92

Balans tussen dekking en nauwkeurigheid

Transactionele vragen

0,97

Zeer lage tolerantie voor fouten

Ik heb querytype-specifieke drempels geïmplementeerd:

klasse AdaptiveSemanticCache:

def __init__(zelf):

zelf.drempels = {

‘veelgestelde vragen’: 0.94,

‘zoeken’: 0,88,

‘ondersteuning’: 0,92,

’transactioneel’: 0,97,

‘standaard’: 0,92

}

self.query_classifier = QueryClassifier()

def get_threshold(self, query: str) -> float:

query_type = self.query_classifier.classify(query)

return self.thresholds.get(query_type, self.thresholds(‘standaard’))

def get(self, query: str) -> Optioneel(str):

drempel = self.get_threshold(query)

query_embedding = self.embedding_model.encode(query)

komt overeen met = self.vector_store.search(query_embedding, top_k=1)

if komt overeen met en match(0).similarity >= drempelwaarde:

return self.response_store.get(matches(0).id)

retour Geen

Methodologie voor drempelafstemming

Ik kon drempels niet blindelings afstemmen. Ik had grondwaarheid nodig over welke vraagparen eigenlijk ‘hetzelfde’ waren.

Onze methodologie:

Stap 1: Voorbeeldqueryparen. Ik heb 5.000 zoekparen op verschillende gelijkenisniveaus (0,80-0,99) onderzocht.

Stap 2: Menselijke etikettering. Annotators bestempelden elk paar als “dezelfde bedoeling” of “andere bedoeling.” Ik gebruikte drie annotators per paar en nam een ​​meerderheidsstemming.

Stap 3: Bereken precisie/recall-curven. Voor elke drempel berekenden we:

  • Precisie: Welk deel van de cachehits had dezelfde bedoeling?

  • Bedenk: welk deel van de paren met dezelfde bedoelingen hebben we in de cache opgeslagen?

def compute_precision_recall(paren, labels, drempelwaarde):

“””Bereken precisie en herinner aan een gegeven gelijkenisdrempel.”””

voorspellingen = (1 als paar.similariteit >= drempel anders 0 voor paar in paren)

true_positives = som(1 voor p, l in zip(voorspellingen, labels) als p == 1 en l == 1)

false_positives = som(1 voor p, l in zip(voorspellingen, labels) als p == 1 en l == 0)

false_negatives = som(1 voor p, l in zip(voorspellingen, labels) als p == 0 en l == 1)

precisie = waar_positieven / (waar_positieven + onwaar_positieven) if (waar_positieven + onwaar_positieven) > 0 anders 0

herinneren = waar_positieven / (waar_positieven + onwaar_negatieven) if (waar_positieven + onwaar_negatieven) > 0 anders 0

terugkeerprecisie, terugroepen

Stap 4: Selecteer een drempel op basis van de kosten van fouten. Voor veelgestelde vragen waarbij verkeerde antwoorden het vertrouwen schaden, heb ik geoptimaliseerd voor precisie (drempel van 0,94 gaf 98% nauwkeurigheid). Voor zoekopdrachten waarbij het missen van een cachehit alleen maar geld kost, heb ik geoptimaliseerd voor terugroepen (drempelwaarde van 0,88).

Latency-overhead

Semantische caching voegt latentie toe: u moet de query insluiten en het vectorarchief doorzoeken voordat u weet of u de LLM moet aanroepen.

Onze metingen:

Operatie

Latentie (p50)

Latentie (p99)

Query-insluiting

12 ms

28 ms

Vector zoeken

8ms

19 ms

Totaal cache-opzoeken

20 ms

47 ms

LLM API-oproep

850 ms

2400 ms

De overhead van 20 ms is verwaarloosbaar vergeleken met de LLM-oproep van 850 ms die we vermijden bij cachehits. Zelfs bij p99 is de overhead van 47 ms acceptabel.

Cache-missers duren nu echter 20 ms langer dan voorheen (insluiten + zoeken + LLM-oproep). Bij ons hitpercentage van 67% pakt de berekening gunstig uit:

Netto latentieverbetering van 65% naast de kostenverlaging.

Cache-invalidatie

In het cachegeheugen opgeslagen reacties blijven verouderd. Productinformatie verandert, beleidsupdates en het juiste antwoord van gisteren wordt het verkeerde antwoord van vandaag.

Ik heb drie valideringsstrategieën geïmplementeerd:

  1. Op tijd gebaseerde TTL

Eenvoudige vervaldatum op basis van inhoudstype:

TTL_BY_CONTENT_TYPE = {

‘prijzen’: timedelta(uren=4), # Verandert regelmatig

‘beleid’: timedelta(dagen=7), # Veranderingen zelden

‘product_info’: timedelta(dagen=1), # Dagelijks vernieuwen

‘general_faq’: timedelta(dagen=14), # Zeer stabiel

}

  1. Op gebeurtenissen gebaseerde ongeldigverklaring

Wanneer onderliggende gegevens veranderen, maakt u gerelateerde cachegegevens ongeldig:

klasse CacheInvalidator:

def on_content_update(self, content_id: str, content_type: str):

“””Cachegegevens met betrekking tot bijgewerkte inhoud ongeldig maken.”””

# Zoek in de cache opgeslagen zoekopdrachten die naar deze inhoud verwijzen

beïnvloed_queries = self.find_queries_referencing(content_id)

voor query_id in beïnvloede_queries:

self.cache.invalidate(query_id)

self.log_invalidation(content_id, len(beïnvloede_queries))

  1. Detectie van veroudering

Voor reacties die zonder expliciete gebeurtenissen verouderd zouden kunnen raken, heb ik periodieke versheidscontroles geïmplementeerd:

def check_freshness(self, cached_response: dict) -> bool:

“””Controleer of het in de cache opgeslagen antwoord nog steeds geldig is.”””

# Voer de query opnieuw uit op basis van de huidige gegevens

fresh_response = self.generate_response(cached_response(‘query’))

# Vergelijk semantische gelijkenis van antwoorden

cached_embedding = self.embed(cached_response(‘reactie’))

fresh_embedding = zelf.embed(fresh_response)

gelijkenis = cosinus_similarity(cached_embedding, verse_embedding)

# Als de antwoorden aanzienlijk uiteenlopen, maak deze dan ongeldig

als gelijkenis

self.cache.invalidate(cached_response(‘id’))

terugkeer Vals

terugkeer Waar

We voeren dagelijks controles uit op de versheid van een aantal in de cache opgeslagen vermeldingen, waarbij we verouderde gegevens opsporen die TTL en op gebeurtenissen gebaseerde invalidatie missen.

Productieresultaten

Na drie maanden in productie:

Metrisch

Voor

Na

Wijziging

Cachehitpercentage

18%

67%

+272%

LLM API-kosten

$ 47.000/maand

$ 12,7K/maand

-73%

Gemiddelde latentie

850 ms

300 ms

-65%

Vals-positief percentage

N.v.t

0,8%

Klachten van klanten (foute antwoorden)

Basislijn

+0,3%

Minimale stijging

Het fout-positieve percentage van 0,8% (query’s waarbij we een in de cache opgeslagen antwoord retourneerden dat semantisch onjuist was) lag binnen aanvaardbare grenzen. Deze gevallen deden zich voornamelijk voor aan de grenzen van onze drempel, waar de gelijkenis net boven de grens lag, maar de intentie enigszins verschilde.

Valkuilen om te vermijden

Gebruik geen enkele globale drempel. Verschillende querytypen hebben verschillende toleranties voor fouten. Stem drempels per categorie af.

Sla de stap voor het insluiten van cachehits niet over. U zou in de verleiding kunnen komen om de insluitingsoverhead over te slaan bij het retourneren van in de cache opgeslagen antwoorden, maar u hebt de insluiting nodig voor het genereren van cachesleutels. De overhead is onvermijdelijk.

Vergeet de ongeldigverklaring niet. Semantische caching zonder invalidatiestrategie leidt tot verouderde reacties die het vertrouwen van gebruikers ondermijnen. Creëer invalidatie vanaf dag één.

Cache niet alles. Sommige zoekopdrachten mogen niet in de cache worden opgeslagen: gepersonaliseerde antwoorden, tijdgevoelige informatie, transactiebevestigingen. Stel uitsluitingsregels op.

def Should_cache(self, query: str, respons: str) -> bool:

“”Bepaal of het antwoord in de cache moet worden opgeslagen.””

# Bewaar geen gepersonaliseerde antwoorden in het cachegeheugen

als self.contains_personal_info(antwoord):

terugkeer Vals

# Bewaar geen tijdgevoelige informatie in het cachegeheugen

if self.is_time_sensitive(query):

terugkeer Vals

# Bewaar transactiebevestigingen niet in de cache

als self.is_transactional(query):

terugkeer Vals

terugkeer Waar

Belangrijkste afhaalrestaurants

Semantische caching is een praktisch patroon voor LLM-kostenbeheersing dat caching-missers met exacte match van redundantie opvangt. De belangrijkste uitdagingen zijn het afstemmen van drempels (gebruik querytypespecifieke drempels op basis van precisie-/herinneringsanalyse) en cache-invalidatie (combineer TTL, op gebeurtenissen gebaseerde detectie en detectie van veroudering).

Met een kostenreductie van 73% was dit onze hoogste ROI-optimalisatie voor productie-LLM-systemen. De implementatiecomplexiteit is gematigd, maar het afstemmen van de drempel vereist zorgvuldige aandacht om kwaliteitsverlies te voorkomen.

Sreenivasa Reddy Hulebeedu Reddy is een hoofdsoftware-ingenieur.

Welkom bij de VentureBeat-community!

In ons gastpostprogramma delen technische experts inzichten en bieden ze neutrale, niet-gevestigde diepgaande inzichten over AI, data-infrastructuur, cyberbeveiliging en andere geavanceerde technologieën die de toekomst van het bedrijfsleven vormgeven.

Lees meer uit ons gastpostprogramma — en bekijk ons richtlijnen als u geïnteresseerd bent om een ​​eigen artikel bij te dragen!

Nieuwsbron

LAAT EEN REACTIE ACHTER

Vul alstublieft uw commentaar in!
Vul hier uw naam in