把 Agent 的每次调用砍掉 95%:一次从字节开始的成本审计,与六个被实测否决的方案
0次浏览0条评论0个赞
判断:Agent 的账单里,最大的那笔钱是每次调用都要重发一遍的固定前缀,不是你让它”想”的部分——工具说明书、常驻指令、历史对话。我把它从 250,542 token/次压到 11,476 token/次(−95.4%)[1],几乎没写一行新代码;压完之后,剩下的浪费变成另外两件事:打断前缀缓存(本会话实测多付约 350 万 token 当量)与闲置的免费算力(利用率 0.1%)。
一次普通的对话,账单是 4900 万 token —— 而我还没说几句话[1。这台机器注册了 706 个工具,每次开口前都要把一份 25 万 token 的”说明书”重发一遍[1]。
一句话结论(可以直接带走):省 Agent 的钱,先砍”每次都要重发的那一份”,再管”你让它想的那一份”——前者能砍掉 95.4%,后者决定你明天还会不会再花一遍[1。
2. 先看账单:三个反直觉的数字
我先做了三件杂事,然后发现”一次闲聊花掉近 5000 万 token”这件事根本不是模型的问题。
第一个数字:工具说明书占了 88%。 这台机器的 MCP 连接(GitHub、逆向工程、Postgres、浏览器调试等)总注册了 706 个工具,schema 合计 1,054,891 字节。按约 4 字节/token 折算,每次调用光”工具目录”就要 25 万 token——你还没说话,钱已经花完了。
第二个数字:缓存命中率 97%,所以别再怪”重复发送”。 前缀缓存把重复部分压到了约 1/10 的价格[5]。这意味着”少发一点”的边际收益小于直觉:真正的大头永远是”前缀有多大 × 调用多少次”。
第三个数字:主会话吃掉 86.4% 的 token。 全量扫描 4.50B token 后:主会话 3.89B,子代理只占 13.6%。子代理的固定开销只有约 1.8 万 token——子代理很便宜,长命主会话很贵。这条后面会变成一个纪律(第 11 节)。
第四个数字(这次是我自己的会话在打我的脸):步数那一项能有多大。 我用扫描脚本量了本机一个正在运行的真实会话:
- 调用 1,307 步,首调用就已带着 299,634 token 的固定开销;
- 累计输入 666,745,296 token(6.67 亿);
- 按同一工作量拆成 2 段会话估算,可省 ≈39%(约 2.6 亿 token)[1]。
把两个旋钮的收益分开算,才不会把功劳混在一起:按需披露压的是固定开销 a——本机累计输入从 1.43 亿降到 ≈2,100 万(≈7×);封顶步数压的是 n——在折叠之后同规模会话再省 26–35%(上面那段 1,307 步的会话是 39%)[1]。
先压 a,再管 n。 反过来做(先拆会话、不压 a)效果差得多:那时每次调用的基数还是 30 万,拆段只省 17%。
3. 第一刀:把”说明书”搬出每次调用
结论先行:把低频工具从”常驻”改成”按需开箱”,可见工具 706 → 49 个、schema 1,054,891 B → 47,409 B,工具块 约 250,542 → 11,476 token/次(−95.4%)。单次调用因此省掉约 23.9 万 token。
做法只有一条原则:高频的留在手上,低频的放进目录。我按 28 个语义组把 626 个工具藏了起来(“密码库管理”110 个、“GitHub”46 个、“浏览器调试 CDP”30 个……),模型需要时调一次 tool_list 打开对应组。能力一个没少——只是”看得见”和”用得到”分开了。
这一步为什么几乎是免费的:它不改变模型能力,只改变信息何时进入上下文。同样的能力,晚 3 秒到达,成本差 20 倍。
所以这意味着:能力不是成本,能力被”随时可见”才是成本。同样的能力,晚 3 秒到达,价格差 20 倍。
验证方式(这是我最看重的一点):我没有相信”应该生效了”,而是让复验脚本去读真实请求头里的工具数与字节数,并和基线对比。工具块从 250,542 降到 11,476 是量出来的,不是估出来的。
4. 第二刀:目录自己也要钱
结论先行:按需组的”目录”会逐条列出被隐藏工具的名字,那本身就是一笔固定开销——实测 27 组 / 628 个名字 = 16,732 字节 ≈ 4,183 token/次,约占工具块的 35%。
于是省钱变成一道取舍算术:某个连接要不要改成按需,取决于 名字条数 × 使用频率。实测几组:逆向工程 REA 138 个名字约占 1,180 token/次,属于”低频重型”,改按需;GitHub 46 个名字约 379 token/次,保留常驻——379 token 买”零摩擦可用”划算;密码库 110 个名字(约 509 token)与”记忆与知识”39 个名字(约 270 token)也保留。
所以这意味着:目录不是免费的索引——它按”你放进去多少名字”计费。
最后那个 39 个名字的数字后来救了我一次——第 8 节会讲:我一度把两个 mnemon 工具藏进这个组,结果把系统的内部循环打坏了。
5. 我犯的三个错(以及它们换来的三条检查)
错误一:我把系统的注入器源码删了。 我判定 ~/.dsh/external/(160 MB)是垃圾——直到发现 node_modules/ 里有个符号链接指向它,19 个 dev_* 工具随之全部消失。教训:node_modules 里的符号链接不会出现在任何依赖声明里,只 grep 配置文件必定漏判。新规矩:删除前先确认目标不是符号链接的指向;先 mv 成 .trash-<日期> 观察一个重启周期再删。
错误二:我用”备份工具”删掉了被备份的插件。 我调 config_backup 给一个本地插件做快照,它内部对插件跑 npm pack,而该插件的 prepare 脚本是 clean && build,构建失败(tsc: not found)直接把 lib/ 删了。教训:会用”构建/打包”的备份工具,对本地链接型插件是危险的;改用 tar 快照。
错误三:我测出的错误率是 96.5%,而真值是 2.49%。 我用正则去匹配工具输出里的 “error/failed”,结果工具输出里本来就大量出现这些词。换成读结构化字段 message.isError 之后,真值是 2.49%(39/1569)。教训:用字符串匹配代替结构化字段,会得到看起来很像结论的数字——这是最危险的一类错。
三条错误指向同一件事:成本优化里最贵的是”以为自己量过了”,不是钱。
6. 小结:能显著省钱的只有五件,其中四件是习惯
结论先行:走到这里,能显著省钱的动作只有五件。我把它们连同”它属于配置还是习惯”一起列出来——这张表是枢纽:其中”别打断前缀缓存”由下一节展开,其余几条在后面各自有反例。
| 顺序 | 动作 | 压哪一项 | 性质 | 实测收益 |
|---|---|---|---|---|
| 1 | 按需披露:把工具说明书搬出每次调用 | a | 配置 | 固定开销 −95.4%(累计输入 ≈7×)[1] |
| 2 | 别打断前缀缓存:会话开头一次性开齐工具组、指令改动攒到会话边界 | a 的有效值 | 习惯 | 本会话 24 次失效尖峰 ≈ 434 万 token 当量[1] |
| 3 | 封顶步数:200 步左右换会话 | n | 习惯 | 同规模再省 26–35%;1,307 步那段省 39%[1] |
| 4 | 并行合并步骤 + 精准读 | n 的有效利用率 | 习惯 | 4 个串行步骤合 1 个 ≈ 省 3× 上下文 |
| 5 | 机械活外包给免费算力 | 单位成本 | 习惯 | 把 0.1% 的利用率变成主力[1] |
顺序本身就是结论:第 1 项是一次性投入、全局受益;第 2–5 项是每次会话都要做的习惯。只做第 1 项,长会话照样失控;只做第 3 项,每一段都在为没压下去的 a 多付钱。
7. 真正的持续浪费:那只看不见的手
结论先行:压完固定开销之后,最大的一笔浪费变成了打断前缀缓存。在写作此刻的 308 次调用里出现 22 次”未缓存输入尖峰”,最高一次 590,212 token,合计多付约 350 万 token 当量[1]——而我原本以为是”输出太长”。
怎么发现的:我把每次调用的 inputTokens(未缓存部分)拉成序列,看到中位数只有 480 token,却夹杂着最高 59 万的尖峰。一行行回溯尖峰前面的动作,触发者清清楚楚:
| 尖峰前一个动作 | 未缓存输入 | 解释 |
|---|---|---|
mcp_connector_disconnect | 342,218 | 工具数组变了 ⇒ 前缀全变 |
写 AGENTS.md | 166,323 | 系统指令变了 ⇒ 前缀全变 |
tool_list(开组) | 275,842 | 工具数组又变了 |
tool_list(再开一组) | 285,321 | 同上 |
为什么它比输出贵:前缀缓存命中价约为全价的 1/10,所以一次失效等于把整个上下文按 10 倍重付一遍。粗算这轮多付的成本 ≈ 该会话输出总成本的 1.6 倍。
这条改的是工作方式,不是配置:① 会话开头一次性开齐要用的工具组,别边干边开;② 指令文件/记忆的改动攒到会话边界;③ MCP 连接装卸、注入模式变更放到会话之外。我把它写进了工作区约定,并把”尖峰检测”加进了自检脚本——看不见的东西无法管理。
8. 折叠打坏了内部循环:一个 17/17 全错的会话
结论先行:我按”名字条数 × 频率”藏了工具,却忘了问谁在内部用它。结果是一个子代理会话 17 次调用全错,白烧约 39 万 token,并且静默丢掉了记忆整理。
现场长这样:
Error: tool "mnemon_document_search" belongs to on-demand group "记忆与知识" which is not open
Error: Mnemon review cannot execute "tool_list"; reuse the inherited checkpoint and bounded Document search.
第一行说明工具被我藏了;第二行说明它在受限策略下不能开箱——于是”要用的工具够不着、用来够它的工具又不许用”,死循环。
推广成一条通用检查:任何”隐藏能力”的优化,都必须审计它的内部消费者。 具体做法:对你准备隐藏的每个工具名,grep 其所属插件的提示词/协议文本,凡是出现在协议里的,必须保持可见。巡检手段我留在自检脚本里:任何子代理会话的错误率接近 100%,优先怀疑这一类。
修法很轻:把那两个工具移出隐藏集(走插件自己的保存接口热生效),并用一次真实调用验证——标准是那个曾经报错的调用真的成功了,而不是”配置看起来对了”。
9. 免费算力只用掉 0.1%
结论先行:这台机器配了免费模型池,白名单里写得明明白白”免费优先、付费兜底”。但账本显示:付费主干 6,650 次调用 / 25.4 亿 token,免费池合计 8 次 / 240 万 token——0.1%[1]。
根因查清了:子代理的模型选择只有白名单、没有默认值;不显式指定时,子代理继承父路由。所以缺的是委派时那个”选择动作”,配置从来不是瓶颈。
改法(纪律,不是配置):只读机械活——批量读+摘要、抽取、格式化、日志归类、清单核对——显式指定免费模型;需要判断力的活仍走主干。判断标准一句话:产出可被验证、且不需要做架构判断。
10. 六个看起来该做、实测不该做的方案,以及我拒绝做的三件事
省 token 的建议满天飞,我把能测的都测了。下面这些是”看起来该做、实测不该做”的:
| 主流建议 | 实测结果 | 结论 |
|---|---|---|
| 降低推理强度(reasoning effort)省钱 | 近 5 个会话 reasoningTokens = 0 | 不省:当前调用根本没产生推理 token |
| 提示压缩(LLMLingua 类)[6] | 对路径/ID/代码极易失真 | 否决:与”判定必须来自实测”冲突 |
| 语义缓存 | 单人、任务高度多样 ⇒ 命中率低;错缓存会让决策基于过期事实 | 否决 |
| 虚拟上下文分页(MemGPT/Letta 类)[7] | 本机已有 3 套记忆系统 | 否决:应收敛而非加第 4 套 |
| 服务端会话状态 | 强绑定单一供应商 | 否决:本机模型路由多变 |
| 优化”工具调用重试” | 真实错误率 2.49% | 优先级低:收益小 |
业界同类做法(供对照):把工具调用搬进代码执行沙箱,Anthropic 实测 150,000 → 2,000 token(−98.7%)[2];用”工具检索”替代全量注入,可降 85% 以上,但可用工具超过 30–50 个后选择准确率下降[3];上下文编辑只替换过期的大块工具结果、不做整段总结[4]。这三条本地都还没用满:前两条分别受限于”先有沙箱”与”先有检索”,第三条与本地 spill 可恢复的机制互补。
我拒绝做的三件事(决定权在我,我选择不动):
- 不为了省约 500 token/次去改一个 46 KB 的组合引擎。 那套多智能体配置的档案行已被插件自身截断,真正的长文本并没有进上下文;而它是一套带自检(
check/verify-tiers)的七层设计——风险/收益不成立。停用更差:那是刻意搭起来的能力。 - 不 hack
node_modules里的第三方提示词。 我实测过它的成本(中文文本 1,931 字节 ≈ 643 token),但改安装版会在任何插件操作后被覆盖——本机已经因为这类改动坏过一次。正确动作是给上游提压缩建议(把 8 条纪律压到 3 条,删掉与服务端强制重复的部分)。 - 不合并”可插拔策略”的三块文本。 它们来自三个独立子插件,是设计的正常产物(另外两个策略已停用,恰好证明可插拔有效)。为约 600 token/次去改两个 npm 包,不值。
唯一我对自己的东西下了狠手的:我写的工作区约定文件涨到了 10,312 字节,比我一直批评的插件提示词还大。我把它压到 4,811 字节(−53%),做法是把”为什么”的叙事挪进不常驻的审计文档,常驻只留可执行条款——这正是我给别的插件提的建议,我得先自己做到。
11. 结语:终点是工作方式
请特别注意 3、4、5 的共性:它们都是”改一个习惯”,不是”装一个东西”。这也解释了我最早的误判——我以为 5000 万 token 是”系统过度设计”造成的,量完之后发现:设计确实冗余(工具面 88% 是说明书),但系统真正的成本上限由使用方式决定。一个配置完美的 Agent,如果被当成无限长的单会话使用,账单照样会失控。
12. 一天之后:账单换了形状,我又修了三个洞
结论先行:这一轮没有再砍掉 95%。它砍的是上一轮结论里的三处过度自信——声明没消失,只是搬了家;折叠不是免费的;空转的注入可以静默存在很久。
12.1 仪器盲区:PTC 把说明书搬进了系统提示
我把会话切到 ptc(代码执行式工具调用)后,闸门读到的是 1 个工具 / 2,130 字节 / ok。
但同一份会话日志的 system/message 里躺着 47 个工具的 TypeScript 声明,45,062 字符 ≈ 11.3k token——一分钱没省,只是从 tools[] 搬进了提示词。
教训:tools[] 只是「工具说明书」的一个存放位置,不是它的定义。换一种调用模式,就要换一个量具,否则你会得到漂亮的假绿。闸门现在同时量 sysChars / declChars / turn1Inj / fixedChars,并加了一条 maxFixedChars 预算。
12.2 修正上一个数字:固定开销 29k,不是 11.5k
按「每次请求都要重发的那一份」重新量:
| 组成 | 字符 | 说明 |
|---|---|---|
| 系统提示(含工具声明 45,062) | 69,481 | 声明占 65% |
| 按需组目录 | 13,090 | 会话一次;其中约 12k 是隐藏工具的名字 |
| 技能目录 | 11,304 | 会话一次 |
| 记忆插件协议 | 10,330 | 见 12.4 |
| mnemon 快照 + runtime + 检索注入 | 12,067 | 每轮 |
合计 116,125 字符 ≈ 29,031 token/请求。上一轮的 11,476 只数了工具块——口径窄了,不是数错。 把口径放宽才发现:折叠省掉的那 23.9 万是对的,但「剩下的一点点」其实还有 29k。
12.3 折叠不是免费的:补一条判据
隐藏一块声明省 B token/步,但开组要多花一整步上下文 C。所以折叠划算的条件是
本机 C ≈ 33k(首调用 32,726)、中频工具组 B ≈ 1.5k ⇒ N > 22 步才回本。
于是:706 → 47 的那一刀(B ≈ 11.5k)在任何超过 3 步的会话里都赚;但「把 vault、visual 这类中频工具再藏起来」在多数会话里是亏的。
「能藏就藏」是错的,折叠是一笔要用步数结算的投资。
12.4 空转的注入:一个静默了很久的浪费
记忆插件每轮注入 3,272 字符、首轮 7,058 字符,而它的后端守护进程从安装起就没起来过:日志里 ECONNREFUSED 127.0.0.1:9077,知识库恒为 No knowledge pages yet。
最难发现的地方是:注入文本自己写着「还在学习,页面稍后出现」——它把失败描述成了进度。
根因两条,都不在插件代码里:
- 守护进程是「现装现跑」的(
uvx hindsight-api),整套依赖 约 6GB,而 CLI 的就绪超时默认只有 180 秒(HINDSIGHT_EMBED_DAEMON_STARTUP_TIMEOUT可调)⇒ 每次都在下载/解包阶段被判失败,而且外层 systemd 一旦配Restart=on-failure就会每 3 分钟重来一遍,把已下载的进度反复浪费; - 外层服务进程的环境里没有抽取用的 LLM key(
needs an LLM for fact extraction),所以即使装完也不会真的去写记忆。
修法:给服务 env 补 HINDSIGHT_API_LLM_*;把守护进程交给 systemd(TimeoutStartSec=2h,冷启动只付一次);刷新频率从每轮降到 1/5 轮。
最后这条是取舍而不是「关掉」:插件作者实测每轮刷新能把「会去查记忆的轮次」从 15% 提到 32.5%,我按 818 token/轮的成本选了 1/5。
纪律:注入块必须有仪器。新增插件先看它的 diag.jsonl 有没有 *_failed——「没有内容」和「有内容但没生效」在提示词里长得一模一样。
回填(同日 14:42,实测通过):修好后的验收只有三行——
curl -s http://127.0.0.1:9077/health # {"status":"healthy","database":"connected",...}
hindsight-embed -p coding-agent memory retain ... # Memory retained successfully
hindsight-embed -p coding-agent memory recall ... # Search Results (2)
冒烟里那条 recall 真的把我 10 分钟前写的「固定文本 29031 token/请求」捞了回来——这是唯一一条由被测量的系统自己记住的结论。
真正的门票是 6GB 依赖 + 两个模型(embedding 133MB + reranker 90MB),以及三个只有撞上才知道的坑:
- 健康端点是
/health而不是/api/health(后者 404,而 404 也是「有响应」,足以把「等待就绪」写成「已就绪」); - PyPI 直连在本机返回
000(要走镜像),HuggingFace 直连不通、hf-mirror 不稳(模型最后落到本地目录,用HINDSIGHT_API_RERANKER_LOCAL_MODEL指过去); HINDSIGHT_API_ENABLE_RERANKING=false只关掉「用不用 rerank」,不阻止启动时加载 cross-encoder —— 想省那 90MB 是省不掉的。
12.5 三条被实测否决的「省钱」做法
- 逐轮清空工具结果:本机 cacheRead 命中 99.7%(1.326B 缓存读 / 4.27M 未命中),命中价约是 miss 的 1/10。每轮改写历史 = 每次都从改写点全价重算。外科式清除只该在接近窗口上限时发生。
- 再往下折叠中频工具:见 12.3,步数不够就是亏。
- 给第三方插件的目录打补丁去名字:npm 包会被下一次
dsh plugin覆盖——和「不 hack 版本化依赖」是同一条。
12.6 这一轮最贵的一课:改工具面 = 打碎缓存
为了让目录变短,我中途改了一次工具面,结果:组目录被重新注入 13,090 字符,闸门记到 2 次缓存尖峰、多付约 3.8 万 token 当量。 「改规则」本身有价格,而且价格按「你正在多长的会话里改」结算。纪律因此补了一条:不要在长会话中段改插件、工具面和指令文件;要改,先换会话。
12.7 一句话
上一轮我证明了「固定开销可以砍 95%」,这一轮证明了砍完之后要再量一次——因为账单会换形状,而仪表不会自己跟着换。
局限性
- 所有数字来自单机、单人、单模型路由的实测;不同厂商的缓存计价与失效策略不同,绝对数值不可直接搬用,方法可以。
- 成本占比用的折算系数(缓存读 ×0.1、输入 ×1、输出 ×5)是量级估算,用于排序而非结算;排序结论对该系数不敏感。
- “免费算力 0.1%“是账本快照,未计入通过其他路径(如直接调 API)发生的用量。
- 两个数字我修正过:常驻提示词块我一度用 5,023 字节计数(把周围代码算了进去,实际 2,043 字节),中文 token 我一度按”字节/4”折算(UTF-8 中文是 3 字节/字,会低估约 40%)。正文用的是修正后的值。
- 未核实项:第三方
agent-teams常驻文本的净收益(我只测出它被截断后的体积,没有 A/B 它的策略价值)。 - 口径修正(2026-10-17):−95.4% 这一降幅量的是
tools[];按「全部固定文本」口径,折叠后仍有 29,031 token/请求(其中 11.3k 的声明在 PTC 模式下搬进了系统提示,见第 12 节)。
参考文献
[1] 本机实测(可复现):DSH 会话日志里的 usage 记录、工具快照与常驻提示词。复现命令:node deploy-dsh/tool-budget-gate.mjs、bash deploy-dsh/verify-audit-20261015.sh。
[2] Anthropic, Code execution with MCP —— 工具调用 150,000 → 2,000 token 的一手实测。
[3] Anthropic, Tool search tool —— 工具检索式发现,降幅 85%+;并指出可用工具超过 30–50 个后选择准确率下降。
[4] Anthropic, Context editing —— 外科式上下文编辑:只替换过期的大块工具结果,不做整段总结。
[5] DeepSeek, Context caching —— 前缀缓存的计价与失效语义。
[6] Jiang et al., LLMLingua —— 提示压缩;因保真度风险否决。
[7] Packer et al., MemGPT;Letta 文档 —— 虚拟上下文分页;因”已有三套记忆”否决。





评论
填个昵称就能发,不需要注册或登录