如何降低 CLI 工作流中的智能体 Token 成本

一份关于如何在 CLI 智能体工作流中通过输出压缩、命令卫生和上下文纪律降低 Token 成本的实用指南。

By Dora 2 min read

我是 Dora。上个月我让 Claude Code 跑了一次 npm test,结果在智能体回复我之前,这个会话大约已经烧掉了 12,000 个输入 token。测试输出有 400 行。真正重要的可能只有 30 行。剩下的那些弃用警告、依赖噪声、Jest 的进度点,全都被直接塞进了模型上下文里。每一个字节我都付了钱。

也就是从那次开始,我不再把 token 当作“模型会自己处理的东西”,而是把它当成一个正在主动泄漏的预算。如果你在 Claude Code、Gemini CLI 或类似工具上运行 agentic CLI workflow,这很可能就是你最大的成本项。解决方案通常也不是换一个更好的模型,而是改善卫生习惯。Anthropic 自己的成本管理文档说得很直白:token 成本会随着上下文大小增长,而大部分优化发生在模型真正看到数据之前。本文讲的是如何在不丢失必要调试信号的前提下,降低 CLI 工作流里的智能体 token 成本。

CLI 工作流在哪里浪费 Token

在修任何东西之前,我得先弄清楚泄漏发生在哪里。有两个模式特别明显,而且几乎出现在我后来审计过的每个 CLI 工作流里。

冗长命令和无关输出

终端命令最初是为人类扫屏设计的,不是为 LLM 逐字节阅读设计的。git status 会打印模型不需要的 ANSI 代码。npm install 会吐出上千行模型早就知道的依赖树。next build 会连续十几秒回显自己的进度。它们进入上下文窗口后,大多数都没有赚回自己的成本。

实际数字比纸面上更糟。一个中等规模 Rust 项目的一次 cargo test 可能产生 8,000 到 15,000 个 token 的输出,其中大部分只是编译噪声。当智能体为了找一个失败断言而读完整份日志时,你等于按 Opus 级别的价格为构建日志流付费。

这也是为什么 rtk 和 tokf 这类社区项目会出现。它们位于 shell 和智能体之间,在输出进入上下文之前过滤样板内容,并且在常见命令上报告了 70% 到 90% 的节省。无论你是否使用包装器,原则都一样:原始终端输出不是 LLM-ready data。

上下文延续和重复读取

第二个泄漏更隐蔽。智能体每次工具调用,文件读取、grep、bash 命令,都会留在对话历史里。到了第十轮,模型每次请求都在重新处理前九轮已经过期的输出。Anthropic 在 4 月故障复盘中描述过 Claude Code 质量问题里完全相同的动态:一个缓存 bug 让 thinking history 在多轮中不断累积,token 使用量在被发现前膨胀了 10 到 20 倍。即使没有 bug,这也是默认行为。长会话就是昂贵会话。

我检查过自己一周前的一个会话。智能体读取了同一个 package.json 四次。这些重复读取没有增加任何信息,文件并没有变化。它们只是智能体不知道自己已经知道什么的副产物。

第一步:压缩嘈杂输出

最便宜的修复方式,是从一开始就阻止垃圾进入上下文。三条规则,按这个顺序来:

在源头过滤,而不是事后过滤。 不要直接跑 npm test,让智能体跑 npm test --silent 2>&1 | grep -E "(FAIL|PASS|Error)"。不要直接跑 git status,跑 git status --short。不要直接跑 cargo build,跑 cargo build --quiet 2>&1 | tail -20。这不是什么聪明技巧,只是纪律。智能体拿到失败测试、修改文件和真正错误,仅此而已。

在 harness 层限制工具输出。 Claude Code 允许设置最大工具输出大小。我把自己的每次调用上限降到了 8,000 个字符。当命令超过上限时,智能体会收到截断提示,并决定是否需要收窄查询。单这一项给我省下的 token,比其他所有改动加起来还多。

当上游工具安静不下来时,使用 CLI 代理。 有些命令没有像样的 quiet flag,比如 next build、webpack,以及任何 Java 体系里的东西。对这些工具来说,一个能剥离已知样板的包装器值得花时间配置。rtk/tokf 这类工具可以通用处理;你也可以为最烦人的三个命令写一个 30 行 bash function。

这里有真实取舍。过度压缩会隐藏调试信号。 如果构建失败的原因刚好被过滤规则删掉,比如一个升级成错误的弃用警告、藏在第 847 行的冷门配置问题,智能体得到的画面就会更短也更不完整。我遇到过两次。两次的修复都是放宽一条过滤规则,而不是放弃这套策略。

第二步:在上下文进入模型前控制它

输出过滤处理的是每一轮新进入的 token。上下文纪律处理的是会话里已经积累的 token。这是两个不同问题。

两个最重要的命令都来自 Anthropic 的 Claude Code 最佳实践/clear/compact/clear 会完全重置会话,适合切换到无关任务时使用。/compact 会总结早期历史,同时保留关键决策和当前状态,适合任务还在继续但早期探索不再承担主要信息负载时使用。Claude Code 在接近上下文上限时会自动 compact,但等到那个触发点通常已经太晚了。那时你已经为前几轮膨胀的上下文付过钱了。

我现在的习惯是:每到一个自然任务边界就运行 /compact,并带上类似 /compact Focus on the failing test and the recent file edits. 的指令。这个指令很重要。没有它时,压缩会粗略总结所有内容。有它时,智能体会保留下一阶段真正重要的部分。

对于 API 方式运行的智能体,而不是 CLI 订阅,Anthropic 的上下文编辑文档描述了更严格的机制:clear_tool_uses_20250919 会在上下文超过阈值后自动清理旧工具结果。智能体保留对话,但丢弃已经处理过的原始输出。对长周期 agentic task 来说,这应该是默认值。

还有一点值得提醒:臃肿的 CLAUDE.md 是永久税。它每一轮、每一个会话都会加载。我把自己的文件从大约 280 行剪到约 90 行。每轮 token 数明显下降,而我能测到的智能体行为没有变化。

第三步:重新设计低浪费的智能体工具

前两步是战术层面的。这一步是结构性的,长期节省也主要来自这里。

设计输出 LLM 友好的工具。 社区驱动的 CLI Spec 对这个论点讲得比我更好:面向智能体的命令应该支持 --output flag,把数据 stdout 和诊断 stderr 分开,并提供分页,而不是直接倾倒无边界 JSON。如果你在构建内部 CLI,并且智能体会调用它,请遵循这类规范。如果你使用的外部 CLI 做不到,就给它包一层。

优先使用窄工具,而不是宽工具。 一个返回三个结构化字段的 git_status_summary 函数,胜过让智能体运行原始 git status 再解析输出。模型多做一层解析,就多烧一层 token 在翻译而不是推理上。我把四个最常用命令改成了返回 JSON 的薄 Python wrapper。这些操作的往返 token 使用量大约下降了 60%。

把读很多文件的工作交给 subagents Claude Code 的 subagent 功能会为“扫描仓库并总结鉴权流程”这类任务开一个独立上下文。返回主对话的是压缩后的发现,而不是 subagent 实际读过的 40 个文件。主对话永远看不到原始数据。对探索密集型任务来说,这是可用的最大结构性收益。

让模型匹配任务。 Opus 4.7 很强,也很贵。大多数 CLI 工作,包括文件编辑、测试修复、常规重构,用 Sonnet 就足够,单 token 成本大约是 Opus 的 40%。还要知道:Opus 4.7 的新 tokenizer 对相同文本最多可能产生比早期模型多 35% 的 token,这会进一步放大成本差距。

诚实的 caveat 是:先测量再优化,优化后再测量。 我先用 /cost(API)或 /usage(订阅)跑了一周基线,然后每改一项重新测量。有两个我以为很聪明的“优化”最后没有可测收益。没有基线,就是在猜。

FAQ

为什么终端工作流会消耗这么多 token?

因为终端输出是为人类设计的,而智能体需要逐字节付费。一个典型构建命令会输出数千行进度、警告和样板信息,其中大部分模型并不需要。再加上永不重置的对话历史和持续积累的工具结果,会话还没开始真正工作就已经烧掉了大量上下文预算。

输出压缩能帮多少?

在我的测量里,命令级过滤加输出上限,可以把测试、构建和 git 操作的每轮输入 token 降低 40% 到 60%。rtk 这类社区包装器在特定命令上报告了 80% 到 90% 的降低,不过这些数字通常假设最坏情况的冗长输出。真实收益取决于你的智能体最常运行哪些命令。审计前五个,先修它们,大部分收益会立刻出现。

团队应该先优化什么?

顺序是:工具输出上限、/clear/compact 纪律、模型选择。输出上限是一次性配置,没有持续成本。会话卫生是一种习惯,但养成后也是免费的。模型选择是最容易被忽略的收益点,把所有任务都跑在 Opus 上,而大部分任务用 Sonnet 就能完成,是一种安静但很大的泄漏。

token 优化什么时候会伤害调试质量?

当你压缩到智能体看不清真正坏在哪里时。被截断的 stack trace、被过滤掉的弃用警告、隐藏真实错误的 --quiet flag,这些都真实浪费过我的时间。我遵循的模式是:对常规命令激进压缩,比如 git statusnpm install、成功的测试运行;对已知失败或不熟悉的操作保留更多输出。如果你发现自己为了调试又无过滤地重跑命令,那说明过滤规则错了,不是策略错了。

结论

CLI 工作流里的 token 成本不是模型问题,而是管道问题。大部分花费消失在终端命令输出和模型实际需要的信息之间的落差里。这个落差可以通过输出过滤、上下文纪律,以及尊重智能体消费方式的工具设计来修复。

我已经用上面的设置跑了大约六周。Claude Code 的日 token 消耗下降了约 55%,更小的上下文也顺带改善了智能体延迟,调试体验也更安静。这些数字并不通用,你的基线和前五个命令会不同。但模式成立:控制进入上下文的内容,控制留在上下文里的内容,让模型把预算花在推理上,而不是阅读构建日志上。

我的数据到这里为止。压缩层还在演进,Anthropic 的 tokenizer 变化也意味着这些数字有保质期。每季度重新做一次基线,值得。

往期文章: