AgentHub 开发手记(二):补上解析测试,让运行过程看得见
上一篇记录了 AgentHub 跑通图片处理的过程。任务能够完成以后,新的问题也出现了:如果结果不对,我怎么知道是哪一步出了问题?
模型回复、解析器、工具和外部服务连在同一条调用链里,只看最终答案,很难定位原因。因此接下来的改动集中在两个地方:让文本解析能应对已知的格式变化,再把每轮调用记录下来。
这篇记录的是 2026 年 9 月 22 日对解析器、运行轨迹和工程计划的完善。
文本协议需要处理实际出现的格式
这一版 AgentHub 用 Action、Action Input 和 Final Answer 从模型回复里提取决策。最开始的实现主要针对格式规整的文本。
但标签可能出现不同写法。例如,以下都是用于说明解析行为的样例:
1 | Action:echo |
1 | **Action**: echo |
还有内容放到下一行的情况:
1 | Action: echo |
如果解析器只认最规整的单行格式,这些变化就可能造成工具名没有提取出来,或输入内容丢失。
这次修复的做法很直接:先统一全角冒号,再按行拆分标签和值,清理标签周围的空白和星号;遇到 Action Input 或 Final Answer 时,把当前值和后续行合并起来。
这里的重点是把已经发现的问题落成明确规则,而不是期待提示词让所有输出永远保持一致。
八个测试把解析行为固定下来
Parse 接收字符串并返回 Decision,没有网络、文件和模型调用。这让它适合用固定输入检查行为。
这次为解析器补了八个测试,覆盖的情况包括:
| 情况 | 检查的行为 |
|---|---|
| 全角冒号 | 能提取 echo 工具名 |
| Markdown 加粗标签 | 能识别 Action |
| 输入从下一行开始 | 能提取后续内容 |
| 多行最终答案 | 保留换行和完整文本 |
| 多行工具输入 | 保留多行输入 |
| 输入后又出现工具标签 | 已提取的工具名不被覆盖 |
| 完整工具请求 | 工具名和输入都正确 |
| 完整最终回答 | 正确判断运行可以结束 |
测试的意义在于把“这个样例应该怎样解析”保存下来。以后修改解析器时,可以检查这些已知行为是否发生变化。
但也要认真看断言覆盖到哪里。例如,“输入后又出现工具标签”的测试,只检查先前提取的工具名仍为 echo。当前实现会把 Action Input 后面的全部文本收进输入,它没有证明后面的标签一定被排除。
同样,这八个测试也不意味着任意非法回复都能处理。字段冲突、缺失字段和复杂的分隔规则,仍然需要另外定义行为。
解析测试和运行过程记录解决不同的问题
解析测试能回答:给定一段文本,程序会提取出什么。
实际运行时,我还想知道:模型这一轮返回了什么,工具有没有被调用,传进去的参数是什么,以及等待时间花在哪里。
因此,我增加了 Observer。在这一版里,它有两个观察入口:
1 | type Observer interface { |
Agent 内核在模型调用和工具调用结束后,把结果交给 Observer。具体怎么输出,放在 ConsoleObserver 里实现。
这样做之后,循环仍然负责执行和消息回传,控制台输出负责展示发生了什么。以后如果需要换一种记录方式,至少有一个可以替换的接口。
看见时间,也看见输入和结果
控制台轨迹主要记录这些信息:
- 当前轮次。
- 模型调用耗时、回复或错误。
- 工具名称、输入、执行耗时、结果或错误。
模型计时围绕一次 Chat 调用,工具计时围绕一次 Execute 调用。因此它们表示的是对应调用经历的时间,包含请求和处理过程,不能直接当成模型内部计算时间。
有了这些记录,排查问题时就能沿着调用链检查:模型是否输出了预期工具名,解析后传入的参数是否合理,工具是否返回错误,以及下一轮是否收到了反馈。
未知工具的情况也会记录到 Observer,并生成错误 Observation 交回模型。这样,工具名称错误本身也成为可查看的事件。
空 Observer 让观察能力保持可选
构造 Agent 时,如果没有传入 Observer,代码会使用 NopObserver。
它实现同一个接口,方法体为空。这样内核仍然可以在固定位置调用观察方法,不需要每次都检查 Observer 是否为空。
这个设计在当前规模下很实用:命令行运行时可以打开控制台轨迹,其他调用场景也可以选择不输出。
不过,这里的 Observer 是事后观察。工具已经执行完,才把结果交给它。它没有负责批准或拒绝工具调用,因此不能把它当成执行前的权限检查。
工程计划要和实现状态分别记录
同一天,我还整理了 AgentHub 的工程化计划,列出了后续的功能方向和验收任务。
计划的作用是把接下来需要解决的问题写清楚。至于哪些已经完成,仍然要回到对应代码和实际运行情况来看。
截至这一阶段,解析器有了八个固定输入测试,运行过程有了 Observer 和控制台轨迹。但是 Agent 循环的完整失败路径还没有相应的自动化测试,记录日志也不能替代这些测试。
