从 Loop 到 Graph:AI Agent 图工程的系统设计与实践方法
[很多 AI Agent 的讨论,最后都会落到两个词:Loop 和 Graph;前者强调 "让模型自己决定下一步",后者强调 "把任务拆成节点和边,并规定它们怎样协作";真正值得掌握的并不是判断哪一个更先进,而是知道什么时候应该把决策权交给模型,什么时候应该把流程写进系统
Loop 适合处理路径未知、需要探索的工作,而 Graph 适合处理结构已知、需要稳定执行的工作;生产级 Agent 往往把二者组合起来,用 Graph 约束全局流程,用 Loop 完成其中不确定的子任务,再用状态、工具协议、护栏和可观测性把整个系统收住
几个容易混淆的概念:
Prompt 是行为说明,而 Context 是当前事实:
Prompt Engineering 可以理解为给模型写行为说明,例如规定角色、语气、输出格式和禁止事项;它解决的是 "模型应该怎样回答"
Context Engineering 解决的是 "模型回答时应该看到什么",它需要把用户输入、业务数据、检索结果、历史消息、工具结果和运行时约束组织成当前请求真正需要的上下文;同一个模型即使拥有相同的 Prompt,拿到不同 Context 后也会作出完全不同的判断
因此,Prompt 和 Context 不是二选一的替代关系;Prompt 更像稳定的规则,Context 更像随请求变化的事实,两者共同构成一次模型调用的输入
Skill 是可复用的程序性记忆:
如果某种做法会被反复使用,就不应该每次都把完整步骤重新写进上下文;可以把它整理成 Skill,也就是一段可复用的程序性知识,例如 "如何处理退款请求","如何检查一个 Pull Request","如何为一份报告做事实核查"
Skill 适合表达稳定的操作顺序和判断规则;它比一条临时提示更容易复用,也更容易测试和版本化,但它本身仍然是流程知识,不等于一个能够自主探索的 Agent
Workflow、Agent 与 Graph 的关系:
官方资料通常把 Workflow 和 Agent 区分为两类系统;Workflow 通过预先定义的代码路径组织模型与工具,Agent 则由模型动态决定接下来调用什么工具、怎样推进任务
Graph 是表达这种流程结构的一种工程形式;它可以描述固定顺序,也可以描述条件分支、并行执行、循环、动态派生的工作者和最终汇总,所以 "Graph" 不必然意味着系统完全确定,也不必然意味着系统已经具备自主性
更准确的说法是:Graph 提供结构,Agent 提供决策;一个 Graph 节点可以是普通函数、工具调用、一次 LLM 调用,也可以是一个完整的 Agent Loop
AI Agent 的演进不是替换,而是增加控制层:
把 Prompt、Context、Skill、Loop 和 Graph 排成一条 "工程阶梯" 很有助于理解系统为什么越来越复杂,但这不是所有项目都必须经历的标准升级路线;每增加一层,解决的是不同问题,也增加了相应的状态管理、测试和运维成本
| 层次 | 主要解决的问题 | 典型控制方式 | 适合的场景 |
|---|---|---|---|
| Prompt | 如何让模型按要求表达和判断 | 指令、角色、格式约束 | 单次问答、内容生成 |
| Context | 模型需要哪些事实 | 检索、记忆、运行时数据 | 客服、销售、知识库问答 |
| Skill | 稳定步骤如何复用 | 固定程序、操作规范 | 代码审查、退款处理、报告模板 |
| Loop | 下一步做什么无法预先写死 | 模型选择工具并检查结果 | 调试、研究、开放式任务 |
| Graph | 已知流程如何稳定编排 | 节点、边、路由、并行与汇总 | 企业流程、批处理、可审计任务 |
该表容易被误读成 "Graph 取代 Loop",实际情况更像是系统从 "只会回答" 逐渐拥有了 "能调用工具、记住事实、重复执行和稳定编排" 的不同控制面
Loop:让模型在工具调用中逐步探索
Loop 的最小结构:
一个 Agent Loop 通常包含四个动作:读取当前状态、让模型决定下一步、执行模型选中的工具、把工具结果写回状态并再次判断;当模型认为任务已经完成,或触发停止条件时,循环才结束
用户请求
|
v
读取状态 -> LLM 判断下一步
|
+------+------+
| |
调工具 直接回答
| |
+-> 写回状态 v
| 结束
+-> 再次判断
抽象成伪代码,大致如下;这里的重点不是某个框架 API,而是 "工具结果重新进入模型上下文" 这一闭环
# 最大执行轮次,防止无限循环
MAX_STEPS = 20
# 初始化会话状态,载入用户输入
state = initialize(user_input)
# 多轮工具调用循环,最多执行 MAX_STEPS 步
for step in range(MAX_STEPS):
# 模型根据当前状态,决定下一步动作(调用工具 / 直接输出答案)
decision = model.decide(state, available_tools)
# 分支1:模型决定调用工具
if decision.kind == "tool_call":
# 分发执行对应的工具,传入参数并获取返回结果
result = dispatch(decision.tool_name, decision.arguments)
# 将工具执行结果追加到会话状态,供下一轮模型决策使用
state = state.add_tool_result(result)
continue # 进入下一轮循环,继续让模型决策
# 分支2:模型决定给出最终回答,终止流程并返回内容
if decision.kind == "final_answer":
return decision.content
# 分支3:不支持的决策类型,直接返回失败
return fail("unsupported decision")
# 达到最大步骤上限仍未输出最终答案,返回超时失败
return fail("step limit exceeded")
Loop 适合什么任务:
Loop 的价值来自路径不确定性;例如修复一个复杂 Bug 时,模型可能先查看代码,再运行测试,再搜索依赖文档,再检查部署配置,下一步取决于上一步发现了什么;如果把所有可能路径都提前写成 Graph,分支数量会迅速膨胀,而且很难覆盖未知情况
开放式研究也是典型的 Loop 场景;系统只知道目标是形成一份可信报告,却不一定知道需要查哪些网页、补哪些证据、何时停止搜索,因此可以让模型在工具预算内逐步探索
Loop 的代价:
Loop 把决策权交给模型,也把不确定性带进了系统;它可能多调用工具、重复搜索、走错方向或在没有新信息时继续运行,所以必须设置最大步数、总耗时、Token 预算、工具白名单、重试次数和人工审批边界
工具还可能产生副作用,例如发送邮件、修改数据库、执行命令或创建云资源;这些操作不能只依赖模型 "自觉谨慎",而应该在工具层设置权限、参数校验、幂等键、审批和审计记录
Graph:把已知的系统形状写出来
Graph 的四个基本组成:
一个可运行的 Agent Graph 至少需要明确四件事:状态 State 保存什么、节点 Node 做什么、边 Edge 怎样流转、结束条件何时触发
- State 是跨节点传递的事实,例如用户请求、分类结果、工具输出、错误信息和最终答案
- Node 是一个可执行单元,可以是函数、工具、LLM、路由器或嵌套的 Agent
- Edge 描述节点之间的控制流,可以是顺序、条件分支、回边或并行汇合
- Termination 定义何时结束,例如到达
END、通过校验、超过预算或进入人工处理
Graph 的意义不是画出一张漂亮的流程图,而是把 "谁拥有状态、谁可以改变状态、失败在哪里收口" 变成可以执行和测试的契约
四种常见结构:
第一种是顺序链;节点 A 的输出直接作为节点 B 的输入,适合提取、转换、校验这类步骤清晰的任务
第二种是路由;先由规则或模型判断请求类型,再把请求送到退款、物流、技术支持等专门分支;如果路由结果来自模型,最好要求结构化输出,并对允许的分支做枚举校验
第三种是并行化;多个彼此独立的节点同时运行,最后由汇总节点合并结果;例如同时检查代码仓库、日历、记忆库和外部新闻,再统一生成晨报
第四种是编排者-工作者;编排者先把任务拆成若干子任务,工作者分别执行,最后由汇总节点合成结果;当子任务数量无法事先确定时,可以使用动态派生的 Worker,而不是预先写死每一个节点
这些结构可以组合使用;一个 Graph 可能先路由,再并行执行多个分支,其中某个分支内部又运行一个 Loop,完成后再回到汇总节点
为什么 Graph 能降低延迟和提高可控性:
在 Loop 中,模型通常一轮只决定下一步,工具调用往往是串行的;在 Graph 中,开发者可以提前声明彼此独立的工作,让它们并行执行,再等待全部结果汇合
假设四个独立工具耗时分别为 1、2、3、8 秒;串行执行的理想耗时接近 14 秒,并行执行后主要等待最长的 8 秒,再加上汇总时间;这并不意味着所有任务都应该并行,因为有依赖关系的节点仍然必须按顺序执行,写共享状态时也需要明确合并规则
Loop 与 Graph 如何协作:
可以把 Graph 看成宏观交通规则,把 Loop 看成某一段路上的导航;Graph 规定先做分类、再查数据、最后汇总,Loop 则在 "查数据" 这个节点内部决定要搜索几次、调用哪些工具以及是否已经找到足够证据
一次 "每日工程晨报" 可以设计成下面的结构:
START
|
+------------> 路由:普通问答 / 晨报 Graph
|
+---------+---------+---------+---------+
| | |
查代码仓库 查日历/记忆 Web Research Loop
| | |
+---------+---------+---------+---------+
|
汇总与校验
|
END
这里的代码仓库查询、日历查询可以是确定性工具调用,Web Research 可以是受预算约束的 Loop;汇总节点不应该只把文本拼在一起,还要检查每个分支是否返回、证据是否足够、结果之间是否矛盾
这也解释了为什么 "Graph Engineering 正在替代 Loop Engineering" 是一个不准确的命题;更合理的系统设计是让 Graph 负责稳定的外部形状,让 Loop 负责局部探索,把不确定性限制在可观察、可取消、可计费的边界内
一个 Agent Harness 应该怎样承载这套系统:
Harness 可以理解为包住 Agent 运行时的一层系统外壳;它不只是一个循环,而是把入口、上下文、记忆、工作流、工具、观测、评估和发布连接起来
一条典型请求链可以这样理解:
渠道 Gateway
|
v
身份与输入校验
|
v
检索 Gate:Skill / 语义记忆 / 事件记忆
|
v
Graph Router
|
+--> 固定 Workflow
|
+--> Agent Loop
|
v
工具执行与权限控制
|
v
结果校验、追踪、评估与反馈
|
v
回复用户或进入人工处理
这里的三类记忆需要区分:Skill 属于程序性记忆,描述 "应该怎样做";语义记忆保存相对稳定的事实和知识;事件记忆保存带时间的历史经历和交互记录;把三者都简单称为 "记忆" 会导致检索策略、更新策略和隐私边界混在一起
Harness 的价值在于把运行控制从某个模型调用中抽离出来;入口可以来自网页、命令行或消息渠道,Graph 可以按版本发布,工具可以统一鉴权,所有节点可以产生追踪记录,模型升级后也可以通过回放和评估比较新旧结果
MCP 应该放在什么位置:
MCP 是 Model Context Protocol,它解决的是 AI 应用如何以统一方式连接外部能力和上下文;官方架构采用 Host、Client、Server 的分层,Host 负责协调应用与安全策略,Client 维护与具体 Server 的会话,Server 暴露工具、资源或提示等能力
因此,MCP 更像能力接入协议,不是 Agent 的 "大脑",也不是 Graph 引擎;一个 Loop 可以调用 MCP Server 提供的工具,一个 Graph 节点也可以调用 MCP 工具,但调用协议本身不会替应用决定任务目标、停止条件或业务流程
把 MCP 工具接入 Agent 时,至少要考虑四个问题:工具描述是否足够准确、参数是否严格校验、权限是否按工具和资源隔离、工具失败是否会被安全地写回状态;如果工具描述模糊,模型就可能选错工具,如果权限过宽,错误路径就可能造成真实副作用
从一个简单 Agent 逐步演化到 Graph:
先做一条可测的最小路径:
先让一次模型调用完成一个边界清楚的任务,例如根据输入生成结构化分类;不要一开始就加入多 Agent、长记忆和复杂路由,因为问题一旦出现,很难判断是模型、上下文、工具还是调度出了错
识别两种不稳定性:
如果不稳定性来自 "下一步需要根据发现继续探索",保留 Loop;如果不稳定性来自 "同一个业务步骤反复执行却没有明确契约",先补齐输入输出和错误处理,再把它固化成 Graph 节点
一个实用判断是:同一条流程在大量真实请求中都呈现相似形状,且企业需要可审计、可回放、可控时延,那么它就值得 Graph 化;如果任务本身没有稳定 SOP,强行 Graph 化通常只是把未知问题藏进大量脆弱分支
为每个节点写清状态契约:
每个节点都应该说明读取哪些字段、写入哪些字段、失败返回什么、是否允许重试、是否可以并行;节点之间尽量传递结构化数据,而不是只传递一大段自然语言
from typing import TypedDict
class GatherState(TypedDict):
question: str
repository_items: list[dict]
calendar_items: list[dict]
research_notes: list[dict]
final_answer: str
errors: list[dict]
状态契约的直接收益是便于测试和追踪;当最终答案有问题时,可以定位到是 research_notes 缺少证据,还是 final_answer 的汇总逻辑误读了上游结果
显式画出并行、分支和回路:
不要让节点之间的关系只存在于 Prompt 中;把可并行的节点、必须等待的依赖、失败后的重试、需要人工审批的操作和最终出口都写成图上的边或运行时策略
把护栏放在系统边界:
护栏至少要覆盖输入、路由、工具参数、工具结果和最终输出;常见措施包括结构化输出校验、工具白名单、权限检查、敏感数据过滤、预算限制、超时取消、重试退避、幂等控制和人工审批
护栏不是只为防止模型说错话;更重要的是防止模型在错误状态下持续行动,或把一个可恢复的判断错误扩大成不可逆的外部副作用
最后补齐观测和评估:
生产系统需要记录每次运行经过了哪些节点、调用了哪些工具、耗时和成本是多少、在哪个护栏处被拦截、最终是否需要人工接管;对 Loop,还应记录每一步为什么继续,对 Graph,还应记录实际走了哪条边
只有把这些信息保存下来,才能回答 "模型变差了还是工具变慢了","并行是否真的降低了延迟","某个路由分支是否经常选错" 这类工程问题
常见误区与边界:
把 Loop 当成万能 Agent:
一个会反复调用工具的循环,并不自动具备可靠规划、正确记忆或安全执行能力;没有状态边界、工具权限和停止条件的 Loop 只是更容易失控的自动化脚本
把 Graph 当成 "更高级的 Loop":
Graph 的优势是结构化和可控,不是自主性更强;如果任务路径本来就未知,Graph 可能迫使开发者提前猜测所有情况,维护成本反而高于一个受约束的 Loop
所有节点都交给 LLM 决定:
能用普通代码判断的条件就用普通代码判断,能用数据库查询解决的事情就不要让模型自由搜索;模型路由适合处理语义不确定性,确定性规则适合处理权限、金额、状态转换和合规边界
并行化只看总耗时:
并行会引入更多并发请求、限流、共享状态冲突和部分失败;需要明确取消策略、结果合并器、超时分支和单个任务失败时的降级行为,不能只把几个函数包进 gather 就认为完成了系统设计
把框架抽象当成系统本身:
LangGraph、Agents SDK 或其他编排框架可以提供状态、持久化、流式输出、追踪和工具调用能力,但它们不会替你决定业务状态、审批边界和失败语义;使用框架时仍然要理解底层模型调用、工具分发和状态更新发生了什么
最后的判断框架:
面对一个新需求,可以按下面的问题做设计选择:
- 这项任务是否只有一次模型调用就能稳定完成;如果是,先不要引入 Loop 或 Graph
- 下一步是否必须根据上一步的新发现决定;如果是,在预算和权限边界内使用 Loop
- 业务流程是否重复出现且步骤大体稳定;如果是,把稳定部分固化为 Graph
- 是否存在互不依赖的子任务;如果是,考虑并行节点,并定义合并和部分失败策略
- 是否需要模型做语义路由;如果是,让模型输出受约束的结构化结果,并由代码校验分支
- 是否有真实外部副作用;如果有,在工具层加入权限、审批、幂等和审计
- 是否需要解释一次运行为什么得到这个结果;如果需要,优先设计状态快照、节点追踪和评估样本
结语:把决策权放在正确的位置
Loop 和 Graph 的差别,本质上是控制权和确定性的分配问题;Loop 让模型根据实时信息发现下一步,Graph 让工程师把已经知道的流程、依赖和边界固定下来
好的 Agent 系统不会把所有事情都交给模型,也不会把所有可能性都硬编码成流程图;它会把稳定的部分做成可测试、可审计的 Graph,把未知的部分限制在一个有预算、有工具权限、有停止条件的 Loop 中,再用 Harness 负责记忆、协议、观测、评估和发布
当你再次听到 "Loop 还是 Graph" 这样的争论时,可以把问题换成更具体的几个问题:哪一步是已知流程,哪一步需要探索,谁拥有状态,谁可以改变外部世界,失败应该在哪里停止;这些问题回答清楚之后,系统该用 Loop、Graph,还是二者组合,通常就不再神秘

2710

被折叠的 条评论
为什么被折叠?



