研究聚焦 Token 与词元化在大模型应用中的工程影响:同一段文本为何在不同模型中产生不同成本、不同上下文占用和不同切分行为。方法上,文章从 BPE、字节级 BPE、SentencePiece、WordPiece 与 Unigram 的基本机制出发,分析中文、代码、Markdown、JSON、RAG 切块、缓存和多模型网关中的 Token 预算问题。结论是,Token 不是“字数换算”问题,而是模型接口、成本控制、上下文治理和产品体验的共同计量单位;任何严肃 AI 应用都应把 tokenizer 结果纳入设计和监控。
Token;词元化;BPE;SentencePiece;中文切分;上下文预算;Prompt Caching;RAG
本文的研究问题是:为什么人类看见的一段文本,进入模型后会变成不同长度、不同成本、不同语义边界的 Token 序列。分析方法采用编码链路分解:从原始 Unicode 文本出发,观察规范化、子词合并、特殊 Token、模型词表、上下文窗口和输出预算之间的关系,再把这些机制映射到 RAG 切块、工具返回、缓存命中和多模型路由。
图中链路说明,Token 预算问题不能等到请求发送前才处理。一个简化预算公式可以写成:
其中, 表示一次请求需要预留的上下文预算, 表示系统指令 Token, 表示用户输入 Token, 表示历史对话 Token, 表示检索证据 Token, 表示工具说明或工具结果 Token, 表示输出预留 Token。若 接近模型上下文上限,系统应先压缩、检索或裁剪低价值内容,而不是把输出空间挤空。
很多人第一次估算大模型成本,会直接把中文字符数当成 Token 数,把英文单词数当成 Token 数,再按模型价格粗略相乘。这个做法适合做很粗的直觉判断,不适合做应用设计。Token 是分词器把文本切分后得到的片段,模型真正看到的是这些片段对应的数字编号。一个 Token 可能是一个汉字,可能是几个汉字,可能是英文里的一个常见词,也可能只是一个词缀、一个空格加单词、一个换行、一个标点、一个代码缩进片段。
同一句话在不同模型、不同分词器、不同编码表里,Token 数可能不同。中文“上下文预算”四个字,有的分词器可能把它切成较短片段,有的可能把常见组合保留得更长;英文“tokenization”可能整体变成一个片段,也可能拆成“token”“ization”一类子词;代码里的function getUserById(id)会被拆成标识符、大小写片段、括号、空格和换行。模型并不是先理解自然语言再切分,而是先把原始输入变成 Token 序列,再在这个序列上计算。
这件事有一个直接后果:应用里所有和长度有关的体验,都不能只用“字数”判断。上传文件是否超限、对话历史是否该压缩、RAG 片段是否该截断、答案最大长度是否足够、一次工具调用返回多少字段合适,都要回到目标模型的分词器上计算。字数是给人看的,Token 是给模型、账单和推理引擎看的。
传统中文分词常问“今天/天气/很好”该怎样切成词,因为中文书写没有英文那样天然空格。大模型分词器处理的是另一个问题:怎样把任意文本稳定映射到一个有限词表里的编号,并且让模型在这些编号上学到可泛化的语言规律。它不是为了生成词典意义上的“词”,而是为了生成可训练、可推理、可还原的离散序列。
一个典型流程可以分成四步。第一步,读取原始文本,包括汉字、拉丁字母、数字、标点、空格、换行、emoji、代码符号和不可见字符。第二步,按分词器规则切成片段。第三步,把每个片段查到词表中的 ID。第四步,把这些 ID 交给模型。模型输出时顺序相反:先生成 ID,再由分词器解码回文字。
这里最容易混淆的是“片段可读性”。有些 Token 直接显示成完整词,看起来很像人类词语;有些 Token 是前面带空格的英文片段;有些是汉字组合;有些如果单独显示会像乱码,因为它们对应字节片段或特殊编码。模型并不要求每个 Token 对人类都有独立意义。只要整个序列可还原、统计规律可学习、词表规模可控,它就能工作。
如果中文全部按字切,英文全部按字符切,系统会很简单,但效率会很差。句子会变得很长,模型需要处理更多位置,注意力计算和 KV Cache 占用都会增加。反过来,如果把所有常见词、短语、专有名词都放进词表,词表会膨胀,稀有词和新词还是处理不好,模型输出层也会变重。
子词分词的目标是在两者之间取平衡。常见片段应该更短地表达,例如高频词、常见后缀、常见中文搭配、常见代码符号可以成为一个 Token;罕见片段也不能变成“未知词”,而应该被拆成更小单元,直到可以被词表覆盖。这样,模型既能用较少 Token 表达高频内容,又能处理从未见过的人名、域名、变量名、拼写错误和混合语言。
这个设计对中文尤其重要。中文常用字数量有限,但词语组合极多;新词、缩写、品牌名、技术名、拼音、英文夹杂、全角半角符号都很常见。用固定中文词典切分会遇到未登录词,用纯字符切分会浪费上下文。子词分词让模型在“字”“词”“短语片段”之间动态折中,虽然它不会完全符合语言学分词,但更适合大模型的训练和推理。
BPE 最初不是为大语言模型发明的,但它的思想很适合处理开放词表。用于文本时,可以把语料先拆成最小单元,然后反复寻找最常一起出现的相邻片段,把它们合并成新片段。训练结束后,分词器得到一张合并规则表和一个词表。编码新文本时,分词器按这些规则把文本切成尽可能匹配训练结果的子词片段。
可以想象一小批训练文本里,“人工智能”频繁出现,“智能体”也频繁出现。训练过程可能先发现“智”和“能”常相邻,于是合并成“智能”;又发现“人工”和“智能”常相邻,于是合并成更长片段。英文里,“ing”“tion”“pre”“un”等常见子串可能被保留。代码语料里,换行、缩进、括号、冒号、常见关键字也会形成稳定片段。
BPE 的优点是直观、可控、覆盖能力强。只要最小单元选择得足够底层,它可以处理任意新文本,而不需要一个“未知词”来兜底。它的缺点也明显:它主要看相邻片段频率,不真正理解语法;合并规则由训练语料决定,语料偏向会影响不同语言、不同领域的压缩效率;同一个自然词不一定总被切成符合人类直觉的边界。
现代分词器常把“字节”作为底层保证,而不是只依赖字符表。这样做的好处是覆盖任意 Unicode 文本。无论输入是中文、阿拉伯文、emoji、罕见符号、混合编码残留,还是日志里出现的异常字节,只要它能作为字节序列进入系统,就能被编码成 Token,再解码回原文。
字节级设计解决了早期词表系统常见的未知词问题。传统词典遇到词表外内容,可能用一个统一的UNK表示。这个做法会丢信息:两个不同人名、两个不同变量名、两个不同拼写错误都可能变成同一个未知符号。字节级或近似字节级的子词系统会把罕见内容拆开保存,虽然 Token 数可能增加,但信息不会轻易消失。
这对工程日志、代码、配置文件、医学缩写、法律条款编号和跨语言输入很关键。生产系统里,用户不会只输入干净自然语言。他们会粘贴 JSON、SQL、堆栈、URL、Markdown 表格、表情、截图 OCR 文本、半截文件内容。分词器必须对这些混乱输入保持稳定,而不是遇到词典外内容就失真。
许多英文 NLP 管线会先按空格和标点做预切分,再在词内部做子词处理。这个假设对中文、日文、泰文等语言并不自然。SentencePiece 的重要贡献,是把原始句子直接当作输入来训练和解码,不要求文本预先按词切好,也不把空格当成外部不可见的分隔条件。它会把空格也作为可建模符号处理,从而让多语言文本在同一个框架下工作。
SentencePiece 支持 BPE,也支持 Unigram 语言模型。BPE 像是从小到大反复合并高频片段;Unigram 更像是先准备一个较大的候选子词集合,再通过概率模型选择能更好解释语料的子词集合,并在编码时寻找概率较优的切分。两者都能产生子词词表,但训练目标和切分行为不同。实践中,具体效果取决于语料、词表大小、语言分布和模型训练方式。
对中文读者来说,SentencePiece 的启发不是“它一定比 BPE 好”,而是“不要把空格语言的预处理习惯强行套到所有语言上”。中文没有天然词间空格,日文混合汉字假名,东南亚一些语言也有复杂边界。如果分词器从设计上就能直接面对原始文本,跨语言模型会少受人工预处理偏见影响。
WordPiece 常见于 BERT 系列,BPE 常见于 GPT 及许多生成模型,Unigram 常见于 SentencePiece 生态。它们的共同目标都是把开放文本拆成可学习子词,差别在训练目标、编码策略、特殊符号和实现细节。讨论某个算法时,不应该只问名字,而要问它服务的模型目标是什么。
掩码语言模型、因果语言模型、翻译模型、嵌入模型、语音文本模型,对分词器的偏好并不完全相同。生成模型需要高效逐 Token 生成;嵌入模型需要让语义片段稳定映射;翻译模型关心跨语言对齐;代码模型关心符号、缩进和标识符。一个分词器在英文新闻上很省 Token,不代表它在中文合同、前端代码和数据库日志上同样省。
所以,生产系统不要抽象地争论“哪种算法最好”。更实际的问题是:目标模型已经绑定了哪个分词器?你的输入语料在这个分词器下平均消耗多少 Token?最坏情况是什么?切分结果是否影响检索粒度?成本估算是否按真实编码计算?迁移模型时,是否重新测过所有关键文档和提示模板?
中文文本没有天然词间空格,但读者能感知词语边界。这种边界来自语义、句法和惯用搭配,而不是字符之间的显式符号。大模型分词器不会像中文分词工具那样追求“北京大学/生前/来/应聘”这种语言学切法,它更关心片段在训练语料中的统计稳定性。
常见中文词可能被合成一个 Token 或少数几个 Token。罕见姓名、企业名、行业术语、地方地名、网络新词可能被拆得更碎。简体和繁体可能有不同统计分布;全角标点和半角标点会影响切分;中文夹英文时,空格是否存在会改变英文片段;数字和单位连写时,分词器可能把“2026年”“3.5%”“128K上下文”拆成不同组合。
中文写作还常使用长句、顿号、书名号、括号解释和中英文混排。对人来说这些都正常,对 Token 预算来说却不完全等价。一个格式精美但符号复杂的标题,可能比朴素标题多出不少 Token;一个 Markdown 表格如果列很多、空格对齐很多,会比普通段落更费;一段 OCR 结果如果夹杂不可见字符和错误换行,Token 消耗会明显增加。
“中文一个字一个 Token,所以中文很贵”是过度简化;“中文信息密度高,所以中文一定省 Token”也是过度简化。真实情况取决于分词器和文本类型。常见中文短语可能压缩得很好,生僻字、人名、古文、专业缩写、混合符号可能很碎。英文常见词可能一个 Token,长技术词可能多个 Token;英文空格常和后面的词一起被编码,标点和大小写也会影响结果。
比较中英文 Token 效率,要用同一个模型的 tokenizer 对同一内容的不同译文测试。比如一段产品说明,中文可能字数少,但某些模型对英文训练更多,英文子词更成熟;一段法律条款,中文标点和条号可能增加片段;一段代码注释,中英混写可能比纯英文更复杂。没有实际统计前,不要凭语言直觉定预算。
产品里可以给用户显示“预计消耗”而不是“字数”。例如上传文档后,系统可以显示该模型下的输入 Token 估算、可用于回答的输出余量、是否触发缓存、是否需要分段处理。这样用户理解的不是抽象分词算法,而是当前任务还能放多少材料、会花多少钱、能生成多长答案。
上下文窗口是模型一次请求能处理的最大 Token 数。它不是只给输入用,也不是只给对话历史用,而是系统消息、用户消息、历史对话、检索材料、工具返回、图片或文件解析内容、已经生成的输出,以及预留给继续生成的空间共同使用。请求放得太满,模型可能没有足够余量输出完整答案。
这就是为什么“把整本文档塞进去”不总是好方案。即使模型支持很长上下文,长输入也会带来更高成本、更高延迟、更大的注意力负担和更难的引用定位。上下文窗口越大,越需要治理内容质量,而不是把所有可能相关的内容无差别塞入。长上下文像一张更大的工作台,桌面变大不等于可以堆满杂物。
工程上应该把上下文预算拆成几个明确账户:固定指令预算、对话历史预算、当前用户问题预算、检索证据预算、工具结果预算、输出预算、安全余量。每个账户有上限和优先级。用户追问时,系统可以保留最新轮次、压缩旧历史、重新检索证据,而不是让历史无限增长到截断。
云模型计费通常会区分输入 Token 和输出 Token。输入 Token 是你发给模型的内容,输出 Token 是模型生成的内容。许多平台还会显示缓存命中的 Token,表示请求前缀与近期请求相同,可以复用部分计算,从而降低延迟或成本。有些推理模型还会出现内部推理相关 Token,它们不一定全部显示在最终答案中,但会影响用量和费用。
这些类别决定了优化方向。输入太贵,就要减少重复提示、压缩检索片段、清理工具返回、使用缓存友好的前缀结构。输出太贵,就要限制答案长度、要求先给结论再给必要依据、把批量生成拆成分页任务。缓存命中率低,就要把稳定内容放在前面,把用户变量放在后面,避免每次都随机改写系统提示和示例。
本地模型没有 API 账单,但同样有 Token 成本。输入越长,prefill 越慢;输出越长,decode 步数越多;并发越高,KV Cache 占用越大;上下文越长,显存和内存压力越明显。把 Token 当成账单单位只是云服务视角,把 Token 当成算力单位才是推理工程视角。
提示词缓存的基本思想是:很多请求前面一大段内容相同,例如系统指令、工具说明、格式示例、固定知识背景。服务端如果近期处理过相同前缀,就可以复用这部分计算,减少延迟和成本。缓存能否命中,关键是前缀是否稳定、足够长、顺序一致、字节级内容一致。
因此,缓存友好的上下文结构通常是“稳定内容在前,变化内容在后”。系统规则、角色边界、输出 schema、少量高质量示例、工具定义放前面;用户问题、当前检索片段、临时状态放后面。不要在系统提示顶部加入时间戳、随机请求 ID、动态用户昵称,否则每次前缀都变,缓存优势会被破坏。
缓存也不改变答案质量本身。它复用的是已经处理过的前缀表示,不是复用上一次的回答。应用设计时,不要把缓存当成记忆,也不要用缓存代替会话状态。缓存适合优化重复结构,不适合承载业务真相。真正的记忆仍然应该来自数据库、会话历史、检索系统和明确的状态管理。
自然段落通常比结构化噪音更友好。Markdown 表格为了对齐会产生大量竖线、空格、换行;JSON 会重复字段名、引号、冒号、逗号和层级括号;HTML 会带标签和属性;日志会带时间戳、线程名、路径、堆栈行号;Base64 和压缩字符串对语言模型几乎没有语义压缩优势,Token 数会膨胀得很快。
这不是说结构化格式不能用。相反,结构化输出对生产系统很重要。问题在于要控制结构粒度。给模型看数据时,应该保留任务需要的字段,删除无关字段;长列表要先筛选再注入;日志要先按错误类型、时间范围和关键堆栈压缩;网页要先抽正文、标题、链接和发布时间,去掉导航、广告、脚本和重复页脚。
生成 JSON 时,也要预留输出预算。一个包含二十个字段、每个字段都要求长解释的 schema,可能让模型输出迅速膨胀。若下游只需要三个字段,就不要让模型生成十七个“看起来专业”的字段。结构化不是越多越生产级,生产级是字段必要、语义明确、可校验、可重试。
知识库切块常见做法是按字符数切,比如每 800 字一段、重叠 100 字。这个方法简单,但不够精确。真正进入生成模型的是 Token,不是字符。中文、英文、代码、表格、目录、公式、网页噪音在同样字符数下 Token 数差异很大。一个“800 字”中文段落可能合适,一个“800 字”JSON 片段可能非常浪费。
更可靠的切块策略应该同时考虑语义边界和 Token 上限。先按标题、段落、列表、章节、表格行、代码块等自然结构切,再用目标 tokenizer 统计长度,超过上限时继续拆分。切块时保留标题路径和来源信息,因为模型需要知道片段来自哪一节、哪份文档、哪个版本。重叠也要有目的:为了保留跨段语义,而不是机械复制大量重复内容。
RAG 的上下文预算还要留给问题、指令和回答。假设模型窗口是 32K,并不代表可以放 32K 检索片段。还要扣掉系统规则、对话历史、引用格式、工具定义和输出空间。实际可用证据预算可能只有窗口的一半甚至更少。把检索片段按 Token 排序、去重、压缩、重排,是长上下文质量的核心。
中文资料常见结构包括章节标题、编号条款、问答列表、政策口径、表格说明和案例描述。切块时如果只按长度硬切,容易把“适用范围”和“例外情况”分开,把“处罚条件”和“处罚标准”分开,把“问题”和“答案”分开。模型拿到半截证据,就会产生看似合理但缺关键限定的回答。
更好的做法是把标题链路附在每个片段前面,例如“第三章 数据处理规则 / 第二节 留存期限 / 第十二条”。这样即使片段被单独召回,模型仍知道它的位置。对于条款类文本,尽量不要把一个条款拆开;必须拆时,要保留条号和前置定义。对于问答类文本,问题和答案应该同块保存,或者至少在元数据中相互关联。
中文还要注意指代和省略。很多段落会说“上述材料”“本办法”“该情形”“前款规定”,单独拿出来意义不完整。切块系统可以在入库时识别这些依赖,把必要的定义段、标题段或前款摘要加入片段元数据。这样做比盲目增加 chunk overlap 更省 Token,也更稳。
聊天应用容易把所有历史消息原样放进上下文,直到超过窗口再粗暴截断。这个策略在早期轮次看不出问题,轮次一多就会浪费大量 Token。用户寒暄、失败尝试、重复解释、已经过期的中间方案都会占据位置,而真正重要的需求、决策、约束和结果反而可能被挤出去。
生产级会话应区分短期上下文和长期记忆。短期上下文保留最近几轮原文,保证语气和指代连续;长期记忆保存经过确认的稳定事实,例如用户偏好、项目边界、已选择方案、关键参数、禁止事项。中间过程可以被摘要,但摘要必须可追溯,不能把未确认猜测写成事实。
当模型需要继续复杂任务时,上下文压缩也要保留操作状态。比如正在写一篇文章,应保留标题、目标读者、已完成结构、未完成部分、引用来源、风格约束,而不是只总结“用户要求写文章”。压缩后的 Token 少了,但任务信息不能少。上下文工程的目标不是短,而是有效。
很多失败回答不是模型不会,而是输出预算不够。请求里塞了大量文档,max_output_tokens又设得太低,模型只能给出半截总结;或者要求生成完整长文、表格和引用,但上下文窗口几乎被输入占满。最后表现为突然停止、结构不完整、引用缺失、JSON 截断。
规划输出预算时,要先估算目标结果长度。短问答可能 300 到 800 Token 足够;详细分析可能需要 2000 到 5000 Token;长文、报告、代码生成可能需要更多。若任务天然很长,就应该分阶段生成:先大纲,再分章节,再整合校对。不要用一次请求完成所有事情。
对用户界面来说,应该把输出长度做成清晰选项,而不是隐藏参数。比如“简要回答”“详细分析”“生成完整报告”对应不同预算和价格预估。用户选择长输出时,系统提示成本和耗时;用户上传大量材料时,系统提示会压缩或分批处理。这样比失败后报错更像一个可靠产品。
很多应用把“最大输入长度”“切块大小”“摘要长度”“价格估算”写死在配置里。换模型时,只改模型名,不重测 tokenizer,会埋下隐患。新模型可能使用不同词表,同一文档 Token 数变化明显;上下文窗口可能更大,但输出价格更高;缓存规则可能不同;特殊消息格式也可能改变额外开销。
迁移检查应该包括几类样本:短中文问答、长中文文档、代码文件、Markdown 表格、JSON、OCR 文本、中英文混合材料、典型工具返回、最长系统提示。每类样本都要记录输入 Token、预期输出 Token、总成本、延迟和截断风险。只有这些数据更新后,才能修改产品限制。
嵌入模型也要重测。知识库切块如果按旧生成模型 tokenizer 做,换嵌入模型后可能不适合检索;某些嵌入模型有自己的最大输入长度,超过后会截断或报错。生成模型窗口很长,不代表嵌入模型也能吃下同样长的片段。RAG 系统至少要分别管理嵌入预算和生成预算。
很多模型有特殊 Token,用来表示消息开始、消息结束、系统角色、工具调用、图像占位、填充、停止符等。它们不一定能在普通文本中直接看见,但会影响模型怎样理解输入结构。聊天 API 把角色消息转换成模型内部格式时,也可能加入额外 Token。
应用层不要随意让用户输入伪造的特殊标记,也不要把内部模板暴露给最终用户。用户看见的应该是清晰的任务界面,而不是“system prompt”“assistant role”“tool call JSON”这种实现细节。内部协议应该由服务端构造,用户输入应该被放在明确的用户区域里,检索材料和工具结果也要有边界说明。
这和安全有关。提示注入常利用模型把不同来源文本混在同一上下文里读取的特点。网页里的一句“忽略之前指令”对人来说是网页内容,对模型来说也是 Token 序列。系统必须明确告诉模型哪些是可信指令、哪些是待分析材料、哪些是工具结果,并在工具层做权限控制。分词器不懂安全边界,应用要建立边界。
最有效的 Token 优化,常常不是写更短提示词,而是去掉重复信息。许多应用的上下文里同时出现系统说明、页面说明、按钮文案、开发者注释、重复历史、重复检索片段、相同文档的多个版本。模型读到很多相似内容,会浪费预算,也更容易在冲突信息中摇摆。
去重可以分三层。第一层是静态模板去重:系统提示不要把同一规则用不同话重复三遍。第二层是检索去重:相同段落、相同网页转载、相邻 chunk 高度重合时,只保留最有来源和最完整的版本。第三层是会话去重:已经固化到状态里的用户偏好,不必每轮用长句重复;历史里已被推翻的方案,不应继续占据上下文。
压缩不是删掉细节,而是删掉无效重复。真正关键的限制、数字、来源、例外、时间、版本不能省。一个好的上下文像一份干净案卷:材料不多不少,证据清楚,来源明确,当前问题突出。Token 少只是结果,信息密度高才是目标。
经验规则很有用。例如英文大致可以用“一个 Token 约等于几个字符”做粗估,中文可以用“一个汉字附近浮动”做初步预估。但经验规则只能用于早期规划,不能用于最终计费、截断和权限判断。不同模型、不同文本类型、不同格式下,误差会很大。
生产系统应在服务端接入目标 tokenizer。用户输入、文件解析、检索片段、工具返回、待输出预算都要能被统计。统计结果最好写入日志,便于分析真实成本。若平台返回 usage 字段,也要把请求前估算和请求后实际用量对比,持续校准。估算偏差长期过大,说明分词器版本、消息包装或文件解析方式有问题。
前端展示时,不需要把所有技术细节塞给用户。可以显示“预计输入量”“预计可生成长度”“可能需要分段处理”“长回答将增加等待时间”。用户关心的是能不能完成、要等多久、是否会多花钱。Token 细节留给需要精确控制的高级入口即可。
文件上传是 Token 浪费重灾区。PDF 解析可能产生重复页眉页脚;网页保存可能带导航、广告、评论和脚本;Word 文档可能包含修订痕迹;表格可能把空单元格和格式说明变成大量文本;扫描件 OCR 可能带错行、乱码和页码。直接计数会把脏内容也算进去,直接入模会让模型读大量噪音。
合理流程是先解析,再清洗,再分段,再计数,再决定处理策略。清洗包括去掉重复页眉、页脚、目录噪音、空行洪水、无意义符号、脚本样式、重复版权声明。分段时保留标题和页码。计数后,如果文档超过预算,系统可以选择摘要、检索、分批问答或让用户选择重点章节。
清洗不能破坏证据。法律、财务、医学、合同类文档里的页码、条号、表头、脚注可能很重要,不能因为“看起来重复”就全部删除。好的清洗策略要按文档类型调整。Token 优化必须服从事实完整性。
智能体调用工具后,工具结果通常还要回到模型上下文。很多系统把数据库原始 JSON、HTTP 响应、调试字段、内部 ID、空字段全部塞回去。这会消耗大量 Token,也会把不该暴露的内部信息带进后续答案。工具返回应该像给模型看的证据摘要,而不是后端对象转储。
一个订单查询工具返回给模型的内容,可能只需要订单状态、金额、支付时间、物流状态、可执行操作和限制条件。内部数据库主键、服务耗时、缓存命中、SQL 字段名、堆栈调试信息不应进入模型上下文。若下游动作需要内部 ID,可以放在服务端状态里,不必让模型自然语言处理。
工具返回还要有稳定格式。模型更容易利用短、清楚、字段语义一致的结果。每次工具返回都换字段名、换顺序、夹杂日志,会增加 Token,也降低推理稳定性。结构化不等于啰嗦,结构化是让模型和程序都能可靠读取必要事实。
很多 Token 问题表面是后端问题,实际是产品问题。用户为什么会粘贴整本手册?因为界面没有提供章节选择。为什么每轮对话都重复背景?因为系统没有显示已记住的项目状态。为什么答案总是太长?因为没有输出粒度控制。为什么成本不可控?因为用户看不到任务规模和模型选择的关系。
面向最终用户的设计应该把复杂预算转成自然操作。文件上传后显示可问答范围;长文生成时让用户先确认大纲;知识库回答显示引用来源和覆盖程度;超长材料提示分批处理;模型选择显示“速度优先”“质量优先”“低成本”而不是一堆内部代号。用户不需要理解每个 Token,但需要掌控结果。
对团队管理者来说,Token 预算还关系到配额、公平使用和成本归因。不同部门、不同项目、不同功能应有用量统计。高成本请求应该能追踪到任务类型,而不是只看到总账单上涨。Token 日志不是为了炫技,是为了知道钱和算力花在了哪里。
可以从一个简单公式开始:总窗口减去固定指令,减去对话历史,减去当前问题,减去检索材料,减去工具结果,剩下部分必须大于目标输出和安全余量。这个公式不漂亮,但很实用。每次请求构造前,都按真实 tokenizer 算一遍。
例如窗口 128K 的模型,不应该默认给检索材料 120K。可以先给固定指令 3K,对话历史 10K,当前问题和附件摘要 5K,检索证据 60K,工具结果 10K,输出 20K,安全余量 20K。若用户任务不需要长输出,就把输出预算让给证据;若需要生成报告,就减少证据数量并分阶段处理。
预算还要有降级顺序。超限时先去重,再删低相关证据,再压缩旧历史,再缩短工具结果,最后才拒绝请求。不要随机截断末尾,因为末尾可能包含用户最新要求。也不要删除系统规则,因为规则通常是最高优先级。上下文裁剪必须可解释、可复现、可观测。
第一,按目标模型真实计数,不用汉字数替代 Token 数。第二,上传文档先清洗、分段、保留结构,再入库或入模。第三,中文知识库切块保护标题链、条号、问答关系和定义段。第四,减少 Markdown 表格、原始 JSON、网页噪音和重复页脚直接进入上下文。第五,为输出预留预算,长任务分阶段生成。
第六,缓存友好地组织提示,把稳定前缀放前面,把用户变量放后面。第七,迁移模型时重测 tokenizer、窗口、价格、延迟和截断风险。第八,把工具返回改造成模型可读的精简证据,不把内部调试字段塞给模型。第九,区分短期历史、长期记忆和外部知识库,不让对话无限增长。第十,把 Token 预算转成用户能理解的任务范围、预计耗时和输出长度选择。
这些规则不依赖某一家模型。无论使用云模型、本地模型、开源模型还是混合网关,Token 都是语言模型处理文本的共同计量层。谁能管好这个计量层,谁就更容易做出稳定、便宜、快、可解释的 AI 应用。
当一个应用同时接入多个模型时,路由通常会看价格、速度、能力、上下文长度和隐私等级。真正做细以后,还应该把 tokenizer 差异纳入路由。某个模型在英文代码上便宜,不代表它在中文合同上便宜;某个模型窗口很长,不代表它处理 Markdown 表格时成本可接受;某个模型输出质量高,但如果同样输入在它那里多消耗许多 Token,批量任务总成本会变得不稳定。
模型网关可以在请求进入时先做一次轻量估算:同一段输入分别用候选模型 tokenizer 计算 Token 数,再结合单价、最大窗口、缓存规则和预计输出长度选择后端。对于短问答,差异可能不重要;对于每天大量处理文档的系统,差异会变成真实账单。尤其是中英文混合、代码、表格和日志场景,用真实 tokenizer 做路由,比靠模型名称猜测更可靠。
这也影响降级策略。主模型不可用时,备用模型不能只看“上下文窗口也够大”。如果备用模型 tokenizer 更碎,原本刚好能放下的证据可能超限;如果备用模型特殊消息包装开销更高,工具调用模板可能被截断;如果备用模型不支持相同结构化输出,输出预算也要重算。多模型系统的稳定性,离不开 Token 层的兼容检查。
中文读者常把“一个字”当成最小单位,但现代文本远比汉字复杂。一个 emoji 可能由多个 Unicode 码点组成,带肤色、性别、职业等组合;一个带音调符号的拉丁字母可能有组合形式;某些不可见控制字符会影响显示;全角和半角看起来相近,却是不同字符。Unicode 文本分割本身就有字形簇、词边界、句子边界等不同层级。
大模型分词器不是 Unicode 标准分割器,但它必须在 Unicode 文本上工作。这个事实提醒我们:不要把“屏幕上看起来一样”误认为“编码上完全一样”。用户复制的空格可能是普通空格、不换行空格、全角空格;破折号、减号、连字符也可能是不同符号;中文引号和英文引号会产生不同 Token。对普通聊天影响不大,对去重、缓存、模板命中和严格结构化输出影响很大。
工程上可以做两类处理。第一类是规范化,把明显等价的空白、换行和标点整理成一致形式,提升缓存和去重效果。第二类是保真保存,对合同、代码、法律条款和用户原始证据保留原文,避免规范化改变意义。什么时候规范化,什么时候保真,要看任务。Token 优化不能牺牲文本证据的真实性。
长上下文模型出现后,有人认为摘要、检索和切块都不重要了。现实相反:窗口越大,越需要把内容组织好。把十万 Token 材料一次性放进去,模型也许能读,但成本、延迟、引用质量和冲突处理都会变难。摘要不是为了弥补短窗口才存在,而是为了把低密度材料变成高密度工作状态。
好的摘要不是泛泛概括,而是面向任务的压缩。写报告时,摘要要保留论点、数据、来源和反例;做客服时,摘要要保留用户诉求、订单状态、已尝试方案和下一步限制;做代码审查时,摘要要保留模块边界、关键改动、风险点和测试结果。这样的摘要会减少 Token,同时提高模型抓重点的概率。
摘要也要分层。原始证据保留在文档库,片段摘要用于检索,任务摘要用于会话继续,最终答案引用回原文。这样既不会让上下文无限膨胀,也不会因为摘要失真而丢掉证据。长上下文并不是替代信息架构,而是给信息架构更大空间。
只在开发时手动数 Token 不够。生产系统应该持续记录几个指标:每次请求输入 Token、输出 Token、缓存命中 Token、检索片段 Token、工具返回 Token、历史消息 Token、截断次数、超限拒绝次数、平均首 Token 延迟、完整响应耗时。把这些指标按功能、用户、模型和任务类型聚合,就能看见成本和性能瓶颈。
例如某个知识库问答功能成本突然上升,可能是文档解析开始保留重复页脚;某个模型延迟变高,可能是对话历史没有压缩;某个部门用量异常,可能是批量任务没有走离线队列;某类请求经常截断,可能是输出预算设计太小。没有 Token 观测,团队只能猜;有了观测,优化会变成具体工程问题。
这些指标不应该原样压到最终用户面前。用户需要的是“本次材料较长,系统将分段处理”“完整报告预计需要更久”“已保留引用来源”。管理者和开发者才需要明细报表。产品层保持清楚,工程层保持可观测,二者结合才是可长期运营的 AI 应用。