Calcule o limite com clareza
Três números determinam tudo:
- O total de contexto é de 100.000 tokens, incluindo prompt e completion.
max_tokensé 2048 por padrão e 16.000 no máximo por requisição.- O corpo da requisição não deve exceder 8 MB; esse limite raramente é atingido para texto puro, o que realmente chega ao topo são os tokens.
A fórmula do orçamento é simples: prompt disponível = 100000 - max_tokens. Se você planeja 4.000 tokens de saída, o prompt tem no máximo 60.000. O endpoint retorna 400 se exceder, sem auto-truncation. Não é estratégia, é aposta.
Outro equívoco comum: quanto maior o max_tokens, mais seguro. Na verdade, ele reserva espaço de saída; definir 16.000 significa que o prompt terá apenas 48.000. Defina conforme a necessidade, não maximize cegamente.
Exemplo: suponha um texto de entrevista de 300 mil caracteres para resumir. Estimando 1,3 token por caractere, são ~390 mil tokens, mais de seis vezes o limite. Não cabe tudo. Se a saída for 800 tokens, cada bloco de entrada pode ter no máximo 63.000, mas reserve 10% de margem, sobrando ~55 mil. Isso ainda não é ideal; blocos grandes reduzem o foco no meio. A melhor abordagem é dividir em blocos menores, mesmo que exija mais chamadas.
Estimativa de tokens: algoritmo aproximado
Sem tokenizador local, use estes valores aproximados. Valores aproximados, não exatos, com variação de 10-20%:
| Tipo de texto | Conversão aproximada |
|---|---|
| Texto chinês | Cerca de 1 a 1,5 token por caractere |
| Inglês | Cerca de 1 token por 4 caracteres ou 1,3 token por palavra |
| Código, JSON e texto denso em símbolos | Consome mais tokens que texto comum; estime pelo valor alto conservador |
Uma função de estimativa útil com cálculo de orçamento com margem:
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)) # 54900Calibração: envie uma entrada típica, leia o usage.prompt_tokens na resposta, compare com sua estimativa e ajuste o coeficiente para sua realidade. Use a estimativa para decidir “se deve segmentar”, não para conferência; a conferência usa o usage.
Resumo de documentos longos: segmentação e fusão
Quando o documento excede o orçamento, use map-reduce: resuma blocos separadamente e fusione os resumos. Atenção aos detalhes:
- Corte nas bordas dos parágrafos, não por tamanho fixo, para não quebrar frases.
- Reserve orçamento por bloco; sugere-se não exceder um terço do limite para deixar espaço para instruções e saída.
- Instrua os resumos a manter nomes, números e eventos-chave, ou a informação se perde na fusão.
- Se o resumo do resumo ainda for longo, faça uma recursão.
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)Use temperature 0.3 para estabilidade. Chamadas independentes permitem requisições simultâneas. Limite de 300 requisições por minuto por chave raramente é atingido com dezenas de blocos.
Tamanho ideal: 10k-15k tokens. Teste amostras com blocos de 5000, 12000 e 25000 tokens para comparar perda de fatos. Documentos densos exigem blocos menores; diálogos redundantes permitem maiores.
Continuação de romances: janela deslizante e esboço contínuo
Ao chegar ao décimo capítulo, o texto inteiro não cabe e não precisa. Use duas memórias:
- Foco recente: os últimos 2-3 capítulos do original para manter coerência de estilo, ritmo de diálogo e detalhes de cena.
- Longo prazo:capítulos anteriores resumidos em esboço, mantendo relacionamentos, ganchos e pistas.
Resuma cada capítulo em 3-5 linhas para o esboço. Se o esboço passar de 3000 tokens, comprima-o.
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)Sugere-se criar uma coluna 'plotlines pendentes' no esboço para garantir que a continuação contínua resolva um gancho por capítulo. Liste nomes e locais no system prompt para evitar mudanças.
Outro problema na continuação é a deriva de estilo. O modelo tende a imitar os capítulos recentes. Se o estilo inicial for diferente, isso se amplifica. Adicione uma “amostra de estilo” no system prompt como âncora fixa, independente da janela.
max_tokens e truncamento
Existem duas situações de resposta truncada:
finish_reasonélength: você atingiu o max_tokens. Aumente o valor ou peça ao modelo para escrever em partes (“pare aqui, continue quando eu pedir”).- Erro 400 direto: prompt + max_tokens excedeu 100.000. Reduza o input ou diminua o max_tokens.
Tabela de orçamento prática:
| Tarefa | max_tokens sugerido | prompt disponível (aprox.) |
|---|---|---|
| resumo, extração | 800 | 63,200 |
| Continuação de capítulo único (aprox. 2000 palavras) | 4000 | 60,000 |
| Saída longa de nível de 10 mil caracteres | 16000 (limite superior) | 48,000 |
Saídas longas também são afetadas pelo “tempo de resposta”. Recomendamos usar streaming para exibir o conteúdo conforme é gerado.
Quando a continuação for interrompida, não envie apenas o trecho cortado ao modelo dizendo “continue”. Uma abordagem mais estável é incluir o trecho já gerado como mensagem do assistant na lista de messages e adicionar uma mensagem do user “continue a partir da última frase, sem repetir”. Isso garante a transição mais natural. Observe que isso aumenta o tamanho do prompt, então você deve recalcular o orçamento.
Lista de verificação para contextos longos
- Antes de cada requisição, use uma função de estimativa para calcular o tamanho do prompt. Se ultrapassar o orçamento, siga o fluxo de segmentação.
- Registre o uso em cada resposta para calibrar o coeficiente de estimativa.
- Coloque o system e o outline no início do conteúdo. Reitere os requisitos-chave das instruções no final.
- Verifique o finish_reason. Se for length, emita um alerta ou realize a continuação.
- Defina um limite para o histórico. Ao atingir o limite, descarte as mensagens mais antigas explicitamente, não espere o erro 400.
Conteúdo relacionado: estrutura do prompt em Como escrever prompts, tratamento de erros em Manual de solução de erros, precificação em Página de preços.
Uma última observação: contexto longo não significa memória longa. O modelo lê o conteúdo que você fornece do zero a cada requisição e não lembra de nada sobre chamadas anteriores. Para manter a continuidade, você deve fornecer o histórico.