作者在构建支持工单 Agent 过程中发现 eval trajectory 匹配方式不当和缺少重试机制导致崩溃,分享了具体修复方案。
迭代到绿灯:真实 Bug,以及何时真正需要框架
本文是这个系列的最后一部分,之前六篇讲述了完整的设计。这一篇讲的是"完成"到底经历了什么——评估集捕获的真实 Bug,以及每个 Agent 构建最终都必须诚实回答的两个问题:是否需要多个 Agent,以及是否需要框架。以下是所有细节:docs/iteration-log.md。
1. 精确轨迹匹配是错误的检查方式
首次评估运行:12/21 通过。大多数失败案例是 Agent 正确地发送了确认邮件,而评估只期望一次查询——行为正确,但断言写错了。修复:将测试框架从精确数组相等改为有序子序列匹配(每个期望的工具必须出现,且顺序正确;中间的额外步骤没关系)。仍然能捕获缺失、乱序或错误的工具调用,同时不会因为良性的非确定性而产生误报。
2. 没有重试/退避意味着一次临时错误会让整个运行崩溃
一次 503 错误——案例 1 中模型过载——导致整个评估框架挂掉。通过对 429/503 特定响应码实现指数退避,加上案例间的节流来控制在免费层速率限制之下来修复。
3. -latest 模型别名静默地切换到了更严格的配额
gemini-flash-latest 之前能正常工作,后来在静默解析到更新的模型后开始失败,原因是每天 20 次请求的配额限制。通过固定一个明确的模型版本而非别名来修复——在此之前先查了提供商的使用量仪表盘,固定模型后配额多了 25 倍的余量。这个经验可以泛化到其他提供商:"latest" 别名优化的是能力,而非配额稳定性,它们解析到的目标会随时间变化,而你的代码完全没动过。
4. 一个看起来像真实 Bug 的测试产物
通过 printf "a\nb\n" | npm run agent 向交互式 CLI 管道输入多个答案时,在第一个提示后间歇性挂起。根本原因:Node.js readline/promises 与快速关闭的管道 stdin 之间的一个奇怪行为——通过在真实的伪终端中重放相同输入来确认为测试产物而非真实 Bug,在那里每次都正常工作。无论原始症状如何,通过复用单个共享的 readline.Interface 而不是每次提示都打开/关闭一个来修复——这本身就是更正确的做法。
5. 模型声称已发起退款但从未真正发起
这是最大的一个——完整内容在第五篇。两个评估案例显示结果:refund_proposed,但轨迹中从未调用 issue_refund。在两个层面修复:一个明确的提示行,以及——真正起作用的层面——一个代码级检查(enforceOutcomeIntegrity),在信任模型自身声明之前验证 state 中是否真实存在 confirmation_id。
原始日志中关于第五条发现的诚实附注:它是通过一次定向重运行验证的,而非完整评估通过,特意为了节省免费层 API 配额。这个缺口在仓库中有意保留——在没有实际运行的情况下声称完整通过,与第五条本身是同一类错误。
多 Agent(协调器分发给专职 Worker)是成本决策,而非架构偏好。协调器 + Worker 的拆分每次运行成本大约是单 Agent 的 3–8 倍——Worker 各自看到的工具更少,这让工具选择更精准,但你在每一次运行中都在为此付费,不只是困难的情况。
在支付这个倍数之前,三个信号应该同时为真:任务确实能拆分出各自受益于更精准、更窄提示的专业角色;子任务能并行运行带来真正的速度提升,而非只是代码更整洁;以及量 × 准确率提升确实超过了成本倍数——用真实数字验证,而非直觉。
在低流量下,数学倾向于即使拆分带来真实准确率提升也优先选择单 Agent——倍数赚不回来。在高流量下,同样的准确率提升可以节省真金白银,因为升级成本降低与流量规模成正比,而固定倍数不会。对于本仓库自己的 Agent,有意保持单 Agent:五个工具远未达到 Agent 开始混淆工具名称的临界点,而且这个参考构建所针对的票据流量完全不值得为一个拆分支付倍数——这种拆分主要只是看起来更整洁。
亲手构建了循环之后,框架所回答的实际问题就变得具体而非抽象了:这个问题是否需要多 Agent 编排、流式 UI 接线、集成的追踪生态,还是团队间的共享抽象?如果都不适用,这个系列构建的约 100 行循环就是生产版本,而非某个框架的占位符。
这个系列中的所有内容都是真实提交的产物——不是清理后的复述。README 有完整的架构图、快速启动和真实运行示例(含截图,不是摆拍的)。AGENTS.md 为之后——人类或 AI 编码 Agent——扩展此仓库的人记录了硬约束:无框架、仅使用模拟数据、受限工具保持受限、护栏写在代码里。
它明确是一个参考构建,而非生产软件——模拟数据、单一免费层模型、无持久化、无认证。如果从整个系列中只能带走一样东西,带走构建顺序,而非代码:固定用例、将工具契约作为规格书、在 Agent 存在之前构建评估集、编写最小可工作的循环、用真实失败迭代、门控有影响的操作。框架问题放在最后,而非最前——到了你真正能回答它的时候,你往往已经不需要了。
Repo: github.com/akash-pal/agent-from-scratch
系列索引:第一篇(什么让一个东西成为 Agent) · 第二篇(用例与工具契约) · 第三篇(评估集) · 第四篇(原生循环) · 第五篇(护栏) · 第六篇(可观测性) · 第七篇(本文)。