把 LLM 智能体接进 CI 流水线,听起来很美:每个 PR 自动分类、自动审查、自动维护 changelog。但跑上几周之后,账单会教你做人——一个看似无害的工作流,可能一天就烧掉几百万 token。
GitHub 团队最近公开了他们对内部 12 个 Agentic Workflow 的优化复盘。他们没有靠"手工调 prompt"这种玄学手段,而是用两个工作流去审计和重构其它工作流,最终 9 个工作流拿到了显著的 token 节省,最高一个砍掉了 62%。
这篇文章记录他们的方法论,重点不在于具体数字,而在于:当 LLM 调用变成基础设施的一部分,应该怎么像对待数据库慢查询一样去优化它。
第一步:先有可观测性
优化的前提是测量。但 Claude CLI、Copilot CLI、Codex CLI 各家的日志格式都不一样,没法横向比较。
GitHub 团队的做法很取巧:他们本来就有一个 API 代理,用来防止 agent 直接拿到凭证。这个代理顺手就成了统一的埋点位置。
每个工作流现在都会输出一个
token-usage.jsonl,每次 API 调用一条记录,包含 input tokens、output tokens、cache-read tokens、cache-write tokens、model、provider 和时间戳。
这是关键设计:埋在 agent 框架之外、所有 LLM 流量必经的路径上。不依赖任何具体框架的日志格式。
用工作流去优化工作流
有了数据,团队搭了两个每日跑的工作流:
Daily Token Usage Auditor:聚合最近的消耗,输出报告,标记三类异常:
- 用量明显增长的工作流
- 总消耗最高的工作流
- 异常跑次(比如一次跑了 18 轮,平时只要 4 轮)
Daily Token Optimizer:拿到标记后的工作流,读源码 + 读最近日志,自动开 GitHub Issue,里面是具体的优化建议。
这两个工作流自己也会出现在每日审计里,“形成了一个小小的良性循环”。
优化点一:删掉没用的 MCP 工具
最常见的浪费是注册了但根本没在用的 MCP 工具。
原理很简单:LLM API 是无状态的,每次请求都要把所有可用工具的名字和 JSON Schema 都带上。一个 GitHub MCP server 注册了 40 个工具,每一轮就要塞进去 10–15 KB 的 schema,哪怕实际只用了其中 2 个。
Optimizer 的做法是把工具清单和实际的工具调用日志做交叉对比,找出"声明了但从没被调用过"的工具。在烟雾测试场景下,删掉无用工具后,单次调用的上下文减少了 8–12 KB,单次运行省下几千 token,行为完全不变。
有一个特别离谱的例子:Glossary Maintainer 这个工作流,一次跑里有一个工具被调了 342 次,占总工具调用的 58%,但根本没必要。还有 Daily Community Attribution,配置了 8 个 GitHub MCP 工具,结果一次都没调过。
优化点二:用 GitHub CLI 替换 GitHub MCP
更结构性的优化是:数据获取类操作(拿 PR diff、读文件、查 review 评论),别走 MCP,走 gh CLI。
为什么?因为 MCP 工具调用是有"推理开销"的:
- agent 要先决定调用哪个工具
- 然后构造参数
- 发请求、等响应
- 再处理返回的结果
这整个过程是一次完整的 LLM round-trip,token 算钱、延迟算时间。
而 gh pr view --json 这种 CLI 调用是确定性的 HTTP 请求,根本不需要 LLM 参与。
GitHub 团队用了两种迁移策略:
1. agent 启动前预拉数据
在 setup 步骤里直接跑 gh 命令,把结果写到工作区的文件里。Agent 启动后读文件就行,完全省掉了运行时的工具调用。
2. agent 运行中用 CLI 代理替换
跑一个轻量的透明 HTTP 代理,把 CLI 流量直接转到 GitHub API,凭证由代理持有(agent 拿不到)。Agent 像在终端里一样用 gh pr view --json,拿回结构化数据。
这些做法把大部分 GitHub 数据获取操作搬出了 LLM 的推理循环。
怎么衡量"真正变省了"
这是论文级别的问题。直接看 token 数会被三个因素干扰:
模型不同:同样的 token 数,Haiku 比 Sonnet 便宜约 4 倍。光看总 token 数会误判。
工作负载会变:今天 PR 是改 5 行代码,明天是 200 行。同一个工作流跑两次,消耗本来就不可比。
质量没法量化:换成更便宜的模型、或者收紧 prompt,输出质量会不会下降?这种"goodput"信号目前没有规模化的采集方式。
为了解决前两个,团队定义了一个 Effective Tokens (ET) 指标:
ET = m × (1.0 × I + 0.1 × C + 4.0 × O)
m:模型成本系数(Haiku = 0.25×,Sonnet = 1.0×,Opus = 5.0×)I:新处理的输入 tokenC:cache-read tokenO:输出 token
输出 token 系数 4× 是因为它在各家 provider 上都最贵;cache-read 0.1× 是因为缓存命中部分的单价低得多。
这个指标的核心价值不是绝对值,而是让不同模型、不同时间段的运行可以横向比较。
优化后的实际效果
12 个工作流里有 9 个跑了 Optimizer 推荐的改动。取前后各至少 8 次运行的数据:
| 工作流 | ET 改善 | 运行次数 |
|---|---|---|
| Auto-Triage Issues | 62% | 109 |
| Smoke Claude | 59% | 多次 |
| Security Guard | 43% | 多次 |
| Community Attribution | 37% | 8 |
| Daily Compiler Quality | 19% | 12 |
Auto-Triage Issues 平均一天跑 6.8 次,按改动前的速率估算,这段观察期里累计省下了约 780 万 ET。
几个反直觉的发现
确定性的数据采集,往往占了一大半的 agent 轮次
Auto-Triage Issues 改善最大,原因不是 prompt 写得更好,而是把大量"读 PR 内容、读 issue 元数据"这种纯 IO 操作搬出了 LLM 循环。最便宜的 LLM 调用,是那次根本没发生的调用。
Security Guard 的 −60% 来自一个相关性门控:PR 如果没改任何安全敏感的文件,整个 LLM 推理直接跳过。
工作负载漂移会掩盖优化效果
Contribution Check 做了优化反而 ET 升了 5%。不是优化失败,而是观察后期碰上了一波改动量更大的 PR。这种事在生产环境里很常见,做对比要小心。
配错一行参数能引发雪崩
Daily Syntax Error Quality token 用量异常高,根因是 bash 命令白名单只允许相对路径模式,编译命令被拦截后,agent 进入了一个 64 轮的兜底循环——它开始手动逐行读源代码,试图自己重建编译器本该返回的错误信息。
修一行配置,整个病灶消失。这是一个很典型的"agent 失控"模式:你没给它正确的工具,它就会用昂贵得多的方式去凑答案。
还没解决的问题
GitHub 团队明说了目前的两个盲区:
- 没法系统地衡量输出质量。所有优化都建立在"行为不变"的假设上,但没有规模化的 outcome 采集机制。
- 缺少 episode 级的可见性。一次工作流运行其实是一连串"episode"——拿上下文、读 artifact、失败重试、最终合成答案。哪些 episode 反复失败?哪些可以抽成确定性的前置步骤?哪些工作流之间在重复劳动?目前的数据粒度还不够回答。
下一步他们打算把单体 agent 拆成"子 agent 团队",用更便宜的小模型分工。
自己怎么用
这套工具已经开源,能直接装:
gh extensions install github/gh-aw
gh aw add githubnext/agentic-ops/copilot-token-audit \
githubnext/agentic-ops/copilot-token-optimizer
装完接到自己的 agentic workflow 里,第二天就能看到一份消耗报告。
一些观察
这篇文章里最值得抄作业的不是 ET 公式,是这几个判断:
-
可观测性要放在框架之外。如果埋点跟着具体 SDK 走,换框架就要重写一遍。GitHub 的做法是埋在 API proxy 上,跟具体 agent 实现解耦。
-
能不调 LLM 就不调 LLM。这听起来像废话,但实际工程里太容易顺手让 agent 去做一件其实
curl就能搞定的事。MCP 工具看着方便,每一次调用都在为 LLM 的推理付费。 -
真正的浪费往往是配置错误,不是 prompt 不够好。64 轮的失控循环不是"模型不够聪明",是白名单写错了。先看日志再调 prompt,顺序别反。
如果你的项目里也有 agent 跑在 CI 上、跑在定时任务里,去翻一下账单,再翻一下日志。大概率能找到几个 342 次无用调用 级别的浪费。
原文:Improving Token Efficiency in GitHub Agentic Workflows,作者 Landon Cox 和 Mara Kiefer。
关于
关注我获取更多资讯