Prompt-Caching in Produktion: LLM-Latenz und Kosten senken ohne veraltete Antworten

#Prompt-Caching
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Prompt-Caching ist der günstigste Performance-Gewinn in den meisten KI-Features und der, den man am leichtesten falsch macht. Gut gemacht, senkt es die Time-to-First-Token einer wiederholten Anfrage von zwei Sekunden auf unter zweihundert Millisekunden und senkt die Input-Token-Rechnung dieser Anfrage um 80 % oder mehr. Unbedacht gemacht, liefert es einem Nutzer die Antwort der letzten Woche auf die Frage dieser Woche, leakt die Daten eines Mandanten in die Antwort eines anderen Mandanten oder cached leise einen Fehler und spielt ihn tausendfach ab. Der Unterschied ist nicht die Caching-Bibliothek. Es ist die Disziplin darum, worauf du keyst, was du wiederverwenden darfst und wie du invalidierst.

Dieser Beitrag ist ein praktischer Leitfaden, um Prompt-Caching in Produktion zu betreiben: die zwei unterschiedlichen Dinge, die Leute mit dem Begriff meinen, wo Wiederverwendung wirklich sicher ist, wie man einen Cache-Key konstruiert, der dich nicht verrät, und wie man invalidiert, ohne die Einsparungen wegzuwerfen, die du erzielen wolltest.

Zwei verschiedene Dinge, die Leute Prompt-Caching nennen

Der Begriff deckt zwei Mechanismen mit sehr unterschiedlichen Risikoprofilen ab, und diese zu vermengen ist die Wurzel der meisten Incidents.

Der erste ist anbieterseitiges Prefix-Caching. Anthropic, OpenAI und Google lassen dich alle einen stabilen Prefix deines Prompts markieren, typischerweise die Systeminstruktionen plus abgerufener Kontext, sodass der Anbieter den berechneten Attention-State warm hält und den wiederholten Prefix zu einem steilen Rabatt abrechnet. Du machst weiterhin einen API-Aufruf, das Modell läuft weiterhin, und die Ausgabe wird weiterhin frisch generiert. Das ist fast reiner Vorteil: Du verwendest keine Antwort wieder, nur die Arbeit, dieselbe Präambel zu lesen. Die einzige echte Einschränkung ist die Reihenfolge. Der gecachte Inhalt muss am Anfang des Prompts stehen und Byte für Byte identisch bleiben, sodass alles Volatile, ein Zeitstempel, eine Request-ID, die neueste Nachricht des Nutzers, nach dem Cache-Breakpoint gehört, niemals in den Prefix.

Der zweite ist anwendungsseitiges Response-Caching: Du speicherst die vollständige Ausgabe des Modells und gibst sie bei einer späteren Anfrage direkt zurück, ohne das Modell überhaupt aufzurufen. Hier sind die Latenz- und Kostengewinne am größten, weil du die Inferenz komplett überspringst, und hier wohnt auch jeder Korrektheits- und Sicherheitsfehler. Der Rest dieses Beitrags handelt größtenteils davon, die zweite Art sicher zu machen, weil die erste Art selten jemandem schadet.

Wo Response-Caching tatsächlich sicher ist

Der ehrliche Test, ob eine Antwort cachebar ist, ist eine einzige Frage: Wenn zwei Anfragen denselben Key erzeugen, ist es immer korrekt, ihnen dieselbe Antwort zu geben? Wenn du das nicht ohne Vorbehalte mit Ja beantworten kannst, hast du keinen Cache, du hast einen Bug mit einer Trefferquote.

Drei Kategorien bestehen den Test sauber. Deterministische Transformationen sind am sichersten: Klassifikation, Extraktion, Übersetzung und Umformatierung einer festen Eingabe, bei Temperatur null ausgeführt, erzeugen jedes Mal dieselbe nützliche Ausgabe, also ist ihr Caching einfach Memoisierung. Teure idempotente Anreicherung ist als Nächstes: Embeddings für einen Dokument-Chunk, eine Zusammenfassung eines unveränderlichen PDF oder eine generierte Beschreibung eines Produkts, das sich nicht geändert hat. Diese kosten echtes Geld in der Berechnung und müssen sich nie ändern, bis die Quelle es tut. Schließlich können geteilte Referenzantworten, die nicht vom einzelnen Nutzer abhängen, die Art von "erkläre dieses Konzept" oder "was bedeutet dieser Fehler"-Inhalt, der für alle identisch ist, über deine gesamte Nutzerbasis gecacht werden.

Zwei Kategorien scheitern fast immer am Test. Personalisierte Generierung, die Nutzerhistorie, Kontostatus oder Berechtigungen einfaltet, darf niemals einen Cache-Eintrag über Nutzer hinweg teilen, und meist auch nicht über Sessions, weil die Eingaben, die sie korrekt machen, genau die Eingaben sind, die sie nicht teilbar machen. Und alles Zeitkritische, eine Anfrage, deren richtige Antwort sich ändert, während sich zugrundeliegende Daten ändern, ist nur cachebar, wenn deine Invalidierung an diese Daten gebunden ist, was der schwere Teil ist, der weiter unten behandelt wird.

Wenn dein Team nicht artikulieren kann, in welchen Eimer jeder KI-Aufruf fällt, ist das das Erste, was zu beheben ist, und das ist meist der Punkt, an dem sich ein fokussiertes Code-Quality-Audit der KI-Schicht innerhalb eines Tages bezahlt macht. Die Audit-Frage ist unverblümt: Zeig mir den Cache-Key für jeden gecachten Aufruf und beweise, dass zwei verschiedene Nutzer nicht auf ihm kollidieren können.

Einen Cache-Key bauen, der dich nicht verrät

Ein Cache-Key ist ein Versprechen: Alles, was die richtige Antwort ändern kann, ist im Key, und nichts, was privat sein sollte, wird durch Auslassung geteilt. Die meisten Cache-Incidents sind ein Feld, das im Key hätte sein sollen und es nicht war.

Ein vollständiger Key für einen Response-Cache hat vier Teile. Die semantische Eingabe ist der eigentliche Inhalt: die normalisierte Nutzer-Query oder der Dokument-Hash, nicht der rohe String, denn abschließende Leerzeichen und Groß-/Kleinschreibung sollten deine Trefferquote nicht fragmentieren. Die Modellidentität zählt, weil GPT-4.1 und sein Nachfolger keine austauschbare Ausgabe produzieren, also gehören Modellname und -version in den Key, sonst liefert dir ein Modell-Upgrade leise die Antworten des gestrigen Modells. Die Verhaltensparameter, Temperatur, Max Tokens, die System-Prompt-Version und jede Tool-Konfiguration, ändern die Ausgabe und ändern damit den Key. Und der Mandanten- und Berechtigungsscope, die Mandanten-ID und ein Rollen- oder Berechtigungs-Fingerabdruck, ist das Feld, das Leute vergessen, und das eine, das einen Cache in ein Datenleck verwandelt.

Ein defensiver Helper in Symfony macht den Vertrag explizit:

// src/Service/PromptCacheKey.php
final class PromptCacheKey
{
    public function build(CacheableRequest $r): string
    {
        $parts = [
            'v3',                          // hochzählen, um alles zu invalidieren
            $r->tenantId,                  // niemals über Mandanten hinweg teilen
            $r->permissionFingerprint,     // Rollen-/Scope-Hash
            $r->modelId,                   // enthält Modellversion
            (string) $r->temperature,
            $r->systemPromptVersion,
            hash('sha256', $this->normalise($r->input)),
        ];

        return 'llm:' . hash('sha256', implode('|', $parts));
    }

    private function normalise(string $input): string
    {
        return trim(preg_replace('/\s+/', ' ', mb_strtolower($input)));
    }
}

Das führende v3 ist bewusst gewählt. Ein globales Versionssegment im Key ist der günstigste Invalidierungshebel, den du besitzt: Wenn du einen Prompt so änderst, dass alte Antworten nicht wiederverwendet werden sollten, zählst du es hoch und der gesamte Cache altert aus, ohne einen Flush. Mach die Mandanten-ID im Konstruktor nicht-optional, sodass es unmöglich ist, einen Key zu bauen, der sie auslässt. Ein mandantenübergreifendes Leck ist kein Bug, den du aus einem Support-Ticket erfahren willst.

Invalidierung, ohne die Einsparungen zu verlieren

Caching ist einfach. Invalidierung ist das Engineering. Der Instinkt, auf alles eine kurze Time-to-Live zu setzen, zerstört den Großteil des Nutzens, denn eine Fünf-Minuten-TTL auf einer Dokumentzusammenfassung, die sich nie ändert, bedeutet, dass du alle fünf Minuten erneut für eine Inferenz einer Antwort zahlst, die bereits korrekt war. Das bessere Modell ist, auf Events zu invalidieren, nicht auf eine Uhr.

Binde jeden Cache-Eintrag an eine Version seiner zugrundeliegenden Daten. Wenn ein Dokument neu indexiert wird, inkrementiere einen Versionszähler für dieses Dokument und nimm ihn in den Key auf, sodass die alten Einträge in dem Moment unerreichbar werden, in dem sich die Quelle ändert, und ohne einen Flush lazily zurückgewonnen werden. Nutze eine TTL nur als Absicherung gegen Einträge, die du zu invalidieren vergessen hast, nicht als deinen primären Korrektheitsmechanismus, und setze sie großzügig, Stunden oder Tage, für wirklich unveränderlichen Inhalt. Für die seltene Antwort, die jetzt korrekt ist, aber es später nicht sein wird und kein sauberes Event hat, an das man sich hängen kann, bevorzuge eine kurze TTL plus ein Stale-while-Revalidate-Muster: Liefere die gecachte Antwort sofort, aktualisiere sie im Hintergrund, sodass der Nutzer Geschwindigkeit bekommt und der nächste Nutzer Frische.

Zwei Fehler verdienen explizite Behandlung. Cache niemals Fehler oder leere Antworten, sonst wird ein einzelner Anbieter-Schluckauf in tausend abgespielte Fehlschläge verstärkt; cache nur validierte, vollständige Ausgaben. Und schütze gegen den Stampede, bei dem ein beliebter Eintrag abläuft und hundert gleichzeitige Anfragen alle verfehlen und alle gleichzeitig das Modell treffen, was du mit einem kurzen Lock entschärfst, sodass der erste Miss berechnet, während der Rest kurz wartet oder Stale ausliefert. Diese Muster sind Standard im HTTP-Caching und übertragen sich direkt; Teams, die bereits eine ausgereifte Webanwendung betreiben, haben die Primitive meist in ihrem Stack und müssen sie nur auf die LLM-Schicht richten.

Ein pragmatischer Rollout

Widerstehe dem Drang, am ersten Tag alles zu cachen. Beginne mit den Aufrufen, die eindeutig sicher und teuer sind: Embeddings, Zusammenfassungen unveränderlicher Dokumente und Temperatur-null-Klassifikation. Instrumentiere Trefferquote, gesparte Latenz und gesparte Kosten pro Aufruftyp, bevor du expandierst, denn ein Cache mit 4 % Trefferquote fügt Komplexität für nichts hinzu und sollte entfernt werden, nicht getunt. Füge personalisiertes oder zeitkritisches Caching erst hinzu, wenn die einfachen Gewinne gemessen und die Invalidierungsgeschichte für diese Daten aufgeschrieben ist, nicht improvisiert.

Die Teams, die dauerhaften Wert aus Prompt-Caching ziehen, behandeln es als ein Korrektheits-Feature, das nebenbei Geld spart, nicht als ein Kosten-Feature, das nebenbei Korrektheit riskiert. Die Key-Konstruktion und die Invalidierungsregeln sind das Produkt; der Cache-Speicher ist ein Implementierungsdetail. Wenn du ein KI-Feature baust oder härtest und ein zweites Paar Augen darauf willst, wo Caching sicher ist und wo es leise veraltete Antworten liefert, ist das genau die Art von Review, die wir im Rahmen von Individualsoftware-Entwicklung machen. Erreiche uns unter hello@wolf-tech.io oder auf wolf-tech.io, und bring deine Cache-Keys mit; sie sagen uns das meiste von dem, was wir wissen müssen.