给 Agent 装了 46 个工具,我算了一笔 token 账
古董级程序员,大厂出来后一直在创业公司,现在仍然在一线写 AI 相关的代码。这些年踩过的坑、对工具链的判断和偶尔的反思,更完整的版本会写在微信公众号「字与码」,这个专栏的后续几篇也会在那里同步。要是这篇账单对你有用,顺手关注一下就行。
过去两个月我一直在推荐别人装那套确定性工具:一条命令,46 个工具,JSON 校验能给你精确到行列的偏移,编码检测会说”这两个汉字我真的分不出来”,线程 dump 会自己把死锁环画出来。
推荐得很顺手,直到我朋友问了一句:这些工具本身要花多少 token?
这个问题有点扎人。工具 schema 是请求的一部分,装在 profile 里就跟着每一次请求发出去——你用不用都要付。我之前的回答基本靠感觉,所以这周把它测了一遍。这篇是第一篇,先把账算清楚;后面再讲这些工具具体长什么样、怎么验收。
先承认”模型自己也能做”
我不想把这篇写成”没有插件就不行”。实测里有一个反例,正好说明问题。
有一组任务是算 hello world 的 sha256。不装插件,只给模型一个 PowerShell,它的做法是自己写 pwsh:
# 不给插件时,模型自己现写的命令(它写了两次才收工)
$bytes = [System.Text.Encoding]::UTF8.GetBytes("hello world")
$sha = [System.Security.Cryptography.SHA256]::Create()
([BitConverter]::ToString($sha.ComputeHash($bytes)) -replace '-','').ToLower()
结果是 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9,是对的。
所以”模型算不准哈希”这个前提,在有 shell 的环境里根本不成立。它写得出、也算得对。真正能拉开差距的是别的东西,而这件事必须先把账摊开才看得清。
固定税:装了不用也要付
先量最大的一块
同一套 headless base、同一个工作目录、同一条 LLM 路由,两次干净启动,只看首次请求的 inputTokens:
| 配置 | 工具名数量 | 首次请求 inputTokens |
|---|---|---|
| 干净 headless base | 75 | 11,931 |
| base + 四个插件 | 121 | 19,608 |
多出来的 46 个工具名,对应 +7,677 tokens/请求。摊到 46 个工具上是平均约 167 tokens 一个,不过这个平均只能当量级看:四个插件各自的 schema 长短不一样,单装 dsh-deterministic-tools(29 个工具)实测大约是 +3.4k,不是 29 × 167。占满配请求 19,608 的 39%。
39% 这个数字是”要不要全装”这件事上唯一的硬指标。我原来以为工具描述只是几十个 token 的开销,实际量出来才发现:这不是零头,这是接近四成的固定开销。
这笔开销的形状
顺便说清楚这个成本的形状。它不随任务大小变化。 你让 Agent 干一件三秒钟的小活,和让它跑半小时的重活,这 7,677 tokens 一模一样地跟着走。所以它更像一笔”每请求税”,而不是”每次调用费”。
四组对照:把”自己算”和”用工具”放同一张表上
固定税只是账单的一半。另一半要看跑起来之后 token 怎么流动。
这里有个容易被忽略的方向问题。调用一次工具其实是两步:模型把数据作为参数生成(这是输出 token),工具结果作为输入 token 回来,然后模型再说结论。而”自己算”通常是一步,或者像上面那样写成 pwsh 变成两到三步。
输出 token 一般比输入贵四倍左右,而且输入还能命中缓存——所以用工具相当于把一部分输出换成了输入,同时减少了步骤,也就减少了上下文重读。
同一个 base、同一条 LLM 路由,B 组和 D 组只多挂 dsh-deterministic-tools(29 个工具)。两个任务:A/B 校验 {"a":1,} 并给出精确行列偏移,C/D 算 hello world 的 sha256。
| 组 | 配置 | 步骤 | input | output | cache read | 原始总量 | 耗时 | 模型选的路 |
|---|---|---|---|---|---|---|---|---|
| A | 无插件 | 3 | 12,569 | 1,323 | 26,496 | 13,892 | 13.0 s | 自己写了 pwsh 两次 |
| B | 装 29 个工具 | 2 | 15,984 | 314 | 17,536 | 16,298 | 6.1 s | json_validate |
| C | 无插件 | 3 | 7,495 | 584 | 31,360 | 8,079 | 7.9 s | 自己写了 pwsh 两次 |
| D | 装 29 个工具 | 2 | 15,312 | 79 | 18,176 | 15,391 | 5.4 s | hash_text |
三件事一眼能看出来。
输出少了一个数量级。 1,323 → 314,584 → 79。模型不用再把整段 JSON、整条哈希命令重新吐一遍,结论直接来自结构化回执。
步骤少一步,快一倍左右。 13.0 s → 6.1 s,7.9 s → 5.4 s。少一步不只是省时间,也少一次上下文重读(cache read 从 26,496 / 31,360 降到 17,536 / 18,176)。
但原始 token 总量反而更高。 A→B 涨了 17%,C→D 涨了 90%。多出来的就是那 29 个工具的固定税。这两组任务都太小了,省下来的输出根本盖不住固定税。
所以”装插件省 token”这个说法,对一次性小任务是错的。
什么情况下真的划算
四组数据只能说明两个具体任务。把工具当成一类东西看,划算与否取决于数据的形状,而不是工具本身好不好:
| 场景 | 判断 |
|---|---|
| 一次性、几十字节的活 | 更贵。固定税加参数往返,省下来的输出盖不住 |
| 同一会话里反复用 | 划算。固定税是每请求成本,用得越多摊得越薄 |
聚合/压缩类(dir_usage、file_duplicates) | 明显省。内容根本不进上下文,只有汇总进来 |
结果远小于输入(text_diff、json_diff) | 省输出。模型不必把两份文档各复述一遍 |
| shell 一行搞不定、要几十行脚本的活 | 省 token 也省时间,更省”写错重来”那一整轮 |
| 模型算不准的(逐字节编码判断、8.3 名、MAX_PATH) | 只能靠工具 |
第一行和第二行其实是一回事的两面:固定税是摊销成本,摊销的前提是你真的重复用。 一个只在会话里用一次的 JSON 格式化,装 46 个工具纯属浪费;一个每天要在几十个目录上做统计的 Agent,这 7,677 tokens 买回来的东西完全值。
一个必须说清的短板
还有一个短板得说清楚,因为它直接决定什么时候不该用这些工具:当前所有工具都吃内联参数。也就是说,一份 2 MiB 的 thread dump 必须先被模型重新吐一遍当参数传进去——这一步在 token 上就非常不划算。给文本类工具加一个有预算的 path 入参(只读、超限拒绝)是 v0.2 该做的事;在那之前,大工件要么先切片,要么交给 dir_usage 这种自己读盘的工具。
不想全装:把 29 个工具裁到 5 个
上面两张表其实给出了一个很实用的结论:固定税按工具数量线性增长,那你只注册要用的就行。
46 个全装约 7.7k tokens/请求;只装 dsh-deterministic-tools 的 29 个,实测约 +4.0k。而按 id 覆盖,把它裁到 5 个:
# ~/.dsh/profiles/<profile>/cordis.patch.yml
# 注意这一层的位置:这个文件头写着"applied after every bundle layer"。
# 用 `dsh --patch <file>` 那种 CLI 覆盖层改同一个 id 会报
# patch: entry "deterministic-tools" not found —— 它在 bundle 层之前应用。
- id: deterministic-tools
config:
only: [json_format, json_validate, json_query, text_diff, csv_to_json]
toolPrefix: zi
同一条任务(校验 {"a":1,})跑三组真实回合,看首次请求的 prompt 总量:
| 配置 | 进请求的工具名 | 首次请求 prompt |
|---|---|---|
| 干净 headless | 75 | 12,838 |
only 裁到 5 个 | 80 | 13,529(+0.7k) |
| 装全部 29 个 | 104 | 16,799(+4.0k) |
裁掉那 24 个 schema 省下约 3.3k,剩下的 5 个工具只占约 0.7k。进请求的工具名也在会话日志里核过:从 104 个变成 80 个,模型那次真的改用了 zi__json_validate(加了前缀,一眼能看出是哪个配置层在生效)。
这个 only 裁剪是整件事里性价比最高的一个动作。裁剪之前,你付的是”工具箱的价格”;裁剪之后,你付的是”你真正会用的那几个工具的价格”。中间那 3k 多 tokens 每个请求都在白白流走。
那这笔钱到底买了什么
算到这里,结论有点反直觉:如果你只看 token,大部分场景下插件是亏的。
一次性小活更贵,这是实测;聚合压缩类才明显省;真正让它划算的前提是你会重复用。那为什么还装?
因为 214 个测试盯着的固定实现买到的不是省钱,是三件事:
一次就对。 模型自己写 pwsh 那段能算对 sha256,但它每次都要重新写命令、处理引号、担心编码和控制台代码页。一次写错就是多一整轮——而一整轮的成本,往往比这个请求的固定税高得多。
可复现。 同样输入永远同样输出,bytesIn/bytesOut 都一致。没有时钟、没有随机、没有环境变量读取。所以结论可以进 issue、进 CI、进复盘文档,别人拿着同样的输入能算出同样的结果。
可审计。 每个工具返回的是 { result, meta } 回执,不是一段自由文本。超出预算的结构化输入会抛 INPUT_TOO_LARGE,并明确说”没有任何截断或部分处理”;解析不了的行进 unknown_lines 并让 confidence 掉下来;jwt_inspect 直接声明自己不验签,netstat_parse 把”PID 是否还活着”放进 limitations[] 而不是猜一个答案。这些都是模型心算给不了的东西。
所以我会这样描述这笔交易:确定性工具是有保费的保险,不是打折券。 保费按请求收,一次性的小活你亏一点;真出事的时候——线上报错原文要变成可核对的结论、审计要追问”这个数是哪来的”——你买的就是”当时是对的,而且能证明它是对的”。
至于怎么把这些承诺做成硬契约(为什么超预算要拒绝而不是截断、为什么”我不知道”必须是一个字段),是这个专栏第二篇的事。
怎么自己复跑
这篇文章里的每一个数字都是本机跑出来的,不是估算。想核对的话:
# 1) 四组对照与固定税:脚本会打印 steps / in / out / cache / sum / 耗时 / 模型选的路
pwsh -NoProfile -File D:\dsh\research\token-economics.ps1
# 2) 裁剪那三组:干净 base / only 5 个 / 全部 29 个,比较首次请求 prompt 总量
pwsh -NoProfile -File D:\dsh\research\measure-base-tokens.ps1
pwsh -NoProfile -File D:\dsh\research\measure-only-trim.ps1
# 3) 核对工具名是否真的进了请求(不是"装了就算",要看会话日志)
node D:\dsh\research\verify-installed-tools.mjs <session.v4.jsonl.zstd>
# 4) 不看仓库也能看工具清单与实测输出
npm view dsh-deterministic-tools readme
npm view @ax2zicode/dsh-dev-toolbox readme
第 4 条要注意包名:合装包在 npm 上是 @ax2zicode/dsh-dev-toolbox(无 scope 的名字会被 npm 以”与已有包过于相似”拒掉),GitHub 仓库仍是 ax2/dsh-dev-toolbox。装的时候别和单独安装的四个插件混用,会重复注册同名工具。
如果你想先试再决定,最快的路径是临时 profile:
dsh plugin --profile web add @ax2zicode/dsh-dev-toolbox
# 装完重启应用:bundle 列表是启动时读的
装完第一件事,建议就去这个 profile 的 cordis.patch.yml 里按需写一遍 only。先把那 39% 砍到你真正需要的规模,再谈值不值。
微信公众号
欢迎关注「字与码」
如果这篇文章对你有用,也欢迎在微信里继续关注后续更新。
X / Twitter
关注 @ax2_zicode
更即时的技术观察、新文章提醒和一些短想法会发在 X 上。