Calcola prima i limiti
Tre numeri decidono tutto:
- Finestra di contesto totale 100.000 token, somma prompt e completion.
max_tokensè 2048 di default, max 16.000 per richiesta.- Il corpo della richiesta non supera 8 MB: questo limite si tocca raramente con testo puro, il vero limite sono i token.
La formula del budget è semplice: prompt utilizzabile = 100000 - max_tokens. Se vuoi 4.000 token di output, il prompt ha al massimo 60.000. Oltre il limite l'API restituisce 400, non tronca. Non è strategia, è fortuna.
Un altro errore comune: più max_tokens è alto, più è sicuro. In realtà è spazio riservato all'output: impostare 16.000 significa che il prompt può essere solo 48.000. Imposta in base alle esigenze, non mettere il massimo a caso.
Esempio: riassumere 300.000 caratteri. Stimando 1,3 token/carattere servono ~390.000 token, oltre sei volte il limite. Non puoi inviarlo tutto. Per un output di 800 token, il blocco input è max 63.000, meno il 10% di margine: ~50.000. Meglio blocchi più piccoli per mantenere l'attenzione del modello.
Stima token: algoritmo approssimato
Senza tokenizer locale, usa questi valori approssimati. Sono stime, non valori esatti, con variazioni fino al 20%:
| Tipo di testo | Conversione approssimata |
|---|---|
| Testo cinese | Circa 1-1,5 token per carattere |
| Inglese | Circa 1 token ogni 4 caratteri, o 1,3 token per parola |
| Codice, JSON, testo denso di simboli | Consuma più token del testo normale; stima con valori conservativi alti |
Una funzione di stima sufficiente è la seguente, con calcolo del budget che include un margine di sicurezza:
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)) # 54900Calibrazione: invia una richiesta con un input tipico, leggi usage.prompt_tokens nella risposta, confrontalo con la tua stima e calcola il moltiplicatore reale per i tuoi dati. Usa la stima solo per decidere se segmentare, non per il conteggio finale (usa usage).
Riassunto documenti lunghi: segmenta e unisci
Quando il documento supera il budget, usa map-reduce: riassumi i blocchi separatamente, poi unisci i riassunti. Attenzione a questi dettagli:
- Taglia ai confini dei paragrafi, non a lunghezza fissa, per non tagliare le frasi a metà.
- Lascia budget sufficiente per ogni blocco: non superare un terzo del limite, per lasciare spazio a prompt e output.
- Chiedi di preservare nomi, numeri ed eventi chiave nei riassunti, altrimenti perdi informazioni durante l'unione.
- Se il riassunto del riassunto è ancora troppo lungo, ricorsione aggiuntiva.
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)Imposta temperature a 0,3 per stabilità. Le chiamate sono indipendenti e possono essere parallele. Attenzione al limite di 300 richieste al minuto per chiave: per documenti con decine di blocchi, non è un problema.
Non c'è una dimensione standard per i blocchi. In media, 10.000-15.000 token bilanciano dettaglio e numero di chiamate. Testa blocchi da 5000, 12000 e 25000 su un campione e confronta i fatti chiave persi. Per documenti densi (contratti), usa blocchi più piccoli; per dialoghi ridondanti, blocchi più grandi.
Continuazione di romanzi: finestra scorrevole con outline dinamico
Dopo una decina di capitoli, il testo intero non ci sta ed è inutile. Usa una memoria a due livelli:
- Contesto recente: gli ultimi due o tre capitoli originali per garantire coerenza di stile, ritmo dialogico e dettagli di scena.
- Lungimiranza:Capitoli passati in outline per trama e personaggi.
Dopo ogni capitolo, chiedi al modello di riassumerlo in 3-5 righe e aggiungilo all'outline. Se l'outline supera 3000 token, comprimi tutto.
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)Il foreshadowing è facile da perdere. Crea una sezione "Foreshadowing non risolto" nell'outline e chiedi di gestirne uno a capitolo. Elencare nomi e luoghi in un system prompt evita che il modello cambi nomi uscendo dalla finestra.
Un altro problema: deriva dello stile. Il modello tende a imitare gli ultimi capitoli. Se lo stile cambia, l'effetto si amplifica. Aggiungi un "campione di stile" nel system prompt: un estratto del tuo testo preferito, usato come ancora fissa che non scorre con la finestra.
max_tokens e troncamento
Ci sono due casi di risposta troncata da distinguere:
finish_reasonèlength: hai raggiunto il max_tokens impostato. Soluzione: aumenta max_tokens o chiedi al modello di scrivere a blocchi ("fermati qui, continua se dico io").- Errore 400: prompt + max_tokens supera 100.000. Soluzione: riduci l'input o abbassa max_tokens.
Un utile riferimento per il budget:
| Attività | max_tokens consigliato | prompt disponibile (circa) |
|---|---|---|
| riassunti, estrazione | 800 | 63,200 |
| Continuazione di un singolo capitolo (circa 2000 caratteri) | 4000 | 60,000 |
| output lungo di migliaia di parole | 16.000 (limite massimo) | 48,000 |
Gli output lunghi sono influenzati anche dal "tempo di risposta"; si consiglia di usare lo streaming per visualizzare il testo mentre viene generato.
Quando la continuazione viene troncata, non limitarti a inviare al modello il testo a metà dicendo "continua". Un approccio più stabile consiste nell'inserire la parte già generata come messaggio assistant nella lista messages, aggiungendo poi un messaggio user con il testo "continua dalla fine dell'ultima frase senza ripetere". Questo garantisce la transizione più naturale. Tieni presente che questa procedura allunga il prompt, quindi ricalcola il budget.
Checklist per i contesti lunghi
- Prima di ogni richiesta, calcola la lunghezza del prompt con una funzione di stima; se superi il budget, passa al branch di suddivisione.
- Registra l'usage per ogni risposta e calibra continuamente il coefficiente di stima.
- Posiziona system e la struttura all'inizio; ripeti i requisiti chiave dell'istruzione alla fine.
- Verifica il finish_reason: se è length, genera un alert o una continuazione.
- Imposta un limite massimo alla cronologia delle conversazioni; quando viene superato, scarta i messaggi più vecchi invece di attendere un errore 400 dall'endpoint.
Argomenti correlati: struttura del prompt in Guida alla scrittura del prompt, risoluzione errori in Manuale di troubleshooting dei codici di errore, prezzi in Pagina dei prezzi.
Un ultimo promemoria: un contesto lungo non equivale a una memoria a lungo termine. Il modello legge da zero ogni contenuto che gli fornisci in ogni richiesta e non ricorda nulla delle chiamate precedenti; se ti serve continuità, devi includere tu la cronologia.