繁中 ▾
取得 API 金鑰

100k 長上下文實戰:續寫、總結、分段、預算

上下文 100,000 token 聽起來很寬裕,真用起來才發現:一部長篇小說放不進去,一份幾十頁的文件加上要求的輸出也會逼近上限。這篇把長上下文當成一筆預算來管理:怎麼粗估 token,怎麼給輸出留位置,長文怎麼分段總結,小說怎麼用滑動視窗加滾動大綱續寫。程式碼用 Python,思路適用於任何語言。

更新於

重點

  • 100,000 是提示詞加上補全的總和,不是單獨的輸入上限。
  • 中文 token 數只能粗估,約每字 1 到 1.5 個,最終以回應裡的 usage 為準。
  • 長文總結用分段加合併,小說續寫用最近章節原文加滾動大綱。
  • max_tokens 預設 2048、最大 16,000,寫長內容前先算預算再設定。

先把上限算清楚

三個數字決定一切:

  • 上下文總量 100,000 token,提示詞與補全合計。
  • max_tokens 預設 2048,單次請求最大 16,000。
  • 請求體不超過 8 MB,這個上限對純文本基本碰不到,真正先到頂的是 token。

預算公式很簡單:prompt 可用 = 100000 - max_tokens。你打算讓模型一次輸出 4000 token,prompt 最多就只剩 60,000。超出時接口返回 400,不會替你自動截斷。所以「把整本書塞進去再說」不是策略,只是賭運氣。

另一個常見誤解:max_tokens 設得越大越保險。其實它是預留的輸出空間,設 16,000 就意味著提示詞只能有 48,000。按需設定,別無腦拉滿。

舉個算帳的例子,假設你有一份 30 萬字的訪談整理稿要總結。按每字 1.3 token 粗估,約 39 萬 token,是上限的六倍多,無論如何都不能整份送進去。再假設單次摘要要 800 token 的輸出,那每塊輸入最多 63,000,實際再留一成餘量,也就五萬多。但這還不是最優解,塊太大時模型對中段內容的關注會變弱,所以下面的做法是把塊切小,寧可多調幾次。

token 估算:近似算法

沒有本地分詞器時,可以用下面的經驗值做粗估。以下均為近似,並非精確值,不同文本差異可達一到兩成:

文本類型近似換算
中文正文約每字 1 到 1.5 token
英文約每 4 個字元 1 token,或每詞 1.3 token 左右
程式碼、JSON、符號密集文本比普通文本更耗 token,建議按偏保守的高值估

一個夠用的粗估函式如下,並帶上留餘量的預算計算:

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))   # 54900

校準方法:隨便取一段典型輸入發一次請求,讀響應裡的 usage.prompt_tokens,和你的估算對比,算出自己業務資料的真實倍率,然後把係數改成實測值。估算只用來決定「要不要分段」,不用來對帳,對帳看 usage。

長文件總結:分段加合併

文件超過預算時,標準做法是 map-reduce:先分段各自概括,再把概括合併成總摘要。注意以下細節:

  1. 按段落邊界切,不要按固定字數硬切,否則會把句子攔腰截斷。
  2. 每塊預算留足,建議不超過上限的三分之一,給指令和輸出留空間。
  3. 分段摘要要求保留人名、數字、關鍵事件,否則合併時資訊已經丟了。
  4. 摘要的摘要仍然太長時,再遞歸一層。
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)

這裡 temperature 取 0.3,總結類任務要穩定。每段呼叫互相獨立,可以並行,但注意每把金鑰每分鐘 300 次請求的限制,幾十段的文件完全不會碰到。

區塊大小無標準答案。經驗上,每塊一萬至一萬五千 token 可兼顧細節與呼叫次數。取樣本以 5000、12000、25000 三種大小測試,比較摘要遺漏關鍵事實數量,即可選出適合文件類型的值。資訊密度高的合約或技術文件需更小區塊;冗餘多的對話記錄則可更大。

小說續寫:滑動視窗加滾動大綱

寫到第十幾章時,全文已經放不下,也沒必要全放。思路是兩層記憶:

  • 近景:最近兩三章原文,保證文風、對話節奏和場景細節連貫。
  • 遠景:更早章節壓縮成大綱,保留人物關係、伏筆和未解決的線索。

每寫完一章,就讓模型把這一章概括成三五行追加進大綱;大綱本身超過 3000 token 時,再整體壓縮一次。

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)

伏筆是最容易丟的東西。建議在大綱裡單列「未回收的伏筆」一欄,續寫時明確要求本章處理其中一條。人名、地名也列成固定清單放在 system 裡,避免滑出視窗後模型自己改名。

續寫時還有一個容易被忽視的問題:風格漂移。模型會越寫越貼近最近幾章,如果前面章節的風格和後來不一致,就會放大。辦法是在 system 裡寫一段「文風樣本」,挑一段你最滿意的原文放進去,作為固定錨點,不隨視窗滑動。

max_tokens 與截斷

回答被截斷有兩種情形,要分清:

  1. finish_reason 為 length:撞上了你設定的 max_tokens。解決辦法是調大 max_tokens,或者讓模型分段寫,「寫到這裡先停,我說繼續再寫」。
  2. 請求直接 400:提示詞加 max_tokens 已超過 100,000。解決辦法是縮短輸入或調小 max_tokens。

一個實用的預算對照:

任務建議 max_tokens提示詞可用(約)
摘要、抽取80063,200
單章續寫(約 2000 字)400060,000
萬字級長輸出16000(上限)48,000

長輸出同時受「回應時間」影響,建議搭配串流輸出,邊生成邊顯示。

續寫被截斷時,不要簡單地把半截內容丟給模型說「繼續」。更穩的做法是把已生成的部分作為 assistant 訊息放回 messages,再加一條 user 訊息「從上一句話之後接著寫,不要重複」,這樣銜接最自然。注意這一步會讓提示詞變長,要重新核對預算。

長上下文上線清單

  • 每次請求前用估算函數算提示詞,超預算就走分段分支。
  • 每次回應記錄 usage,持續校準估算係數。
  • system 與大綱放在內容最前,指令裡的關鍵要求在最後重申一遍。
  • 檢查 finish_reason,是 length 就告警或續寫。
  • 歷史對話設保留上限,超限時丟棄最舊的,不要等介面報 400。

相關內容:提示詞結構見 Prompt 寫法,報錯處理見錯誤碼排障手冊,計價見價格頁。

最後提醒一點:長上下文不等於長記憶。模型每次請求都是從零讀你給的內容,不會記得上一次呼叫的任何事,需要連續性就必須你自己把歷史帶上。

常見問題

100k 上下文包含輸出嗎?

包含。上限是提示詞和 completion 的總和,所以 max_tokens 設得越大,能放进去的輸入就越少。

中文一個字到底是幾個 token?

只能粗估,大致每字 1 到 1.5 個,隨文本不同有浮動。最準確的辦法是讀回應裡的 usage 欄位,用實測值校準。

超過上限會自動截斷輸入嗎?

不會,請求會返回 400。需要你在請求前自己分段或丟棄舊內容。

單次最多能生成多長?

max_tokens 預設 2048,單次最大 16,000。更長的內容請分多次生成,並用大綱或摘要保持連貫。

只需填寫表單即可取得金鑰

建立帳戶,複製金鑰,修改 Base URL。設定就是這麼簡單。