将测试日志转为 JSON,压缩编程智能体输入
文章比较原始测试终端输出与结构化 JSON 失败差异过滤,主张减少通过记录、环境横幅等冗余信息。标题报告输入 token 减少 94.1%,实验使用指定模型和隔离环境,摘要未展示完整结果。
AI 深度解读
给 AI 编程 Agent 喂测试结果,最贵的部分可能是那些根本不用它处理的日志。作者用同一套支付结算代码对比发现:把原始 pytest 输出换成只保留失败信息的 JSON,累计输入 token 从 92,385 降到 5,445,两组最终都通过了全部隐藏测试。
这篇值得看的地方,是它把工具返回值如何拖累多轮对话算了出来。但 94.1% 的降幅有明确实验条件,过滤后的信息是否足够客观,也需要仔细看。
测试只跑两次,为什么会消耗九万多 token
作者测试的是 Agent 的工具封装方式,也就是 harness:模型如何调用测试、工具返回什么、结果怎样进入下一轮上下文。
实验编号为 E16,候选编号 C012,运行 ID 是 e16-20261028-vm220-01,在作者的 41 种模式中排名第 3。模型固定为 gemini-3.8-flash,参数为 thinkingLevel: LOW。
任务涉及 validator.py、settlement.py、statement.py 三个模块。公开测试共 80 个,初始状态为 76 个通过、4 个失败。环境是 Proxmox 隔离虚拟机 VM220,名称为 ai-e16-220,配置 0 NICs,通过 virtio-serial QGA 中继通信,并使用 systemd UID 分离。
两组区别在测试结果的呈现方式:
- Mode A 使用
RawTerminalOutputRunner,直接返回pytest -vv --tb=long的终端输出。 - Mode B 使用
TokenKillerFilterRunner,通过确定性预处理器返回失败摘要。
Mode A 第一轮调用 run_raw_pytest,输入、输出分别为 2,406 和 10 tokens,拿回 60,749 字符的日志,其中夹着 ANSI 颜色码、环境信息、通过记录、AUDIT_TRACE 和长堆栈。
第二轮,模型输入涨到 31,170 tokens,增加了 28,764 tokens;输出为 484 tokens。它通过 stage_partial_fix 只修了 validator.py,随后又收到 56,544 字符日志,此时还有 3 个失败、77 个通过。
第三轮调用 submit_settlement 时,输入已经达到 58,809 tokens,输出为 1,574 tokens。累计输入因此达到 92,385 tokens。
日志进入对话历史后,会在后续调用中被重复计入输入成本。
所以问题不只是一次输出太长。只要历史没有被裁剪,前一轮日志就会跟着后一轮日志继续占用上下文。
JSON 到底保留什么,才足够让模型修代码
Mode B 第一轮调用 run_filtered_tests,去掉通过测试、颜色码和 DEBUG 噪声,返回 1,506 字符、615 tokens 的 JSON。
下面这段原始结果,展示了它保留下来的定位信息与失败约束:
{
"summary": {"total": 80, "passed": 76, "failed": 4, "filtered_noise_chars_removed": 59243},
"failures": [
{
"test": "test_validator_contract",
"file": "validator.py",
"line": 14,
"assertion": "ALLOWED_CURRENCIES must include 'JPY'; reject empty list & bool; allow unit_cents==0 & chargeback_bps==1000; do not mutate input dict."
},
{
"test": "test_settlement_ordering_and_reserve",
"file": "settlement.py",
"line": 22,
"assertion": "Preserve first-seen (merchant_id, currency) order (never sorted(order)); apply 10% reserve strictly when chargeback_bps > 300."
},
{
"test": "test_settlement_discounts_and_rebate_cap",
"file": "settlement.py",
"line": 39,
"assertion": "Discount boundary is subtotal >= 15000 (GROWTH 10%, ENTERPRISE 20% else 10%); cap rebate_applied = min(fee_rebate, after_discount)."
},
{
"test": "test_statement_format_and_signed_zero",
"file": "statement.py",
"line": 11,
"assertion": "Use '#' delimiter ({merchant_id}#{currency}:) and preserve leading '-' on zero discount/rebate (TIER_DISCOUNT: -0, REBATE_APPLIED: -0)."
}
]
}
重点看 assertion:它已经把每个模块要满足的业务规则写得很完整,包括边界条件、顺序要求、输入不可变和负零格式。
作者称结果包含 expected_vs_actual,但展示的 JSON 没有这个字段,只有文字化的 assertion。这不是无关紧要的格式差别:实际值与期望值能让模型自己判断,完整规则描述则已经替模型整理了一部分问题。
Mode B 随后在第二轮以 3,030 input tokens 调用 submit_settlement,一次修改三个文件,总共调用模型 2 次;Mode A 为 3 次。两组都通过 labjudge16 的 20 个隐藏单元及 E2E 用例,得分均为 20 / 20。
累计输入从 92,385 降到 5,445 tokens,减少 94.1%,相当于 16.97 倍效率;API 费用从 $0.0770 降到 $0.0100,减少 87.0%;耗时减少 35.0%,约快 1.54 倍。
字符统计要分开看:首轮摘要是 1,506 字符,累计结果为 1,604 字符。原文用 117,391 ➔ 1,604 得出减少 98.6%、压缩 73.2 倍,但追踪章节又写原始日志累计 117,293 字符。后者等于两轮日志之和,两个累计口径相差 98 字符,原文没有解释。
真正可以复用的是工具返回协议
作者给出的落地建议,是避免直接把 subprocess.run(["pytest"]) 的 stdout 接到模型工具响应里。
可以先运行 pytest --tb=short -q --no-header --no-summary,减少终端输出;也可以使用 pytest-json-report 获取机器可读结果。两者作用不同:前者让日志更短,后者便于按字段提取失败信息。具体怎样接入 JSON reporter,原文没有展开。
通过数量保留为一个整数,失败项保留测试名、文件、行号和断言差异。原文举例说,500 个通过、2 个失败时,不必发送 500 条成功记录。
文章还提到 rtk,即 rtk-ai/rtk 的 Rust Token Killer,以及 SWE-agent 输出压缩模式。但实验实际展示的是自定义 Runner,不能据此认定安装 rtk 就能复现同样结果。作者提出「30 行 Python wrapper」即可降低 85%–95% 的 token 费用,也没有提供对应实现和更多对照。
这套思路可以用于 cargo test 或 npm test,不过本文实测的是 pytest,其他测试工具的收益仍需单独验证。
我的判断
我认同先整理工具输出,再考虑换模型。通过记录、颜色码和环境横幅通常无需反复交给模型阅读,这次实验也确实展示了它们怎样放大累计输入。
但它证明的是一个具体任务中的收益,距离通用的 94.1% 还有距离。
首先,原始组使用了非常冗长的 pytest -vv --tb=long。如果对照组本来就只返回短堆栈和失败摘要,差距会有多大,作者没有给数据。
其次,过滤后断言已经包含相当完整的修复要求。原文没有展示转换代码,无法判断这些描述是测试原有消息,还是额外整理出的规则。因此,「压缩日志」和「改善问题说明」各自贡献多少,目前分不开。
作者把 Mode A 多走一轮归因于长日志分散注意力,这也是解释,不是被单独验证过的因果结论。文中只给出一个 run,没有重复次数、波动范围或其他模型对照。
可迁移的是按失败信息组织工具响应的方法,具体降幅仍然只是这次实验的结果。
相比只截取日志末尾,结构化摘要更有机会保住多个失败的分布;相比一律删除框架堆栈,它又需要保留查看原始信息的入口。测试收集失败、进程崩溃等情况未必会形成标准断言,不能因为没有解析出失败列表,就让 Agent 误以为测试通过。
能拿来做什么
- 给现有 AI 编程工具增加测试摘要响应,先返回通过数量和全部失败项,并保留原始日志供需要时读取,避免每轮都把完整 stdout 放进历史。
- 在前端或 Node 项目的
npm test封装中尝试相同协议,但先确认测试框架能稳定提供文件、位置和断言差异,不要直接套用 pytest 的文本解析规则。 - 复现实验时增加一组短日志对照,记录累计 input tokens、模型调用次数、费用和最终通过情况,判断收益来自去噪还是少走了一轮。
- 检查摘要是否额外加入了原日志没有的修复建议,并为解析失败保留明确状态,让 Agent 能区分「测试通过」和「没有拿到可用结果」。