ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从ReAct到Graph编排:构建复杂AI工作流的新范式

2026/8/11 3:45:04 拓冰建站 浏览量
从ReAct到Graph编排:构建复杂AI工作流的新范式 1. 从“链式”到“图式”为什么我们需要超越ReAct如果你最近在折腾AI Agent或者大模型应用开发大概率已经对ReActReasoning Acting这个范式不陌生了。它让大模型学会了“思考一步行动一步”通过循环调用工具来解决复杂问题这无疑是AI应用开发史上的一大步。但当你真正上手想把一个稍微复杂点的业务流程自动化时很快就会发现ReAct的局限性它本质上是一条单链。想象一下你要开发一个智能客服Agent它需要同时处理用户查询、查询知识库、检查订单状态、并可能调用外部API生成优惠券。在ReAct的循环里这些步骤只能一个接一个地发生。如果“查询知识库”和“检查订单状态”之间没有依赖关系理论上它们可以并行执行以提升效率但ReAct做不到。更棘手的是如果流程中出现了分支判断比如如果是老用户就走A路径新用户就走B路径或者需要循环处理一个列表中的每一项用单纯的ReAct来实现就会变得异常臃肿和难以控制。你的提示词Prompt会变成一个充满复杂条件判断的“怪物”可维护性急剧下降。这就是“Graph编排”概念开始受到关注的核心原因。它不再将AI的工作流视为一条单一的、不可分割的链条而是将其解构为一个有向无环图。在这个图里每个节点Node代表一个独立的、可复用的功能单元比如调用一次大模型、执行一个工具、做一个条件判断节点之间的边Edge则定义了数据流动和执行的依赖关系。DAGDirected Acyclic Graph有向无环图确保了流程不会陷入死循环。所以“Graph编排不只是ReAct的通用DAG”这个标题精准地指出了一个演进方向ReAct是Graph的一个特例一个简单的、线性的图而Graph编排是一个更通用、更强大的范式能够描述和驱动任意复杂的、非线性的AI工作流。它把工作流的控制逻辑从晦涩难懂的提示词中剥离出来用清晰的、可编程的图结构来定义这极大地提升了复杂Agent的可构建性、可观测性和可维护性。最近社区里出现的LangGraph、Snap Graph Builder等工具和graph engineering的热议正是这一趋势的体现。2. Graph编排的核心构件节点、边与状态要理解Graph编排我们必须先拆解它的几个核心构件。这就像搭积木只有清楚了每一块积木的形状和作用才能构建出稳固而复杂的结构。2.1 节点工作流的功能单元在Graph中节点是执行具体任务的单元。它可以是LLM调用节点接收输入调用大模型返回文本结果。这是最基础的节点类型。工具调用节点执行一个具体的函数比如调用搜索引擎API、查询数据库、运行一段代码。条件判断节点根据当前工作流的状态State决定下一步该走哪条分支。这是实现分支逻辑的关键。人工审核节点将流程暂停等待人工输入或确认后再继续。这在关键业务场景中必不可少。自定义函数节点任何你可以用代码实现的逻辑都可以封装成一个节点。节点的设计追求“单一职责”和“可复用性”。一个设计良好的“查询天气”节点既可以被“出行规划”工作流使用也可以被“穿衣建议”工作流使用。2.2 边控制流与数据流边定义了节点之间的连接关系它决定了两件事执行顺序和数据传递。顺序边最简单的边表示一个节点执行完后紧接着执行下一个节点。这模拟了链式调用。条件边从一个条件判断节点出发根据判断结果的不同指向不同的下游节点。例如if user_is_vip then node_A else node_B。并行边让多个没有依赖关系的节点同时开始执行最后汇聚到一个节点进行结果合并这能显著提升整体效率。数据通过边在节点间流动。通常整个Graph会维护一个共享的“状态”State对象每个节点读取状态的一部分作为输入并将自己的输出写回状态。下一条边上的节点读取的就是更新后的状态。2.3 状态工作流的“记忆体”状态是Graph编排的灵魂它是一个在节点间共享和传递的上下文对象。你可以把它想象成一份不断被填写和修改的“工单”或“病历”。状态的结构通常是一个字典Dictionary或类似的结构包含诸如messages对话历史intermediate_results中间结果user_query用户问题等字段。状态的更新每个节点执行后都有机会修改这个状态对象。例如一个工具调用节点可能会在状态中新增一个weather_info字段。状态的持久化对于长时间运行或需要中断恢复的工作流状态需要能够被序列化存储并在下次执行时加载。这是构建可靠生产级系统的关键。理解了这三个构件我们就能看出Graph相对于ReAct的优势ReAct将“思考”、“行动”、“更新状态”这几个动作压缩在一个不可分割的循环内而Graph将它们拆分开并通过显式的边和状态来管理从而获得了极大的灵活性和表现力。3. 实战对比用ReAct vs. Graph实现一个智能查询Agent理论说得再多不如看一个具体的例子。假设我们要构建一个智能查询Agent它的逻辑是接收用户一个问题。判断问题类型如果是事实性问题如“珠穆朗玛峰多高”直接调用网络搜索工具如果是需要复杂分析或创作的问题如“写一首关于秋天的诗”则直接调用大模型生成。将得到的结果格式化后返回给用户。我们用两种方式来实现它。3.1 ReAct 实现方式提示词中的“隐形”逻辑在ReAct范式下我们通常需要编写一个非常精巧的提示词试图让大模型自己学会这个判断逻辑。提示词可能长这样你是一个智能助手。请遵循以下步骤 1. 思考分析用户的问题属于以下哪一类 - A类事实性问题有明确答案需要查询最新信息。例如“...多高”、“...是谁”、“...最新新闻”。 - B类分析性、创作性或主观性问题不需要查询外部信息。例如“写一首诗”、“分析一下...”、“你觉得...”。 2. 行动根据你的思考执行相应操作 - 如果认为是A类你必须且只能使用 search_web 工具。 - 如果认为是B类你必须直接回答不能使用任何工具。 3. 最终将你的行动结果整理成友好的回复。 当前问题{user_question}然后我们实现一个ReAct循环不断调用LLM让它输出“Thought: ... Action: ...”并解析它的Action来调用工具。这个方法的弊端非常明显控制脆弱大模型可能不严格按照你设定的格式输出或者做出错误的分类判断导致流程走偏。逻辑耦合判断逻辑和工具调用逻辑都糅杂在提示词和模型的“黑箱”推理中难以调试和优化。难以扩展如果想增加第三种问题类型比如需要查询数据库就必须重写整个提示词破坏原有的逻辑。3.2 Graph 实现方式显式的、可编程的流程图现在我们用Graph的方式来重构这个Agent。我们使用LangGraph这个流行的框架来示意。首先我们定义三个节点函数# 节点1路由判断节点 def route_question(state): question state[question] # 这里可以用一个简单的规则也可以用一个小型分类模型 if any(keyword in question for keyword in [多高, 是谁, 最新, 定义]): return {next_node: search_node} else: return {next_node: generate_node} # 节点2搜索节点 def search_node(state): question state[question] search_result call_search_tool(question) # 假设的工具函数 return {answer: f根据搜索结果是{search_result}} # 节点3生成节点 def generate_node(state): question state[question] llm_response call_llm(question) # 假设的LLM调用 return {answer: llm_response}然后我们定义Graph的结构from langgraph.graph import StateGraph, END # 定义状态结构 from typing import TypedDict, Literal class AgentState(TypedDict): question: str next_node: Literal[search_node, generate_node, __end__] answer: str # 创建图 workflow StateGraph(AgentState) # 添加节点 workflow.add_node(router, route_question) workflow.add_node(search, search_node) workflow.add_node(generate, generate_node) # 设置入口 workflow.set_entry_point(router) # 根据路由结果动态决定下一个节点 workflow.add_conditional_edges( router, lambda state: state[next_node], # 根据next_node字段的值决定路由 { search_node: search, generate_node: generate, } ) # 搜索和生成节点执行完后都直接结束 workflow.add_edge(search, END) workflow.add_edge(generate, END) # 编译图 app workflow.compile()最后运行这个Graph# 初始化状态 initial_state AgentState(question珠穆朗玛峰多高, next_node, answer) # 执行图 final_state app.invoke(initial_state) print(final_state[answer]) # 输出根据搜索结果是8848.86米... # 另一个问题 initial_state2 AgentState(question写一首关于秋天的五言诗, next_node, answer) final_state2 app.invoke(initial_state2) print(final_state2[answer]) # 输出大模型生成的诗歌对比与优势控制权明确判断逻辑完全由route_question函数控制是确定性的、可调试的代码不再依赖大模型的“自觉”。结构清晰整个工作流被可视化为一幅图router - (search | generate) - END。任何开发者一眼就能看懂业务逻辑。易于扩展如果想增加一个“查询数据库”的节点和路径只需要添加一个新节点并在路由函数中增加一个判断分支和边即可原有结构不受影响。可观测性强每个节点的输入输出状态变更都可以被记录和监控便于排查问题。这个简单的例子揭示了Graph编排的核心价值它将AI工作流的“控制逻辑”从提示词的“魔法”中解放出来变成了可编程、可调试、可维护的软件工程问题。4. 高级模式与工程实践构建健壮的AI工作流当我们掌握了Graph的基本用法后就可以探索更复杂的模式并将其应用于工程实践以构建真正健壮、可投入生产的系统。4.1 循环与聚合处理列表任务很多业务场景需要处理列表。例如分析一份产品评论列表对每一条评论进行情感分析和要点提取最后生成一份总结报告。用ReAct实现这种循环非常别扭而Graph可以很优雅地处理。思路是创建一个“循环体”子图。主图节点将评论列表放入状态然后进入循环子图。循环子图每次处理一条评论更新进度并判断是否还有下一条。处理完所有评论后退出循环进入一个“聚合节点”来生成总结报告。LangGraph通过Pregel引擎支持这种循环你可以通过检查状态中的index或list是否已处理完来控制边的走向。4.2 并行与竞争提升效率与鲁棒性Graph编排最强大的能力之一是支持并行执行。并行处理当多个子任务间没有依赖时可以同时执行。例如在准备一份市场报告时可以同时并行执行“爬取竞品价格”、“获取社交媒体声量”、“查询行业趋势”三个节点最后一起汇总效率远高于串行。竞争模式有时我们不确定哪种方法最好可以让多个节点同时处理同一个问题然后通过一个“裁判”节点来选择最佳结果。例如对于一个复杂问题同时让GPT-4和Claude生成答案再用一个轻量级模型或规则来判断哪个答案更优。实现并行需要在定义边时让一个节点同时指向多个下游节点并设置一个“汇聚”节点来等待所有并行分支完成。这需要框架支持异步执行和状态合并。4.3 错误处理与持久化生产级的必须品任何线上系统都必须考虑失败。在Graph中错误处理可以做得非常精细。节点级重试对于可能因网络波动失败的节点如调用外部API可以内置重试机制。子图级回退如果一条路径失败可以沿着条件边切换到备用的“降级”路径。例如如果搜索最新信息失败就改为从缓存数据库中获取历史信息。状态持久化与断点续跑这是Graph编排相比传统脚本的巨大优势。每一次Graph运行都有一个唯一的run_id每个节点执行后的完整状态都可以序列化保存到数据库如Redis、PostgreSQL。如果流程因任何原因中断服务器重启、节点崩溃我们可以根据run_id加载中断时的状态从下一个节点继续执行而不是从头开始。这对于处理耗时很长的业务流程如订单审核流程至关重要。4.4 可观测性与调试给工作流装上“仪表盘”当工作流变得复杂调试就成了挑战。Graph的显式结构天生有利于可观测性。成熟的框架如LangGraph通常提供可视化自动生成工作流的拓扑图直观展示所有节点和边。执行追踪记录每个节点的开始时间、结束时间、输入状态快照、输出状态快照以及任何错误信息。这相当于一份详细的“飞行数据记录仪”。中间状态检查你可以在任何节点执行前后插入钩子hooks来检查或修改状态这对于调试复杂的数据流转问题非常有用。将这些追踪数据与像LangSmith这样的LLM应用监控平台结合你就能获得一个功能强大的“AI工作流运维中心”可以监控耗时、分析错误、优化性能。5. 主流框架选型与“Graph Engineering”的崛起随着Graph编排模式走红相关的工具和框架也如雨后春笋般出现。选择哪个工具取决于你的具体需求和技术栈。5.1 框架对比LangGraph目前生态最活跃、功能最全面的选择之一。它深度集成在LangChain生态中但也可以独立使用。优势在于其强大的状态管理、对循环和并行的原生支持、优秀的可观测性工具与LangSmith集成以及活跃的社区。它定义了StateGraph、MessageGraph等抽象适合构建复杂的、有状态的Agent工作流。如果你的项目已经在用LangChain或者需要构建生产级复杂AgentLangGraph是首选。Snap Graph Builder这是一个较新的工具从其名称“Builder”可以看出它更侧重于可视化、低代码/无代码的方式构建Graph。用户可以通过拖拽节点、连接边来设计工作流降低了使用门槛非常适合产品经理、业务分析师或不想写太多代码的开发者快速原型设计。它的后端可能仍然依赖于某个Graph执行引擎。Dagster / Airflow这两个是传统数据工程领域的知名工作流编排器。它们本身并不是为AI Agent设计的但其核心的DAG理念是相通的。如果你的AI工作流需要与复杂的数据管道ETL、调度、依赖管理深度集成特别是工作流中混合了AI节点和传统数据处理节点那么使用这些成熟的、久经考验的编排器可能更合适。它们需要更多的“胶水代码”来封装AI节点。自定义实现对于简单或特定的场景你也可以用任何编程语言Python的networkx库甚至直接用字典和函数自己实现一个轻量级的Graph执行引擎。这给了你最大的灵活性但也需要自己处理状态管理、并行、持久化等所有问题不推荐用于复杂项目。5.2 “Graph Engineering”成为新技能“Graph Engineering”这个词开始流行正说明构建AI工作流正在从“调提示词的艺术”转变为“设计系统的工程”。一个合格的Graph工程师需要具备以下能力系统设计能力能够将一个模糊的业务需求分解为清晰的、模块化的节点和边设计出高效、健壮的数据流和控制流。状态建模能力如何设计状态对象的结构使其既能承载必要信息又不过于臃肿是一项关键设计决策。框架精通深入理解所选框架如LangGraph的API、执行模型和最佳实践。运维意识必须考虑错误处理、日志、监控、性能优化和部署这与开发任何后端服务没有区别。Graph编排不是要完全取代ReAct。对于简单的、线性的任务一个精心设计的ReAct提示词可能更快捷。但对于任何有分支、循环、并行、人工介入需求的复杂业务流程Graph编排是通向可维护、可扩展、可观测的AI应用的必经之路。它标志着AI应用开发正逐步走出“脚本”和“提示词工程”的早期阶段迈向真正的“软件工程”范式。