Erweitertes Prompt-Caching
Auf dieser Seite
Jenseits des automatischen Caching
Sie haben die Grundlagen des Prompt-Cachings in Lektion 3 kennengelernt. Dort erschien Caching als ein automatischer Mechanismus, der Ihnen Vorteile bringt, ohne dass Sie besondere Anstrengungen unternehmen müssen. Das ist wahr, solange Ihre Anwendung gut strukturierte Anfragen sendet. Die Realität in der Produktion ist jedoch unübersichtlicher: Mehrere Nutzer treffen gleichzeitig auf dieselbe API, jeder mit seinem eigenen Ordner, seinen eigenen Dokumenten und eigenen Fragen. Der gemeinsame Präfix wird dann auf fast nichts reduziert, und die Trefferquote des Caches bricht zusammen, ohne dass jemand bemerkt, dass dies geschieht, da nichts kaputtgeht – die Rechnung geht einfach nur in die Höhe.
Mehrbenutzer-, mehrdokumentarische und Verarbeitungs-Pipelines: Wir werden diese komplexen Szenarien einzeln angehen, um die Cache-Trefferquote zu maximieren. Das leitende Prinzip lässt sich in einem Satz zusammenfassen: Caching ist etwas, das Ihnen nicht widerfährt; es ist etwas, das Sie gestalten.
Prompt-Architektur zur Maximierung des Caching
Caching gilt für den längsten gemeinsamen Präfix über Ihre Anfragen hinweg. Je länger und stabiler dieser Präfix ist, desto mehr sparen Sie. Die praktische Konsequenz ist, dass ein Prompt von den stabilsten zu den variabelsten Elementen organisiert sein muss, ähnlich wie ein Buch, dessen erste Seiten niemals geändert werden und nur das letzte Kapitel bei jeder Neuauflage neu geschrieben wird.
Das folgende Beispiel wendet dieses Prinzip auf einen Dokumentenanalyse-Assistenten für eine Anwaltskanzlei an. Drei Schichten folgen einander: Globale Anweisungen, die eingefroren sind; das Wissensbasis, das höchstens einmal im Monat überarbeitet wird; und die Aktenakte sowie die Frage, die bei jedem Aufruf wechseln.
import openai
client = openai.OpenAI()
# Schichtierte Architektur: von den stabilsten zu den variabelsten Elementen
# Schicht 1: Globale Anweisungen (niemals ändern)
GLOBAL_INSTRUCTIONS = """
Sie sind ein Dokumentenanalyse-Assistent für eine Anwaltskanzlei.
Regeln:
- Antworten Sie immer auf Französisch
- Zitieren Sie relevante gesetzliche Artikel
- Strukturieren Sie die Antwort wie folgt: Analyse / Risiken / Empfehlungen
- Seien Sie faktisch und präzise
"""
# Schicht 2: Wissensbasis (ändert sich selten)
KNOWLEDGE_BASE = """
[Fügen Sie hier 5.000+ Token an Rechtsprechung, gesetzliche Artikel
und Analysevorlagen ein – dieser Inhalt ändert sich höchstens einmal im Monat]
"""
# Schicht 3: Aktenkontext (ändert sich pro Mandant)
def build_prompt(case_context: str, question: str) -> str:
return f"""{GLOBAL_INSTRUCTIONS}
{KNOWLEDGE_BASE}
--- Aktuelle Akte ---
{case_context}
--- Frage ---
{question}"""
Der klassische Fehler besteht darin, den Mandanten-Namen oder das heutige Datum am Anfang des System-Prompts zu platzieren, um ihn zu „personalisieren”. Bereits ein einzelner variabler Zeichen am Anfang reicht aus, um den Präfix einzigartig zu machen und die Cache-Trefferquote für alle nachfolgenden Schichten auf null zu reduzieren. Alles, was variiert, muss am Ende stehen.
Natürlich müssen Sie überprüfen, ob sich die Theorie in Zahlen widerspiegelt. Der folgende Monitor liest input_tokens_details.cached_tokens bei jeder Antwort aus und sammelt die Messwerte, wodurch Sie eine nutzbare durchschnittliche Rate erhalten, anstatt nur auf Bauchgefühl zu vertrauen.
class CacheMonitor:
"""Überwacht die Effizienz des Prompt-Cachings."""
def __init__(self):
self.calls: list[dict] = []
def record(self, usage) -> dict:
total_input = usage.input_tokens
cached_tokens = getattr(
usage.input_tokens_details, "cached_tokens", 0
)
rate = cached_tokens / total_input if total_input > 0 else 0
entry = {
"total_input": total_input,
"cached_tokens": cached_tokens,
"cache_rate": rate,
}
self.calls.append(entry)
return entry
@property
def average_rate(self) -> float:
if not self.calls:
return 0.0
return sum(a["cache_rate"] for a in self.calls) / len(self.calls)
def report(self) -> str:
if not self.calls:
return "Keine Aufrufe."
return (
f"Aufrufe: {len(self.calls)} | "
f"Durchschnittliche Cache-Rate: {self.average_rate:.1%} | "
f"Eingesparte Token: "
f"{sum(a['cached_tokens'] for a in self.calls):,}"
)
monitor = CacheMonitor()
Drei Muster, die den Unterschied machen
Das erste Muster betrifft die Verarbeitungsreihenfolge. Wenn Sie eine Charge heterogener Anfragen verarbeiten, führt das Ausführen in Ankunftsreihenfolge dazu, dass Präfixe abwechselnd verwendet werden und das Caching nicht warm bleibt. Das Gruppieren nach gemeinsamem Kontext löst das Problem: Sammeln Sie zuerst, führen Sie später aus, sodass jede Gruppe von dem durch die erste Anfrage vorbereiteten Präfix profitiert.
from collections import defaultdict
def group_by_context(
requests: list[dict],
) -> dict[str, list[dict]]:
"""Gruppieren Sie Anfragen nach gemeinsamem Präfix."""
groups = defaultdict(list)
for request in requests:
# Schlüssel = Hash des gemeinsamen Kontexts
key = hash(request.get("shared_context", ""))
groups[key].append(request)
return dict(groups)
def process_by_cohort(requests: list[dict]):
"""Verarbeiten Sie gruppierte Anfragen, um das Caching zu maximieren."""
groups = group_by_context(requests)
for key, group in groups.items():
# Alle Anfragen in der Gruppe teilen sich denselben Präfix
for request in group:
response = client.responses.create(
model="gpt-5.6-terra",
instructions=request["shared_context"],
input=request["question"],
)
monitor.record(response.usage)
Das zweite Muster behandelt kalte Starts. Die erste Anfrage in einer Reihe kostet den vollen Preis, da noch nichts im Cache gespeichert ist; wenn diese erste Anfrage von einem echten Nutzer am Montagmorgen um 9 Uhr kommt, trägt er die Latenz. Eine Vorwärm-Anfrage, die vor der Eröffnung des Dienstes gesendet wird, verlagert diese Kosten an eine Stelle, wo sie niemanden stört. Beachten Sie die Wächterfunktion am Anfang der Funktion: Unter 1.024 Token gilt Caching nicht, und das Vorwärmen wäre reine Kosten.
def warm_up_cache(system_prompt: str):
"""Sendet eine minimale Anfrage, um den Cache vorzubereiten."""
if len(system_prompt) // 4 < 1024:
print("Prompt zu kurz für Caching (< 1.024 Token)")
return
response = client.responses.create(
model="gpt-5.6-terra",
instructions=system_prompt,
input="Bestätigen Sie, dass Sie bereit sind.",
max_output_tokens=10,
)
usage = response.usage
print(
f"Cache vorbereitet: {usage.input_tokens} Eingabetoken, "
f"{getattr(usage.input_tokens_details, 'cached_tokens', 0)} gecacht"
)
Das dritte Muster macht die Cache-Inaktivierung sichtbar. Das Beheben eines Kommas in einem 5.000-Token-System-Prompt scheint trivial, doch dieses Komma spült den Cache für alle aus. Indem Sie dem Inhalt einen Fingerabdruck hinzufügen, erkennen Sie die Änderung bei der Bereitstellung, anstatt erst am nächsten Tag auf der Rechnung, und können sofort mit dem Vorwärmen nachziehen.
import hashlib
class VersionedPrompt:
"""Verwaltet Prompt-Versionen zur Steuerung des Caching."""
def __init__(self, content: str):
self.content = content
self.version = hashlib.md5(content.encode()).hexdigest()[:8]
def is_same_version(self, other: "VersionedPrompt") -> bool:
return self.version == other.version
# Wenn sich die Version ändert, wird der Cache inaktiviert
prompt_v1 = VersionedPrompt("Anweisungen v1...")
prompt_v2 = VersionedPrompt("Anweisungen v2 geändert...")
if not prompt_v1.is_same_version(prompt_v2):
print("Warnung: Prompt geändert, Cache wird inaktiviert")
warm_up_cache(prompt_v2.content)
Was dies in Bezug auf das Volumen bedeutet
Nehmen Sie einen realen Fall von 10.000 Aufrufen pro Tag mit einem 3.000-Token-System-Prompt. Ohne Caching werden Sie 10.000 × 3.000, also 30 Millionen Eingabetoken pro Tag in Rechnung gestellt. Mit einer Trefferquote von 80 % bleiben 6 Millionen Token zum vollen Preis und 24 Millionen fallen auf den halben Preis, wodurch die Einsparungen bei den Eingabetoken bei etwa 40 % liegen. Die Zahl scheint im Vergleich zu einem einzelnen Aufruf bescheiden; über ein Jahr der Produktion ändert sie jedoch die Kostenstruktur der Anwendung.
Kernaussagen
- Strukturieren Sie Ihre Prompts in Schichten von den stabilsten zu den variabelsten Elementen
- Gruppieren Sie Anfragen nach gemeinsamem Kontext, um den Cache warm zu halten
- Wärm den Cache vor Verkehrsspitzen auf
- Überwachen Sie die Cache-Rate mit
input_tokens_details.cached_tokens - Versionieren Sie Ihre Prompts, um Cache-Inaktivierungen vorzusehen