先聊聊钱的事。我在某云服务商的控制台看到账单时第一反应是“这破对话怎么这么贵”第二反应才是“哦原来是我一直在往上下文里塞废话”。如果你也搞大模型应用开发迟早会撞上同一个问题如何压缩token。这词听起来像某个底层优化技巧实际上它就是一门“给上下文减脂”的手艺直接决定了你的接口成本、响应速度甚至模型会不会突然失忆。这篇文章我会把压token这件事拆成提示词、对话历史、长文档、系统参数四个层面附上我实测过的对比数据和踩坑记录适合正在做AI应用、写脚本调接口、或者单纯被上下文长度逼疯的人参考。1. Token为什么会失控先搞清楚烧钱大户长什么样1.1 一个token不是“一个字”很多人第一次看账单时会懵明明我就发了一句话怎么扣了这么多token因为token不是按“字”算的而是按“片段”算的。英文里一个单词大概对应1到1.3个token中文更离谱一个汉字经常要拆成1到2个token有些生僻字甚至吃掉3个。你用中文写一段200字的提示词实际消耗可能超过300个token这和“200个字”的直觉差距很大。更要命的是大多数模型对输入和输出是同时计费的。你发给模型的每一轮对话包括系统提示、历史记录、用户消息、工具返回结果全都算输入token。输出token反而是小事因为正常人不会要求模型吐一万字但输入侧很容易在不知不觉中堆出几千甚至上万token。1.2 上下文膨胀的四条主要渠道我把常见的token浪费场景归成四类你可以对号入座渠道典型表现浪费程度提示词冗余背景说明写了一大段核心指令只有一句中高多轮历史全量携带每次请求都把之前的对话原样重发极高长文档暴力拼贴把整份PDF直接塞进上下文极高输出约束缺失模型绕弯子说一堆没用的中拿多轮对话举例你问模型“昨天说的那个方案还有后续吗”它光看你这一句是答不上来的你必须把昨天的对话一起发过去。聊了30轮之后每轮平均几百token累计起来就是上万token的输入。这种场景下哪怕你提示词写得再精简历史记录也会把你的预算打穿。所以在动手压token之前先得接受一个事实token控制的关键不在“挤牙膏”而在“有所不送”。下面每一层策略本质上都是在教你判断什么东西可以不送。2. 提示词瘦身在源头就把token按住2.1 把“作文题”改成“填空题”我自己早期写提示词特别喜欢铺垫比如“你是一位经验丰富的资深数据分析师拥有十年以上行业经验请基于以下数据给出专业建议注意结合行业背景……”这一句话就吃掉近40个token而真正有用的信息只有“基于以下数据给出建议”。后来我改成了一种思路把提示词当成一个高密度的配置表而不是一段话。需求背景、角色设定、任务指令、输出格式分开写能省就省。比如角色数据分析师 任务基于给定数据输出3条结论 格式每条结论不超过50字标明依据 数据...同样的信息量这种写法能比长段落少用30%到50%的token。原因也很简单模型对结构化的内容解析效率更高它不需要靠上下文推断“你这段话里哪些是废话”而长段落里大量修饰词确实会被tokenizer拆成实实在在的token。2.2 Few-shot示例不是越多越好给模型塞几个示例few-shot确实能提升输出质量但每个示例都是实打实的token。我做了一个小实验一个分类任务不给示例时提示词约120token给1个示例变180token给5个示例直接飙到420token。而分类准确率从91%升到94%之后再加示例基本不涨了。所以我的实操建议是先给一个示例跑通再评估有没有必要加第二个。大多数任务下2到3个精心挑选的示例已经足够与其堆数量不如挑覆盖边界的极端例子。边界例子能让模型更快学会判断标准三个烂大街的正常样本反而没什么用。2.3 格式化输出能反向省token让模型输出JSON或Markdown表格看起来是增加输出token实际上是帮你减少后续处理成本。因为结构化的结果可以被程序直接解析你不需要再写一轮提示词让模型“把刚才的内容整理成表格”。但这里有个细节如果你用JSON务必在提示词里指定字段名和类型否则模型会自作主张加注释、加空字段甚至是加一段解释性文字。我见过最离谱的一次模型在JSON后面跟了一句“以上为分析结果如有疑问请随时提出”这不是它话多是我没告诉它“只准输出JSON”。只补了“不要输出任何其他内容不要使用markdown代码块”这十几个字输出token立刻砍掉三分之一。3. 多轮对话的账本管理缓存、摘要与选择性遗忘3.1 全量带历史的代价比你想象中高如果你做过聊天机器人一定遇到过这种情况对话进行到第20轮用户问“那你刚才说的第二个方案具体怎么实施”此时你要是不带历史模型根本不知道“第二个方案”是什么。于是你只能把20轮对话全部塞回去一轮请求直接吃几千token成本瞬间爆炸。更麻烦的是很多模型API对单次请求的上下文长度是有限制的。你辛辛苦苦聊了50轮某次请求可能直接报“超出最大上下文长度”然后整个对话直接崩掉。这就是必须做对话历史管理的核心原因不是“要不要压缩”而是“不压缩就没法继续聊”。3.2 滚动摘要法让模型自己给自己写小抄我目前用得最多的是滚动摘要法。核心逻辑是设定一个阈值比如最近5轮保留完整原文之前的全部压缩成摘要每次新对话进来时把旧的摘要最近几轮原文一起发给模型让它更新摘要。具体流程大概是维护一个摘要变量初始为空。每轮对话结束后把“旧摘要 本轮用户输入 本轮模型输出”喂给模型让它生成一个不超过200token的新摘要。请求时只发送“新摘要 最近5轮原文”而不是全部历史。这个方案的效果很直观假设原本第50轮请求要携带5000token历史现在只需“摘要200token 最近5轮约1500token”省了将近三分之二。代价是摘要本身会丢失部分细节如果用户追问“你第10轮说的那个数字是多少”摘要里可能没有。所以我一般还会在摘要里强制要求保留“关键数字、结论、待办事项”直接把这条写进摘要提示词里。3.3 缓存机制什么时候值得用现在有些API平台提供上下文缓存功能意思是同一段前缀如果多次重复发送第二次起可以按更低的单价计费。这个机制对负载比较高的应用很有吸引力但它的核心条件是“前缀稳定”。什么叫前缀稳定比如你有一个固定的系统提示词加上一份固定的产品说明每次请求都原封不动地拼在最前面这就是可以被缓存的部分。如果你的前缀每次都在变那缓存几乎起不到作用。我试过把系统提示词和产品资料固定成前缀然后开启缓存跑了一周多轮对话应用。结果输入token的费用降了大概四成但前提是我严格保证前缀一致连末尾换行符都没改过。如果你的代码里会在前缀后面动态拼接用户昵称、时间戳之类的内容缓存效益会大打折扣这时候优先级应该放在做摘要而不是做缓存。3.4 选择性遗忘有些历史不值得留还有一种策略听起来有点“暴力”但实践中非常好用直接丢掉一部分历史。比如用户问完天气又问菜谱这两个话题之间没有任何关联你就可以在新的一轮请求里只保留菜谱相关的最近几轮把天气部分直接删除。判断哪些历史可以丢我用一个简单的规则如果一轮对话中模型输出不含任何“需要后续记住”的信息比如结论、编号、用户明确要求这轮就可以丢如果输出包含“我帮你把方案更新了”“编号是A123”这类内容就得保留。这个规则用代码实现起来也不复杂判断输出里是否出现“方案”“编号”“记住”“下一步”等关键词即可。宁可稍微多留一点也不要误删关键信息导致用户重复提问那反而更费token。4. 长文档处理的压缩思路先检索再喂料4.1 不问就不给检索增强的省token逻辑当你要让模型处理一份100页的PDF时最容易想到的做法是把PDF全文塞进上下文。先不说很多模型根本塞不下就算塞得下这也是一次性透支几万token的奢侈行为。检索增强的思路就一句话模型不需要读完全文只需要读它回答问题所需的那几段。比如用户问“这份报告里第三季度的销售额是多少”你可以先在本地把PDF转成文本切块并做向量检索把最相关的二到三块内容揪出来拼成一个几百token的上下文喂给模型。这样做的token消耗可能是暴力塞全文的十分之一甚至二十分之一。我第一次在一份80页的报告上做对比时全文大概3.2万token检索增强后只喂了1200token而模型生成的答案和全文模式几乎一样准确——因为用户问的是“第三季度”而第三季度的内容只分布在正文里某几个小节中。4.2 分块大小与重排细节决定省多少检索增强不是简单地“切割文档”分块策略会直接影响检索效果和token开销。我踩过的坑是分块太小检索到的内容碎片化比如一块只有100个字模型很难拼出完整上下文分块太大比如一块2000字检索到的内容又会带进来大量无关信息。目前我用的是滑动窗口分块每块500字左右相邻块之间重叠100字这样既保证内容完整性又不至于让单块太大。检索出结果后按相关度排序取前三块再按它们在原文中的顺序重新排列后拼给模型。如果不重排模型读到的内容顺序是乱的输出质量会明显下降。4.3 引用原文还是二次概括这个选择很多人容易忽略。你检索到三块相关内容总共1500字直接把原文拼给模型模型可以一字不差地引用细节。但如果你先把这些内容喂给一个“摘要模型”让它压缩成300字再喂给最终模型能再省一笔token但会增加一次摘要的API调用和延迟。我个人的取舍是如果最终答案里需要精确数字、法规条文、专有名词就用原文如果只要求“概括大意”才做二次概括。有一次我为省钱做了二次概括结果用户问“报告里明确说的截止日期是什么”摘要模型把日期弄错了最终答案跟着错。从那以后凡是涉及数字和日期的内容我再也不做二次概括。5. 参数层面的精打细算别忽略模型设置和结构设计5.1 max_tokens和温度限制输出也是压缩输入token往往被盯得很紧但输出token同样花钱。很多人调接口时把max_tokens设成4096结果模型真的给你吐一篇三千字的废话而你要的只是“一句话结论”。把max_tokens从默认的偏大值调到一个符合任务需求的数值是成本直接下降最快的操作之一。同时把温度调低一点比如0到0.3模型会更倾向于简洁地直接给答案而不是发散性地展开。注意这只是“倾向”不是硬约束。真正硬约束是靠提示词里的“请用三句话以内回答”和max_tokens双管齐下。5.2 Stop序列让模型在正确的地方闭嘴Stop序列是个容易被忽略的省钱工具。它的作用是告诉模型“生成到这里就停”。比如你要求模型先给结论再给理由你可以把“理由如下”设成stop模型输出到“理由如下”就停止你再去程序里拼接后面的内容。更常见的用法是模型输出JSON时在生成完右括号后立即结束避免它后面跟一句“希望以上信息能帮到你”。你可以在API的stop参数里配置\n\n、}这类终止符。我实测过加了合理的stop配置后部分任务的输出token减少了20%到40%因为模型“想多说两句”的冲动被硬性截断了。5.3 换一个tokenizer更友好的模型这个方法比较“大开大合”但效果立竿见影。不同模型对同一段中文文本的token消耗差距挺大有的模型中文优化做得好同一句中文只占十几个token有的模型则可能吃掉二十几个。我在一个实际项目里把模型A换成模型B后者对中文更友好同样的系统提示词和用户输入输入token直接下降了35%。有些平台的模型列表里会标注“中文优化”“高性价比上下文”值得多花时间试几个版本跑一组真实的业务文本做对比别只看基准测试分数。下面是我在某个内部任务上做的一组对比同一段提示词和用户输入约300字中文模型输入token输出token相对成本模型A通用型2891521.00模型B中文优化1861410.62模型C复杂推理型3641681.35这个测试结果不代表所有场景但它说明选模型本身就是一个token压缩手段而且不牺牲任何精度就能省下三成左右的开销。6. 我的实测记录压缩前后的成本和效果对比6.1 一个客服机器人的改造过程我用一个实际的客服机器人数据来展示压缩前后的差距。这个机器人需要携带“产品手册摘要 最近对话历史”来回答用户问题。改造前的做法是产品手册摘要直接放2000token对话历史全量保留最多时一次请求要吃5500token。我按前面说的策略逐项改造产品手册从2000token压到600token砍掉章节导语、重复说明只保留参数和常见问答对话历史改成摘要最近3轮平均从3000token压到800token输出加了max_tokens限制和stop序列平均输出从250token降到110token换用中文友好的模型版本token数在此基础上再降约20%。最终单次请求的平均token数从5500降到大约1300成本直接降了四分之三。用户对话体验没有明显变化唯一要注意的是某些细节问题如果太早之前的对话会答不上来但客服场景里90%的咨询都集中在最近几轮影响不大。6.2 压缩过程中踩过的几个“反弹坑”第一个坑是摘要提示词本身太啰嗦。我为了让摘要“全面准确”把摘要提示词写了200多token结果每轮生成摘要也要花200token输出反而比原来更贵。后来我把摘要提示词精简到80token指定“只保留数字、结论、待办”开销立刻降下来。第二个坑是过度压缩导致用户问“我刚才说什么来着”。有一回我把历史压到只剩摘要用户突然问“我最早提到的那个需求你记得吧”摘要里确实有“用户提到需求”但没细节模型只能瞎猜。为此我在摘要规则里加了一条“必须保留用户原话中的关键短语”虽然摘要token涨了几十个但有效防止了回答跑偏。第三个坑是分块与重排的逻辑缺陷。我在长文档处理里有一版代码把检索到的块按相似度从高到低排列结果模型看到的上下文逻辑断裂答非所问。改成按原文章节位置重新排序后效果立刻恢复。这个“重排”步骤不能省否则你以为是token优化问题其实是信息顺序问题。6.3 什么场景不推荐用力压缩并不是所有任务都需要极致压缩。比如你让模型做创意写作、头脑风暴token多一点反而有助于模型发挥。再比如复杂的多步推理任务上下文里缺了某段中间过程模型可能给出看似合理但错误的结果。这类场景下我宁愿多花点token也不希望模型因为“没看到关键前提”答错。另外对响应延迟敏感的应用也要权衡。摘要和检索本身要额外调用一次模型或跑一次向量查询如果用户等着你秒回这些前置环节可能让延迟翻倍。在成本和速度之间不是越省越好而是省得合理。我在实际项目里的原则是用户随时可能追问细节的对话最少保留摘要加三到五轮原文一次性问答任务能检索就绝不全文塞入输出侧统一卡max_tokens和stop。这套组合拳打下来既不会让代码复杂到失控又能把账单控制在一个可接受的范围。最后再分享一个容易被忽略的小技巧把你常用的系统提示词和用户提示词丢进tokenizer工具里看一眼你会发现很多“看起来很短”的文字实际token数高得吓人。像“以下是一些关于项目背景的补充信息供你参考”这种话一个字一个字地拆出来都是钱。把这类废话从所有提示词模板里清掉不做任何架构调整账单也能肉眼可见地往下掉。这就是压缩token最朴素也最容易被忽略的开始。