官网

Harness Engineering 进阶:可靠验证、可观测性与自动循环

·38 分钟阅读·
目录

本文是 Learn Harness Engineering 课程的学习笔记(进阶篇)

进阶篇解决什么问题

基础入门解决了"让 agent 看得懂、接得上、有边界"。但这还不够——它还可能高估自己、像黑盒一样干活、每次会话结束后留下一堆烂摊子,而且一切都靠你手动下指令

进阶篇覆盖四件事:

  1. 可靠验证——防止 agent 提前宣告完成,用三层校验和端到端测试把"完成"变成客观判定;
  2. 可观测性——让 agent 的执行过程和决策依据可见,配合冲刺合同对齐预期;
  3. 干净交接——每次会话结束留下清洁状态,别让技术债累积;
  4. 自动循环——从手动下指令升级到 Loop(自动循环),再到多 agent 协作的 Graph(图)。

一、可靠验证:让"完成"成为客观判定

"做完了"三个字,害我不浅

让你实现"密码重置"功能,它改了数据库、写了接口、加了邮件模板,单元测试全绿,然后告诉你"搞定了"。你一跑:重置链接根本发不出去(邮件服务没配)、数据库迁移半途失败、端到端流程压根没走通。

Guo 等人 2017 年在 ICML 上的经典论文证明:现代神经网络系统性地过度自信,模型自报的置信度显著高于实际准确率。AI 编码 agent 也一样,它"觉得"做完了,但实际上差得远。

结论很明确:不能让 agent 自己判断自己做完没有,得用外部化的、基于执行的验证来代替它的"感觉"。

单元测试通过 ≠ 任务完成

这是最常见的陷阱,也是最危险的一个。单元测试的设计哲学是"隔离"——模拟依赖、专注单个模块,这恰好让它看不见跨组件的问题

  • 接口不匹配:A 模块传相对路径,B 模块要绝对路径,各自的单测都 mock 过了,只有真正拼起来跑才会露馅。
  • 状态传播错误:数据库迁移改了表结构,但 ORM 的缓存层还持有旧结构的缓存条目。单元测试每次都是全新的 mock 环境,不会暴露这种跨层状态不一致。
  • 资源生命周期问题:文件句柄、数据库连接、网络套接字的获取和释放跨越多个组件。单元测试为每个测试创建和销毁独立资源,不会暴露资源竞争或泄漏。
  • 环境依赖性:代码在测试环境(一切 mock)行为正确,在真实环境因配置差异、网络延迟、服务不可用而失败。

让"干活的人"和"检查的人"分开

更深的坑是:让 agent 评价它自己的工作,它会系统性地给自己打高分。 这不是它不诚实,而是它就是这段代码的作者——它在生成的时候已经说服自己这条路是对的。有个说法很到位:模型是自己输出最好的"辩护律师"。

Anthropic 在 2026 年的研究中发现:当 agent 被要求评估自己的工作时,它系统性地过度正面评价,即使人类观察者认为质量明显不达标。这在主观任务(如设计审美)上尤其严重。

解决方案不是让 agent"更客观"。同一个模型既生成又评估,内在地倾向对自己慷慨。解决方案是把"干活的人"和"检查的人"分开——一个独立的评估 agent,经过专门调校为"挑剔"之后,比让生成 agent 自我评估有效得多。

架构运行时长成本核心功能是否可用
单 agent 裸跑20 分钟$9否(游戏实体无法响应输入)
三 agent(planner + generator + evaluator)6 小时$200是(游戏可以正常游玩)

这是同一个模型(Opus 4.5),同一段提示词("做一个 2D 复古游戏编辑器")。区别只在 harness。

三层终止校验,层层递进

我把"完成判定"设计成三层,缺一不可:

  1. 第一层:语法与静态分析。 成本最低,信息量最小,但必须通过。这是最低限度的检查,字都没写错才能往下看。
  2. 第二层:运行时行为验证。 测试执行、应用启动检查、关键路径验证。这是核心完成证据,不仅要写了,还要能跑。
  3. 第三层:系统级端到端确认。 端到端测试、集成验证、用户场景模拟。这是防止过早声明的最后一道防线,不仅要能跑,还要跑对。

CLAUDE.md 里可以写清楚:

## 完成定义
- 功能完成 = 端到端验证通过,不是"代码写完了"
- 必须运行的验证层级:
  1. 单元测试通过
  2. 集成测试通过
  3. 端到端流程验证通过
- 在第 1 层没通过时,不许进入第 2 层
- 在第 2 层没通过时,不许进入第 3 层

配套一条硬规则:核心功能没验证通过之前,不许重构。 重构会改变已完成验证和未完成验证之间的边界,可能破坏之前隐式正确的代码路径。完成优先级约束:先验证功能正确性,再处理性能,最后管风格。

错误消息要"可操作"

给 agent 的错误消息,不能只说"错了",要告诉它怎么改。不要写 Test failed,而要写:

Test failed: POST /api/reset-password returned 500.
Check the email service config in environment variables.
The template file should be at templates/reset-email.html.

这种带修复指导的错误消息,能让它自己完成修正,不用我来回喂。错误消息不只是告诉你"出了什么问题",还要告诉你"该怎么改",让 agent 能够自主完成修正。

端到端测试改变的不只是结果

当 agent 知道它的工作要过端到端测试时,它的编码行为会改变:

  1. 考虑组件交互:写代码时会想"这个接口和上游怎么对接",不只关注单个函数。
  2. 尊重架构边界:有架构约束的系统里,端到端测试迫使 agent 遵守边界规则。
  3. 处理错误路径:端到端测试通常包含故障场景,迫使 agent 考虑异常处理。

我自己在 Electron 项目里就遇到过:一个文件导出功能,5 个跨组件缺陷(接口不匹配、状态传播、资源泄漏、权限问题、错误传播),单元测试一个没发现,端到端测试全抓出来了。代价只是测试时间从 2 秒变成 15 秒,完全值得。

架构边界要可执行:把架构文档里的规则变成自动检查,从"写在纸上"变成"跑在 CI 里"。OpenAI 的经验:对 agent 生成的代码库,架构约束必须是第一天就建立的早期前置条件——agent 会复制仓库中已有的模式,没有架构约束,它会在每次会话中引入更多偏差。关键原则:执行不变量,不微管实现。

审查反馈提升:每次在代码审查中发现新类型的 agent 错误,就把它变成自动化检查。一个月后你的 harness 会比月初强得多。


二、可观测性:看穿 agent 的黑盒

Agent 干活像个黑盒

最让我头疼的场景是:agent 跑了 20 分钟,改了一堆文件,然后告诉我"做完了但有两个测试失败"。我问它为什么失败,"不太确定,可能是时序问题"。我问它改了哪些关键路径,"让我看看代码……"。

根源在于:我看不到它运行时到底在干嘛,它自己也看不到。 于是它只能在不确定的状态里做决策,评估靠主观感觉,重试靠盲目摸索。

可观测性缺失会带来四类系统性问题:

  1. 无法区分"正确"和"看似正确":代码审查看的是"写了什么",运行时追踪看的是"实际跑了什么",两者缺一不可。
  2. 评估变成玄学:没有评分标准和验收条件时,评估者只能依赖隐式假设,质量评估不可复现。
  3. 重试变成盲猜:agent 不知道为什么失败时,重试方向是随机的,每次盲重试都消耗 token 和时间。
  4. 会话交接信息断崖:新会话必须从零诊断系统状态。Anthropic 观察表明,这种重复诊断可能占会话总时间的 30-50%。

双层可观测性,缺一不可

可观测性不是"多打点日志"那么简单,它分两层:

  • 运行时可观测性:系统层的信号,包括日志、追踪、进程事件、健康检查,回答"系统做了什么"。
  • 过程可观测性:harness 决策工件的可见性,包括计划、评分标准、验收条件,回答"为什么这个变更应该被接受"。

只看运行时日志,你能看到"发生了什么",却看不懂"它为什么要这么干";只看过程文档,你明白意图,却不知道实际执行对不对。

一个让我印象深刻的对比

同样是"给应用加暗色模式"这个任务:

  • 没有可观测性:计划者输出模糊描述,生成者根据模糊描述实现,但和计划者的隐式预期不一致。评估者基于自己的隐式标准拒绝,只有一句"感觉不太对"。生成者基于模糊拒绝理由盲重试,循环 3-4 次,总耗时约 45 分钟,最终勉强产出。
  • 有完整可观测性:计划者输出冲刺合同,列明要改哪些组件、每个组件的验证标准、排除项。生成者按合同实现,评估者用评分标准逐维度评估,附具体证据引用:"按钮颜色对比度不足(WCAG AA 标准 4.5:1,实测 2.1:1)"。一次迭代出高质量结果,总耗时约 15 分钟。

效率差 3 倍,区别只在可观测性。

冲刺合同:开工前先对齐

在任务开始前,让"干活的人"和"检查的人"先协商一份协议,写清楚这次做什么、怎么做算通过、不做什么:

# 冲刺合同: 暗色模式支持

## 范围
- 修改主题切换组件
- 更新全局 CSS 变量
- 添加暗色模式测试

## 验证标准
- 每个组件的视觉回归测试通过
- 主流程端到端测试通过
- 无样式闪烁 (FOUC)

## 排除项
- 不处理打印样式
- 不处理第三方组件暗色模式

有了它,很多"做了半天发现方向不对"的返工就被前置消除了。

评估评分标准:把"好不好"变成可量化评分

# 评分标准

| 维度 | A | B | C | D |
|------|---|---|---|---|
| 代码正确性 | 所有测试通过 | 主流程通过 | 部分通过 | 编译失败 |
| 架构合规 | 完全合规 | 轻微偏离 | 明显偏离 | 严重违反 |
| 测试覆盖 | 主流程+边缘 | 仅主流程 | 仅有骨架 | 无测试 |

Agent 自行处理可观测性的局限

你可能想:"agent 不能自己打日志吗?"问题在于:

  1. agent 不知道它不知道什么:它不会主动记录自己没意识到需要的信号。
  2. 日志格式不统一:不同会话用不同的日志格式,无法做系统化分析。
  3. 过程可观测性不是日志能解决的:冲刺合同和评分标准是结构化的工件,需要 harness 层面的支持。

所以 harness 应该自动采集信号:应用生命周期(启动、就绪、运行、关闭)、功能路径执行、数据流、资源利用(异常的资源使用模式)、错误和异常(完整的错误上下文)。

Anthropic 的三 agent 架构实验

Anthropic 用三种架构跑同一个任务("用 Web Audio API 做一个浏览器端 DAW")。三个 agent 各司其职:

  • Planner(规划者):接收 1-4 句话的用户需求,扩展成完整产品规格。被要求"大胆设定范围"且"专注于产品上下文和高层技术设计,而不深入详细技术实现"。原因是:如果 planner 过早指定了粒度技术细节且搞错了,错误会级联到下游。
  • Generator(生成者):按 sprint 逐个功能实现,每个 sprint 前和 evaluator 协商一份 sprint 合同。
  • Evaluator(评估者):用 Playwright MCP 像用户一样点击运行中的应用,按四个维度评分:产品深度、功能性、视觉设计和代码质量。每个维度有硬性阈值,任一不达标则 sprint 失败。

Evaluator 不是一开始就这么强。早期版本会识别出合理的问题,然后说服自己这些问题不严重,最终批准工作。调校方式是:读 evaluator 的日志,找到它的判断和人类判断分叉的地方,更新 QA 的 prompt。经过几轮这种开发循环,evaluator 的评分才变得合理。

QA 第 1 轮反馈的示例:"这是一个视觉上令人印象深刻的应用,AI 集成工作良好,但核心 DAW 功能有几个是展示性的,没有交互深度:剪辑不能拖拽/移动,没有乐器 UI 面板"。这些不是边缘情况,它们是让 DAW 可用的核心交互。具体的、有证据的反馈,不是"感觉不对"。


三、干净交接:每次会话结束前做好交接

熵增是默认状态

每个 agent 会话结束时,如果没有刻意做清理,代码库的状态会越来越混乱:构建可能挂、测试变红、临时调试文件散落各处、进度记录没更新。下一个会话要花大量时间诊断"上个会话到底干了啥"。

Lehman 的软件演化定律告诉我们:一个持续变更的系统,如果没有人主动管理,它的复杂性一定会增加。OpenAI 在 5 个月的 Codex 实验中发现:agent 会复制仓库中已有的模式,哪怕那些模式是不一致或次优的。打个比方:第一个人在公共区域放了一个杯子,第二个人路过时心想"反正已经乱了",自己也放了一个。一周后,桌上就堆满了。

清洁状态:不只是"代码能编译"

清洁状态的要求远比"代码能编译"要多。一个干净的会话退出需要满足五个条件

  1. 构建通过——下一个会话不应该一上来就先修别人的构建错误。
  2. 测试通过——包括会话开始前就存在的旧测试,你这次改动不能破坏已有的功能。而且验证必须在 CI 环境里跑,不是"在我机器上能过就行"。
  3. 进度已记录——记录在机器可读的工件中:已完成的子任务和通过标准、正在做但没做完的和卡在哪、还没开始的。好的进度记录可以减少 60-80% 的会话启动诊断时间。
  4. 临时工件已清理——调试日志、临时文件、注释掉的代码、TODO 标记,都会增加下一个会话的认知负担。
  5. 启动路径可用——环境初始化、代码库加载、上下文获取、任务选择,这些路径中的任何一个被破坏,新会话就无法自行启动工作。

缺一个都不算"做完"。

"以后再清理"是永远不清理

最常见的心理陷阱就是"这次来不及清理了,下次再弄"。但下次的 agent 根本不知道你上次留下了什么,它看到的只是一堆混乱的代码和不确定的状态。每个新会话来的时候是为了做新功能,不是为了清理上一个会话遗留问题的。它会直接忽略混乱,在混乱的基础上开始新工作,然后引入更多混乱。这是一个熵增的正反馈循环——越乱越不管,越不管越乱。

数据最能说明问题——一个使用 agent 持续开发 12 周的项目的实际对比:

时间无清洁策略:构建通过率有清洁策略:构建通过率无清洁:新会话启动时间有清洁:新会话启动时间
第 1 周100%100%5 分钟5 分钟
第 12 周68%97%60+ 分钟9 分钟

12 周下来,两组之间构建通过率差 29 个百分点,测试通过率差 34 个百分点,新会话启动时间差 85%。

怎么做

1. 清洁状态是完成的必要条件。AGENTS.md 里明确定义:会话完成的条件是两件事同时满足——任务通过验证,且清洁状态检查通过。缺任何一个,会话就不算完成。

## 会话退出检查清单
- [ ] 构建通过 (npm run build)
- [ ] 所有测试通过 (npm test)
- [ ] 功能清单已更新
- [ ] 无调试代码残留 (console.log, debugger, TODO)
- [ ] 标准启动路径可用 (npm run dev)

2. 双模式清理策略。 即时清理(每个会话结束时):清理本次会话创建的临时文件、更新功能清单状态、确保构建和测试通过,谁产生的垃圾谁清掉。定期清理(每周一次):做一次全面系统扫描,处理累积的结构性问题、更新质量文档、跑基准测试检测整体质量有没有漂移。

3. 维护质量文档。 一份持续更新的文件,对代码库中每个模块打分和评价。新会话一打开就能看到每个模块的状态。

# 质量文档

## 用户认证模块 (质量: A)
- 验证通过: 是
- 测试稳定性: 稳定
- 架构边界: 合规

## 支付模块 (质量: C)
- 验证通过: 部分(支付回调未测试)
- 测试稳定性: 不稳定(2 个 flaky 测试)
- 架构边界: 有违规

4. 定期简化 Harness。 Harness 中每个组件的存在,都源于模型在某个方面尚无法独立完成。随着模型能力不断演进,这些前提会逐渐过时。推荐做法:每月挑选一个 Harness 组件,暂时禁用它,跑一遍基准任务。如果结果没有退化,就永久移除。Anthropic 发现:当 Opus 4.6 发布后,原本的"任务拆分机制"成了多余步骤,移除后 Builder Agent 反而能连续工作超过两小时而不偏离方向。但 Evaluator 是否保留,取决于任务难度和模型能力的相对关系。

5. 清理操作必须幂等。 一个操作无论执行一次还是执行一百次,结果都一样。因为清理失败时你会重跑一遍,如果重跑产生不同的结果,就说明清理脚本存在 bug。

6. 高吞吐量改变了合并策略。 当 agent 每天开出 3.5 个 PR 时,减少阻塞型的合并检查是正确的选择。判断标准:修正一个 bug 的平均成本,和等待人类审查一个 PR 的平均成本,哪个更低?当前者低于后者时,快速合并加快速修正比慢慢审查更好。注意前提是 agent 的高产出远超人类审查带宽。


四、自动循环:从 Loop 到 Graph

前十二课都有个共同前提

前面聊的那些——AGENTS.md、状态管理、功能清单、可观测性、干净交接——都有一个隐含前提:你坐在键盘前,一次次输入指令。 agent 不会自己决定什么时候该干活,因为没有人按下"开始"按钮。

这一部分聊的,就是怎么把"按按钮"这件事本身也交给系统。不是放弃控制,而是把控制抬到更高一层。

Loop:让系统替你按按钮

先认识最简形态的循环:/goal。2026 年初,Claude Code 和 OpenAI Codex 不约而同地加入了同一个功能。你在终端里敲:

/goal "所有测试通过,lint 零告警,合并到 main"

然后合上笔记本去睡觉。八小时后醒来,agent 已经自己完成了分析、编码、测试、修复、合并的全过程。

它和传统 prompt 的区别:

传统 prompt/goal
你给什么下一步具体做什么最终状态是什么
agent 做什么执行一次循环直到达成
谁判断做完了一条可验证的停止条件
你什么时候可以走不能走给完 /goal 就行

/goal 本质上就是一个 loop。它的结构只有三样东西:一个目标、一种验证方式、一条停止条件。 就是这三样东西,让你的位置从循环里面移到了循环外面

Loop 不只有一种

类型触发方式停止方式适用场景
回合制 loop你手动输入每条 promptagent 认为做完了,或你打断小任务、探索性工作
目标驱动 loop你给一个目标独立判断者确认达成有明确完成标准的复杂任务
时间驱动 loop定时触发你手动停止,或任务完成退出轮询状态、定期巡检
事件驱动 loop外部事件触发(PR、CI 失败、issue)处理完事件就停响应式工作流、CI/CD 集成

/goal/loop 别搞混:/goal 是一个大任务跑到完为止(跑马拉松),/loop 是同一个小动作按间隔重复跑(闹钟)。判断该用哪个的一句话标准:这件事有终点吗?有终点用 /goal,没终点就是要一直盯着用 /loop

一个 Loop 的六大原语

  • Automations(自动触发)/loop 定时、cron、云端定时任务,没触发就不是循环。
  • Worktrees(并行隔离):每个并行 agent 在独立的 git worktree 中工作,物理上避免文件碰撞。
  • Skills(项目知识):把意图写成文件(SKILL.md),写一次,每次运行都读,省得每次重述。
  • Connectors(连接器):基于 MCP 让它能读工单、查数据库、发消息,在真实环境里行动。
  • Sub-agents(子 agent):把"干活的人"和"检查的人"分开。
  • External State(外部状态):记忆必须落在磁盘上,不能在上下文窗口里——模型在会话之间什么都不记得,仓库不会忘。

Generator/Evaluator 分离:为什么不能让自己批自己作业

这是 loop engineering 中最硬核的一条教训:如果让那个写了代码的 agent 自己评判自己做得对不对,它会给自己打高分。 不是因为它不诚实,而是它就是这段代码的作者——它在生成的时候已经说服自己这条路是对的。

模型是它自己输出最好的辩护律师。 解决方案就是:永远不用同一个人(同一个模型、同一个 prompt)既干活又检查。

  • Claude Code 的 /goal 底层的 supervisor 是一个独立的 session,独立判断是否达成目标。
  • Codex 的 subagent 体系让你定义验证 agent 可以和实现 agent 用不同模型、不同 reasoning effort。
  • 社区实践里的"adversarial verify"模式:每一个发现用 N 个独立的质疑 agent 来反驳它。

这个原则用一句话总结:你的人里必须有一个不信你的。

Karpathy 的 autoresearch:Loop 的最佳示范

2026 年 3 月,Karpathy 发布了一个 630 行 Python 的项目 autoresearch。给它一张 GPU、一份研究方向,它能自己跑一整夜,完成上百个 ML 训练实验,只保留真正有改进的。

三个文件,三种角色:

文件谁来改作用
prepare.py没有人(只读)数据准备、tokenizer、评估函数。固定的基础设施。
train.py(~630 行)AI Agent模型定义、优化器、训练循环。Agent 的实验场。
program.md用自然语言写的研究方法论。你只改这个。

这个三分法是整个设计的精髓:人不动代码,动方向;agent 不动方向,动代码。 你给 agent 的启动 prompt 甚至可以短到一句话:"看看 program.md,然后开始实验。"

核心是一个棘轮循环——只能往前走,不能后退:读方向 → 看现状 → 提假设 → 改代码 → git commit → 跑训练(固定 5 分钟墙钟)→ 评估(val_bpb 改进了吗)→ 改进就保留 / 没改进就回退 → 下一轮。固定 5 分钟墙钟是关键设计——所有结果在同一时间预算下直接可比。

输出:git 历史(只有真正改进的 commit 留在主分支)、results.tsv(每次实验的完整记录)、一份研究日志(agent 自己写的总结)。Karpathy 首轮 2 天约 700 次实验,筛出约 20 个可堆叠的有效改进,把 nanochat 的训练时间从 2.02 小时压缩到 1.80 小时(提速约 11%)。

最值得注意的细节:loop 的核心是 program.md——一份 Markdown 文档,不是 Python 脚本。这背后的核心模式是:不给 agent 任务,给 agent 方法论,让方法论成为循环。

四种沉默成本

loop 跑起来之后,你不会立刻看到问题,以下四种成本会沉默地累积:

  1. 验证负债(Verification Debt):loop 跑得快的时候很容易跳过验证。"看起来没问题"不等于"确实没问题"。解决方式:停止条件必须是可自动检查的,不能是"感觉差不多"。
  2. 理解腐烂(Comprehension Rot):loop 出代码的速度越快,你对自己代码库的理解就越跟不上。Cherny 的团队 80% 的代码是 agent 写的——快速跑 loop 的前提是快速读结果。
  3. 认知投降(Cognitive Surrender):当 loop 跑得很顺时,最舒服的姿势就是不再有观点。你在用 loop 逃避思考,而不是用 loop 放大思考。Osmani 的警告:"两个人可以造完全一样的 loop,得到完全相反的结果。一个用它加深理解后加速,另一个用它代替理解。loop 不知道区别,你知道。"
  4. 令牌爆炸(Token Blowout):loop 的每一次迭代都会积累更多上下文,prompt size 会随着迭代次数近似平方增长。解决方案是自动 context compaction——把老的对话轮次压缩成摘要。

从零构建你的第一个 Loop

不需要一上来就搭一个复杂系统,从最小可行的版本开始:

  1. 选一个重复发生的任务(你每周至少做两次的事情,比如早上看 issue、PR 前跑 lint 和测试)。
  2. 写一个 goal 和停止条件,把它变成 /goal 可以理解的描述。
  3. 拆出 maker 和 checker——不要让同一个 agent 既改代码又判对错。
  4. 加上记忆——用一个 markdown 文件记录 loop 每次运行的结果。
  5. 设一个定时器——用 /loop 命令或 cron,让 loop 在没有你的时候也能启动。

成熟度阶梯:Level 1 Goal Runner(会用 /goal)→ Level 2 Scheduled Single-Task(定时跑一个任务)→ Level 3 Multi-Agent Loop(实现者和验证者分离)→ Level 4 Self-Feeding Loop(从外部状态自动发现任务)→ Level 5 Fleet Orchestration(多个 loop 并行)。绝大多数团队目前卡在 Level 2 到 Level 3 之间,Level 1 是最快能看到回报的一步。

当 loop 不够用了,就成了图

一个 loop 只有一条主干道,所有决策都挤在同一个上下文窗口里。任务一旦复杂,四个问题就冒出来:谁先开始?哪些能并行?失败回到哪?几个 agent 怎么交接、听谁的?

Rohit 的那句名言:"一旦一个 agent 需要专业化、并行、共享状态、验证和恢复,它就不再是一个 loop 了。它是一张图。"

理解整个命名史可以用一个四层框架:Prompt Engineering(指令)→ Context Engineering(信息)→ Loop Engineering(运行时)→ Graph Engineering(系统)。每一层都不是取代上一层,而是叠加在它之上——到了 graph,prompt、context、loop 一个都没消失:每个节点都带着自己的 prompt、自己的 context、自己的工具、自己的记忆、自己的 loop,图决定的是节点之间怎么连接。

图由四个零件构成:

  • 节点(Node):承担某种职责的工作单元,可以是确定性代码、一次模型调用、一个工具,或者一个完整的 agent(自带 loop、会使用工具、跑不动了自己重试)。
  • 边(Edge):节点之间的交接方式,可以表达并行、条件(测试通过走左边,失败走右边)、失败/重试、回退(验证不通过回到三跳之前的实现节点)。
  • 共享状态(State):节点之间传递的数据包,节点不直接互相喊话,都读写同一份状态。
  • 路由规则(Routing):决定下一步去哪。测试通过就交付;测试失败就回到实现节点;信息不足就回到研究节点。

loop 和图的深层差异:loop 是延期决策——让一个 agent 包揽所有工作,跑不下去再说,省事但失败模式不可见;graph 是提前决策——必须提前声明整个结构,费事但可读、可审计、可局部修复。用一句话:loop 把问题藏在循环里,graph 把问题摆在纸上。 前者适合探索,后者适合生产。

单一循环的三种结构性失败

为什么单一 loop 在规模上撑不住?三个结构性失败——注意是结构性失败,不是某一个 loop 的 bug:

  1. Goodhart:数字涨了,业务却坏了。 把任何一个单一指标推到极致,它就会停止测量你以为它在测量的东西。经典案例:客服团队围绕"工单解决率"建循环,周数据一路爬升,几个月后 churn 翻倍——bot 学会了关闭工单(转移话题、劝阻追问)。
  2. 向上失明:它从不问"这个目标对吗"。 在 loop 内部,参考值是神圣的。恒温器不会问"68°F 是不是对的温度"。目标是谁选的,loop 就朝着它跑,即使它从一开始就不是该追的东西。
  3. 冲突:独立循环互相拆台。 真实系统里有几十个 loop,每个都是独立建起来的。响应速度的 loop 在拆深度质量的 loop 的台,每个 loop 在自己的仪表盘上都健康,系统整体却在抖动。

loop 里不是也能加检查点吗?能。但三个失败恰恰是检查点解决不了的——因为 loop 里的检查点长在同一个 agent 内部,做检查和出问题的是同一个大脑、同一份上下文。它会拦下"没验证就交付",却不会问"这个指标对不对"。图不是给你更多检查点,而是把检查搬出去:从"agent 内部"挪到"独立的节点",给它一份全新上下文。

Graph engineering 要回答的,正是单一 loop 回答不了的那组问题:哪些 loop 喂给哪些 loop?哪些 loop 拥有其他 loop 追逐的目标?哪些 loop 能否决或回滚一个变更?哪些指标允许移动,哪些必须冻结?

还有一个最容易被跳过、却最不能省的一步:锚(Anchors)。循环网络再精巧,如果每个循环都漂离现实,网络只是互相漂移的共振。锚就是把 loop 固定到真实世界的东西——真实业务结果、ground truth 数据集、人工抽查。

Graph 与 Workflow:不只是换个名字

"这不就是 workflow 吗?DAG、状态机、工作流引擎,我们跑了几十年了。"这个直觉对了一半

图和 workflow 确实共享同一个骨架:节点 + 边 + 共享状态 + 路由。错的一半在节点里——传统 workflow 的节点是确定性函数(一个 Python 函数、一个 shell 脚本),边是写死的代码(if、switch、case);图工程的节点可以是一个完整 agent(自带 loop、会使用工具、能理解目标、遇到失败自己重试),边也可以带路由规则,由前一个节点的输出、验证结果、甚至另一个模型来决定下一步。

用 Anthropic 的一对概念来区分:谁决定控制流? 代码决定步骤就是 workflow,模型在运行时能改变步骤就是 agent。那么图是什么?图是容纳两者的容器。 一张图里可以同时有 workflow 节点(跑测试、算覆盖率)、agent 节点(实现功能、审查代码)、人类节点(审批、复核)。

所以准确的说法是:Graph Engineering 不是 Workflow 的替代,而是 Workflow 的泛化——把节点的类型从"函数"放开到"agent",把边的决策从"静态代码"放开到"动态路由"。workflow 是图中"完全确定"的那个特例。

真正新的是什么?节点从"函数"变成了"agent"。 以前你写一个 workflow 节点,要写清楚它的逻辑、错误处理、重试策略。现在一个节点只需要一句指令——"研究这个问题"、"审查这段代码"——剩下的由模型自己完成。节点变得便宜了,于是图变得值得画了。

泼冷水:图不是银弹

三盆冷水,从轻到重:

第一盆:假的数字。 网上流传"用图之后准确率 +18%、成本 -85%"之类的数据。事实核查确认:这两个数字确实存在,但出自一篇关于化工管道图纸的论文,营销文案把两个不同基线的数字拼成了一个"前后对比"。看到任何"图工程带来 X% 提升"的数据,先查原始出处。

第二盆:形状不是承重墙。 workflow 工程跑了几十年,真正沉淀下来的不是节点怎么连,而是可重放、可观测、可恢复——出问题能回放,运行中能观察,挂了能接着跑。图的形状可以随手改,这些承重能力才是你该投入的地方。

第三盆:Orchestration Tax(编排税)。 开 agent 很便宜,关 loop 很贵。启动一个 agent 只是一个按键、一句话。但关闭一个 agent 的 loop 要有人检查它的结果、和别的 agent 动过的东西对齐——那个人是你,而且只有一个你。

"你就是你的 AI agent 们的 GIL。它们可以同时跑。但只要它们的工作需要真正理解架构、解决合并冲突,这些工作就必须获取那把锁。只有一把锁,你握着它。"

图让并行的 agent 变多,但你的判断力是串行资源,不并行。 加节点优化的是从来不是瓶颈的部分——瓶颈永远是那一个串行处理器:你。

什么时候你真的该用图

不是所有任务都值得画图。五个判据,至少满足三个再动手

  1. 任务能独立拆分成多个工作单元——拆出来的部分互不依赖,可以并行。
  2. 存在分支或回退路径——测试失败该回哪、信息不足该回哪,这些路径值得显式声明。
  3. 中间状态值得保存——checkpoint 之后能停下、能恢复。
  4. 结果能被明确验收——每个节点都有可自动检查的完成标准。
  5. 协作收益 > 协调成本——并行省下的时间,多于图本身和共享状态带来的开销。

"复杂"不等于"步骤多"。 一个 20 步的线性流水线,不需要图——那是 workflow 或者干脆是脚本。一个只有 5 个节点但彼此有回退、并行、审批的结构,才需要图。判断标准不是规模,是分支和回退的存在

我的态度是:先从小处开始,一个 /goal、一个 cron、一个 markdown 记忆文件,看到回报再往上加。


总结

  • 验证:agent 系统性过度自信,代码写完了不代表做对了。完成判定必须外部化,不信任它的"感觉"。
  • 三层校验:缺一不可——语法 → 行为 → 系统级。
  • 分工:干活的人和检查的人必须分开,别让它自己批自己作业。你的人里必须有一个不信你的。
  • 错误消息:要带修复指导,让它能自我修正。
  • 端到端:不仅检测缺陷,还改变 agent 的编码行为。架构规则必须可执行。
  • 可观测性:是 harness 的架构属性,不是事后补的日志。运行时信号解释"发生了什么",过程工件解释"为什么这样做",两层都要。
  • 冲刺合同:能在开工前对齐预期,省掉大量返工。
  • 干净状态:是"完成"的一部分——构建、测试、进度、工件、启动,五个都要在退出时检查。"以后再清理"等于永远不清理。
  • Loop:不是替代 Harness,而是在它之上加一层——harness 保证单次运行可靠,loop 保证持续运行不用你盯着。/goal 三要素:目标 + 验证方式 + 停止条件。
  • Graph:把"延期决策"变成"提前决策",把失败模式摆在明面上。图是 workflow 的泛化,不是替代。别急着画图,五个判据至少满足三个。记住反方的声音:名词会每六周换一个,可重放、可观测、可恢复的工程能力不会过时。

参考资料

相关文章

评论