第3章 Token 与上下文窗口¶
学习目标¶
- 理解 Token 的概念与 BPE 分词原理
- 掌握上下文窗口的含义及其对 Agent 的影响
- 区分 Chat Completions API 与 Responses API
- 学会使用 tiktoken 估算 Token 数量与成本
- 掌握长上下文管理的工程化策略
问题导入¶
"为什么我的 Prompt 明明只有 1000 字,API 却报错说超出上下文?""为什么模型会忘记我前面说过的话?"——这两个高频问题背后,都指向同一个核心概念:Token 与上下文窗口。LLM 不以"字"为单位处理文本,而是以 Token 为最小单元;模型的记忆容量也由上下文窗口大小决定。对于 Agent 开发者而言,Token 直接影响成本、延迟、能力上限:一次调用可能因为上下文膨胀而消耗数倍预算,也可能因为关键信息被截断而导致任务失败。本章将揭开 Token 的底层机制,并给出实用的上下文管理策略。
核心概念¶
1. Token 与 BPE 分词¶
Token 是 LLM 处理文本的最小单元,介于"字"与"词"之间。OpenAI 模型使用 Byte Pair Encoding(BPE) 分词:先初始化为单字节,再统计高频字节对合并为新 token,迭代直到词表规模达到上限(如 GPT-4 的 cl100k_base 词表约 10 万 token)。
- 英文:约 1 token ≈ 4 个字符 ≈ 0.75 个单词。
- 中文:一个汉字通常占 1-3 个 token(视模型与字频而定)。
- 代码、emoji 的 token 消耗显著更高。
2. 上下文窗口(Context Window)¶
上下文窗口是模型在单次推理中能处理的最大 token 数,包含输入与输出。常见模型上下文长度: - GPT-4o / GPT-4.1:128K - Claude 3.5 Sonnet:200K - Gemini 1.5 Pro:1M-2M - Qwen2.5-Turbo:1M
注意:上下文长度 ≠ 有效记忆长度。研究表明,当关键信息位于超长上下文中间时,模型可能出现"中间迷失(Lost in the Middle)"现象。
3. Chat Completions API vs Responses API¶
- Chat Completions API:OpenAI 经典对话接口,输入为
messages列表,无状态,每次需传入完整历史。结构稳定,兼容性最广。 - Responses API:OpenAI 2025 年推出的新一代接口,原生支持工具调用、文件搜索、计算机使用等内置能力,并可在服务端维护会话状态,减少重复传输。适合构建 Agent。
4. Token 计费机制¶
LLM 按 token 计费,通常分输入 token 与输出 token 两档(输出价格往往是输入的 3-5 倍)。还需注意: - 缓存输入(Cached Input):复用前缀可享大幅折扣(OpenAI 缓存命中可降 50%-90%)。 - 工具调用开销:工具定义、返回结果均计入输入 token。 - 系统提示:长 system prompt 在多轮对话中会重复计费。
5. 上下文管理策略¶
- 摘要压缩:将早期对话总结为简短摘要,节省 token。
- 滑动窗口:仅保留最近 N 轮对话。
- 向量检索:把历史存入向量库,按需取回相关片段(RAG 思路)。
- 状态外置:用 Responses API 的服务端会话或外部存储维护状态,避免每次重传。
6. 长上下文失效模式¶
- 截断:超出窗口时,最早的内容被丢弃。
- 稀释:信息过多导致模型"抓不住重点"。
- 遗忘:关键指令被中间大量文本淹没。
原理图¶
flowchart LR
Text[原始文本\n我爱AI Agent] --> BPE1[BPE分词]
BPE1 --> Tokens[Token序列\n我/爱/AI/ Agent]
Tokens --> EmbedTok[Token Embedding]
EmbedTok --> Model[LLM推理]
Model --> OutTokens[输出Token]
OutTokens --> Detok[解码]
Detok --> Result[最终文本]
subgraph 上下文窗口
SysTok[系统提示]
HistTok[历史对话]
UserTok[当前输入]
OutTok2[输出预留]
end
SysTok --> Window[窗口容量上限]
HistTok --> Window
UserTok --> Window
OutTok2 --> Window
图:BPE 分词流程与上下文窗口构成
示例代码¶
使用 tiktoken 估算文本的 token 数量,并计算调用成本。
import tiktoken
def count_tokens(text: str, model: str = "gpt-4o") -> int:
"""统计文本在指定模型下的 token 数。
Args:
text: 待统计的文本。
model: 模型名称,用于选择分词器。
"""
encoding = tiktoken.encoding_for_model(model)
return len(encoding.encode(text))
def estimate_cost(
input_tokens: int,
output_tokens: int,
input_price: float = 2.5,
output_price: float = 10.0,
) -> float:
"""估算单次调用的费用(美元)。
Args:
input_tokens: 输入 token 数。
output_tokens: 输出 token 数。
input_price: 每 1M 输入 token 价格(美元)。
output_price: 每 1M 输出 token 价格(美元)。
"""
return input_tokens / 1_000_000 * input_price + output_tokens / 1_000_000 * output_price
if __name__ == "__main__":
sample = "人工智能正在重塑软件开发的方式,Agent 将成为下一代应用的核心范式。"
tokens = count_tokens(sample)
print(f"文本长度: {len(sample)} 字")
print(f"Token 数: {tokens}")
print(f"预估单次调用成本: ${estimate_cost(tokens, 100):.6f}")
# 演示不同文本的 token 差异
for text in ["hello world", "你好世界", "import numpy as np"]:
print(f"{text:30s} -> {count_tokens(text)} tokens")
运行结果说明:会输出中文与英文文本的 token 数对比,直观展示"你好世界"通常比"hello world"消耗更多 token,从而帮助开发者预估成本。
企业案例¶
案例 1:长文档处理的上下文优化¶
某律所使用 LLM 审查 200 页合同,单份文档约 8 万 token。直接喂入 GPT-4o(128K)虽可行,但单次成本超 1 美元且响应慢。优化方案:先按章节分块,仅对相关章节做精读,整体成本降低 80%,准确率反而提升(避免长上下文稀释)。
案例 2:客服对话的状态外置¶
某 SaaS 公司将客服对话历史存入 Redis,每次只向模型发送"摘要 + 最近 3 轮 + 当前问题",单次输入 token 从 6000 降至 1200,月度 API 成本从 5 万降至 1.2 万元,且响应延迟从 4 秒降至 1.5 秒。
案例 3:缓存优化节省成本¶
某 AI 编程助手利用 OpenAI 的 Prompt Cache,将系统提示与项目上下文作为稳定前缀缓存,缓存命中率 85%,输入 token 成本下降 70%,首 token 延迟下降 40%。
常见问题¶
Q1:1 个汉字到底等于几个 token?
A:取决于模型与字频。常见汉字多为 1-2 个 token,生僻字可能 3 个。tiktoken 可精确计算。
Q2:上下文窗口越大越好吗? A:不一定。大窗口带来更高成本与延迟,且存在"中间迷失"问题。应结合任务选择,并配合分块与检索策略。
Q3:Chat Completions 与 Responses API 该用哪个? A:构建 Agent 优先 Responses API(内置工具、服务端状态);简单对话或兼容旧系统用 Chat Completions。
Q4:为什么工具调用的 token 消耗比想象中高? A:因为工具的 JSON Schema 定义、历史工具调用记录与返回结果都会计入输入 token,且每轮重复发送。
面试题¶
1. 解释 BPE 分词的原理。 参考答案:BPE 从字节级初始化,统计高频相邻 token 对并合并,迭代扩展词表,最终得到一个平衡覆盖率与词表大小的分词方案。
2. 什么是"Lost in the Middle"现象?如何缓解? 答题要点:模型对位于超长上下文中部的信息召回率下降。缓解方法:关键信息放在开头或结尾、分块检索、压缩无关上下文、使用重排序。
3. 设计一个多轮对话系统,如何控制 token 成本? 答题要点:滑动窗口 + 摘要、向量检索历史、缓存稳定前缀、按需调用工具、小模型预筛 + 大模型精答。
4. 输入 token 与输出 token 价格为何不同? 参考答案:输出 token 需要逐个自回归生成,计算量远大于输入的并行前向传播,且推理延迟更高,因此定价更高。
练习题¶
- Token 实验:用
tiktoken统计一段 500 字中文与等长英文的 token 数差异,并计算 100 万次调用的成本差。 - 上下文设计:为一个法律咨询 Agent 设计上下文管理方案,要求支持 100 轮以上多轮对话且单次输入不超过 4K token。
- 思考题:如果模型上下文窗口无限大,是否还需要 RAG?为什么?
小结¶
Token 是 LLM 的计量与处理单元,BPE 决定了文本到 token 的映射;上下文窗口限制了模型单次能"看见"的信息量。合理管理上下文——通过摘要、滑动窗口、检索与缓存——是控制成本与提升效果的关键工程能力。延伸阅读:OpenAI Tokenizer 在线工具、tiktoken GitHub、论文《Lost in the Middle: How Language Models Use Long Contexts》。