导学阶段-Token、上下文窗口与成本
本文整理自 CNB 上的开源课程 《Vibe Agent:从零搭建 Coding Agent》。课程讲义、示例代码与练习仓库均在 CNB 上公开,文中示例出自该仓库对应章节。
一次 API 调用的账单,和模型一次能读多长,由同一个单位决定。这个单位介于字和词之间,长度不固定,取多少取决于分词器。
切在子词上
文本要先变成数字才能进入模型,这一步叫分词。朴素的两种切法都走不通。按单词切,英文有几十万词,词表会膨胀,遇到 ChatGPT 这类新词找不到对应项。按字符切,单个字母或汉字承载的语义太少,模型要从过细的粒度学起。
现代模型取中间路线:子词分词。常见的词整体保留,罕见的词拆成词根、词缀一类更小的碎片。Tokenization 常被拆成 Token 和 ization 两段,各自有意义,拼回去还能还原;the 这样的高频词则单独占一个 token。
主流算法是 BPE(Byte-Pair Encoding) 家族的。它把文本先拆成字节,统计出现最频繁的相邻字节对,合并成新 token,反复合并直到词表达到预设大小,课程给的常见规模是 32K 到 200K。字节级方案有 256 个字节兜底,没见过的字符也能编码,不会出现 <UNK>。
以 low、lower、lowest 三个词为例,第一轮把出现最多的 l 与 o 合并成 lo,第二轮把 lo 与 w 合并成 low,几轮之后词表里就出现了 low、er、est 这些单元。BERT 用的 WordPiece、T5 用的 Unigram,属于同一思路下的不同切法。
token 是文本与向量之间的一层
分词只解决了切分问题,模型做的是矩阵运算,输入必须是向量。
完整的链条是:文本切成 token 序列,每个 token 在词表里查到整数 ID,ID 经嵌入矩阵映射为高维向量,再进入前向传播。以 GPT 系列常用的 cl100k_base 词表为例,Hello 对应 15496;向量维度在不同模型上大致落在 1024 到 12288 之间。模型真正看到的是这些向量,不是字符串碎片。分词器的作用,是决定把哪些字符打包成一个语义单元。
这层映射也解释了语义相似为什么能落到数值上。king 与 queen 的向量距离,比 king 与 banana 近得多。
一个汉字、一行代码各占多少
课程给出的对照如下:
| 文本 | 字符数 | Token 数 |
|---|---|---|
Hello, world! |
13 | 4 |
你好,世界! |
6 | 7 |
def foo(): return 1 |
19 | 6 |
经验值是这样:英文一个 token 大约对应 4 个字符、0.75 个单词;中文约 1 字 ≈ 1 个 token,随分词器浮动,上面 6 个字符的句子切出 7 个 token,标点和生僻字会让数量上浮;代码里标点和缩进各自成 token,同样长度的代码比自然语言更贵。emoji 不会被当成一个字符处理,它在词表里往往由几个字节片段拼成,实测 🤖 AI 四个字符切出 4 个 token。
这些比例只用于估算。具体切法取决于分词器,不同模型的词表不同,同一个句子切出的数量可以不一样。
计费按 token,输出侧更贵
API 按 token 收费,不按字符。输出侧的单价通常比输入侧高 4 到 6 倍。
课程给出的 2026 年 11 月价格,按一次「输入 5K、输出 1K」的 Agent 调用计算:
| 档位 | 单次调用 | 一天 100 次,一个月 |
|---|---|---|
| 旗舰档(GPT-5.6 Sol) | 约 0.27 元 | 约 800 元 |
| 主力档(GPT-5.6 Terra、Claude Sonnet 5.5) | 约 0.15 元 | 约 450 元 |
| 便宜档(DeepSeek-V4.1-Flash) | 约 0.01 元 | 约 30 元 |
同样的活,成本差一个数量级。缓存命中还能再省 50% 到 90%,批处理大约省一半。对 Agent 来说,压低输出长度往往比压缩输入更有效,因为输出单价更高。
1 | import os |
用服务端返回的 usage 统计,比本地按字符估算可靠。分词器在服务商那一侧,本地估算要么引入额外的分词库,要么按比例硬猜,遇到不同模型的分词器就会失准。usage 是实际计费依据,直接拿来算最准。
窗口是一份共享预算
上下文窗口按 token 计算,输入和输出共用一个上限。一个 1M 窗口大约装得下 100 万字中文,看着宽松,把整个代码仓库塞进去照样会满。
超限的表现有三种。输入超过窗口时,API 返回 context_length_exceeded,请求直接失败,不会自动截断。聊天产品为了避免报错,会主动丢掉最早的历史,聊久了忘事就来自这里。还有一种更隐蔽:没有超限,但窗口越满,模型越容易忽略中间位置的信息,这个现象叫 Lost in the Middle。
后果是同一个问题放在长上下文的开头、中间、结尾,得到的回答质量可能不同。长度和位置都在影响效果,窗口余量不是越大越可以随便用。把整份需求文档一次性丢进去、让模型自己找重点,效果往往不如分几次带着具体问题去问。
对搭 Agent 意味着什么
Agent 每跑一轮,都要把 system、历史、工具返回结果重新拼成一次请求。轮数增长,输入变长,费用和延迟同步上升。上下文管理因此是工程问题,不是提示词技巧。几个直接的做法:稳定不变的部分放在前缀,利用缓存命中;大文件分块读取,不整仓塞入;历史超过阈值后做摘要或截断。缓存只对前缀有效,前面的内容一改,后面全部要重算。
还有一种情况是信息根本不在窗口里:当前时间、私有文档、训练截止之后的事件。这些内容再怎么整理提示词也拿不到,只能从外部取。下一节处理这个缺口。


