速读#
最近开始流行的 Graph Engineering,听起来像是在给 AI 工程再造一个新词。把它拆开看,其实没有那么神秘:一个复杂任务先被拆成几个有边界的节点,再用边表达依赖、并行、条件分支和回退,最后用状态记录流程走到哪里、留下了什么。
它和单 Agent 的区别,不是多开几个聊天窗口,而是把「下一步做什么」从一段不断变长的对话里拿出来,变成可以观察、暂停、重试和恢复的流程。
Loop 是最小的 Graph#
Loop Engineering 解决的是一个 Agent 内部的问题:模型调用工具,得到结果,发现不对,再修正,然后继续调用。它本质上是一条会回到前面节点的图。
当任务只有一个执行者,而且每一步都必须依赖上一步的结果时,Loop 已经够用。比如:
- 读取文件。
- 修改代码。
- 跑测试。
- 测试失败,回到修改代码。
- 测试通过,结束。
真正让流程变复杂的,是任务开始出现多个相对独立的角色:后端、前端和测试可以同时推进;集成后要根据测试结果决定是交付还是回到某个模块;中间还可能需要人确认。此时,单条 Loop 会把所有事情重新压成串行对话,调度本身就成了瓶颈。
所以我对两个词的区分是:Loop 是一个执行单元内部的循环,Graph 是多个执行单元之间的协作结构。
Graph 的三个基本对象#
Node:只负责一件事#
节点可以是一个 Agent,也可以是普通代码、脚本、测试命令,甚至是等待人确认的暂停点。节点边界越清楚,流程越容易替换和重试。
一个节点不应该同时负责「理解需求、写接口、改页面、跑测试、决定是否上线」。这样做只是把一串职责塞进一个函数名里,出了问题仍然不知道是哪一步出了问题。
更好的拆分是:
plan:分析需求并确定任务边界backend:实现后端接口frontend:实现页面tests:编写和执行测试integrate:合并产物并做全局检查approval:等待人工确认
Edge:决定下一步走哪里#
边不是简单的箭头,它表达了流程规则。
- 确定性边:
plan -> backend,每次都这样走。 - 模型决策边:由 Agent 根据上下文选择下一步,例如先查文档还是先读代码。
- 条件边:测试通过就进入验收,测试失败就回到对应模块。
条件边尤其重要。没有它,失败只能从头再跑;有了它,流程可以把状态里的失败信息送回正确的节点,已经完成的分支继续保留。
State:流程的可恢复记录#
状态是整个 Graph 的记忆,不只是「最近一条消息」。它可以是结构化对象:
{ "backend": { "status": "done", "artifact": "api/export.js" }, "frontend": { "status": "running" }, "tests": { "status": "pending" }, "tokens_used": 12000}状态至少应该能回答四个问题:当前在哪个节点、每个节点产出了什么、哪条路径失败了、已经消耗了多少资源。只要状态被可靠地保存,流程中断后就可以从最近的检查点继续,而不是把整段聊天记录再翻一遍。
Graph 解决了什么问题#
并行:把等待时间拿回来#
如果后端、前端和测试可以依据同一份 API 契约独立工作,就没有必要按「后端完成后前端开始,前端完成后测试开始」的顺序排队。
可以先由规划节点确定共享契约,再分出三条支线:
+--> backend --+plan ------------+--> frontend -+--> integrate --> approval +--> tests ----+ \ +--> rework这里的关键不是「开三个 Agent 就会变快」,而是三条支线必须拥有清楚的输入、互不覆盖的工作区和明确的汇合条件。没有契约的并行,只会把冲突更早制造出来。
暂停:把人放在真正需要判断的位置#
有些节点不适合自动执行,例如修改生产配置、合并高风险变更、确认研究结论。Graph 可以在这里停住,等待人确认后再继续。
这比让 Agent 自己在对话里说「我认为可以上线」更可靠,因为暂停点是流程的一部分,能被记录、审计和重复执行。
失败回退:只重做坏的那一段#
集成测试失败时,流程应该携带具体结果回到后端、前端或测试节点,而不是无条件从规划开始。其他节点已经生成的产物和状态可以保留,减少重复调用,也减少回退时引入的新变量。
这要求节点产物可定位、状态可序列化,而且每次重试都要知道自己使用的是哪一个版本的输入。否则「可恢复」只是看起来像恢复,实际上是在旧对话上继续猜。
一个开发需求怎么画成图#
假设需求是「给记账站增加数据导出功能」。串行做法通常是先写后端,再写前端,再补测试。中间任何一步的理解偏差,都会拖到后面才暴露。
Graph 的做法可以是:
plan明确导出格式、接口契约和验收条件。backend、frontend、tests按契约并行推进。integrate合并三条分支,执行全局测试。- 测试通过进入
approval;失败则根据失败模块回退。 - 人工确认后合入主分支。
用 LangGraph 表达时,骨架大致是这样:
from langgraph.graph import END, START, StateGraph
graph = StateGraph(DevState)graph.add_node("plan", plan_task)graph.add_node("backend", backend_task)graph.add_node("frontend", frontend_task)graph.add_node("tests", tests_task)graph.add_node("integrate", integrate_task)graph.add_node("rework", rework_task)graph.add_node("approval", human_approval)
graph.add_edge(START, "plan")graph.add_edge("plan", "backend")graph.add_edge("plan", "frontend")graph.add_edge("plan", "tests")graph.add_edge(["backend", "frontend", "tests"], "integrate")graph.add_conditional_edges( "integrate", check_tests, {"pass": "approval", "fail": "rework"},)graph.add_edge("rework", "integrate")graph.add_edge("approval", END)
app = graph.compile()这里最值得注意的不是 API,而是代码和流程图能互相对照:规划、分支、汇合、条件回退和人工确认都在明面上。每个节点的具体实现可以更换,但流程关系不会藏在一段长 prompt 里。
不用 LangGraph 也能做 Graph Engineering#
Graph Engineering 是设计方法,不等于 LangGraph。LangGraph 提供了节点、边、状态和检查点等实现;也可以使用 AutoGen、Google ADK、Claude Code workflow,甚至自己写一段足够小的调度代码。
在 Claude Code 里,可以这样理解已有能力的对应关系:
| Graph 概念 | Claude Code 中的对应物 |
|---|---|
| Node | 一个 subagent 或一个明确的工作阶段 |
| Deterministic Edge | workflow 脚本中的固定顺序 |
| Conditional Edge | 测试结果、hook 或脚本分支 |
| State | 脚本变量、文件产物、任务进度和检查记录 |
| Human-in-the-loop | 等待确认的阶段或显式停点 |
例如,「拆成后端、前端、测试三块并行开发,完成后合并并跑全量测试,失败就回到对应模块,最后等我确认」这句话,本身已经是在描述一个 Graph。workflow 做的事情,是把这段描述变成可执行的节点和边。
另一个典型例子是深度研究:先拆分问题,再同时搜索国内动态、海外公司、基准数据等角度,之后抓取来源、交叉验证,最后综合成带引用的报告。主 Agent 负责拆解和汇总,具体搜索放在独立节点中,既能并行,也避免主对话被大量中间过程塞满。
什么时候不要用 Graph#
Graph 不是复杂任务的默认答案。以下任务直接用一个 Agent 的 Loop 更合适:
- 能按固定顺序在几分钟内完成的小脚本或小 bug。
- 每一步都强依赖上一步,拆分后没有实际并行空间。
- 没有明确的产物、验收条件和失败回退路径。
- 任务本身还在探索,先用对话把问题定义清楚更重要。
我会在出现下面三类信号时再引入 Graph:任务能拆出相互独立的分支;不同环节需要不同工具、权限或模型;流程中有必须人工确认、长期等待或中断恢复的节点。再加一条:流程足够长,值得记录每个分支的进度和成本。
如果这些条件一个都没有,画出来的 Graph 只是在给一条直线加装饰性的节点名。
最后:新词会变,边界不会#
Graph Engineering 的价值不在于把 Agent 数量堆上去,而在于把协作关系写清楚:谁负责什么,什么时候可以并行,什么结果允许继续,失败回到哪里,状态存在哪里,最终哪一步必须由人确认。
Loop 解决单个执行者如何持续行动,Graph 解决多个执行者如何共同完成一条流程。LangGraph 和 Claude Code workflow 让这套设计更容易落地,但它们都不能替代任务拆解、契约设计和终止条件。
这个词以后可能会被另一个新词替代,节点、边、状态和调度却不会凭空消失。真正值得留下的不是名词,而是把复杂协作从一段不可检索的聊天记录,变成一条能观察、能恢复、能解释的工程流程。
