一句问候消耗17.7万Token,如何排查
Bold.org团队发现内部OpenCode Agent的Token消耗增长远超会话量,于是从SQLite中的会话历史和逐次调用用量入手调查。文章以异常问候会话为切口,展示如何通过调用明细追查成本,所给摘录尚未揭示根因。
AI 深度解读
只给公司内部 Agent 发一句「hi」,就消耗了 177,444 个 token。Bold.org 的技术负责人最初怀疑是长对话或推理开销,最后发现,真正占地方的是每次请求都随身携带的 MCP 工具说明,其中一个集成就贡献了大部分负担。
这次排查值得看,因为它把两件容易混淆的事分开了:调用工具要花 token,让工具随时可用也可能花 token。作者没有更换模型,而是开启按需发现工具的机制,让同样一句问候的用量下降了约 92%。
一句问候,怎么会带上十几万个 token?
Bold.org 的内部 Agent 基于 OpenCode,员工用它排查生产问题、查询资料和整理报告。集成越来越多之后,AI 用量增长明显快于会话和自动化任务的增长。Astra 更贵能解释部分费用变化,却解释不了 token 为什么涨得这么快。
作者先查部署中的 SQLite。托管服务使用 OpenCode 1.18.31,数据库保存了会话历史和逐次模型调用的用量。他提取了 9 月 14 日、15 日的数据,发现一段 Astra 对话累计记录了 5,040 万个 token,涉及 121 次模型调用,其中 4,990 万来自缓存读取。
这让「长对话反复读取上下文」看起来很合理。它也说明,累计 token 很高,不等于模型生成了同样多的文字。被检查的会话使用默认推理设置,推理 token 只占很小一部分。
但继续追到首次调用,解释就不够用了:某次调查尚未拿到任何结果,输入已经约有 177K token。作者于是新建 Astra 会话,只发了一句「hi」,记录用量仍然达到 177,444。
上下文负担在任务开始之前就已经存在。
这个实验排除了历史消息和工具调用结果的影响,把排查范围缩到了请求预先携带的内容。
最大的负担,来自还没调用的工具
作者接着怀疑指令和 skills 太多,于是使用 OpenCode TokenScope 检查新会话。
报告记录了 177,431 个输入 token,模型回复只有 13 个 token。不过,能看到的内容远不足以解释这个输入量:skill 目录估算约 3,400 token,可检查的工具定义估算约 5,800 token。
关键在于,TokenScope 在他们的配置下没有覆盖完整的 MCP 定义。这里不能直接拿可见项目相加,再认定剩下的都是模型内部开销。
作者让托管机器上的 Agent 单独检查工具目录,才找到 Meta Ads:它提供了 97 个工具,描述和参数 schema 合计约 595K 字符,占实测 MCP 定义文本的约 72%。
这些内容是工具的使用说明,不是广告查询结果。模型需要知道工具叫什么、接收哪些参数,才能正确发起调用;如果系统提前把所有说明都塞进请求,用户即使只打招呼,也要带上这份目录。
关闭 Meta Ads 后,作者保持模型、推理设置和 skills 不变,再发同样的问候,用量从 177,444 降到 54,181 token。单个集成解释了原始请求约 69% 的用量。
这里有两个不同口径:72% 是 MCP 定义文本的占比,69% 是关闭集成后的请求用量降幅。595K 也是字符数,不能当成 token 数使用。
保留全部工具,为什么还能降到 13,274?
关闭 Meta Ads 能验证问题,却不适合作为长期方案,因为员工确实需要它。作者想保留工具,同时避免每次后端排障都携带完整广告工具说明。
答案就在已安装的版本里:实验性的 Code Mode。
作者在 OpenCode 服务上启用 OPENCODE_EXPERIMENTAL_CODE_MODE=true,恢复 Meta Ads,再发送「hi」,用量降到了 13,274 token,比最初减少约 92%。
Code Mode 包含两层变化。第一层是按需发现:模型先拿到精简目录,需要哪个工具,再查询对应签名。第二层是用代码组织调用:模型通过 execute 在受限运行环境中执行 JavaScript,可以组合操作、过滤结果,减少中间响应反复进入对话。底层工具调用仍由 MCP 处理。
可以把它理解成先拿目录索引,查到需要的条目再读说明,而不是每次提问都附上一整本工具手册。
不过,这次问候实验直接验证的是开启整套机制后的初始用量变化,没有分别测出按需发现和代码执行各自贡献了多少。真实调查仍会加载工具定义和结果,不能据此推算所有任务都能节省 92%。
版本差异也解释了作者为什么此前没察觉。按原文描述,OpenCode 2 对 MCP servers 默认采用 Code Mode;作者本地用了至少一个月的 beta,没有遇到相同的预加载负担,托管服务却仍在 V1,需要手动开启。
原文还提到 Claude Code 的 MCP Tool Search,以及 Codex 在受支持配置中默认采用的延迟 MCP 发现机制。它们做法不同,但都在减少完整工具目录提前进入上下文的需要。
我的判断
我认为这次排查最有价值的是证据顺序:先看逐次调用,再检查首次请求,最后用关闭单个集成和开启配置做对照。对这个部署而言,工具定义造成大量初始开销,证据比较充分。
相比删掉员工常用的集成,按需发现更符合他们的需求。相比先精简几段提示词,检查完整工具目录也更切中问题,因为已看到的 skills 用量根本解释不了十几万 token。
但原文没有给出最终账单对照,也没有提供真实任务的耗时、成功率或答案质量测试。作者观察到更频繁的上下文压缩、更慢的响应和更差的答案,这些属于使用感受,尚不能确定分别有多少由工具目录引起。
缓存同样要分开看。它可以降低重复读取的费用,但不会让已经占据的上下文空间消失。那段长对话里大量缓存读取,既提醒我们别把累计用量直接当成生成量,也提醒我们继续追查反复读取的到底是什么。
可以采纳这套排查方法,但别把问候实验的降幅直接写进预算。
能拿来做什么
- 给接了多个 MCP 的日常编程 Agent 新建空会话,发送最短问候,记录首次输入用量。把它作为初始开销基线,再和实际任务比较,能更快识别任务开始前就存在的负担。
- 排查 Node 服务上的 Agent 用量时,优先获取逐次调用记录,区分输入、输出、缓存读取和推理 token。若首次请求已经异常大,就继续检查完整工具目录,并确认分析工具是否漏掉了 MCP 定义。
- 对工具较多的 OpenCode 部署,核对实际版本和 Code Mode 配置。原文的 OpenCode 1.18.31 需要通过
OPENCODE_EXPERIMENTAL_CODE_MODE=true开启,不能根据本地 beta 的表现推断托管服务。 - 调整发现机制后,同时复测空会话和常用调查任务。前者检查初始负担,后者检查工具能否找到、调用能否完成,以及实际用量和响应时间是否改善。