Prompt-Cache-Misses in Claude Code: Warum eine Trefferquote von 86 % die Kosten verschleiert
Ein bepreistes Modell einer iOS-Monorepo-Sitzung zeigt, dass sieben Prompt-Cache-Misses 33 Treffer überwiegen – und wie man Tools, Skills und CLAUDE.md als Cache-Schlüssel stabilisiert.
Eine Trefferquote von 86 % klingt gesund. Doch an einem modellierten Nachmittag mit Claude Code kosteten sieben Cache-Misses mehr als die übrigen 33 Turns zusammen. Das Bild ändert sich, sobald man jeden Miss bepreist und auf eine Ursache zurückführt.
Version 2.1.260 von Claude Code hat /cost um eine Zeile ergänzt: eine bestmögliche Vermutung, warum Ihr letzter Prompt-Cache-Miss passiert ist. Infrage kommen geänderte Tool-Definitionen, ein geänderter System-Prompt oder eine Pause, die länger als die TTL war. Dann brachte 2.1.261 /skill-doctor, das geladene, aber nie genutzte Skills meldet und zeigt, was sie an Kontext kosten. Dieselbe Version behob einen Bug, bei dem Teammitglieder eines Agent-Teams ihre Tool-Ankündigungen aus dem ersten Turn im zweiten wiederholten, damit das Präfix der Anfrage veränderten und den Cache verfehlten.
Zusammengenommen zeigen diese Einträge: Der Anbieter behandelt das Zusammensetzen des Kontexts inzwischen als Cache-Key-Engineering, die meisten Nutzer noch nicht. Ein Prompt-Cache ist ein Präfix-Cache. Alles, was vor der Unterhaltung steht, in genau der Reihenfolge, in der Sie es platziert haben, ist der Schlüssel. Um zu sehen, was das an einem gewöhnlichen Tag kostet, stammen die Zahlen unten aus dem Modell einer einzigen Sitzung.
Wie der Cache Anfragen verschlüsselt
Die Caching-Dokumentation von Anthropic beschreibt die Struktur klar. Anfragen sind geschichtet: zuerst Tools, dann System, dann Nachrichten, und jede Änderung auf einer Ebene löscht diese Ebene und alle danach. Ein Tool umbenennen oder sein Schema anfassen verwirft den ganzen Cache; den System-Prompt bearbeiten behält den Tools-Eintrag und verliert den Rest. Bei jeder Anfrage hasht die API das Präfix bis zu Ihrem Breakpoint, und findet sie nichts Passendes, geht sie Block für Block zurück und prüft dabei höchstens 20 Positionen. Sie können bis zu vier Breakpoints setzen; in diesem Szenario liegen sie hinter dem Tool-Array, hinter dem System-Prompt und auf der letzten Nachricht.
Standardmäßig lebt ein Eintrag fünf Minuten, und jeder Lesezugriff verlängert ihn kostenlos. Einen einstündigen Eintrag zu schreiben kostet das Doppelte des Basis-Eingabepreises. Claude Fable 5.1 berechnet 10 $ pro Million Eingabe-Tokens, 12,50 $ pro Million für das Schreiben eines Fünf-Minuten-Eintrags, 20 $ für einen Stunden-Eintrag und 0,25 $ pro Million für das Lesen. Ein Cache-Lesezugriff kostet also 2,5 % dessen, was dieselben Tokens ungecacht kosten, und ein neuer Fünf-Minuten-Schreibvorgang das 50-Fache eines Lesezugriffs. Ein Miss ist daher keine kleine Verzögerung: Was aus dem Cache gefallen ist, wird plötzlich 50-mal teurer abgerechnet.
Ein Nachmittag, sieben gewöhnliche Ereignisse
Das Swift-Paket ContextCacheKit ordnet eine Anfrage so an, wie die API sie hasht, simuliert einen Cache nach den dokumentierten TTL- und Lookback-Regeln und bepreist jeden Turn. Sein Szenario bildet eine realistische iOS-Monorepo-Sitzung nach. Das Tool-Set umfasst 16 Harness-Tools und 17 MCP-Tools von Xcode, GitHub und Linear (19.140 Tokens). Der Rest sind eine Harness-Präambel, eine CLAUDE.md mit 200 Zeilen, eine Memory-Datei und zwei Skills, die beim Start geladen werden (17.700 Tokens). Jeder Turn beginnt mit diesem gemeinsamen Präfix von 36.840 Tokens, darauf folgen 40 Nutzer-Turns im Abstand von 45 Sekunden.
Dann treten sieben Ereignisse ein, wie sie in echten Teams vorkommen. In Turn zwei schreibt ein Teammitglied die erste Nachricht um. Turn acht lädt einen Networking-Skill, der in den System-Prompt eingefügt wird. In Turn 14 verbindet sich der GitHub-MCP-Server neu und registriert seine acht Tools erneut am Ende der Liste. Vor Turn 20 liegt eine 22-minütige Mittagspause. Turn 26 bringt eine gemergte CLAUDE.md-Änderung, Turn 33 einen neu geladenen Test-Skill, und in Turn 37 rendert ein neu verbundener Client die Bash-Tool-Definition neu, die von 1.450 auf 1.480 Tokens wächst.
Insgesamt trifft die Sitzung in 85,6 % der Fälle, eine Zahl, bei der auf einem Dashboard niemand zweimal hinsieht. Das Kostenbuch erzählt etwas anderes. Die Eingabe kostete 4,65 $. Perfekte Wiederverwendung des vorherigen Präfixes in jedem Turn hätte 1,53 $ gekostet, ganz ohne Cache 23,10 $. Sieben Misses machen 3,41 $ der Rechnung aus, die übrigen 33 Turns zusammen 1,24 $.
Sortiert nach Dollar über dem Ideal ist der größte Übeltäter das Neu-Rendern des Tool-Schemas mit 0,91 $. Es folgen drei separate System-Prompt-Änderungen mit zusammen 0,88 $, dann die Mittagspause mit 0,69 $, die Umsortierung der Tools mit 0,64 $ und weit abgeschlagen die Umformulierung des Teammitglieds mit 0,002 $.
Zwei Posten in dieser Rangliste verändern den Blick darauf, woher die Kosten kommen. Dreißig zusätzliche Tokens im Bash-Schema ließen das System alle 75.249 Tokens der Anfrage erneut senden, weil Tools an der Spitze der Hierarchie stehen. Bloßes Umsortieren der GitHub-Tools, bei Byte für Byte unverändertem Schema, tat dasselbe mit 52.648 Tokens. Der Bug dagegen, der einen Changelog-Eintrag bekam – das Teammitglied, das Nachricht null umschrieb –, war der billigste Miss und cachte nur 157 Tokens neu, weil Nachrichten ganz unten liegen. Der Harness hat den Miss repariert, den er sehen konnte, die teuren kommen aus Ihrer eigenen Konfiguration.
Die Diagnoselogik ist simpel: Gewinner ist das Erste in Hash-Reihenfolge, das das Präfix brechen würde.
// Abridged from MissDiagnoser.swift: the first thing in hash order that would break the prefix wins.
public static func diagnose(previous: ContextSnapshot?, current: ContextSnapshot,
elapsed: TimeInterval, ttl: CacheTTL) -> MissCause {
guard let previous else { return .none } // cold start, not a miss
if toolsDiffer(previous, current) { return .toolSetChanged(...) }
if previous.tools.map(\.name) != current.tools.map(\.name) { return .toolOrderChanged }
if previous.system != current.system { return .systemSegmentChanged(segments: changed) }
if let i = firstRewrittenMessage(previous, current) { return .messagePrefixRewritten(atIndex: i) }
return elapsed > ttl.seconds ? .idlePastTTL(idleSeconds: elapsed) : .none
}
Das Präfix als Cache-Schlüssel behandeln
Spielen Sie den Nachmittag mit einem konstruierten Präfix noch einmal durch, dem Szenario stabilized. Die Tool-Reihenfolge ist fest, die Schemas bleiben die ganze Sitzung über eingefroren. Skills kommen als Nachrichten mit System-Rolle in die Unterhaltung und nicht als Text, der in den System-Prompt der obersten Ebene eingefügt wird – laut Dokumentation lässt das bei Fable 5.1 die System- und Nachrichten-Caches intakt. Der CLAUDE.md-Merge wartet bis zu einer Sitzungsgrenze. Die Mittagspause bleibt, wie sie ist, denn eine TTL ist eine Richtlinienentscheidung und kein Präfixfehler.
Dieser Durchlauf hat einen einzigen Miss, die Mittagspause. Trefferquote: 94,2 %. Kosten: 2,22 $, also 0,69 $ über dem Ideal. Alle Korrekturen änderten, was der Harness sendet; keine berührte das Modellverhalten.
Der verbleibende Hebel ist die TTL. Stellen Sie den stabilisierten Durchlauf auf Stunden-Einträge um, und es bleiben keine Misses, bei Kosten von 2,11 $. Das spart gegenüber der Fünf-Minuten-Variante nur 11 Cent, weil jedes frische Token nun 20 $ statt 12,50 $ beim Schreiben kostet und sich dieser Aufschlag über einen Nachmittag auf etwa eine Mittagspause summiert. Die längere TTL lohnt sich nur an Tagen mit Lücken, also legen Sie sie pro Team fest und nicht global.
Am meisten Geld bewegt die Platzierung der Skills. Derselbe Skill, dieselben 5.200 Tokens, nur der Ort unterscheidet sich:
// Same skill, same 5,200 tokens. Only the placement changes.
.loadSkill(name: "skill:networking-layer", tokens: 5_200, placement: .systemPrompt)
// → system prompt changed: re-sends the transcript after it
.loadSkill(name: "skill:networking-layer", tokens: 5_200, placement: .appendedMessage)
// → hit: costs only its own 5,200 tokens, once
Was man messen sollte
Planen Sie das gemeinsame Präfix wie die Startzeit einer App. Die Tools und Skills, die Ihr Monorepo standardmäßig lädt, summieren sich auf 36.840 Tokens, die in jedem Turn gehasht werden, und eine Trefferquote mittelt über eine ganze Sitzung und verdeckt, in welchem Turn das Geld verbrannt wurde. Die Zahl, die aufs Dashboard gehört, sind Dollar über dem Ideal, nach Ursache, nach Tag. /cost liefert jetzt die Ursache, das Kostenbuch die Dollar.
Eine Trefferquote ist ein Durchschnitt, ein Cache-Miss aber ein Ereignis – also budgetieren Sie die Ereignisse.
Das Szenario ist ein Modell und keine Telemetrie, seine Konstanten sind Annahmen, mit Ihrer eigenen Tool-Liste und Ihren Präfixgrößen verschiebt sich die Rangfolge also. Die Grundform nicht. Ursachen an der Spitze der Hierarchie kosten am meisten, und kein Changelog repariert sie für Sie. Simulator, Szenarien, 30 Tests und eine SwiftUI-Ansicht des Kostenbuchs liegen im Demo-Repository des Projekts; das Demo-Target war zum Zeitpunkt der Veröffentlichung noch nicht auf einem Simulator gelaufen.