十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent 成本控制实战:从 token 压缩到上下文与缓存优化

Agent 成本控制实战:从 token 压缩到上下文与缓存优化 1. 今日热榜的真实信号Agent 不缺新能力缺的是成本控制1.1 热榜扫下来的三个直观印象今天我照例花了十几分钟把 GitHub 今日热榜从头翻到尾越看越觉得有意思。前几个月 Agent 项目霸榜靠的是新概念什么能自主写代码的、能自动浏览网页的、能处理视频的标题一个比一个猛。但今天的榜单明显换了一种气质大量项目不管底层用什么框架宣传点都收敛到同一件事上省 token。直观印象有三个。第一个印象Agent 项目从秀能力进入拼成本阶段。同一个 Agentdemo 里跑一次成功不稀奇稀奇的是能在生产环境连续跑几百次还不把预算烧穿。热榜上几个项目都直接晒出了任务成本下降百分比这种数据在前几个月是很少见的。第二个印象token 用量已经从技术指标变成了产品指标。过去聊 Agent 大家关心的是回答质量、工具调用成功率现在话题明显转向了上下文压缩比、缓存命中率、模型路由节省了多少成本。GitHub 热榜的选题方向某种意义上就是开发者群体当前焦虑点的投射。第三个印象也是最让我有共鸣的一点大家都在尝试把能力很强但很贵的大模型包装成能力够用且便宜的生产组件。热榜上的项目不管是做了 prompt 压缩、上下文管理还是结果缓存本质都在做同一件事把大模型的 token 消耗从不可控变成可控。这对我这种天天跟 Agent 打交道的人来说是个非常积极的信号。说明 Agent 真的要开始解决真实问题了而不是继续停留在能跑通 demo的层面。1.2 为什么偏偏是现在省 token成了主线这波趋势背后有几个直接推手。第一个推手是模型能力跃迁带来的大胆预期。今天热榜相关的讨论里围绕下一代模型的代际跃迁预期非常强烈。模型变强之后开发者敢把更复杂的任务交给 Agent 了多智能体协作、长周期任务、自主规划执行以前觉得不靠谱的场景现在都敢尝试了。但任务复杂化带来的直接代价就是 token 消耗呈指数级增长账单先扛不住了。第二个推手是 token 用量的三本账。我平时估算一个 Agent 项目的成本至少要看三块单次调用的直接推理成本这个最直观。上下文增长导致的重复计费。Agent 每多跑一轮工具调用就要把之前的历史记录重新发给模型历史越长单轮成本越贵。失败重试的隐藏成本。任务一旦中断之前烧掉的 token 全部作废重试又要重新烧一遍。我经常给团队举一个例子一个需要 10 轮工具调用的 Agent 任务如果每轮都把完整历史塞进去总 token 消耗可能逼近单轮调用的 20 倍。场景单轮输入 token调用轮数累计输入 token备注单轮问答无历史1,00011,000基线5 轮工具调用保留完整历史1,000 递增5约 15,000历史重复计费明显10 轮工具调用保留完整历史1,000 递增10约 55,000长任务接近失控10 轮调用每轮只传摘要1,000 500 摘要10约 15,000压缩后成本可控这个表格只是示意实际情况中历史长度增长是非线性的尤其当工具返回大段文本时膨胀速度比你想象中快得多。这也是为什么省 token能从锦上添花的优化项摇身一变成为 Agent 工程化的生存刚需。2. 热榜项目里最常见的省 token 打法输入侧做减法2.1 Prompt 不是越详细越好而是越按需越好很多人对 prompt 有一个误解system prompt 写得越详细模型表现越稳定。实际上在 Agent 场景里system prompt 是一个常驻成本项每次调用都要完整发送一遍。如果你把几十条工具使用规则、几十个历史对话示例、全套输出格式说明全都塞进 system prompt那么哪怕用户只问了一句今天天气怎么样你也要为那一大堆静态文本买单。热榜上那些做完 token 优化的项目几乎都做对了同一件事按需组装 prompt。我把自己的做法拆开讲一下。我的 system prompt 分成两层第一层是固定不变的骨架包括角色定位、输出纪律、安全边界第二层是会根据当前任务动态生成的内容包括本轮要用的工具列表、当前任务相关的少样本示例、用户最近的明确诉求。第二层每次调用前都会重新计算绝不把上一次任务的冗余信息带进来。少样本示例的取舍更重要。我之前整理过一组数据一个含 8 条示例的 prompt 比含 3 条示例的 prompt 在简单分类任务上准确率几乎没差别但 token 开销高了快一倍。所以现在的原则是示例只保留与当前任务类型匹配的 2 到 3 条宁可少放绝不滥放。2.2 代码与文档场景的表示层优化Agent 最常见的生产场景是处理代码仓库和长文档。很多刚上手的人会直接把整个文件、整个目录甚至整个仓库塞给模型认为信息越全回答越准。这个思路在文件很小的时候没问题但一旦文件超过几十 KB或者仓库里有几百个文件token 会瞬间爆炸而且模型的注意力会被无关内容稀释回答质量反而下降。正确做法是把原始信息转换成索引 按需切片。我现在的套路是三步走先给模型一个目录树或者仓库地图让它知道哪里有什么。模型根据任务定位到具体文件再按需读取文件的关键片段。只把真正相关的代码段落拼进上下文其余全部用文件路径引用代替。这个思路和搜索引擎很像不要指望模型一口气读完整个图书馆而是先给它一张索引卡再按需取书。热榜上不少 Agent 项目已经在做这件事了有的还内置了基于嵌入向量的代码检索组件效果比我手动切片稳定得多。2.3 工具调用的定义精简Function calling 是 Agent 调用外部能力的核心通道但工具定义本身也是 token 消耗的大头。每个工具的 JSON Schema 都会被序列化成文本发给模型工具越多、描述越长每次调用的基础开销就越大。精简工具定义有四个原则参数描述只写必要信息比如用户邮箱地址不用写用户注册时填写的邮箱地址用于接收系统通知和找回密码这种废话。参数尽量用 enum 限定取值范围一方面省 token另一方面减少模型自由发挥导致的调用失败。描述里不要重复参数名模型的 schema 解析已经能看到参数名了描述只需要补充参数名无法表达的信息。能合并的工具就合并比如 get_user_info 和 get_user_preferences 可以考虑合并成一个 get_user_profile。工具返回值的截断同样值得注意。很多工具接口返回的是完整数据对象比如一个列表接口可能返回 100 条记录但当前任务只需要前 10 条。我通常会在工具层做一层返回结果裁剪只把任务真正需要的字段回填给模型大段无关数据直接丢掉。这既省了模型的处理 token也避免了上下文被噪音污染。3. 上下文窗口管理把有限的空间留给真正重要的信息3.1 滑动窗口解决不了长任务的失忆问题滑动窗口是目前最简单粗暴的上下文控制方案只保留最近 N 轮对话更早的对话直接丢弃。它的优点是可预测、实现简单缺点也很明显——模型会失忆。用户在第 2 轮说过的一个重要约束可能在第 8 轮就被滑出窗口了然后 Agent 就会一本正经地做出违背用户要求的操作。我的经验是滑动窗口只适合短会话场景比如客服机器人单轮问答、一次性代码生成这些任务根本不需要跨轮记忆。但只要是长周期任务比如帮我把这个仓库的 TODO 全部整理并实现滑动窗口就会频繁掉链子。更靠谱的做法是窗口 关键信息摘录的组合。长任务运行时把用户的核心诉求、已经确认的决策、未完成的事项这三类信息固定在窗口内窗口滑动时优先保留它们普通对话记录先被淘汰。这相当于给 Agent 做了一次重点记忆。3.2 摘要压缩的正确姿势不是随便让模型总结一下触发式摘要压缩是长上下文场景里性价比最高的方案之一但很多人用错了。最常见的错误是让模型把之前的对话总结一下然后直接把总结塞回上下文。这样做出来的摘要往往缺少任务推进所必需的结构化信息模型读完之后依然不知道任务做到哪一步了。我自己在项目里用的是分层摘要而且摘要模板是固定的。对话层摘要记录最近几轮的交互要点任务层摘要记录目标、已完成步骤、当前阻塞点会话层摘要记录用户的核心诉求和偏好。每一层摘要都有固定模板当前目标xxx已完成1. xxx 2. xxx未完成1. xxx 2. xxx当前状态正在等待 xxx 结果用户关键约束xxx关键触发时机也很重要。我习惯在上下文的 token 用量达到模型窗口上限的 70% 时触发摘要压缩而不是等到报错才处理。压缩完成后把原始对话从上下文中移除只保留摘要和最近一轮的完整内容这样既能延续任务状态又不会让上下文无限膨胀。3.3 子任务独立上下文让每个 Agent 只操心自己的事多 Agent 协作框架今天在热榜上很常见但我发现很多实现只是把一个超长上下文拆成了多个中等长度的上下文总 token 并没有省下来反而因为信息重复传递变得更贵了。省 token 的正确姿势是子任务完全隔离上下文。每个子 Agent 只接收与它职责相关的输入执行完后只输出一个精简结果由主 Agent 负责汇总。子 Agent 不需要知道整个任务的前因后果它只需要知道我这一个子任务的目标是什么、输入是什么、期望输出什么。这个设计很像一个项目经理带着几个外包工程师干活。项目经理掌握全局信息外包工程师只需要拿到自己那一块需求说明就行做完提交一个阶段性成果没有人需要把整本需求文档背下来。实际落地时我会给每个子 Agent 单独设置 context budget。比如主 Agent 的上下文预算是 20k token每个子 Agent 的预算是 8k token子 Agent 跑完就销毁上下文只把结构化结果传回主 Agent。这样即使子 Agent 数量很多总 token 消耗也是可控的。4. 从热榜反推落地方案token 账本、缓存与模型路由4.1 先量化再优化给项目建一个 token 账本我见过太多人一上来就搞 prompt 压缩、上下文优化折腾半天却说不清楚到底省了多少。没有量化就没有优化。不管项目大小我都会建议先建一张 token 账本把每次调用的关键指标记录下来。字段层面我至少会记这些字段说明典型用途timestamp调用时间观察成本波动趋势model实际使用的模型分析模型成本占比input_tokens输入 token 数定位输入膨胀output_tokens输出 token 数定位输出浪费cache_hit是否命中缓存评估缓存效率task_type任务类型按任务分析成本cost_estimate估算费用直接看预算消耗有了这张表之后很多问题一眼就能看出来。比如哪个任务类型的输入 token 增长最快哪些调用其实重复了 N 次输出 token 是否经常超过实际需要。我自己的项目里每次模型调用都会打印一行结构化日志后期写个小脚本聚合一下比凭感觉优化靠谱一个量级。关于热搜词里反复出现的credits 和 token 的换算问题这里一起说清楚没有一个通用的换算比例。credits 是各家开放平台自己的积分体系token 是模型实际处理文本量的单位两者之间的兑换比例取决于平台定价和模型规格。比如某个平台定价是每百万输入 token 折算 0.15 credits、每百万输出 token 折算 0.6 credits那么 2500 credits 能换多少 token 取决于你输入输出的比例。所以看到2500 credits 相当于多少 token这种问题时正确思路不是找一个固定数字而是根据自己任务的实际输入输出比例反推。随便信一个现成答案大概率会算错预算。4.2 缓存策略能不进模型的就不进省 token 的终极手段不是压缩而是让一部分请求根本不进模型。缓存是这个思路最直接的实现。第一层是精确缓存。相同请求直接复用上次的结果。适合的场景包括固定代码片段的解释、常见配置项生成、FAQ 问答。用一个简单的哈希 key 就能实现key 由 model、prompt、temperature、tools 定义拼接而成命中直接返回零 token 消耗。第二层是语义缓存。精确缓存解决不了问题表述不同但意图相同的场景这时候需要把用户输入向量化在向量数据库里做相似度检索命中高相似度历史问题后直接复用答案。这一层会消耗一次向量化计算的成本但相比完整的大模型推理便宜得多。第三层是局部缓存也就是把 prompt 里经常不变的部分做缓存。比如一份很长的企业知识库说明如果每天的内容是一样的完全可以做成内容版本号 向量索引的形式而不是每天重新传给模型。很多 Agent 框架已经开始支持 prompt 的 KV cache同样一段前缀文本只需要计算一次后续调用可以直接复用。这三种缓存按性价比排序精确缓存最便宜语义缓存次之局部缓存工程复杂度最高但省得最多。4.3 模型路由小模型先上大模型兜底另一个被热榜项目反复验证的方案是模型路由。核心思路很简单不是所有任务都需要顶级模型先让便宜的小模型处理搞不定的再升级到大模型。我在生产项目里常用的是一个三级路由意图分类器先判断任务类型比如闲聊简单问答代码生成复杂推理。简单任务直接交给小模型响应快、成本低质量也够用。小模型置信度低或者任务本身就属于复杂推理类再升级到大模型。这里的关键是升级判定怎么设计。我的做法是在小模型的输出后加一个质量评估步骤让模型对自己的回答打一个置信分低于阈值才升级。虽然质量评估也会消耗少量 token但相比直接全程用大模型整体成本仍然能省一大截。模型路由还有一个额外的好处延迟更稳定。小模型的推理速度通常更快用户体验也更好。唯一要注意的是路由判断本身不能太复杂否则分类器的成本会吃掉省下来的钱。我的经验是把路由规则做成可配置的规则引擎而不是让路由逻辑也变成一次昂贵的大模型调用。5. 绕不开的坑token 失效与登录态恢复实战5.1 token exchange failed 403 的完整排查链路热榜上 Agent 项目再多也躲不开一个实际问题Agent 在跑批任务时突然抛出一句token exchange failed: token endpoint returned status 403 forbidden然后整个流程就中断了。这类报错在 GitHub API、第三方登录、OAuth 集成场景里非常常见而且消息里的 403 特别容易让人误以为是不是权限不够。我整理了一套完整的排查链路遇到这类问题按顺序走一遍基本都能定位。第一步区分 401 和 403。401 表示你是谁的问题通常是 token 过期、格式错误403 表示你被允许做这件事吗的问题通常是权限不足。但 token exchange 的场景里403 反而经常指向凭证本身不被认证服务器接受而不是业务权限。这个反直觉的点最容易坑人。第二步检查系统时间。JWT 的签发时间和过期时间依赖服务端校验如果本机时钟偏差超过一两分钟刷新令牌的签名验证就会失败返回 403。很多线上疑难杂症最后都栽在这个细节上。第三步核对授权范围。打开创建 token 时的授权页面确认当前 token 勾选了请求所需的最小权限集。Agent 调用 GitHub API 时如果要读写仓库token 里必须有对应的repo和workflow权限缺一项就会出现 exchange 失败。第四步检查 token 是否已经被吊销或替换。如果之前在 GitHub 安全日志里手动吊销过 token或者重新生成了 token旧值会立刻失效。很多 Agent 项目把 token 硬编码在配置里一旦轮换密钥全链路都会报错。提示某些认证服务端返回 403 时会附带一段简短的原因码比如country或invalid_grant。原因码是服务端策略侧的判断遇到后不用慌优先检查凭证本身是否有效再确认当前环境是否符合服务端的接入要求。码块本身只是告诉你这次交换被拒绝了,并不代表你的项目和代码有问题。5.2 JWT 续签机制Agent 自动恢复的最后一环解决了单次报错之后更核心的问题是Agent 是无人值守的你不可能每次报错都手动去刷新登录态。所以必须在 Agent 内部实现一套登录态自愈机制这就是 JWT 双 token 续签的核心价值。双 token 机制大家应该不陌生access token 有效期短通常几分钟到几小时refresh token 有效期长用来在 access token 过期后换新。Agent 在调用 API 前如果发现 access token 即将过期就先用 refresh token 换一个新的然后再发起实际请求。但实际工程里刷新失败的情况远比文档里描述的复杂。我踩过的坑主要有三个。第一个坑是刷新请求本身失败时的重试策略。无脑立即重试只会加重服务端压力而且刷新失败往往是因为服务端临时不可用或网络抖动退避重试才是正确做法。我在项目里用的是指数退避第一次失败等 1 秒第二次等 2 秒第三次等 4 秒最多重试 5 次。超过上限直接进入重新授权流程发通知让用户手动登录不无限死循环。第二个坑是 refresh token 的存储位置。很多 Agent 项目把 refresh token 写在配置文件里这等于把长期有效的钥匙和锁放在同一个抽屉里。我现在的做法是存到系统密钥环配合环境变量注入至少保证不随代码仓库泄漏。第三个坑是 token 刷新与请求并发的竞态。多个子任务同时对同一个快过期的 access token 发起刷新会造成刷新风暴。我的解决办法是加一个全局的 token 管理器所有刷新操作走同一个互斥通道避免重复刷新。有了这套自愈机制Agent 遇到sign-in could not be completed token exchange failed这类问题时会先尝试静默刷新刷新成功就继续跑任务刷新失败再告警。实测下来因为登录态过期导致的 Agent 任务中断率能降到原来的零头。6. 框架与路线harness 和 agent 的边界以及我现在的选择6.1 harness 与 agent 到底是什么关系很多刚开始接触 Agent 开发的人会问harness 和 agent 到底啥区别这两个词在今天的 GitHub 热榜项目介绍里频繁出现但很多文档自己都没讲清楚。我的理解是agent 是思考者harness 是搭台子的人。Agent 负责感知环境、做决策、提出下一步行动harness 负责提供工具调用能力、控制循环流程、管理上下文窗口、执行 token 预算。你可以把一个 Agent 想象成一台发动机harness 就是搭载它的整车底盘底盘决定了发动机的发挥空间。这个边界理解到位之后你就会明白为什么省 token这件事通常应该在 harness 层解决而不是让每个 Agent 自己去抠。Agent 应该只关心我要做什么而怎么做才能省 token是 harness 的统一职责它决定何时触发摘要压缩、如何路由模型、怎么管理工具结果缓存。6.2 个人开发者的选型建议市面上的 Agent 框架分两派一派是重框架自带完整的 harness 实现缓存、路由、上下文压缩、可观测性全都内置了开箱即用另一派是轻框架只提供最小化的 Agent 循环其余都得自己搭。我的建议是刚入门的人先手写一个不带任何框架的最小 Agent就用最原始的while 循环 模型调用 工具函数实现一个能自主查天气、查数据库的小东西。这个过程会让你对 Agent 运行机制产生体感后面用任何框架都能理解它的设计动机。有了体感之后再用重框架你会发现自己能真正看懂它的配置项在干什么而不是囫囵吞枣地填参数。框架方面重点看三件事上下文管理是否内置、token 用量是否可观测、模型路由是否可配置。这三项直接决定你在省 token 这件事上要自己写多少代码。学习路线上我现在的建议顺序是先跑通一个最小 Agent再学 function calling 和工具定义接着实践上下文压缩和缓存最后研究模型路由和多 Agent 协作。不要一上来就铺十几个项目的框架那只会让你迷失在配置项里。7. 写在最后一次 token 爆炸事故给我的教训聊这么多方法论最后分享一个真实经历。早期我给团队做一个资料整理 Agent输入是一堆产品文档任务是自动提取要点生成周报。当时图省事直接把整份文档全文塞给模型一次处理几十页 PDF。第一次跑通的时候还挺兴奋结果月底一看账单吓一跳——光是这个 Agent 就烧掉了大量预算而且因为文档太长模型经常在输出到一半时截断重试又重复烧钱。后来我花了一个周末把方案改成了文档目录树 按需切片 分段摘要文档不再整篇送入模型只处理相关的几个片段最后再把各段摘要合并。同样的任务成本几乎降到了原来的五分之一周报质量反而更稳定了。从那以后我养成了一个习惯任何 Agent 项目上线前先跑 20 个典型任务样本记录 token 用量的分布把异常峰值解决掉再谈功能。现在每次刷 GitHub 热榜看到越来越多的 Agent 项目开始把省 token当成核心卖点我都觉得这个领域是真的成熟了。省 token 的本质不是抠门而是让 Agent 在有限的资源约束下稳定地完成更多真实任务。这个能力才是 Agent 从实验室走向生产环境真正的入场券。
返回列表