Bereken eerst de limiet
Drie getallen bepalen alles:
- Context totaal 100.000 tokens, prompt en completion samen.
max_tokensis standaard 2048, max 16.000 per verzoek.- Request body is maximaal 8 MB; deze limiet raak je bij pure tekst zelden, tokens zijn de echte bottleneck.
Budgetformule: prompt beschikbaar = 100000 - max_tokens. Je wilt 4000 tokens output, prompt is max 60.000. Bij overschrijding retourneert de API 400, geen automatische truncatie. 'Boek erin proppen' is gokken.
Een veelvoorkomend misverstand: hoe hoger de max_tokens, hoe veiliger. Het is echter gereserveerde outputruimte; 16.000 betekent dat de prompt maximaal 48.000 tokens mag zijn. Stel in op basis van behoefte, niet op maximum.
Stel je voor: je moet een interview van 300.000 woorden samenvatten. Geschat 390.000 tokens (×1,3), meer dan zes keer de limiet. Bij een gewenste output van 800 tokens per segment is de inputlimiet 63.000; met 10% buffer is dat ongeveer 50.000. Maar dit is niet optimaal: bij grote segmenten verzwakt de model attention voor de middelste inhoud. De betere aanpak is kleinere segmenten, liever vaker aanroepen.
Token-schatting: benaderende algoritme
Zonder lokale tokenizer: gebruik deze schattingen. Dit zijn benaderingen, geen exacte waarden. Verschil kan 10-20% bedragen:
| Teksttype | Benaderde conversie |
|---|---|
| Chinese tekst | Ongeveer 1 tot 1,5 token per karakter |
| Engels | Ongeveer 1 token per 4 tekens of 1,3 token per woord |
| Code, JSON, symbolen | Vereist meer tokens dan normale tekst. Hanteer een conservatieve schatting. |
Een bruikbare schattingsfunctie met budgetberekening:
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)) # 54900Kalibratie: stuur een typische input, lees usage.prompt_tokens uit de response. Vergelijk met je schatting om de echte factor te bepalen. Schatting bepaalt alleen 'segmenteren of niet'.
Samenvatting van lange documenten: segmenteren en samenvoegen
Bij overschrijding van het budget is map-reduce de standaard: segmenteer en vat elk deel samen, voeg de samenvattingen samen tot een totaal. Let op:
- Snij bij alinea-grenzen, niet bij vast aantal karakters, om zinnen niet af te breken.
- Reserveer voldoende budget per segment (max 1/3 van de limiet) voor instructies en output.
- Vraag bij segment-samenvattingen om behoud van namen, cijfers en sleutelgebeurtenissen, anders gaat informatie verloren bij het samenvoegen.
- Als de samenvatting van de samenvattingen nog te lang is, pas dan een extra recursie toe.
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)Gebruik temperature 0,3 voor stabiliteit. Segmenten zijn onafhankelijk en kunnen parallel worden verwerkt. Let op de rate limit van 300 verzoeken per minuut per key; dit is zelden een probleem voor documenten met tientallen segmenten.
Geen vaste maat voor segmentgrootte. 10.000-15.000 tokens per segment balanceert detailbehoud en aantal calls. Test een sample met blokmaten van 5000, 12000 en 25000 tokens en vergelijk het aantal gemiste feiten in de samenvatting om de beste maat voor jouw documenttype te kiezen. Hoge informatiedichtheid (contracten, tech docs) vereist kleinere blokken; redundante data (chatlogs) mag groter.
Romanvervolging: sliding window en dynamisch overzicht
Na tientallen hoofdstukken past de volledige tekst niet meer en is dat ook niet nodig. Gebruik tweeledige herinnering:
- Korte termijn: de laatste 2-3 hoofdstukken voor consistentie in stijl, dialoog en details.
- Lange termijn: eerdere hoofdstukken samengevat tot een overzicht met personages, plotlijnen en openstaande verhaallijnen.
Samenvat na elk hoofdstuk tot drie regels en voeg toe aan de outline. Als de outline zelf meer dan 3000 tokens overschrijdt, comprimeer dan de hele outline.
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)Foreshadowing gaat het makkelijk verloren. Voeg een vaste sectie 'niet-afgehandelde foreshadowing' toe aan de outline en eis dat elk hoofdstuk er één afhandelt. Zet namen en locaties in een vaste lijst in de system prompt om te voorkomen dat het model namen wijzigt zodra ze uit het venster vallen.
Een vaak over het hoofd gezien probleem is stijlverandering: het model imiteert de laatste hoofdstukken, wat inconsistenties versterkt. Los dit op door een 'writing style sample' in de system prompt te plaatsen: een stuk tekst dat je perfect vindt, als vaste ankerpunt dat niet meeschuift met het venster.
max_tokens en afkapping
Er zijn twee redenen waarom een antwoord wordt afgekapt:
finish_reasonislength: je hebt de ingestelde max_tokens bereikt. Verhoog max_tokens of laat het model segmenteren ('stop hier, ga door als ik zeg').- Directe 400-fout: prompt plus max_tokens overschrijdt 100.000. Verkort de invoer of verlaag max_tokens.
Praktische budgetvergelijking:
| Taak | Aanbevolen max_tokens | prompt beschikbaar (ca.) |
|---|---|---|
| samenvatting, extractie | 800 | 63,200 |
| hoofdstuksgewijs doorgaan (ca. 2000 woorden) | 4000 | 60,000 |
| lange uitvoer van tienduizenden woorden | 16.000 (maximaal) | 48,000 |
Lange outputs worden ook beïnvloed door response time; gebruik streaming om output direct te tonen tijdens het genereren.
Wanneer de voortzetting wordt afgekapt, geef de halve tekst dan niet zomaar aan het model met de opdracht "ga door". Een robuustere aanpak is om het gegenereerde deel terug te plaatsen als assistant-bericht in de messages, en daarachter een user-bericht toe te voegen met "schrijf verder na de vorige zin, zonder herhaling". Zo verloopt de overgang het natuurlijkst. Houd er rekening mee dat dit de prompt langer maakt; controleer opnieuw of je budget nog volstaat.
Checklist voor lange context
- Bereken voor elk verzoek de promptgrootte. Overschrijd je het budget, ga dan naar de segmented branch.
- Log bij elke respons het usage-gebruik om de schattingscoëfficiënten continu te kalibreren.
- Plaats de system-prompt en het overzicht vooraan. Herhaal de belangrijkste instructies uit de prompt aan het einde.
- Controleer de finish_reason. Is deze 'length', geef dan een waarschuwing of ga door met voortzetten.
- Stel een limiet in voor de conversation history; verwijder bij overschrijding de oudste berichten voordat de API 400 retourneert.
Zie ook: Prompt Writing voor prompt structuur, Error Code Troubleshooting Manual voor fouten, Pricing voor prijzen.
Let op: lang contextvenster is geen lang geheugen. Het model leest bij elk verzoek alles opnieuw van nul; het onthoudt niets van vorige calls. Voor continuiteit moet jij de conversation history zelf meesturen.