局部规则、宿主流程与真实任务三层验证,模拟响应流固定回归,以及用 call id 串起遥测时间线。
前面把推进任务、调工具、管上下文、中断恢复等机制走了一遍。最后一章解决很实际的问题:改完这套系统后,怎么知道它按预期运行?模型回「问题修好了」心里没底——要看有没有真改文件、跑的是不是相关测试、退出结果如何、原要求有没有满足。第十三章从 Codex 的测试辅助设施与遥测看:哪些问题可固定反复验证,哪些仍需真实场景判断。
三层容易混在一起的「完成」
- 模型响应结束:这次响应收完了;
- 工具返回成功:对应工具达到它自己的成功条件;
- 用户目标是否真完成:还要结合真实文件与任务要求判断。
测 Agent 时不要只比最后一段自然语言输出,应顺着调用过程看:模型做了哪些动作、宿主执行了什么、结果有没有回到后续请求、最终状态是否符合预期。任务验收放在最下面,需要前面几层证据共同支撑。集成测试可验证结果回写,但不能因此判断模型在所有真实任务里都会选对证据或工具。
验证分成三层
- 局部规则:工具输出怎么截断、调用参数怎么处理——固定输入应得固定结果,适合小范围测试;
- 宿主流程:假设模型这次返回一个工具调用,宿主会不会正确执行、记录结果并构造下一次请求——用可控响应替代真实模型,让测试每次走到同一位置;
- 真实任务:给实际仓库与真实问题,看模型与工具共同工作后是否满足验收——能发现前两层看不到的选错工具、漏掉要求等问题,结果也更依赖模型、环境与任务本身。
用模拟响应流固定宿主行为
测试工具可拼出模型响应流:先返回函数调用条目,再返回响应完成事件;下一次请求再收到另一段预设响应。辅助函数把这几段按顺序挂到本地模拟服务上——Codex 仍走自己的请求与工具处理流程,只是收到的模型输出由测试安排。模拟服务同时捕捉 Codex 实际发出的请求,可检查第二次请求有没有带上第一次调用的结果、call id 是否对应、顺序有没有错。特别适合把曾经导致错误的响应顺序固定下来,改代码后重复跑。记住:模拟模型总会返回写好的内容,不能替代「真实模型能否选对动作」的验证。
断言写在哪里
- 调用本身:工具名对不对、参数是否预期;
- 调用与结果的关联:尤其 call id;
- 结果最终去了哪里:第一次工具返回测试失败,下一次请求里应能找到对应结果。
测试辅助对象可提供读请求列表、按 call id 找函数输出等方法。工具生命周期测试还会记录工具启动时看到的会话历史,用来核对顺序与内容。工具会改文件时,还要读实际文件检查变化——只看到模型提出修改调用,不能证明文件已改对。审批拒绝时,反过来检查不该出现的副作用有没有出现。断言最好用用户能观察到的结果表达;内部函数怎么拆,只要调用关系与最终行为一致,测试就不必因实现结构变化一起大改。
并发测试:别用「都很快结束」证明并行
机器快慢、进程启动与系统调用都可能影响结果。更具体的安排:让两个测试工具都到达同一个同步屏障,只有两者都参与了才继续;若执行层串行跑,第一个会等到超时,从而暴露串行问题。时间检查要给慢一点的 CI 调度留余量,不要把某一毫秒数当成所有环境的性能承诺。执行顺序、完成顺序与写入历史的顺序是不同问题——证明两个工具同时在跑之后,还要按需要检查结果怎么归集。
失败与边界用例同样重要
真正难查的地方经常是:做到一半被拒绝或取消,或恢复以后少了一段历史。失败用例要先写清预期——例如审批拒绝后不应产生被拒绝动作的副作用;运行中追加的输入要在允许消费的位置进入后续请求;压缩并恢复后模型看到的历史应保留约定内容。仓库里分别有审批、输入提交、压缩后恢复与分叉等测试,检查对象不同,不是用一个「出错时也能结束」的断言代替。给自己的系统补回归时,可从一个实际故障开始:固定触发顺序,列出必须发生与不能发生的结果,再做成可重复用例。
遥测:解释已经发生的事
测试帮我们提前发现问题;运行时遥测帮我们解释已发生的事。若日志只有「开始了/结束了」,很难判断是不是同一个任务。Codex 的遥测事件可带会话信息;工具调用与工具结果会记录工具名、命名空间与 call id,从而把某次许可判断与后续执行结果对应起来。请求记录可含耗时、状态与尝试次数;工具结果还有进程内递增序号,表示结果被记录的顺序——不是工具开始顺序,也不是重启后仍连续的永久编号。并发时不能靠它代替调用关联。排错时先让同一次调用的几段记录连起来,再考虑加多少图表。
记录越多越方便,也越大、越敏感
要看清哪类数据进了哪条通道。工具结果事件里,诊断日志可含参数与经限制的输出预览;追踪事件可用参数长短、输出长度行数、工具来源等元数据。两边共享 call id、耗时、成功标记以及预览是否被截断等信息。输出预览按自己预算截断,尽量停在字符边界再追加截断说明——这只说明遥测层是否删减,不能证明更早阶段原始工具结果一直完整。用户提示词可有单独记录开关,关闭时用脱敏占位;别把该开关理解成所有日志里任何敏感信息都会自动清除——部署时仍要检查工具参数日志保留范围与谁能读取。
任务跑很久时怎么拆时间线
只看总时长不够:可能是模型请求重试几次、在等待许可,或命令进程本身没结束。先拆开时间线,找到时间花在哪一段:请求侧看尝试次数、状态与耗时;工具侧看调用记录、call id、执行耗时与结果;需要知道当前轮次状态时再结合会话事件与界面显示。不是每一项等待都有现成独立计时器,缺少时要补观测。再判断怎么改:重复请求查重试原因;等待确认把待办状态展示清楚;命令慢则看进程与输出——不要还没定位原因就把模型或所有超时整体放大。同样,跑得快 ≠ 任务做对;性能指标解释开销,文件与测试解释结果,用户要求决定验收。
把整条链路合起来
任务进入 → 模型依当前上下文做决定 → 工具经权限与执行处理 → 结果写入记录 → 下一次请求继续使用;任务长了要管进程与上下文;中途有新要求要处理输入;重启后要重建可恢复状态。测试与可观测性沿着这条流程安排:重要规则用固定输入检查;宿主流程用可控响应复现;真实任务按实际产物验收;运行中把必要事件关联起来,遇到故障才能找到偏差位置。解决过的问题留下能稳定复现的回归;属于真实场景判断的问题,把验收条件与场景保存清楚——下次调整工具、权限或上下文策略时才有东西对照。
若你也在准备做一个 Agent:先拿一个小任务,把输入、调用、结果、继续条件与验证证据保完整,再考虑增加能力;每多加一个机制,就补上对应的状态检查点。
本系列文字稿对应合集已完结。可回到系列导读页按主题跳转各篇,或到 B 站合集对照源码视频。