Kosten sparen, schneller werden
Fünf kleine Experimente – feste Unterlagen nach vorn gestellt spart 64 % der Kosten, eine Bitte um Kürze senkt die Ausgabe um 92 %, Denken bei einfachen Fragen abschalten, ein eigener Ergebnis-Cache, und Parallelität drückt 10 Anfragen von 9,5 auf 2,3 Sekunden.
- Etwa 35 Minuten
- Niveau: Fortgeschritten
- Getestet: 2026-09-14 deepseek-flash
Code und Programmausgaben stehen genau so da, wie sie gelaufen sind – Kommentare und Ausgaben sind daher auf Chinesisch.
Eine KI-Anwendung im Betrieb wird täglich Tausende Male aufgerufen. Spart man bei jedem Aufruf ein wenig, summiert sich das beträchtlich; ist jeder Aufruf ein wenig schneller, fühlt es sich für Nutzer viel besser an.
Diese Lektion macht fünf kleine Experimente, jedes ein Mittel, das man direkt im Projekt einsetzen kann, und jedes mit gemessenen Zahlen. Alle Experimente nutzen deepseek-flash; die Preise sind nach dem Spitzentarif vom September 2026 berechnet. Vollständiger Code in code/06-production/cost_latency.py.
1. Feste Unterlagen nach vorn
Modul 01, Lektion 4 hat den Kontext-Cache von DeepSeek erklärt: Stimmt der Anfang einer Anfrage mit einer früheren überein, wird dieser Teil zum Cache-Treffer-Preis berechnet, einem Fünfzigstel des Preises ohne Treffer.
Experiment: drei verschiedene Fragen, jedes Mal mit derselben httpx-Dokumentation (etwa 15.000 Tokens). Eine Variante stellt die Frage nach vorn und die Dokumentation dahinter; die andere legt die Dokumentation in die System-Nachricht und die Frage dahinter.
for label, build in [
("问题在前", lambda q: [{"role": "user", "content": f"问题:{q}\n\n参考文档:\n{DOCS}\n\n一句话回答。"}]),
("文档在前", lambda q: [{"role": "system", "content": f"参考文档:\n{DOCS}"}, {"role": "user", "content": q + "一句话回答。"}]),
]:
== 1. 缓存:固定的资料放在前面,还是问题放在前面
问题在前:三次的缓存命中 ['0/15168', '0/15169', '0/15167'],共 0.01379 美元
文档在前:三次的缓存命中 ['0/15167', '14976/15168', '14976/15166'],共 0.00500 美元
Mit der Frage vorn gab es in drei Durchläufen keinen einzigen Treffer: Jede Frage ist anders, also ist auch der Anfang der Anfrage anders, und die gleiche Dokumentation dahinter nützt nichts. Mit der Dokumentation vorn gab es beim ersten Mal keinen Cache, die beiden folgenden trafen je 14976 Tokens. Die Gesamtkosten sanken von 0,01379 auf 0,00500 US-Dollar, 64 % gespart.
Hier wurde nur dreimal gefragt, und der „Kaltstart“ beim ersten Mal macht den Großteil aus. Je öfter gefragt wird, desto näher kommt die Variante mit der Dokumentation vorn an „man zahlt jedes Mal nur für die Frage selbst“.
Nur die Reihenfolge wurde getauscht, keine einzige Zeile Code mehr. Prüfe deine Prompts: Steht in der System-Nachricht etwas, das sich jedes Mal ändert, wie die aktuelle Zeit, der Nutzername oder eine Zufallsnummer? Verschieb es ans Ende.
2. Das Modell weniger reden lassen
Ausgabe kostet viermal so viel wie Eingabe, und die Generierungsgeschwindigkeit bestimmt direkt, wie lange der Nutzer wartet.
q = "httpx 和 requests 有什么区别?"
for label, prompt in [("不做要求", q), ("要求简短", q + "用三句话以内回答。")]:
== 2. 输出长度:不做要求 vs 要求简短
不做要求:输出 650 词元,3.3 秒,0.00078 美元
要求简短:输出 52 词元,0.8 秒,0.00007 美元
Nur ein zusätzlicher Satz, „antworte in höchstens drei Sätzen“, und die Ausgabe sinkt von 650 auf 52 Tokens, die Kosten auf ein Zehntel, die Wartezeit von 3,3 auf 0,8 Sekunden.
Modelle neigen standardmäßig zu umfassenden Antworten; in manchen Szenarien ist das gut, in anderen Verschwendung. Überleg, wie viel Information deine Nutzer wirklich brauchen, und schreib es in den Prompt. Man kann zusätzlich max_tokens als harte Obergrenze setzen, damit es in Einzelfällen nicht endlos weiterschreibt, muss aber bedenken, dass die Antwort dann einfach abgeschnitten wird (Modul 00, Lektion 3); die Hauptarbeit leistet also der Prompt.
3. Bei einfachen Fragen kein Denken
== 3. 简单问题开不开思考
不思考:输出 26 词元,0.7 秒,0.00004 美元 | '用 `params` 参数传字典,如 `httpx.get(url, param'
思考:输出 146 词元,1.9 秒,0.00019 美元 | '用 `params` 参数传字典或元组列表,如 `httpx.get(url, '
Bei Fragen wie „Wie übergibt man Query-Parameter?“ sind die Antworten beider Modi fast gleich; mit Denken kostete es fast das Fünffache und dauerte 1,2 Sekunden länger. Modul 02, Lektion 3 hat gezeigt: Denken lohnt sich bei Fragen mit mehrstufigem Schlussfolgern. In einer Anwendung sind die Fragen oft teils schwer, teils leicht, und man kann sie nach Art trennen: Einfache Abfragen ohne Denken, nur komplexe Analysen mit. Wie man schwer und leicht unterscheidet, lässt sich mit dem Klassifikator der nächsten Lektion gleich miterledigen.
4. Gleiche Frage, direkt die letzte Antwort zurückgeben
In vielen Anwendungen stellen Nutzer immer wieder dieselben Fragen. Statt jedes Mal das Modell aufzurufen, speichert man die Antworten:
cache = {}
def cached_ask(question):
key = hashlib.sha256(" ".join(question.lower().split()).encode()).hexdigest() # 忽略大小写和多余空格
if key in cache:
return cache[key], 0.0
text, u, _ = ask([{"role": "user", "content": question}])
cache[key] = text
return text, cost(u)
Viermal gefragt, leicht verschieden formuliert: „httpx 怎么设置代理?“ (Wie setzt man bei httpx einen Proxy?), derselbe Satz noch einmal, „HTTPX 怎么设置代理?“ (Großbuchstaben, ein Leerzeichen mehr), „httpx 怎么设置代理“ (ohne Fragezeichen).
== 4. 自己做结果缓存:同样的问题直接返回上次的答案
问了 4 次(写法略有不同),实际调用 2 次模型,共 0.00239 美元
Die ersten drei wurden als dieselbe Frage erkannt, das Modell wurde nur einmal aufgerufen. Die letzte galt wegen des fehlenden Fragezeichens als neue Frage. Die Normalisierung reicht also noch nicht: Auch Satzzeichen sollten entfernt werden. Übung 2 lässt dich sie verbessern.
Weiter gehen kann man mit den Vektor-Embeddings aus Modul 01, Lektion 5 als „semantischem Cache“: Auch Fragen mit ähnlicher Bedeutung („wie konfiguriert man einen Proxy“ und „wie richtet man einen Proxyserver ein“) treffen denselben Cache-Eintrag. Aber Vorsicht: Ähnliche Bedeutung heißt nicht gleiche Antwort; die Vektoren von „wie setzt man bei httpx ein Timeout“ und „wie setzt man bei requests ein Timeout“ liegen sehr nah beieinander.
Wann man keinen Ergebnis-Cache verwenden darf:
- Die Antwort ändert sich. Antworten, die von Echtzeitdaten oder persönlichen Daten des Nutzers abhängen, darf man nicht über Nutzer oder Zeit hinweg teilen.
- Mehrrundengespräche. Bei einer Frage wie „und asynchron?“ hängt die Antwort vom Vorherigen ab; nur diesen einen Satz kann man nicht cachen.
- Vielfalt ist erwünscht. Bei Aufgaben wie Werbetexten oder Namensfindung wollen Nutzer ohnehin unterschiedliche Ergebnisse.
Der Cache braucht eine Ablaufzeit. Wird die Dokumentation aktualisiert, sind alte Antworten womöglich veraltet.
5. Parallelität
== 5. 串行 vs 并发
10 个请求:一个一个来 9.5 秒,5 个并发 2.3 秒
10 voneinander unabhängige Anfragen nacheinander gesendet brauchen 9,5 Sekunden; 5 gleichzeitig nur 2,3 Sekunden. Die Zeit eines Modellaufrufs besteht größtenteils aus Warten auf den Server; in dieser Zeit tut dein Programm nichts und kann ohne Weiteres andere Anfragen gleichzeitig senden.
Modul 03, Lektion 4 hat gezeigt, wie man mit Thread-Pool und Semaphor die Zahl paralleler Anfragen steuert. Mehr Parallelität ist nicht immer besser: Anbieter haben Grenzen für gleichzeitige Anfragen und liefern darüber 429; bei zu viel Parallelität halten auch dein Rechner und dein Netz womöglich nicht mit.
Weitere Mittel
- Ein kleineres, billigeres Modell. Die Methode aus Modul 01, Lektion 6: auf deinem Evaluationsdatensatz vergleichen; erfüllt das billige Modell die Anforderungen, das billige nehmen. Man kann auch nach Schwierigkeit verteilen: Einfaches an ein kleines Modell, nur Schweres an ein großes.
- Spitzenzeiten meiden. Der Preis von DeepSeek in Nebenzeiten ist halb so hoch wie in Spitzenzeiten (Stand September 2026 sind Werktage 9:00–12:00 und 14:00–18:00 Pekinger Zeit Spitzenzeit). Nicht eilige Stapelaufgaben, etwa nächtliche Evaluationen oder das Abarbeiten von Rückständen, in die Nebenzeit legen.
- Unnötigen Kontext reduzieren. Holt das RAG 5 oder 3 Chunks? Behält der Gesprächsverlauf 20 oder 10 Nachrichten? Können die Tool-Rückgaben des Agenten noch kürzer sein? Jede Änderung mit dem Evaluationsdatensatz prüfen und erst ändern, wenn die Wirkung nicht nachlässt.
- Streaming-Ausgabe. Spart kein Geld, lässt es sich für Nutzer aber viel schneller anfühlen (Modul 03, Lektion 2).
Erst messen, dann ändern
Jede Optimierung dieser Lektion sollte man in der eigenen Anwendung erst messen: Wohin fließt das Geld jetzt, wohin die Zeit? Das Log der vorigen Lektion beantwortet genau das. Nach Kosten sortieren, die teuerste Art von Anfragen finden und bei ihr mit der Optimierung beginnen. Danach mit dem Evaluationsdatensatz bestätigen, dass die Qualität nicht gelitten hat. Geld sparen und dafür falsch antworten lohnt sich nicht.
Übungen
- Verbessere die Normalisierung von
cached_ask: alle Satzzeichen und Leerräume entfernen, Voll- und Halbbreitenzeichen vereinheitlichen. Führ das Experiment erneut aus: Rufen die 4 Fragen jetzt nur noch einmal das Modell auf? - Berechne mit dem Log aus Lektion 3 (
traces.jsonl), welcher Anteil der Eingabe-Tokens beim Beantworten einer Frage durch den Agenten den Cache trifft. Überleg, wie eine andere Reihenfolge der Nachrichten die Trefferquote erhöhen könnte. - Ersetze im 2. Experiment „antworte in höchstens drei Sätzen“ durch
max_tokens=60, ohne den Prompt zu ändern. Wie sieht die Antwort aus? Welche Methode ist besser?
Selbsttest
1. Warum ist bei gleicher Dokumentation und gleicher Frage „Dokumentation vorn“ so viel billiger als „Frage vorn“?
Der Cache von DeepSeek erkennt nur den übereinstimmenden Anfang einer Anfrage. Steht die Frage vorn, ist sie jedes Mal anders, also auch der Anfang, und die Dokumentation dahinter kann den Cache nicht treffen; jedes Mal wird der volle Preis berechnet. Steht die Dokumentation vorn, ist der Anfang jedes Mal gleich, ab dem zweiten Mal trifft der Cache, berechnet zu einem Fünfzigstel des Preises.
2. Wann darf man keinen Ergebnis-Cache verwenden?
Wenn sich die Antwort mit der Zeit ändert (Echtzeitdaten, Dokumentation wird aktualisiert), wenn die Antwort von persönlichen Daten des Nutzers abhängt, wenn die Frage vom Gesprächskontext abhängt (etwa „und asynchron?“) und bei Aufgaben, bei denen Nutzer ohnehin jedes Mal ein anderes Ergebnis wollen (Werbetexte, Namensfindung). Außerdem braucht der Cache eine Ablaufzeit, damit keine veralteten Antworten zurückkommen.
3. Warum verkürzen parallele Anfragen die Gesamtzeit so stark? Ist mehr Parallelität immer besser?
Den größten Teil eines Modellaufrufs wartet man auf die Antwort des Servers, und das Programm ist in dieser Zeit untätig; sendet man mehrere Anfragen gleichzeitig, überlappen sich diese Wartezeiten. Mehr ist nicht immer besser: Über der Grenze des Anbieters wird gedrosselt (429), und auch der eigene Rechner und das Netz haben eine Belastungsgrenze. Deshalb steuert man die Parallelität mit einem Semaphor oder Ähnlichem.
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…