1. 今日热榜的真实信号:Agent 不缺新能力,缺的是成本控制
1.1 热榜扫下来的三个直观印象
今天我照例花了十几分钟把 GitHub 今日热榜从头翻到尾,越看越觉得有意思。前几个月 Agent 项目霸榜靠的是"新概念":什么能自主写代码的、能自动浏览网页的、能处理视频的,标题一个比一个猛。但今天的榜单明显换了一种气质,大量项目不管底层用什么框架,宣传点都收敛到同一件事上:省 token。
直观印象有三个。
第一个印象,Agent 项目从"秀能力"进入"拼成本"阶段。同一个 Agent,demo 里跑一次成功不稀奇,稀奇的是能在生产环境连续跑几百次还不把预算烧穿。热榜上几个项目都直接晒出了"任务成本下降百分比",这种数据在前几个月是很少见的。
第二个印象,token 用量已经从"技术指标"变成了"产品指标"。过去聊 Agent 大家关心的是回答质量、工具调用成功率,现在话题明显转向了上下文压缩比、缓存命中率、模型路由节省了多少成本。GitHub 热榜的选题方向,某种意义上就是开发者群体当前焦虑点的投射。
第三个印象,也是最让我有共鸣的一点:大家都在尝试把"能力很强但很贵"的大模型,包装成"能力够用且便宜"的生产组件。热榜上的项目不管是做了 prompt 压缩、上下文管理还是结果缓存,本质都在做同一件事:把大模型的 token 消耗从"不可控"变成"可控"。
这对我这种天天跟 Agent 打交道的人来说,是个非常积极的信号。说明 Agent 真的要开始解决真实问题了,而不是继续停留在"能跑通 demo"的层面。
1.2 为什么偏偏是现在"省 token"成了主线
这波趋势背后有几个直接推手。
第一个推手是模型能力跃迁带来的"大胆预期"。今天热榜相关的讨论里,围绕下一代模型的代际跃迁预期非常强烈。模型变强之后,开发者敢把更复杂的任务交给 Agent 了:多智能体协作、长周期任务、自主规划执行,以前觉得"不靠谱"的场景现在都敢尝试了。但任务复杂化带来的直接代价就是 token 消耗呈指数级增长,账单先扛不住了。
第二个推手是 token 用量的三本账。我平时估算一个 Agent 项目的成本,至少要看三块:
- 单次调用的直接推理成本,这个最直观。
- 上下文增长导致的重复计费。Agent 每多跑一轮工具调用,就要把之前的历史记录重新发给模型,历史越长,单轮成本越贵。
- 失败重试的隐藏成本。任务一旦中断,之前烧掉的 token 全部作废,重试又要重新烧一遍。
我经常给团队举一个例子:一个需要 10 轮工具调用的 Agent 任务,如果每轮都把完整历史塞进去,总 token 消耗可能逼近单轮调用的 20 倍。
| 场景 | 单轮输入 token | 调用轮数 | 累计输入 token | 备注 |
|---|---|---|---|---|
| 单轮问答,无历史 | 1,000 | 1 | 1,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 从实验室走向生产环境真正的入场券。