文章
别再为同样的 token 付两次钱
我让五个审查代理跑了一个 16K token 的 PR。最朴素的方案花了 $1.32。两处架构改动把账单降到 $0.49。这是账单本身,以及实验过程中令我意外的地方。
Read in English → · Leer en español →
别再为同样的 token 付两次钱
我让五个审查代理跑了一个 16K token 的 PR。最朴素的方案花了 $1.32。两处架构改动把账单降到 $0.49。这是账单本身,以及实验过程中令我意外的地方。
五个专家代理审查了同一个 pull request。Security、tests、perf、observability、API design。每一个都重新读了整个 diff。每一个都按全价付费。在 Claude Opus 4.6 上跑一次 PR 审查,账单是 $1.32。
一个 PR。一次审查。一刀三毛二。
如果你在任何有意义的规模上跑多代理代码审查,那你就是在为同样的 token,一遍又一遍,按全价烧钱。
我搭了一个 harness 来量化这种出血,并测试两种显而易见的修复方式。数字比我预期的更鲜明,其中一个甚至颠覆了我对 prompt caching 的假设。
我构建了什么
一个小型 Python harness,三种模式,共享同一个 49K 字符的 pull request diff。五个审查角色,每个都有一段紧凑的 system prompt:
REVIEWER_ROLES = {
"security": "Auth, injection, secrets, access control...",
"tests": "Missing tests, skipped tests, weak assertions...",
"perf": "N+1 queries, sync I/O in request paths...",
"observability": "Logging hygiene, error handling, missing metrics...",
"api-design": "Endpoint naming, status codes, idempotency...",
}
针对同一个 diff 的三种模式:
- Naive(朴素)。 每个审查者各自发起调用,把完整 diff 放进 user message。角色文字写在最前面,所以每次调用的前缀都不同。什么都不会被缓存。
- Cache(缓存)。 每个审查者把 diff 放进 system message,五次调用里完全一致。第一次调用写入 provider 的 prompt cache。第二到第五次以 90% 的折扣从缓存读取。
- Librarian(图书馆员)。 先用一次调用把 diff 摘要成紧凑的 JSON。审查者读摘要,不读原始 diff。灵感来自 Cho 等人的 arXiv 论文 Long Live the Librarian!,论文用一个持久的搜索子代理抑制多代理之间的冗余探索 token。
每次调用都按 Anthropic API 的官方挂牌价计费:$15/M 输入、$1.50/M 缓存读取、$75/M 输出。token 数是真实测出来的,价格是按官价做算术。
数字
大 diff(49K 字符,每次调用大约 16K tokens),五个审查者,一次全新的审查:
| 模式 | 输入 | 缓存 | 输出 | 合计 |
|---|---|---|---|---|
| Naive | 80,248 | 0 | 1,500 | $1.316 |
| Cache | 80,268 | 63,968(5 次里 4 次命中) | 1,500 | $0.453 |
| Librarian | 20,915 | 0 | 2,300 | $0.486 |
Naive 浪费得近乎滑稽。Cache 和 librarian 都把账单砍掉了大约三分之二。
Cache 比 librarian 便宜三美分。这是真实差距,它改变了我对先拉哪根杠杆的判断。
什么时候 caching 真的赢过 librarian
Cache 在”热序列”上获胜。第一次调用写入缓存,后续四次从中读取。如果你能保证五个审查者在同一个热会话里背靠背针对同一个共享前缀执行,prompt caching 就是你能做的最干净的一招。两行 system message 重构。账单立减 66%。
问题在这里:生产环境里的代码审查 bot 几乎从来不是这样运行的。
每个 PR 都是一个全新的会话。PR 来自不同的开发者、不同的仓库、不同的分支,到达时间也无法预测。根本没有”热序列”。只有源源不断的冷启动。
每次调用都是冷启动时,cache 模式就塌回了 naive 模式。每次调用一样的美元开销。一样的出血。
Librarian 模式不在乎会话是冷是热。每个审查者读的都是一个小小的摘要,而不是原始 diff。节省被烤进了调用的形状里,而不是依赖 provider 集群里某个缓存的运行时状态。
| 模式 | 热序列(5 次背靠背) | 每次都冷启动(运营现实) |
|---|---|---|
| Naive | $1.32 | $1.32 |
| Cache | $0.45 | $1.32 |
| Librarian | $0.49 | $0.49 |
如果你的审查者在单一会话里紧凑地连发,用 cache。如果它们在多个冷启动之间独立运行,用 librarian。大多数真实系统属于后者。
Caching 让我意外的地方
我给 harness 加了一个”出声失败”的护栏。如果 cache 模式里第 2 到第 N 次调用报告 cached tokens 为零,harness 直接中止并打印诊断信息。被缓存的前缀应该命中,如果没命中,这场实验就是无效的。
它在小 diff 那一轮触发了两次。
同一份代码,同一个五步调用序列,同一个 4,003 tokens 的 system message。两次跑的结果都是每次调用都返回 cached_tokens=0。Cache 模式和 naive 模式的总价精确到四位小数都是 $0.413。
然后我跑了大 diff,每次调用 16,000 tokens。五次调用中有四次干净地命中了缓存。每次缓存 15,992 tokens。
所以在 4K 到 16K tokens 的相同前缀之间,针对 claude-opus-4.6、走 Copilot 代理、用 OpenAI 格式的请求时,prompt caching 才会激活。低于这个阈值时不会。Anthropic 的文档说最低门槛大约是 1024 到 2048 tokens。在经验上,激活曲线并不是平的。
如果你按一个声称的缓存折扣做预算,把你的命中率当成一个区间来建模。在你真实的 payload 体积上跑一遍实验。不要假设营销页上的算术对你实际发出的调用形状仍然成立。
自己跑一遍
完整的 harness 放在 github.com/AndreaGriffiths11/librarian-demo。三个 Python 文件,一个 diff 场景,一个 pricing 模块。这样跑:
export COPILOT_OAUTH_TOKEN=$(sqlite3 ~/.copilot/data.db \
"SELECT access_token FROM github_accounts WHERE is_default=1;")
python demo.py all --verbose --diff scenario/sample_pr_large.diff
如果你装了 GitHub Copilot CLI,那 ~/.copilot/data.db 里已经存了一份 OAuth token。把 OpenAI SDK 指向 https://api.githubcopilot.com,加上 Copilot-Integration-Id: copilot-cli 这个 header,你就能在 chat-completions 这套接口上拿到完整的模型目录(Anthropic、OpenAI、Google),并且 prompt_tokens_details.cached_tokens 字段会被正常返回。不需要 PAT,不需要 token exchange。大约 30 行胶水代码:
import sqlite3, pathlib
from openai import OpenAI
con = sqlite3.connect(
f"file:{pathlib.Path.home()}/.copilot/data.db?mode=ro", uri=True
)
token = con.execute(
"SELECT access_token FROM github_accounts WHERE is_default=1"
).fetchone()[0]
client = OpenAI(
base_url="https://api.githubcopilot.com",
api_key=token,
default_headers={"Copilot-Integration-Id": "copilot-cli"},
)
关于 billing 的一点说明:GitHub Copilot 从 2026 年 6 月 1 日起转向基于用量的计费。新模型下,token 消耗直接按 Anthropic 公布的 API 费率折算为 GitHub AI Credits,因此本文中的美元数字同样适用于 Copilot 用户,而不仅仅是直接调用 API 的人。架构上的结论是一样的。如果你在使用付费 Copilot 计划,这些节省就是你月度信用额度里实打实的美元。
我真要落地的话会怎么做
如果我明天要带着这些数据重建一个审查流水线:
- 把所有共享上下文挪到 system message 里。这是软件里最便宜的重构,并且即使缓存没激活,你也什么都没损失。
- 当审查者数量超过三个、或共享上下文大到能跨过激活阈值时,就构建 librarian 那一步。摘要在冷会话之间会自己赚回本钱。
- 不要把缓存命中率当成一个常数。在你自己的 payload 上、自己的环境里、对着你要发布的那个模型去测。写一个出声失败的护栏,假设一旦破了就让它尖叫。
Cho 等人的论文在 SWE-Bench Verified 上测的是 GPU 能耗节省,多代理跑的时候能达到 25%。我的 harness 测的是一次 PR 审查的真实美元开销。不同的代理,相同的教训:多代理之间持久的共享上下文是一根免费的架构杠杆,就躺在地板上。
论文:Cho, Choi, Heo, Choi. “Long Live the Librarian! A Persistent Search Sub-Agent for Energy-Efficient Multi-Agent Software Engineering Systems”(arXiv:2605.27787,2026 年 5 月)。
所有数字都是 2026 年 5 月 29 日通过 GitHub Copilot 代理对 claude-opus-4.6 实测得到的。完整的逐次调用明细和 harness 源码放在仓库的 runs/findings.md 里。
关于作者: Andrea Griffiths 是 GitHub 的高级开发者倡导者,帮助工程团队采用和扩展开发者技术。她热衷于让技术概念对人类和 AI 代理都可访问。在 LinkedIn、GitHub 或 Twitter/X 上与她联系。 · Read in English