Skip to content

Token 与上下文窗口:模型为什么会「忘事」 ​

接大模型 API 的前端,最先撞上的两个词就是 token 和上下文窗口:账单按它算,报错也因它起。这篇把它们讲清楚——token 到底怎么切、中文为什么更贵、窗口是谁和谁共用的、以及模型「忘事」时你能查什么、改什么。

Token 是子词,不是字也不是词 ​

模型不认识字符串,只认识整数 ID。分词器(tokenizer)负责把文本切成 token 再映射成 ID,常见算法是 BPE(字节对编码)。

  • 切法不按语法:unhappiness 可能被切成 un + happi + ness。
  • 空格、标点、代码缩进都会被算进去,一个都跑不掉。
  • 一句话:token 是模型眼里的最小拼装块,比字大、比词小,全凭训练时统计出来的词表决定。

换一家厂商,token 数就变了 ​

同一个字符串,不同分词器切出的数量并不一样。

  • GPT 系列用 tiktoken:cl100k_base 词表约 10 万,o200k_base 约 20 万,对非英文更友好。
  • Gemini 用 SentencePiece;Claude 的分词器官方未公开。
  • 所以别拿一家的 token 数去套另一家的账,要准就调各家的计数接口。
ts
// 示意代码:前端本地数 token
import { getEncoding } from "js-tiktoken";
const enc = getEncoding("o200k_base");
const n = enc.encode("你好,前端").length; // token 数

中文为什么更费 token ​

词表大多在英文语料上练成,常用汉字和词往往被拆得更碎。

  • 经验值:英文 1 token ≈ 4 个字符 ≈ 0.75 个单词;中文 1 token ≈ 1 到 2 个汉字。
  • 同样一段话,中文通常花掉比英文更多的 token,账单和窗口占用都更高。
  • 代码和 JSON 也别忽略:缩进、引号、重复的字段名都会被逐块计入。

上下文窗口:输入和输出共用一个额度 ​

  • 上下文窗口是模型单次请求能同时「看到」的 token 上限,包含你的输入加模型生成的输出。
  • 它不是「输入一份再加输出一份」,而是一个总池子:输入占得多,留给输出的就少。
  • 请求里设的输出上限(max tokens)同样吃这份预算,加起来超了就报错。

上下文被谁吃掉了 ​

窗口没满却已经开始「忘事」,通常是这些在抢位置:

  • 系统提示词(system prompt)和工具、函数定义。
  • 历史对话全文,每一轮都原样重发。
  • 检索塞进来的文档(RAG)和长网页正文。
  • 图片等多模态输入,以及你没注意的调试日志。

「忘事」是工程问题,不是玄学 ​

模型没有记忆,也不会真「逐渐忘」;本质是应用层每次往 prompt 里拼了多少历史。

  • 超出窗口时,要么请求直接失败,要么上游做了截断或摘要,把早期内容丢掉——你看到的就是「忘事」。
  • 所以历史得在客户端管好:滚动窗口、对话摘要、把稳定内容交给缓存,都是常规手段。
  • 和状态管理一个道理:别把整棵 store 每轮全量序列化再发出去。

怎么估算,以及超窗会怎样 ​

  • 粗估:英文可按「字符数 ÷ 4」;中文按「汉字数 × 1 到 2」。这只是量级,不是精确值。
  • 要准就用厂商的计数接口,或本地 tiktoken;具体模型和参数以官方文档为准。
  • 超窗两种表现:一是直接报错,例如 OpenAI 返回 400、invalid_request_error,提示 context_length_exceeded 这类语义;二是静默截断,内容悄悄丢了。
  • 工程上留余量:估算值离上限留出一截,再用响应里的 usage 字段回头校准。

警告

别把「字符数 ÷ 4」当万能公式,那是英文经验值,中文明显偏离,一不留神就超窗、超预算。也别以为所有厂商超窗都会报错——不少产品是静默截断,你以为历史都在,其实前面早被丢了。分词方式、词表大小、免费额度这类信息会变,以官方文档为准。

最近更新