
做完一整年的 Token 节省实战之后我可以很负责任地说大部分 AI 应用的 API 成本问题根本不是模型单价太贵而是 Token 消耗失控造成的。我见过一个调用次数只涨了不到两倍、账单却翻了五倍的典型项目拉出 usage 明细一看50% 以上的 Token 都花在了重复的历史消息、冗长的系统提示词、以及模型多写的那些废话上。这篇文章不聊空泛的概念只讲我实际踩过坑之后攒下来的方法论Token 都流向了哪里、省 Token 的正确思路、可以直接落地照抄的实操方案、以及我在实测中整理出的排查技巧。如果你正在做 AI 应用开发、在给产品接大模型 API或者只是想把 API 账单压下来一部分这篇都值得多花几分钟读完。1. 先弄清楚钱都花在哪了Token 流向拆解1.1 输入 Token 与输出 Token 的价差被大多数人忽略市面上主流的大模型 API 基本都是按 Token 计费但输入和输出的单价不一样输出 Token 往往会比输入 Token 贵出好几倍。很多人只记得“我发了多少次请求”却忽略了每一次请求其实由三部分组成系统提示词、对话历史、当前输入。这三部分全都算在输入 Token 里每次调用都会完整带一遍。举个例子你设置了一段 500 Token 的系统提示词那么用户无论问一句“你好”还是抛一段 5000 字的长文这 500 Token 都会原封不动地计入每一次调用。用户多了之后这就是一笔固定的“过路费”。它不会因为你这段提示词写得好就免费也不会因为模型已经记住它就打折。很多团队在优化时只盯着“输入内容”的各种改动却忘了把系统提示词的体积单独拿出来做一次体检。我自己习惯的做法是把一段 system prompt 扔进模型对应的 tokenizer 库里数一遍凡是超过 300 Token 的一律进入精简流程。后面我会专门讲怎么压。1.2 多轮对话历史的“复利效应”这是隐藏最深的消耗黑洞。很多人以为多轮对话就是“把之前的话继续带上”但实际上每一轮的输出和输入都会完整累积越往后越膨胀。我整理了一个典型场景的消耗变化大家可以感受一下。假设系统提示词 500 Token用户每轮输入 200 Token模型每次输出 150 Token并且对话历史全部保留。那么每一轮的消耗是这样的轮次本轮请求带入历史本轮输入模型输出本轮消耗累计消耗1500200150850850285020015012002050312002001501550360041550200150190055005190020015022507750103650200150400024250第 10 轮时一次请求就要带上 4000 个 Token而其中真正有用的信息可能只有最后两轮。这样的对话如果每天跑几万次费用增长是几何级别的。更夸张的是很多产品把对话历史从第一轮就一直带着用户聊 50 轮之后一次请求可能就要吞掉几万个 Token。我后来做了一个改动第 3 轮之后把早期对话压缩成摘要只保留最近 2 轮完整内容 一段 100 Token 的摘要。同样算到第 10 轮累计消耗从 24250 降到了约 9300直接省了六成左右用户体验几乎没有变化。1.3 工具调用、结构化输出与隐藏开销除了历史累积还有一个容易被忽略的地方工具调用和结构化输出。给模型配置工具函数时函数名、参数描述、JSON Schema 都会作为系统消息的一部分参与计费。如果函数定义写得特别啰嗦一个工具就可能吃掉几百 Token。我见过一个项目挂了 8 个工具光函数定义就占了 2000 多 Token每次调用都要全额带上哪怕用户只是问了一句“今天天气怎么样”。结构化输出同样如此。强制模型输出 JSON 时模型为了满足你的键名要求会把summary、title、content这些字段名都当作真实 Token 输出。字段名越长、嵌套越深消耗就越大。更麻烦的是你让模型“自由发挥”时它更容易写出一大段解释性文字这些解释其实大多不是产品想要的。所以我把所有非聊天类的接口都改成了“只输出必要字段”的严格约束让模型少说客气话、少写总结语。后面第 3 部分我会把具体的提示词写法放出来。2. 省 Token 的正确方向先从思路下手2.1 第一步不是优化 Prompt而是决定要不要调用大模型我在优化 Token 时拿到的最重要结论是省 Token 的最高效手段是减少不必要的模型调用。很多简单需求根本不需要大模型。比如某问答系统里有 30% 的问题是“今天几号”“帮我设置提醒”“这个按钮在哪”这类问题完全可以先用规则、正则、白名单命中。只有规则判断不确定的才交回给模型。这里不需要多聪明的模型只需要一个非常准确的本地意图匹配模块。我还试过另一个思路对用户输入做一层“前置拦截”。当问题与当前业务无关时直接给一句固定话术不触发模型调用。实测下来这个拦截把整个系统的月度 Token 消耗砍掉了一半。因为省掉的不只是输入 Token模型没有输出本身就是最大的节省。当然前置拦截会带来误伤风险。我踩过的坑是刚开始白名单写得过严把很多实际问题误判成了“无关问题”用户反馈体验变差。后来我把判断逻辑改成“优先放行不确定才拦截”把误伤率压到了 4% 以下。做这类功能一定要看线上真实日志不能拍脑袋定阈值。2.2 按任务难度分档选模型别拿旗舰模型处理所有请求这是成本优化里最该做、却最容易被忽略的一环。一个 AI 应用会有多种任务类型意图识别、实体提取、文本改写、答案生成它们需要的模型能力完全不同。我手头有个问答项目最初的方案是任何问题都发给当时能用的最强模型一个月下来费用居高不下。后来我改成了三段式流水线先用一个小巧且便宜的模型做意图分类再让轻量模型抽取关键词最后才在需要总结和推理的环节调用强模型。这样一个 500 Token 的输入请求前两步大约只花 200 Token只有真正要生成答案时才走大模型。整体成本下降超过 60%。模型分档的核心原则是复杂任务交给强模型简单任务交给轻量模型能不用模型就不用模型。每个模型的输出风格和价格都不一样选型前最好先做一次“任务难度分级表”把产品里的所有模型调用点列出来逐个标记难度与所需能力再决定用哪一档。2.3 让上下文“只装必要物品”省 Token 的底层逻辑其实一句话就能说清上下文里只放与当前任务相关的信息而不是把过去所有内容都塞进去。我早期犯的错就是把“对话越完整越智能”当成了真理结果模型确实是智能了但账单也智能地涨了上去。实际上模型在前面对话中接收的很多冗余信息并不会让它更聪明只会让它更贵。人类的即时记忆也是有限的你不会记得三周前同事随口提的那句闲话但你会记住“项目下周演示”这个关键节点。上下文管理本质上是模拟这种“有限但高效”的记忆方式。我通常采用三条策略的组合近 N 轮保留完整原文更早的历史压缩成摘要必须长期记住的事实比如用户偏好、项目状态单独抽取出来存成结构化字段。这样既保住了跨轮记忆又避免把全文反复喂给模型。这个设计思路我在第 3 部分会拆开讲。3. 可落地的 Token 节省实操方案3.1 系统提示词精简少写废话多写约束系统提示词是每次调用都会全额计费的固定开销但它往往也藏着最多的水分。我见过不少从网上复制下来的系统提示词动不动几百 Token里面全是“你是一个 AI 助手请尽可能详尽地回答如果不知道请坦诚说明”这类话。这类话该删就删模型的能力本身已经包含这些行为倾向你写进去只会白白付钱。一个比较典型的前后对比优化前约 800 Token你是一个专业的智能客服助手。请理解用户的意图并从知识库中寻找相关答案。回答问题时请做到准确、客观、简洁但又要完整尽量提供足够的细节。如果遇到不确定的问题不要胡乱猜测应该如实告知用户。如果用户没有提供足够的信息请引导用户补充描述。回答完毕前请总结你的结论。优化后约 220 Token你是客服助手仅回答与产品相关的问题。信息不足时直接说不清楚不要编造。回答格式结论先行最多补充两条要点。控制总字数不超过 100 字。看出差异了吗优化前有很多“准确、客观”这类无法量化、模型本来就会尝试满足的空洞要求还有“总结你的结论”这种等于在暗示模型多输出一段内容。优化后每句话都是明确约束只答产品问题、不能编造、控制长度、格式固定。省下的是 580 Token 的固定成本输出还更稳定了。我建议把系统提示词当成代码来维护定期跑一遍 tokenizer 统计体积超过 300 Token 就自动告警同时把“不太可能改变用户行为的描述”全部删掉。3.2 对话历史的取舍与摘要化处理多轮对话时我不建议直接“保留最近 N 轮”就收工因为这仍然是在盲猜。更稳的做法是给每一段历史标上它的实际用途再决定保留还是压缩。我的常用配置是最近 3 轮对话保留原文用于保证连贯性更早的对话交给一个轻量模型生成摘要然后再把摘要放回上下文。摘要本身也要限制长度否则摘要反而会成为新的重量级历史。我一般把摘要控制在 100 Token 以内只保留“与用户目标高度相关”的事实。给你一段我实际在用的摘要压缩指令请阅读以下对话记录提取与当前任务相关的关键信息用户偏好、已确认结论、 待办事项压缩成不超过 100 token 的摘要。忽略寒暄与无关细节。 只输出摘要正文不加前缀。这个方案需要额外一次轻量模型调用但它带来的收益通常远大于成本。以之前那个模拟数据为例第 10 轮时全量保留要花 24250 Token摘要化后只要约 9300 Token节省接近三分之二。而且越往后的轮次节省比例越明显。还有一个小技巧在切换话题时主动触发摘要。比如检测到用户表达中出现了与上一轮明显不同的关键词立即把上一段对话压缩归档而不是继续在新话题里反复背着旧话题的历史包袱。3.3 输出控制让模型少说话输出 Token 比输入 Token 贵得多所以限制输出长度是性价比最高的优化手段之一。很多开发者在提示词里写“请简洁回答”但模型对“简洁”的理解往往比你宽松得多。要真正控制输出必须给出明确的上限和格式。我在做结构化接口时会在请求参数里把最大输出 Token 数直接写死同时在提示词里要求“只输出 JSON不要输出解释”。比如请分析用户反馈的情感倾向只输出 JSON {sentiment: positive|negative|neutral, keywords: [关键词]} 不要输出任何其他内容。这样模型基本会老老实实输出 100~150 Token而不是像之前那样先来一句“根据您提供的反馈内容我认为……”再给结论最后又补一句“如有其他问题欢迎继续咨询”。那类客气话全是纯消耗。输出控制还有一个容易被忽略的点关闭或禁止模型对你的指令做复述。比如你问“请把下面这段文字翻译成英文”如果要省 Token把“好的我将为您翻译如下”这段复述直接禁掉。只要在提示词末尾加一句“不要复述指令直接给出结果”就能稳定省掉 20~50 Token千万别小看这个数字高频接口一天下来就是几十万的差距。3.4 Prompt 缓存的实际用法Prompt 缓存是很多人没启用或者没用好的一项能力。它的原理很简单同一个请求前缀比如系统提示词 示例在短时间内被反复调用时服务端会用缓存结果直接省去重复处理缓存部分的计费价格通常远低于常规输入价格大约只有常规价的十分之一甚至更低。最典型的适用场景是固定系统提示词 固定示例 动态用户输入。你把固定的部分全部放在请求开头确保它们逐字节一致这样缓存命中率会非常高。动态内容比如用户答案、时间戳放到固定前缀之后。我算过一笔账假设系统提示词加示例共 1000 Token每天有 1 万次调用一个月按 30 天算就是 3 亿次“有效前缀 Token”。如果启用了缓存这部分费用差不多降到原来的 10%光这一项就能省下六位数级别的小目标。启用缓存时最需要注意的是“前缀严格一致”任何多出来的空格、换行都可能导致缓存失效。我之前就因为“末尾多一个空行”这种问题辛苦配置的缓存命中率几乎为零后来用日志对比才发现差异。建议在代码里把固定前缀用统一常量拼接不要手写模板去拼。3.5 检索侧 Token 控制RAG 场景的裁剪与重排如果你的应用是 RAG检索增强生成架构那么检索到的知识片段同样会全部计入输入 Token。知识库检索最常见的浪费方式是一口气召回 10 个片段每个片段 500 字拼接到上下文里就是 5000 字。但真正对回答有帮助的可能只有其中两三个片段。我在项目里用的处理办法有三个一是把检索数量top-k从 10 降到了 5大部分场景下答案质量并没有下降二是把每个命中片段裁剪到 300 字左右去掉开头结尾的冗余说明三是加上一层“重排”逻辑用一个小模型对已命中的片段按相关性排序只把相关性高的片段放进上下文。这三步改完我的 RAG 接口单次输入 Token 直接少了一半同时因为噪声减少回答准确率反而提升了。这里想强调一个反直觉点不是喂给模型的信息越多越好信息越杂模型越容易生成“看似全面但实际跑偏”的答案。控制检索内容的精度既省了钱又提升了质量属于一举两得。4. 实测中踩过的坑与排查技巧4.1 常见问题速查表实践过程中我踩了不少坑这里整理成一张速查表方便你直接对照排查。现象可能原因排查方法解决办法调用量没涨多少账单却暴涨对话历史全量携带 / 长文被反复处理拉取 usage 日志按接口统计历史摘要化限制单轮请求长度接口输出时多出一大段解释文字提示词没有约束输出格式看单次请求的 output_tokens 是否异常加 max_tokens 限制明确“直接输出结果”请求参数没变但费用不稳定缓存未命中 / 前缀被动态内容污染对比相同前缀的计费差异统一常量拼接固定前缀动态字段放末尾接完 RAG 后成本不降反升检索片段过多、拼接后超长检查检索日志中的拼接 Token 数调小 top-k裁剪片段加重排过滤明明选了小模型账单还是高部分错误请求没有走正确路由查看调用日志里每个接口实际使用的模型加模型路由中间层统一拦截摘要替代历史后用户感觉“失忆”摘要粒度太粗关键信息被合并掉了抽查摘要内容与原始对话的匹配度分主题压缩保留用户偏好等关键实体其中最坑的是“缓存命中失败”这种问题。表面上看一切都正常但日志里缓存命中率长期为 0。后来我把完整请求体打印出来才发现固定前缀里居然混了一段时间戳导致每次请求内容都不同。这类问题只有通过日志对比才能暴露别指望肉眼盯着代码看一眼就能发现。4.2 用量统计与异常定位流程省 Token 是一个持续优化的过程不是改完一版就完事了。我建议在项目里接一层最基础的用量统计每次调用模型时把请求里的 prompt_tokens、completion_tokens、模型名称、接口名称、用户标识一起落到结构化日志里。有了这些数据才能做后续的对比分析。我习惯用一个很简单的聚合逻辑来定位异常# 伪代码按接口和模型维度聚合每日 Token 消耗 daily_stats {} for record in daily_logs: key record[api_name] | record[model_name] daily_stats[key][input_tokens] record[usage][prompt_tokens] daily_stats[key][output_tokens] record[usage][completion_tokens] daily_stats[key][calls] 1 # 再按“单次平均消耗 总额 / 调用次数”排序 avg_cost total_tokens / calls这个排序表会把平均单次消耗最高的接口排到最前面然后你一眼就能看出哪个接口是“单次消耗异常大户”。我以前就是靠这个表发现某个搜索接口单次平均带了几千 Token 的历史消息但其实它根本不需要历史改成无状态调用之后成本立刻降下来了。除了按接口维度还可以按用户维度看看。有些用户会开着客户端挂机反复触发重试这些请求会产生大量无效消耗。给这类场景加一个冷却时间或者手动刷新机制也能避免不小的浪费。最后一个建议是设置预算告警。把每天的 Token 消耗同步到一个简单的计数器里超过设定阈值就发告警比如“今日输入 Token 已消耗 500 万请检查是否有异常”。不要把告警设得过于频繁一个月看一次整体趋势就够了否则你自己也会被告警淹没。做了这么久 Token 节省我最深的体会其实是这不是一次性工程而是持续巡检。每次改完版本我都会重新拉一遍 usage 数据看看有没有新的浪费点冒出来。这套功夫不需要很深的技术功底但它决定了你的 AI 应用是能长期跑下去还是被账单拖垮。从今天开始先把你线上模型的 usage 日志打出来按接口排个序大概率你也能立刻找到一两个可以优化的“黑洞”。