Lazy loaded image
runtime轮子项目05丨如何验证Agent运行真的稳定
字数 3798阅读时长 10 分钟
2026-1-11
type
Post
status
Published
date
Jan 11, 2026
slug
agent05
summary
tags
Agent
category
icon
password
我做完前几篇里那条执行流程以后,有一段时间其实挺满足:runtine执行次=层能读文件、能改代码、能跑测试、能产出修改结果,看起来已经像一个能干活的 mini agent了。但后来往下想,我意识到这还不够。一次演示跑通,只能说明它在那一次顺利走完了;换一个相似任务、换一种失败方式、换一个入口,它还稳不稳,演示本身回答不了。我需要确认的是“同一类任务能不能反复跑通”。
所以我给第一版补验证时,把验证主线压得很窄:固定任务先让失败能复现,自动验证器跑完整流程,失败原因字典告诉我该修哪一层,修完再用同一组任务跑回来。代码里固定题库叫 Eval Pack,自动验证器叫 EvalRunner,名字可以不用先记,先记住这条链就够了。我想说的是,验证是为了让失败能出现、能归类、能回修、能复跑确认。

先看我遇到情况

我补了一个验证模块,这个验证真的把一次失败带回了修复。这告诉我,好的验证会把“坏了”压到某一个能修的位置。
最有代表性的一次,是一个“读多个文件、只改一个目标文件”的小题暴露了计划输出不稳定的问题。当时题目的重点明明在文件资料和单点修改,但失败先落在第一步:模型没有按我要求的固定格式返回计划,后面的读文件、修改、测试都还没开始。如果这次失败只被记成“没跑通”,我容易把它当成偶发;把它拆成“计划格式不对”以后,下一步就很明确了:先修计划输出的约束,不要急着扩更多题。
expected: {"plan_markdown": "...", "todos": [...]} got: I will describe the plan instead of returning JSON.
下面这段是当时留下的原始记录。里面的英文是程序里的内部名字,我保留它们只是为了让结果能核对,正文里按中文意思理解就够了:
Eval Expansion checkpoint: auto_approve_edits: 5/6 failure_reason_counts: {"invalid_model_output": 1} multi_file_context_single_edit: FAIL, stop=runner_failed Plan Output Hardening checkpoint: auto_approve_edits: 6/6 failure_reason_counts: {} multi_file_context_single_edit: PASS
这段前后对比告诉我第一步计划这层格式不稳。所以我改的是两件小事:计划阶段先做一次有边界的格式修正,如果还是不对就停;同时把这类失败单独归到 plan_invalid_output,不要混进宽泛的“执行失败”。再跑同一组题,这个失败才从 5/6 回到 6/6。所以我想说的是,一条好的验证结果要能指向下一刀修哪里。

我把验证收成一条链

面试里最容易被追问的就是“失败以后你怎么知道该修哪里”。
我把这套验证收成一条固定链路:先用固定题让问题能复现,再让自动验证器跑完整任务链,失败以后落到具体原因,最后回到对应的执行层修,修完再跑同一组题。验证链路必须能回答“坏在哪里、修哪里、怎么确认修好了”。
验证链路
它回答的问题
我当前的实现抓手
固定验证题库
失败能不能复现
6 个内置小题
自动验证器
任务停在哪一步
EvalRunner 跑完整流程
失败原因字典
坏在哪一层
计划、修改、资料、工具边界等分类
同组复跑
修完是不是偶然通过
5/6 -> 6/6 这种前后记录
复现记录
以后能不能核对
记录点、模型、通过数、失败原因

Eval Pack(固定验证题库)回答:失败能不能复现

为什么?因为一次完整演示太粗,失败了也很难知道是哪一层出了问题。
固定题库可以先理解成一组小题:固定仓库模板、固定任务输入、固定测试命令、固定最多走几步。目标很窄,就是让同一类执行路径能被反复核对。我保留的第一组题包括拼接字符串、修边界值、压缩空白字符、只改实现文件、根据失败测试回到代码、读多个文件但只改目标文件。固定题库先一次演示拆成能重复跑的小任务。
这些题最重要的不是难度,是它们能不能稳定复现同一类工程动作。比如“根据失败测试回到代码”会逼模型先从测试失败里找线索,再去改实现;“读多个文件但只改目标文件”会逼它看两个文件,但最后只能落到一个目标文件上。一旦当前资料或目标文件判断不好,就会出现重复读、改错文件、无效修改或提前说完成。题要小到能复现,也要尖到能暴露执行流程的缺口。

EvalRunner(自动验证器)回答:任务停在哪一步

我想验证的是整条执行流程。自动验证器做的事情很朴素:创建一个干净的小仓库,启动一次任务,生成计划,进入有限步执行,处理审批和工具结果,跑验证测试,最后写出报告。压成一条线就是:干净仓库 -> 创建任务 -> 生成计划 -> 有限步执行 -> 处理审批 -> 跑测试 -> 写报告。重点是它跑的是完整任务链。验证跑的是完整流程,不是单独测一句提示词。
一次验证挂掉以后,报告里不只会有“失败”,还会留下停在哪一步、失败原因、测试结果和资料记录。这样我就能继续问:它是计划没过、没读就改、修改片段对不上、反复读同一个文件、测试失败信息没带上,还是模型请求本身失败。完整流程验证的价值是把失败现场保留下来。

failure taxonomy(失败原因字典)回答:该回到哪一层修

“没跑通”太粗了,粗到没法指导下一次修复。
失败原因字典说白了就是把“没跑通”拆成一组能行动的原因,比如:没读文件就想改、修改片段对不上、改错文件、重复读同一个文件、计划格式不对、测试失败信息没带上、模型请求失败。如果这些都只叫“执行失败”,我后面根本不知道该去修审批、修修改规则、修当前资料,还是修模型调用。失败分类的价值是把“没跑通”拆成能行动的缺口。
下面这张小表把“失败现象”直接连到“回哪一层修”。它也提醒我别把所有锅都甩给模型:有些问题其实是计划格式、修改规则、当前资料、工具边界没有收好。能回到执行层的失败,才有工程修复价值。
失败现象
归类
回到哪里修
计划没按格式返回
plan_invalid_output
计划输出约束
修改片段对不上当前文件
修改片段对不上
修改规则 / 重新读文件
反复读同一个文件
同文件反复读取
当前资料选择
绕去普通命令
工具边界问题
受控测试入口
另一个例子是改代码失败。有一种失败是“模型给的旧代码片段和当前文件对不上”,另一种失败是“它提出了一次没有实际变化的修改”。它们看起来都和改代码有关,但处理方式不一样。拆开以后,我就不会直接说“模型不够聪明”,而是继续判断:这次要不要在审批前拦掉,要不要给它一次重新读文件再改的机会。所以我想说的是,好的失败分类会减少甩锅,增加可修正性。

context bundle(随身资料包)回答:为什么会原地打转

代码助手卡住的时候,经常不是不会改,而是不知道当前最该看什么。
随身资料包可以理解成每一步喂给模型的“当前资料”。它不会把整个仓库塞进去,而是收当前任务最该看的内容:最近读过的文件、最近修改结果、最近执行记录、最近一次测试失败。一个典型失败画面是:测试已经失败了,模型也读过测试和代码,但后面没有进入修改,而是在“跑测试、读测试、读代码”之间来回绕,直到步数用完。当前资料给得不准,任务最后会停在原地。
第一版里我保留了三个很小的记录项:一共读了几次文件、有没有重复读同一个文件、是不是卡在同一个文件上不往修改或测试走。这些记录看起来很朴素,但它们能提醒我:问题可能不在模型回答质量,而在我给它的当前资料没有把下一步推清楚。所以我想说的是,当前资料不能只靠感觉来给,要留下最小记录。
记录项
能用它发现什么
读文件次数
一个题大概需要读几次文件,路径有没有突然变长
重复读文件次数
模型是不是反复读已经看过的文件
同文件反复读取
任务是不是卡在同一个文件上,没有推进到修改或测试

pilot(真实仓库试跑)回答:真实文件里会不会绕路

真实仓库试跑就是前面说的:复制当前项目,在项目副本里跑一条受控任务。“先跑测试再修一条旧问题”的例子,能同时碰到工具边界、真实文件、测试入口和单文件修改。这个任务是为了看runtime在真实文件面前会不会绕路。真实仓库试跑的价值要落到具体仓库毛刺上。
这条试跑怎么走
内容
任务输入
先跑完整测试,再修一条关于命令审批分类的旧问题
期望行为
不绕去直接跑普通命令,而是先通过受控测试入口确认失败
错误表现
模型可能绕到普通命令,也可能给出和当前文件对不上的修改片段
失败时怎么分类
绕到普通命令,就暴露工具边界;修改片段对不上,就记成片段对不上
正确推进
先读相关测试和实现文件,最后只改目标实现文件
这个例子给我的提醒很实际:真实仓库里的失败不一定来自大功能,可能来自一个很小的修改片段、一个注释位置、一次计划输出、一次工具选择。真实模型运行曾经暴露过“修改片段对不上”:它想改的旧片段已经和当前文件不一致,所以修改无法安全落下去。这个结果没有让我去扩大产品范围,反而让我回到修改规则和失败原因上。真实仓库试跑的价值是让小毛刺能回到执行流程里被修掉。

repro pack (复现记录)回答:别人能不能核对当时怎么跑

复现记录也不用想复杂,它就是一份以后能复述的最小证据:这次记录点是什么、代码版本是哪一个、模型是谁、命令从哪里来、通过了多少题、平均走了几步、平均重复读了几次、失败原因是什么。原始记录可以留在本地,文章和仓库里保留稳定字段:
Checkpoint: Plan Output Hardening Model: RightCode / gpt-5.4-mini auto_approve_edits: 6/6, failure_reasons={} stop_on_request: 0/6, failure_reasons={"edit_approval_required": 6} avg_duplicate_reads: 0.0
别人问“你怎么知道后来没有退化”,我不需要回答“我感觉没有”。我可以说:固定题库有固定模板和固定测试命令,自动验证器每次创建干净仓库跑完整流程,报告里有失败原因和资料记录;真实仓库试跑再用当前项目副本跑一组真实小任务;摘要记录保留每次关键变化。可重复验证的价值,是把“我跑过”变成“我还能解释当时怎么跑的”。

结尾

我现在没有把它写成正式榜单平台,没有成本看板,也没有完整上线流程。第一版已经完成的是一套小而能跑的验证和复现流程,它能帮我发现执行流程缺口、固定失败原因、观察当前资料是否退化,并把结果留下来。没有反复验证,我只能说“它这次跑通了”;有了反复验证,我才能说“这类任务它反复跑过,失败时也知道该往哪修”。
到这里,我对这篇文章的收束也很简单。一套代码助手关键是让每一次失败都能被复现、被分类、被定位,并最终回到执行流程里修掉。
验证的价值是把“它怎么坏”变成“我知道修哪一层,修完还能复跑确认”。

总结

怎么验证Agent运行真的稳定了?
这篇文章说:把一次 demo 拆成固定题库,用自动验证器反复跑完整执行流程;失败后按原因归类,回到对应执行层修复;最后用同一组任务复跑,确认不是偶然通过。
而Eval Pack、EvalRunner、failure taxonomy、context bundle、pilot、repro pack 是Agent是最基础的能稳定运行的工具
 
回到首页