Najpierw oblicz limity
Trzy liczby decydują o wszystkim:
- Łączny kontekst 100 000 tokenów, łącznie z promptem i completion.
max_tokensdomyślnie 2048, max na zapytanie 16 000.- Ciało zapytania nie przekracza 8 MB; limit ten rzadko jest problemem, prawdziwym ograniczeniem są tokeny.
Wzór na limit jest prosty: prompt dostępny = 100000 - max_tokens. Planujesz wygenerować 4000 tokenów? Pozostaje Ci maksymalnie 60 000 tokenów w prompcie. Przekroczenie limitu zwraca błąd 400, bez automatycznego przycinania. Wrzucenie całej książki to więc nie strategia, tylko loteria.
Kolejne błędne przekonanie: większe max_tokens to bezpieczeństwo. To rezerwacja miejsca na output; 16 000 oznacza tylko 48 000 na prompt. Ustawiaj rozsądnie, nie ustawiaj na max.
Przykład: masz 300 000 słów wywiadu do streszczenia. Przy 1,3 tokena/słowo to ok. 390 000 tokenów, czyli ponad sześciokrotność limitu. Nie wyślesz tego całościowo. Przy outputcie 800 tokenów, blok wejściowy to max 63 000, z rezerwą ok. 50 000. To jednak nie jest optymalne — duże bloki słabiej skupiają się na środku. Lepiej podzielić bloki i wywołać model kilka razy więcej.
Szacowanie tokenów: algorytm przybliżony
Bez lokalnego tokenizera użyj przybliżonych wartości. Wartości są przybliżone, nie dokładne, różnice mogą wynosić 10-20%:
| Typ tekstu | Przybliżone przeliczenie |
|---|---|
| Chiński tekst literacki | Szacunkowo 1–1,5 tokena na słowo/znak |
| Angielski | Ok. 1 token na 4 znaki lub 1,3 tokena na słowo |
| Kod, JSON, tekst z symbolami | Więcej tokenów niż tekst zwykły; szacuj z góry. |
Funkcja szacująca z rezerwą:
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)) # 54900Kalibracja: wyślij próbkę, odczytaj usage.prompt_tokens, oblicz współczynnik dla swoich danych. Szacuj podział, do rozliczeń używaj usage.
Podsumowanie długich dokumentów: podział i scalanie
Przekroczenie budżetu? Użyj map-reduce: podsumuj bloki, połącz wyniki. Uwaga:
- Dziel po granicach akapitów, nie na sztywno, by nie urywać zdań.
- Zostaw rezerwę na instrukcje i output; blok nie powinien przekraczać 1/3 limitu.
- Wymagaj zachowania imion, liczb i kluczowych faktów w podsumowaniach.
- Jeśli podsumowanie podsumowania jest wciąż za długie, zastosuj rekurencję.
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)Ustaw temperature na 0,3 dla stabilności. Zapytania są niezależne i mogą być równoległe, ale pamiętaj o limicie 300 zapytań na minutę dla klucza — przy dokumentach kilkunastu stron nie przekroczysz tego.
Nie ma idealnego rozmiaru bloku. Doświadczenie mówi: 10 000–15 000 tokenów balansuje między szczegółami a liczbą wywołań. Przetestuj próbki z blokami 5000, 12 000 i 25 000 tokenów, porównaj liczbę pominiętych faktów kluczowych i wybierz najlepszą wartość. Dokumenty o wysokiej gęstości informacji (umowy, specyfikacje) wymagają mniejszych bloków; zapisy dialogowe z dużą nadmiarowością mogą być większe.
Kontynuacja powieści: okno przesuwne i rosnący zarys
Po kilkunastu rozdziałach cała książka nie zmieści się ani nie jest potrzebna. Użyj dwupoziomowej pamięci:
- Bliski kontekst: ostatnie 2–3 rozdziały oryginału, zapewniają spójność stylu, rytmu dialogów i szczegółów sceny.
- Daleka perspektywa: Wcześniejsze rozdziały skompresowane do streszczenia, zachowujące relacje między postaciami, zapowiedzi i nierozwiązane wątki.
Po każdym rozdziale poproś model o streszczenie go do 3–5 zdań i dodaj do outline. Jeśli outline przekroczy 3000 tokenów, skompresuj go całościowo.
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)Najłatwiej zgubić zapowiedzi. Zalecamy dodanie w streszczeniu osobnego pola „niezrealizowane zapowiedzi” i w trakcie kontynuowania wyraźne wskazanie, która z nich ma zostać poruszona w tym rozdziale. Imiona postaci i nazwy miejsc umieść w stałej liście w system prompt, aby uniknąć sytuacji, w której model wymyśla nowe nazwy po wyjściu z okna kontekstu.
Podczas kontynuowania tekstu często pomija się problem zmiany stylu. Model ma tendencję do coraz większego dopasowania się do ostatnich rozdziałów, co może prowadzić do niespójności, jeśli styl wczesnych fragmentów różni się od późniejszych. Rozwiązaniem jest dodanie w system prompt fragmentu „próbki stylu” — wybierz jeden z najlepszych oryginalnych akapitów i użyj go jako stałego kotwicy, niezależnie od przesuwania się okna kontekstu.
max_tokens i ucinanie
Rozróżnij dwa przypadki ucinania odpowiedzi:
- Gdy
finish_reasontolength: osiągnąłeś limit max_tokens. Rozwiązanie: zwiększ max_tokens lub poproś o podział outputu: „napisz tutaj i zatrzymaj się, kontynuuj, gdy powiem”. - Błąd 400: prompt + max_tokens > 100 000. Rozwiązanie: skróć input lub zmniejsz max_tokens.
Tabela budżetowa:
| Zadanie | Sugerowane max_tokens | dostępny prompt (szacunkowo) |
|---|---|---|
| streszczenie, ekstrakcja | 800 | 63,200 |
| kontynuacja rozdziału (ok. 2000 słów) | 4000 | 60,000 |
| Długie odpowiedzi | 16000 (limit) | 48,000 |
Długie odpowiedzi zależą też od czasu odpowiedzi, dlatego zalecamy użycie strumieniowania, aby wyświetlać tekst w miarę jego generowania.
Gdy kontynuacja zostanie ucięta, nie wrzucaj połowy treści z komendą „kontynuuj”. Bezpieczniej jest wstawić wygenerowaną treść jako wiadomość assistant, a następnie user: „kontynuuj od ostatniego zdania, nie powtarzaj”. To zapewnia płynne przejście. Pamiętaj, że to wydłuży prompt i wymaga ponownego sprawdzenia budżetu.
Checklista długiego kontekstu
- Przed każdym zapytaniem oblicz wielkość promptu za pomocą funkcji szacującej; jeśli przekraczasz limit, przejdź do gałęzi z podziałem na fragmenty.
- Za każdym razem zapisuj usage z odpowiedzi i stale kalibruj współczynniki szacunkowe.
- Umieść system i plan na początku treści, a kluczowe wymagania z instrukcji powtórz na samym końcu.
- Sprawdzaj finish_reason; jeśli wynosi length, wygeneruj alert lub kontynuację.
- Ustaw limit zachowania historii rozmów; gdy zostanie przekroczony, usuń najstarsze wiadomości, nie czekaj na błąd 400 od interfejsu.
Zobacz: Jak pisać prompt, Przewodnik rozwiązywania błędów i kody błędów API, Cennik.
Pamiętaj o jednej ważnej rzeczy: długi kontekst to nie to samo co długa pamięć. Model odczytuje dostarczoną treść od zera przy każdym zapytaniu i nie pamięta niczego z poprzednich wywołań; jeśli zależy Ci na ciągłości, sam musisz dostarczyć historię.