如何让 Claude Code 稳定执行:Graph Engineering 实践与思考
在上一篇中,我关注的是如何让 Agent 根据真实反馈持续修复问题。接下来遇到的问题是:当任务变成多阶段、多条并行链路时,谁来可靠地决定下一步?
如果所有计划、中间结果和调度决策都回到同一个会话里,模型既要解决问题,又要充当任务调度器。任务越多,越容易遗漏检查、重复工作,或把“流程走过了”误当作“目标完成了”。
Loop 管反馈,Graph 管编排
我把 Loop 理解为“执行、验证、修复、继续”的反馈机制;把 Graph 理解为组织这些工作的一层控制流:哪些节点先执行,哪些可以并行,结果如何汇总,什么时候退出。
两者可以叠加。一个图中的节点可以包含自己的验证与修复循环;一个 Agent 内部也可以继续使用工具和反馈。复杂工作流并不是简单任务的必需品,而是当协调成本上升时的一种工程选择。
Claude Code 的 Dynamic Workflows 是这类设计的一个具体实现:让脚本持有编排逻辑,由运行时执行,而不是由主会话逐轮记住所有调度步骤。具体能力与接口应以官方文档为准。
哪些约束适合写进代码
| 问题 | 可以显式化的约束 |
|---|---|
| 必要检查被跳过 | 必经节点和依赖关系 |
| 重试没有尽头 | 重试次数、截止时间和预算 |
| 下游拿到不完整结果 | 结构化输出与字段校验 |
| 多个执行者修改同一资源 | 所有权、串行写入或隔离工作区 |
| 实现者宣布自己成功 | 读取外部证据的验证节点 |
| 中断后不知道从哪里继续 | 可持久化状态和恢复规则 |
代码能保证某个分支被执行,却不能保证分支里的模型判断正确。Schema 能保证结果有一个 passed 字段,却不能保证它真的通过了验收。稳定性仍然来自控制流、证据和验证器共同工作。
把验证与修复循环放进编排器
下面是一个概念示例。implement、verify 和 repair 代表你自己的工具适配层;验证结果必须来自约定的检查,而不是实现者的自述。
async function runScenario(scenario, tools, maxRepairs = 3) {
await tools.implement(scenario);
let result = await tools.verify(scenario);
for (let attempt = 0; attempt < maxRepairs; attempt++) {
if (result.status === "passed" || result.status === "blocked") {
return result;
}
await tools.repair(scenario, result.evidence);
result = await tools.verify(scenario);
}
return result.status === "passed" || result.status === "blocked"
? result
: { ...result, status: "budget_exhausted" };
}
这里有几个容易遗漏的细节:实现后先验证;修复后必须再次验证;被阻塞的任务不被当作成功;耗尽预算时返回真实状态。生产实现还需要异常处理、超时、取消、持久化和运行级预算。
并发不是越多越好
如果下一阶段需要所有结果,例如去重和总体报告,可以使用屏障:等待所有前置任务完成,再推进。若每个条目可以独立走完整条链路,则可以采用流水线,让已完成的条目继续向前。
但独立性必须检查。两个场景即使来自不同页面,也可能修改同一个文件或共享一份测试数据。此时盲目并行会引入竞争。应限制并发数,隔离工作区或测试资源,并把冲突写入编排设计。
流水线可能缩短等待时间,但不会自动带来固定倍数的加速。实际收益取决于任务分布、资源限制、模型限流和节点之间的依赖。
恢复结果不等于恢复副作用
一个节点的返回值可以存进日志,但它执行过的动作未必能够重放。
例如,一个节点已修改文件或向外部系统提交请求;重跑时直接使用缓存,可能忽略环境已经变化,重新执行又可能制造重复操作。因此恢复点至少应关联输入、代码版本、验证证据和副作用状态。
纯计算节点可以优先复用结果。有副作用的节点需要幂等键、状态核对,或明确的人工处理出口。恢复的目标是继续正确的工作,而不只是让进度条接着增长。
模型分层需要先建立任务边界
设计控制流、处理证据冲突和最终综合判断,可以使用能力更强的模型。格式归一、字段提取和边界清楚的检查,才适合尝试成本更低的模型。
模型分层是否合算,要看固定样本上的质量和总成本;如果下游需要不断修补上游错误,局部节省可能变成全局浪费。每个节点应有明确输入、输出结构、验证方式和失败出口。
什么时候值得引入 Graph
单个目标、单条反馈链,通常先用简单 Loop 就够了。只有当任务包含大量独立条目、多个阶段、可复用的质量门禁,或需要可审查和可恢复的调度时,脚本编排才更有价值。
我的演进顺序是:先找到一条有效的反馈路径,记录常见失败,再把已经稳定的协调规则写成代码。开放性探索仍然可以留在节点内部,不必把所有判断提前固定。
Skill 保存领域知识与操作惯例,Agent 执行有边界的任务,Workflow 组织依赖和状态。把这几层区分开,才能知道一次失败该修提示词、工具、验证器,还是控制流。
Graph 的价值不是画出一张漂亮的图,而是让可以确定的部分变得可检查、可复现,同时给需要判断的部分保留探索空间。