Kontextfenster und Kosten
Die gesamte httpx-Dokumentation in eine einzige Anfrage packen und sehen, was das kostet und ob das Modell einen in der Mitte versteckten Satz findet; dann, wie der Cache den zweiten Aufruf über 30-mal billiger macht; zum Schluss eine Funktion, die aus usage die Kosten berechnet.
- Etwa 40 Minuten
- Niveau: Einsteiger
- Getestet: 2026-09-14 deepseek-flash
Code und Programmausgaben stehen genau so da, wie sie gelaufen sind – Kommentare und Ausgaben sind daher auf Chinesisch.
Heutige Modelle haben Kontextfenster von Hunderttausenden bis über einer Million Tokens. Viele denken als Erstes: Wozu dann noch RAG, man kann doch einfach alle Unterlagen hineinstopfen?
Manchmal geht das tatsächlich. Du solltest aber vorher wissen, was das jedes Mal kostet, wie viel langsamer es wird und ob das Modell in einem Berg von Text den einen Satz findet, den du brauchst. Diese Lektion misst all das mit der gesamten httpx-Dokumentation.
Was das Kontextfenster ist
Das Kontextfenster (context window) ist die Obergrenze an Tokens, die ein Modell auf einmal verarbeiten kann; Eingabe und Ausgabe zusammen dürfen sie nicht überschreiten. Stand September 2026 haben deepseek-flash und deepseek-v4-pro beide ein Kontextfenster von 1 Million Tokens und eine maximale Einzelausgabe von 384.000 Tokens.
In einer Anfrage belegen all diese Dinge Kontext:
┌──────────────────────────────────────────────┐
│ system 消息:规则、人设 │
│ 之前的对话记录(user 和 assistant 轮流) │
│ 你塞进去的资料:文档、检索结果、工具返回的内容 │ 输入
│ 这一轮用户的问题 │
├──────────────────────────────────────────────┤
│ 模型的思考过程(如果开了思考模式) │ 输出
│ 模型的正式回答 │
└──────────────────────────────────────────────┘
Bei einfachen Fragen sind das zusammen vielleicht nur ein paar hundert Tokens. Bei mehrstufigen Gesprächen, Fragen mit Unterlagen oder Tool-Aufrufen wächst es aber schnell. Ist der Kontext voll, bricht die Anfrage direkt mit einem Fehler ab.
Experiment: die ganze Dokumentation hineinpacken
Die offizielle httpx-Dokumentation besteht aus gut 20 Markdown-Dateien, zusammen 116.933 Zeichen, abgelegt im Kurs-Repository unter data/httpx-docs/. Das folgende Skript macht zwei Experimente: erst die gesamte Dokumentation in die system-Nachricht und dieselbe Frage zweimal hintereinander; dann einen Satz in der Dokumentation verstecken und prüfen, ob das Modell ihn findet.
import os
import pathlib
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ.get("LLM_BASE_URL", "https://api.deepseek.com"),
)
MODEL = os.environ.get("LLM_MODEL", "deepseek-flash")
NO_THINKING = {"thinking": {"type": "disabled"}}
docs = pathlib.Path("data/httpx-docs")
text = "\n\n".join(p.read_text() for p in sorted(docs.rglob("*.md")) if p.name != "LICENSE.md")
print(f"文档共 {len(text)} 个字符")
# 实验一:同样的请求连发两次
system = "你是 httpx 的答疑助手。下面是 httpx 的全部文档:\n\n" + text
for i in range(2):
r = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": "httpx 默认的超时时间是多少秒?一句话回答。"},
],
extra_body=NO_THINKING,
)
u = r.usage
print(f"第 {i + 1} 次:输入 {u.prompt_tokens},其中缓存命中 {u.prompt_cache_hit_tokens},"
f"输出 {u.completion_tokens} | {r.choices[0].message.content}")
# 实验二:把一句无关的话插到文档的不同位置
needle = "(备注:RepoBot 项目的内部口令是“蓝鲸四十二”。)"
paragraphs = text.split("\n\n")
for fraction in [0, 0.25, 0.5, 0.75, 1.0]:
k = int(len(paragraphs) * fraction)
haystack = "\n\n".join(paragraphs[:k] + [needle] + paragraphs[k:])
r = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": haystack + "\n\n问题:上面的文字里提到的 RepoBot 内部口令是什么?只回答口令本身。"}],
extra_body=NO_THINKING,
)
print(f"口令放在 {fraction:>4.0%} 处:输入 {r.usage.prompt_tokens} 词元 → {r.choices[0].message.content}")
Im Kursverzeichnis ausgeführt, bekomme ich (deine Formulierung wird anders sein, die Tokenzahlen sollten ähnlich sein):
文档共 116933 个字符
第 1 次:输入 29397,其中缓存命中 0,输出 12 | httpx 默认超时是 5 秒。
第 2 次:输入 29397,其中缓存命中 29184,输出 19 | httpx 默认的超时时间是 5 秒(指网络不活动的超时)。
口令放在 0% 处:输入 29404 词元 → 蓝鲸四十二
口令放在 25% 处:输入 29404 词元 → 蓝鲸四十二
口令放在 50% 处:输入 29404 词元 → 蓝鲸四十二
口令放在 75% 处:输入 29404 词元 → 蓝鲸四十二
口令放在 100% 处:输入 29404 词元 → 蓝鲸四十二
Zuerst Experiment eins. Die Antwort ist richtig; in der httpx-Dokumentation steht wörtlich „The default behavior is to raise a TimeoutException after 5 seconds of network inactivity“. Die ganze Dokumentation sind etwa 29.000 Tokens, also nur 3 % eines Fensters von einer Million. Dieselbe Frage zweimal hintereinander: Von den 29.397 Eingabe-Tokens des zweiten Aufrufs trafen 29.184 den Cache. Wie viel das spart, kommt gleich.
Findet es einen in der Mitte versteckten Satz?
Experiment zwei ist ein einfacher „Nadel im Heuhaufen“-Test. Ein oft zitierter Artikel, Lost in the Middle (Liu u. a., 2023), stellte fest, dass die damaligen Modelle Informationen am Anfang und Ende langer Eingaben am besten fanden und solche in der Mitte am ehesten übersahen.
Ich habe einen Satz, der nichts mit httpx zu tun hat, an Anfang, 25 %, 50 %, 75 % und Ende des Textes eingefügt, und alle fünf Positionen wurden richtig beantwortet. Bei etwa dreißigtausend Tokens und der einfachen Aufgabe, einen Satz zu finden, zeigte deepseek-flash kein „Vergessen in der Mitte“. Jener Artikel testete Modelle von 2023; die Fähigkeit mit langen Kontexten hat sich seither stark verbessert.
Schließ daraus aber nicht, dass lange Kontexte kein Problem sind. „Einen auffälligen Satz finden“ ist die leichteste Langkontext-Aufgabe. Müssen über das Dokument verstreute Informationen zusammengeführt oder in Dutzenden ähnlicher Absätze feine Unterschiede erkannt werden, macht das Modell eher Fehler; Benchmarks wie RULER (Hsieh u. a., 2024) testen genau diese schwierigeren Fälle. Zu welcher Art deine Aufgabe gehört, prüfst du am besten mit deinen eigenen Daten; Übung 3 ist so ein Test.
Kosten: warum nicht immer alles hineinpacken
Finden heißt nicht, dass es sich lohnt. Schreiben wir eine Funktion, die aus usage die Kosten berechnet:
PRICES = {
# 模型: (输入-缓存命中, 输入-缓存未命中, 输出),美元 / 每一百万词元,高峰价
"deepseek-flash": (0.006, 0.30, 1.20),
"deepseek-v4-pro": (0.044, 1.32, 3.96),
}
def cost_usd(usage, model, off_peak=False):
hit_price, miss_price, out_price = PRICES[model]
# DeepSeek 的 usage 里有 prompt_cache_hit_tokens,别家不一定有,没有就当全部未命中
hit = getattr(usage, "prompt_cache_hit_tokens", 0) or 0
miss = usage.prompt_tokens - hit
total = (hit * hit_price + miss * miss_price + usage.completion_tokens * out_price) / 1_000_000
return total / 2 if off_peak else total # 低谷时段五折
Die Preise sind die offiziellen DeepSeek-Preise vom September 2026; prüf sie vor der Verwendung auf der Preisseite. Setzen wir die beiden Zahlengruppen aus Experiment eins ein und schätzen die Kosten für einen Monat bei 1000 Aufrufen pro Tag (vollständiger Code in code/01-llm-basics/cost.py):
第一次:0.008833 美元
第二次:0.000262 美元
每月 3 万次,全部命中缓存:7.85 美元;全部不命中:265.00 美元
Die einfache Frage aus Modul 00, Lektion 3 kostete 0,00019 Dollar; der erste Aufruf mit der ganzen Dokumentation hier 0,0088 Dollar, 46-mal so viel, und der Nutzer sieht nur einen Satz als Antwort. Bei 1000 Aufrufen pro Tag über einen Monat sind es ohne Cache 265 Dollar.
Dazu kommt die Geschwindigkeit. Je länger die Eingabe, desto länger braucht das Modell, um sie zu lesen, und desto länger wartet der Nutzer auf das erste Zeichen. Bei dreißigtausend Tokens fällt das noch nicht auf; bei Hunderttausenden Tokens spürt man es deutlich.
Alles hineinzupacken passt also, wenn die Unterlagen selbst nicht groß sind, wenn es wenige Aufrufe gibt oder der Cache verlässlich greift. Sind die Unterlagen groß, gibt es viele Aufrufe und braucht man jeweils nur einen kleinen Teil, lohnt es sich eher, zuerst die relevanten Teile herauszusuchen und nur diese dem Modell zu geben. Genau das macht RAG in Modul 04.
Cache: ein wiederholter Anfang ist fast kostenlos
Der zweite Aufruf war über 30-mal billiger (0,000262 gegen 0,008833), und das verdankt er dem Kontext-Cache von DeepSeek: Stimmt der Anfang dieser Anfrage exakt mit dem Anfang einer früheren Anfrage überein, kann der Server die damals berechneten Ergebnisse wiederverwenden, und dieser Teil wird zum Preis eines „Cache-Treffers“ abgerechnet. Bei deepseek-flash kostet ein Cache-Treffer ein Fünfzigstel eines Fehlschlags (0,006 gegen 0,30).
Laut Cache-Dokumentation von DeepSeek (Stand September 2026):
- Der Cache ist standardmäßig aktiv, am Code muss nichts geändert werden.
- Der Cache speichert „Präfix-Einheiten“. Der Server legt solche Einheiten an den Grenzen jeder Anfrage an, an Anfängen, die mehrere Anfragen gemeinsam haben, und bei langen Eingaben alle festgelegte Anzahl Tokens. Nur wenn eine Einheit vollständig übereinstimmt, zählt dieser Teil als Treffer.
- Nicht mehr genutzte Caches werden in der Regel nach einigen Stunden bis Tagen gelöscht.
- Die Zahl der Treffer- und Fehlschlag-Tokens steht in
usageunterprompt_cache_hit_tokensbzw.prompt_cache_miss_tokens.
Dass im Experiment beim zweiten Mal noch 213 Tokens danebengingen, liegt daran, dass der Cache einheitenweise abgleicht: Der letzte Abschnitt mit der Nutzerfrage und alles, was keine vollständige Einheit bildet, wird als Fehlschlag berechnet.
Aus diesen Regeln folgt, wie du Nachrichten anordnen solltest:
- Unveränderliches nach vorn: system-Nachricht, feste Unterlagen und Beispiele ganz nach vorn.
- Veränderliches nach hinten: die Frage des Nutzers und alles, was sich jedes Mal ändert, ans Ende.
Stellst du die Frage des Nutzers vor die Dokumentation, ist bei jeder anderen Frage auch der Anfang anders, und die Dokumentation dahinter kann nie einen Treffer erzielen.
Ausgabe ist teurer als Eingabe
Zurück zur Preistabelle: deepseek-flash kostet für die Ausgabe 1,20 Dollar pro Million Tokens, für Eingabe ohne Cache-Treffer 0,30, für Eingabe mit Cache-Treffer 0,006. Ausgabe ist viermal so teuer wie Eingabe und 200-mal so teuer wie Eingabe mit Cache-Treffer.
Ein paar direkte Folgerungen:
- Das Modell weniger schwafeln zu lassen, spart Geld. Verlangst du im Prompt „in einem Satz antworten“ oder „nur JSON ausgeben“, wirkt das sofort.
- Im Denkmodus wird der Denkprozess als Ausgabe berechnet. Bei einfachen Aufgaben das Denken auszuschalten, spart den Großteil der Ausgabekosten.
- Lange Unterlagen in der Eingabe sind, wenn sie den Cache treffen, eigentlich nicht teuer. Was wirklich Geld kostet, sind meist lange Eingaben ohne Cache-Treffer und ausschweifende Ausgaben.
Häufige Probleme
Fehler: Kontext zu lang: Prüf zuerst, ob der Gesprächsverlauf immer weiter anwächst. Modul 03, Lektion 1 zeigt, wie man den Verlauf kürzt und komprimiert.
Die Zahl der Cache-Treffer ist immer 0: Prüf, ob der Anfang jeder Anfrage wirklich identisch ist. Typische Ursachen sind die aktuelle Uhrzeit oder eine zufällige ID in der system-Nachricht oder eine jedes Mal andere Reihenfolge der Unterlagen.
Haben andere Anbieter auch einen Cache?: Die meisten, aber Abrechnung und Regeln unterscheiden sich, und manche verlangen, dass man den zu cachenden Teil von Hand markiert. Lies vorher deren Dokumentation.
Übungen
- Rechne mit
cost_usd: Wie viel spart man bei ebenfalls 30.000 Aufrufen pro Monat ohne Cache-Treffer, wenn alle in der Nebenzeit stattfinden (off_peak=Trueübergeben)? - Vertausch in Experiment eins system- und user-Nachricht (Frage vorn, Dokumentation hinten), frag zweimal hintereinander und schau, wie sich die Cache-Treffer beim zweiten Mal verändern.
- Wandle Experiment zwei ab: Füg drei Sätze an verschiedenen Stellen ein, etwa „Teil eins des Passworts ist Blauwal“, „Teil zwei ist vierzig“, „Teil drei ist zwei“, und frag nach dem vollständigen Passwort. Teste mehrmals mit ausgeschaltetem und mit eingeschaltetem Denken. Ist das Ergebnis dasselbe wie beim Finden eines einzelnen Satzes?
Selbsttest
1. Das Kontextfenster umfasst 1 Million Tokens. Heißt das, ich kann 1 Million Tokens eingeben?
Nein. Das Kontextfenster ist die Obergrenze für Eingabe und Ausgabe zusammen. Belegt die Eingabe 990.000, bleiben für die Ausgabe höchstens 10.000, und mit eingeschaltetem Denken zählt auch der Denkprozess als Ausgabe. Außerdem heißt „passt hinein“ nicht „lohnt sich“; lange Eingaben sind langsam und teuer.
2. Deine Anwendung schickt dem Modell jedes Mal ein festes Produkthandbuch zusammen mit der Frage des Nutzers. In welcher Reihenfolge ist es am billigsten?
Das Handbuch ganz nach vorn (etwa in die system-Nachricht), die Frage des Nutzers ans Ende. Dann beginnt jede Anfrage gleich, der Cache greift, und das Handbuch wird zum Cache-Trefferpreis berechnet, dutzendfach billiger. Steht die Frage vorn, ist der Anfang jedes Mal anders, und der Cache greift nie.
3. In einem Test hat das Modell einen von dir in einem langen Dokument versteckten Satz genau gefunden. Beweist das, dass seine Langkontext-Fähigkeit in Ordnung ist?
Nein. „Einen Satz finden“ ist die leichteste Langkontext-Aufgabe. Wenn Informationen aus mehreren Stellen zusammengeführt oder in vielen ähnlichen Inhalten Details unterschieden werden müssen, macht das Modell eher Fehler. Ob es deinen Anforderungen genügt, musst du mit Tests prüfen, die deiner echten Aufgabe nahekommen.
Fragen und Diskussion
Hängst du in dieser Lektion fest? Frag hier. Und wenn du die Frage von jemandem beantworten kannst, tu es gern.
Eine Frage bringt 3 Punkte, eine Antwort 6. Beiträge erscheinen nach der Prüfung.
Diskussion wird geladen…