上一篇《Agent 自进化到底在进化什么》最后收束到一个闭环:
run -> trace -> failure attribution -> proposal -> controlled patch
-> train / selection / protected eval -> accept or reject
-> deploy or rollback -> continue collecting evidence
这篇接着讲下一步:怎么把这个闭环做好。
闭环画出来并不难,难的是每一环都不虚。trace 要能支持归因,归因要能指向具体 harness 层,proposal 要有边界,验证要能发现回归,accept / reject 要能留下证据。否则流程看起来完整,实际只是把 bad case 丢给模型,让它反思几句,再把反思写进 prompt 或 memory。
真正有价值的自进化闭环,应该像一个实验系统。它要能把失败轨迹变成可定位的问题,把问题变成有边界的修改,再用评估决定这个修改是否值得保留。
所以这篇文章真正想讨论的是这条链路怎么做实:
bad case -> failure attribution -> harness patch -> regression gate
而不是:
bad case -> reflection -> longer prompt
Bad Case 本身不是经验#
很多系统都会收集 bad case。问题是,bad case 本身并不自动等于经验。
一个任务失败了,可能有很多原因。
模型可能没有看到关键信息。也可能看到了,但是没有用上。可能是工具描述太模糊,导致它选错工具。也可能是工具本身缺少校验。还有可能是验证器太弱,模型以为做完了,其实只是产物看起来像完成。
如果只是把这些失败打包给模型,让它总结“下次要更仔细”,通常不会有稳定收益。这类总结太宽,既不知道应该影响哪个环节,也不知道什么时候该触发,更不知道会不会伤害原来能做对的任务。
所以第一步不是总结经验,而是把失败结构化。
一个有用的 bad case,至少要回答三个问题:
最终失败原因是什么?
哪个 agent 行为和失败有因果关系?
背后的可复用机制问题是什么?
比如同样是 timeout,原因可能完全不同。
一个 case 是外部下载太慢,agent 没有设置合理超时。另一个 case 是 agent 一直探索文件,却迟迟不写结果。表面上都是 timeout,但一个应该改工具策略或网络退避,另一个应该改执行流程和停止条件。
如果把它们聚在一起,只会得到一句空泛建议:遇到超时要更谨慎。这样的“经验”没有什么用。
归因之后,先定位该改哪一层#
失败被归因之后,下一步不是马上改 prompt,而是判断:这个问题应该落到 harness 的哪一层。
这里有一个重要的区分:归因回答的是“什么机制坏了”,分层回答的是“应该在哪修”。归因告诉你 agent 在哪个环节做出了错误行为,分层告诉你修改应该作用在 harness 的哪个位置才能稳定地改变这个行为。两个问题都答清楚,才能从“知道哪里不对”走到“知道怎么改”。
上一篇我把 harness 按组件拆成了 prompt、memory、tools、skill、workflow、workspace、policy、evaluation。那是为了说明 harness 里到底有什么东西可以被进化。
这一篇要讨论的是“在哪里修”,所以我换了一个视角,把 harness 按功能层来切:
Context: 模型看见什么,信息如何压缩和排序
Memory: 哪些历史经验被保存、召回和信任
Skill: 可复用流程和任务方法
Protocol: 工具、API、输出格式和状态机契约
Orchestration: 控制循环、重试、终止和子 agent 协作
Verification: 结果检查、测试、rubric 和回归验证
Governance: 权限、审批、审计、回滚和成本边界
这个切法也延续了上一篇提到的几类工作:GEPA 更偏 prompt 候选的演化和选择,SkillOpt 更偏 skill 文档的更新,Agentic Harness Engineering 则把 prompt、tool、middleware、memory 等组件都纳入可观察、可修改的 harness。这里我关心的是更工程化的一步:失败归因之后,修改到底应该落在哪个功能层。
这个分层的价值是,它逼迫我们不要把所有问题都塞进 prompt。
如果模型经常忘记验证结果,可能确实可以改 instruction。但如果它经常误用某个工具,更好的位置可能是 tool description 或 wrapper。如果它经常输出非法格式,应该加强 protocol 和 schema checker。如果它会删除关键产物,靠 prompt 提醒不够,应该在工具层加硬拦截。
经验应该落在最能稳定改变行为的位置。
有些经验适合写进 prompt,因为它是通用行为纪律。
有些经验适合沉淀成 skill,因为它是一套可复用流程。
有些经验适合写进 memory,因为它和某个项目、用户或历史决策有关。
有些经验应该变成 verifier,因为它本质上是合法性检查。
还有一些经验应该直接变成代码策略。比如路径权限、危险命令、API 参数约束、状态机流转,这些不应该靠模型“记得”,而应该由 harness 执行。
先出 Proposal,不要直接改生产系统#
当我们知道应该改哪一层后,下一步很容易冲动:直接动手改 prompt、改配置、改工具描述。但这很危险。
直接修改生产系统,意味着你同时在做三件事:提出假设、执行修改、承担后果。一旦改坏了,你分不清是假设错了还是改法粗糙。更麻烦的是,如果同时改了多处,出了问题根本不知道回滚哪里。而且生产环境里的真实任务会被当成实验品。这对用户不公平,对系统也不安全。
所以正确的做法是先生成 proposal。
一个合格的 proposal,不只是一个 patch。它应该同时说明:
它针对哪个 failure pattern;
它修改哪个 harness 层;
它预期改变什么行为;
它可能带来什么回归;
它如何被验证。
这个区别很重要。
如果只看 patch,很容易陷入“这句话看起来挺合理”的判断。但自进化系统需要的是实验记录,而不是灵感记录。每个修改都应该能追溯到一个失败模式,也应该能在后续评估里被证实或证伪。
这也是为什么 proposal 要有边界。
一次修改最好只解决一个清晰问题。不要因为几个 bad case,就重写整段系统提示词、重排整个 workflow、顺手加一堆工具规则。那样即使分数涨了,也很难知道到底是哪一部分起作用。分数跌了,更不知道该回滚哪里。
好的自进化修改应该像小实验:
假设:某类任务失败,是因为 agent 太晚创建 required artifact。
修改:在 execution instruction 中提前 artifact 初始化要求。
预期:missing artifact 类失败减少。
风险:可能让 agent 过早写入低质量产物。
验证:检查 held-in 中相关失败是否修复,同时看 held-out 是否出现新回归。
这比“让 agent 更注意创建文件”有用得多。
验证比生成更重要#
很多自进化讨论把重点放在“怎么生成更好的 proposal”。但从工程角度看,更重要的是:谁决定 proposal 是否保留?
模型可以提出建议,但不能自己说自己变好了。
一个 proposal 至少要过几道门:
schema 是否合法;
是否只修改允许编辑的 harness 层;
是否触碰 forbidden 边界;
patch 是否能干净应用;
harness 是否能 load / compile;
dry-run 是否通过;
train 上是否修复目标失败;
validation / protected set 是否没有明显回归。
这里的 protected set 很关键。它是一组不容退化的核心任务:这些任务上,任何 proposal 都不能让原来能过的 case 变成失败。
因为一个 proposal 可能真的学到了一点东西,但只适用于很窄的任务族。比如它提升了 broad research 类任务,却伤害了 local guidance、流程归因或精确推荐。只看平均分,可能会误以为它是好修改。
所以评估不能只看 aggregate score,还要看 case-level delta 和 task-family delta。
哪些 case 从 fail 变成 pass?哪些 case 从 pass 变成 fail?收益集中在哪类任务?退化又集中在哪类任务?成本、延迟、工具错误率有没有变差?
这也是 Harness Updating != Harness Benefit 那个区分在工程里的落点:会产生修改,只说明系统有 updating capability;修改在 protected set 和 held-out 上站得住,才说明它有 benefit。
这些问题不回答,自进化就很容易变成“看起来一直在更新,但不知道有没有真的变好”。
不要只选平均分最高#
还有一个容易被忽略的问题:候选选择。
直觉上,我们会选平均分最高的 proposal。但在 harness evolution 里,这不一定对。
不同 proposal 可能解决的是不同 failure family。
一个 proposal 擅长让 agent 更早验证产物。另一个 proposal 擅长减少无效工具调用。还有一个 proposal 擅长处理格式和协议错误。它们的平均分可能差不多,甚至某个 proposal 平均分不最高,但在一组关键 case 上明显更好。
如果每轮都只保留平均分最高的候选,很容易过早收敛到一种策略,把其他有潜力的路线丢掉。
更好的方式是保留一组没有被完全支配的候选。也就是说:如果一个候选在所有维度上都不如另一个候选,并且至少一个维度更差,那它可以淘汰;但如果它在某些 case 或某些任务族上有独特优势,就应该暂时留下。
这就是 Pareto frontier 在这里的价值。
它不是为了让流程看起来更高级,而是为了避免平均分掩盖策略分化。
当然,Pareto 不能替代安全门槛。越权、破坏验证、污染数据、明显回归的 proposal,不应该因为在某几个 case 上有收益就被保留。正确顺序应该是:
先过 hard gate;
再进入 Pareto selection;
最后再决定 promote、merge 或继续观察。
Merge 需要重新验证,组合可能互相抵消#
保留多个候选之后,还有一个问题:能不能把几个好 proposal 合并?
答案是可以,但要谨慎。
Harness 组件之间不是线性相加。举一个例子。
假设有两个 proposal,各自在独立评估中都表现不错:
- Proposal A 在 verification 层加了一个中间产物检查器,要求 agent 在每步工具调用后确认产物完整性。单独跑,missing artifact 类失败下降,延迟只小幅增加。
- Proposal B 在 orchestration 层加了一个上下文压缩策略,当对话轮次超过阈值时自动对历史消息做摘要。单独跑,超长任务的 timeout 率下降,对短任务几乎没有影响。
两个 proposal 各自有效,于是有人想把它们合并。
合并之后问题出现了:Proposal A 的每步检查让对话轮次快速膨胀,很快触发 Proposal B 的压缩阈值。压缩后的摘要丢失了产物完整性信号,检查器频繁误报,agent 反复重试,进一步推高轮次,触发更多压缩。最终 timeout 率反而比 baseline 更高,成本也涨了。
这个例子说明:两个各自有效的修改,组合后可能通过重复验证、上下文变长、信号丢失、工具调用增加和超时形成干扰,把单点收益吃掉。
所以 merge 之后不能默认更好,必须重新跑验证。必要时还要做 component ablation:只打开其中一个组件,看收益来自哪里;再组合起来,看是否互相抵消。
这也是很多自进化系统容易变成技术债的原因。每次修改看起来都合理,但长期累积后,prompt 变长,memory 变脏,workflow 变复杂,工具规则互相覆盖。系统不是进化了,而是长胖了。
被拒绝的 Proposal 也有价值#
自进化系统不应该只记录 accepted proposals。被拒绝的 proposal 同样重要。
被拒绝的原因可以归为两类。
第一类是工程门禁失败。修改在到达评估之前就应该被拦下:
schema failure: 格式不合法;
boundary failure: 越过 editable / forbidden 边界;
application failure: patch 无法应用;
activation failure: 没有识别出该进化;
第二类是学习质量失败。修改通过了工程门禁,但评估证明它没有价值或有害:
attribution failure: 归因错了;
generalization failure: train 有效,validation 退化;
duplicate failure: 和之前拒绝过的方向高度相似;
benefit failure: 合法也能跑,但没有稳定收益。
这些记录会形成 reject registry。它的作用不是给系统记黑账,而是避免反复探索同一条无效路线。
如果几轮 proposal 都在重复“加更多检索”“加更长 planning”“加更严格输出格式”,但每次都伤害相邻任务,那问题可能不在 proposal 写得不够好,而在可优化空间、评估设计或失败归因本身。
会拒绝,才说明系统真的在学习。
最终测试必须隔离#
训练集用来产生反馈,验证集用来选择候选。多轮迭代之后,验证集本身也会被间接过拟合。
所以最终还需要一个不参与选择的 test set。更严格一点,还应该有 challenge set、跨任务族评估、跨模型评估,甚至换不同 timeout、成本预算和工具环境再跑一次。
这听起来麻烦,但它决定了我们能不能区分两件事:
系统修复了当前 benchmark 的常见失败;
系统真的学到了更通用的执行结构。
很多自进化结果,本质上证明的是 in-domain repair。这个价值已经不小,但它不是开放式自我进化。把前者讲成后者,会让系统设计变得危险。
把闭环每一环做实#
把这些串起来,上一篇那条自进化闭环可以展开成更具体的工程版本:
run baseline
-> save full trace
-> mine failure patterns
-> locate harness layer
-> generate bounded proposal
-> validate legality
-> evaluate train / validation / protected set
-> select with hard gates + Pareto frontier
-> promote / reject / merge
-> log accepted and rejected candidates
-> final held-out test
这套流程的重点不在于多了几个步骤,而在于每一环都有明确产物。
trace 不是日志堆积,而是能支持失败归因的证据。
failure attribution 不是一句“模型不够仔细”,而是能说清楚哪个行为、哪个机制、哪个 harness 层出了问题。
proposal 不是灵感,而是带有目标失败模式、修改位置、预期收益和回归风险的小实验。
evaluation 不是跑一个平均分,而是看 case-level delta、task-family delta、protected set、成本和工具错误。
accept / reject 也不是拍脑袋,而是把通过的修改、失败的修改和重复的方向都写进 registry。
所以,做好自进化闭环的关键不是让系统更会反思,而是让失败轨迹稳定地转化为可验证的 harness patch。闭环真正成立的标志,是每一次修改都能回答:
这个失败模式是什么?
应该改在哪一层?
proposal 的边界是什么?
验证证明了什么?
有没有伤害原来能做对的任务?
如果错了,怎么回滚?
这些问题回答清楚,自进化闭环才不是一张流程图,而是一个可以长期运行的工程系统。
说到底,自进化首先是反馈、评估和治理问题。把 bad case 变成 harness patch 的每一步,都是在为这个判断补上工程细节。