Grenzwerte klar berechnen
Drei Zahlen bestimmen alles:
- Gesamtes Kontextfenster: 100.000 Token, inklusive der Summe aus Prompt und Completion.
max_tokenshat einen Standardwert von 2048, maximal 16.000 pro Anfrage.- Der Request-Body darf 8 MB nicht überschreiten. Das ist bei reinem Text selten das Limit; die Token sind es eher.
Die Budget-Formel ist einfach: verfügbare Prompt-Token = 100.000 - max_tokens. Wenn du das Modell 4.000 Token ausgeben lässt, bleiben für den Prompt maximal 60.000 übrig. Wird das Limit überschritten, gibt die API einen 400-Fehler zurück und triggert nicht automatisch eine Truncation. Daher ist es keine Strategie, das ganze Buch einfach hineinzustecken, sondern ein Glücksspiel.
Ein weiterer Irrtum: Je höher max_tokens, desto sicherer. Es reserviert Ausgabekapazität. 16.000 bedeuten, dass der Prompt nur 48.000 groß sein darf. Setze es bedarfsgerecht, nicht einfach auf Maximum.
Beispiel: Du hast einen 300.000-Zeichen-Interviewtext zur Zusammenfassung. Bei 1,3 Token pro Zeichen sind das ca. 390.000 Token, also mehr als das Sechsfache des Limits. Eine einzelne Eingabe passt nicht. Bei 800 Token Ausgabe pro Block bleiben maximal 63.000 für die Eingabe. Mit 10 % Puffer sind es ca. 50.000. Das ist aber nicht optimal: Bei zu großen Blöcken verliert das Modell den Fokus auf mittlere Inhalte. Besser: Blöcke kleiner halten und öfter aufrufen.
Token-Schätzung: Näherungsalgorithmus
Falls kein lokaler Tokenizer verfügbar ist, nutze die folgenden Richtwerte zur groben Schätzung. Alle Angaben sind Näherungswerte und keine exakten Werte, die Abweichung zwischen verschiedenen Texten kann 10–20 % betragen:
| Texttyp | Näherungswert |
|---|---|
| Chinesischer Fließtext | Ca. 1 bis 1,5 Token pro Zeichen |
| Englisch | Ca. 1 Token pro 4 Zeichen oder 1,3 Token pro Wort |
| Code, JSON, Symbol-lastiger Text | Verbraucht mehr Token als Normaltext. Schätze konservativ mit dem höheren Wert. |
Eine ausreichende Schätzfunktion mit Budget-Berechnung:
CONTEXT = 100000
def est_tokens(text: str) -> int:
"""粗估:中文按每字 1.3 token,其余字符按每 4 个字符 1 token。仅为近似。"""
zh = sum(1 for c in text if "\u4e00" <= c <= "\u9fff")
other = len(text) - zh
return int(zh * 1.3 + other / 4) + 1
def max_prompt_budget(max_tokens: int, margin: float = 0.1) -> int:
"""给定计划的 max_tokens,返回 prompt 最多能用多少 token(留 margin 余量)。"""
return int((CONTEXT - max_tokens) * (1 - margin))
# 例:计划让模型写 3000 token,prompt 预算
print(max_prompt_budget(3000)) # 54900Kalibrierung: Sende eine typische Eingabe als Anfrage und lies die usage.prompt_tokens aus der Antwort aus. Vergleiche sie mit deiner Schätzung, ermittle den echten Faktor für deine Daten und passe den Koeffizienten entsprechend an. Die Schätzung dient nur der Entscheidung über die Segmentierung, nicht der Abrechnung – für die Abrechnung schaue auf usage.
Zusammenfassung langer Dokumente: Segmentierung und Zusammenführung
Bei Dokumenten, die das Budget sprengen, ist Map-Reduce Standard: Zuerst segmentieren und zusammenfassen, dann die Zusammenfassungen zu einem Gesamtzusammenfassung verschmelzen. Beachte:
- An Absatzgrenzen schneiden, nicht an festen Zeichenanzahlen, um keine Sätze zu zerschneiden.
- Pro Block genug Budget einplanen. Nicht mehr als ein Drittel des Limits nutzen, um Platz für Anweisungen und Ausgabe zu lassen.
- Fordere bei der Segment-Zusammenfassung bei, dass Namen, Zahlen und Schlüsselereignisse erhalten bleiben, sonst gehen Informationen beim Verschmelzen verloren.
- Wenn die Zusammenfassung der Zusammenfassung immer noch zu lang ist, wende die Rekursion erneut an.
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.wushenchaapi.com/v1", api_key=os.environ["API_KEY"])
def split_paragraphs(text, budget):
"""按段落切块,每块估算不超过 budget token。"""
chunks, cur, used = [], [], 0
for p in text.split("\n"):
t = int(len(p) * 1.3) + 1 # 中文粗估
if used + t > budget and cur:
chunks.append("\n".join(cur))
cur, used = [], 0
cur.append(p)
used += t
if cur:
chunks.append("\n".join(cur))
return chunks
def ask(prompt, max_tokens=800):
r = client.chat.completions.create(
model="uncensored",
temperature=0.3,
max_tokens=max_tokens,
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
def summarize_long(text, budget=12000):
parts = split_paragraphs(text, budget)
partial = [ask("用不超过 200 字概括下面这段,保留人名和关键事件:\n\n" + p) for p in parts]
merged = "\n".join(f"第{i+1}部分:{s}" for i, s in enumerate(partial))
return ask("下面是分段摘要,请合并成一份 400 字以内的整体摘要:\n\n" + merged, max_tokens=900)Stelle hier temperature auf 0.3, damit Zusammenfassungsaufgaben stabil laufen. Jeder Aufruf ist unabhängig und kann parallel erfolgen. Achte aber auf das Limit von 300 Anfragen pro Minute und Schlüssel; bei einigen Dutzend Abschnitten eines Dokuments wirst du das Limit nicht erreichen.
Die Blockgröße hat keine feste Vorgabe. Erfahrungswerte liegen bei 10.000 bis 15.000 Token pro Block, um Details und Aufrufhäufigkeit auszubalancieren. Teste eine Probe mit Blockgrößen von 5.000, 12.000 und 25.000 Token. Vergleiche die Anzahl verlorener Fakten in den Zusammenfassungen, um den besten Wert zu finden. Bei hohen Informationsdichten (Verträge, Tech-Docs) kleinere Blöcke, bei redundanten Texten (Dialoge) größere.
Romanfortsetzung: Sliding Window und dynamische Gliederung
Ab Kapitel 10 passt der ganze Text nicht mehr rein und muss es auch nicht. Strategie: Zweistufiges Gedächtnis:
- Nahaussicht: Die letzten zwei bis drei Kapitel im Original, um Stil, Dialogrhythmus und Szenendetails konsistent zu halten.
- Fernblick: Frühere Kapitel als Zusammenfassung, die Figurenbeziehungen, Setup und offene Fäden bewahrt.
Füge nach jedem Kapitel eine Zusammenfassung dieses Kapitels in drei bis fünf Zeilen zur Gliederung hinzu. Wenn die Gliederung selbst 3.000 Token überschreitet, komprimiere sie insgesamt.
def continue_story(chapters, outline, new_hint, keep_last=3):
"""chapters: 已写章节列表。只带最近 keep_last 章原文,更早的用 outline(滚动大纲)代替。"""
recent = "\n\n".join(chapters[-keep_last:])
prompt = (
f"【全书大纲(早期章节摘要)】\n{outline}\n\n"
f"【最近章节原文】\n{recent}\n\n"
f"【下一章要求】\n{new_hint}\n\n请写下一章,约 2000 字。"
)
return ask(prompt, max_tokens=4000)Setup ist das Element, das am leichtesten verloren geht. Füge im Entwurf ein separates Feld „Nicht aufgelöstes Setup“ hinzu und fordere beim Fortschreiben explizit auf, in diesem Kapitel eines davon aufzulösen. Figuren- und Ortsnamen als feste Liste ins System aufnehmen, um zu verhindern, dass das Modell nach dem Verlassen des Fensters die Namen ändert.
Ein weiteres Problem bei Fortsetzungen: Stil-Drift. Das Modell orientiert sich immer stärker an den letzten Kapiteln. Wenn der Stil früherer Kapitel anders ist, wird dies verstärkt. Füge im System-Prompt eine „Stil-Probe“ hinzu: Wähle ein passendes Originaltext-Stück als festen Anker, der nicht mit dem Fenster wandert.
max_tokens und Truncation
Es gibt zwei Gründe für abgeschnittene Antworten:
finish_reasonistlength: Du hast max_tokens erreicht. Lösung: max_tokens erhöhen oder das Modell segmentieren lassen („Hier aufhören, weiter bei Befehl“).- Anfrage direkt mit 400: Prompt plus max_tokens überschreiten 100.000. Lösung: Eingabe kürzen oder max_tokens verringern.
Eine praktische Budget-Tabelle:
| Aufgabe | Empfohlenes max_tokens | Prompt verfügbar (ca.) |
|---|---|---|
| Zusammenfassung, Extraktion | 800 | 63,200 |
| Fortsetzung eines Kapitels (ca. 2000 Wörter) | 4000 | 60,000 |
| Lange Ausgabe mit 10.000 Wörtern | 16.000 (Obergrenze) | 48,000 |
Lange Ausgaben werden auch von der Antwortzeit beeinflusst. Wir empfehlen, Streaming zu nutzen, um die Ausgabe während der Generierung anzuzeigen.
Wenn die Fortsetzung abgeschnitten wird, gib den halben Text nicht einfach an das Modell weiter mit der Anweisung „Weiter“. Eine stabilere Vorgehensweise ist, den bereits generierten Teil als assistant-Nachricht in die messages-Liste zurückzunehmen und eine neue user-Nachricht hinzuzufügen: „Schreibe ab dem letzten Satz fort, wiederhole nichts“. Das ergibt die natürlichste Übergabe. Beachte, dass dies die Länge des Prompts erhöht und du das Budget neu prüfen musst.
Checkliste für lange Kontexte
- Schätze vor jeder Anfrage die Prompt-Länge mit einer Funktion. Wenn das Budget überschritten wird, wechsle zur Segmentierung.
- Notiere die usage-Daten jeder Antwort, um die Schätzfaktoren kontinuierlich zu kalibrieren.
- Platziere system-Nachrichten und Gliederungen ganz am Anfang. Wiederhole die wichtigsten Anforderungen aus den Anweisungen am Ende.
- Prüfe finish_reason. Ist dieser auf length gesetzt, löse eine Warnung aus oder starte die Fortsetzung.
- Setze eine Obergrenze für die Historie. Wenn diese überschritten wird, verwerfe die ältesten Einträge, statt auf einen 400-Fehler der API zu warten.
Weiterführende Informationen: Zur Prompt-Struktur siehe Prompt-Schreibweise, bei Fehlern zur Fehlerbehebung Handbuch zu Fehlercodes und Fehlerbehebung, zur Abrechnung zur Preisseite.
Ein wichtiger Hinweis: Ein langer Kontext bedeutet nicht ein langes Gedächtnis. Das Modell liest bei jeder Anfrage den von dir bereitgestellten Inhalt von Null. Es erinnert sich nicht an vorherige Aufrufe. Für Kontinuität musst du die Historie selbst bereitstellen.