尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

LangGraph实战:用状态机与图编排构建复杂Agent应用

LangGraph实战:用状态机与图编排构建复杂Agent应用 写过 Agent 的人大多经历过这个阶段单轮对话很顺利但只要加上分支判断、用户确认、工具调用失败重试、多个角色并行协作代码就迅速退化成一张“if-else while 循环 全局变量”缠绕成的网。后来把项目用 LangGraph 重写才发现很多看起来复杂的 Agent 工程问题本质上是因为缺少一个清晰的状态机和图编排模型。LangGraph 不是把 LangChain 换了个名字。它对 Agent 开发最大的价值是把“一步一步调用 LLM”的线性思维改成了“用图来建模流程”的状态机思维。你可以把整个 Agent 理解为一张有向图节点负责干活边负责决定下一步往哪走State 则负责让所有节点共享同一个运行上下文。一旦理解了 State、Node、Edge 这三个核心组件再去看条件路由、子图、多 Agent 协作就会顺畅很多。这篇文章会从一个最小可运行的示例开始逐步拆解 LangGraph 的状态管理、条件分支、并行分支、子图、多 Agent 协作和记忆机制。文中所有示例都不依赖任何外部模型 API Key你只需要本地 Python 环境就能跑通接真实模型时只需把示例里的模拟 LLM 函数替换成你自己的模型接口。读完你应该能回答这几个问题State 到底怎么在节点之间传递节点函数如何正确修改状态值条件路由为什么经常报错多 Agent 应用该怎么组织1. 为什么 LangGraph 值得学它解决的是 Agent 工程化问题先说判断如果你的 Agent 只是“接收问题 - 调一次模型 - 返回答案”那确实不需要 LangGraph直接用 LangChain 的ChatModel或者官方 SDK 反而更轻。但一旦流程开始复杂比如需要先判断意图、再走不同处理分支、某个环节需要人工审核、失败后要重试、多个子任务需要并行执行代码复杂度就会爆炸。传统写法一般是这样def run_agent(user_input): intent classify_intent(user_input) if intent technical: answer handle_technical(user_input) elif intent billing: answer handle_billing(user_input) else: answer handle_default(user_input) return answer这个例子看起来还能接受但真实业务里每个分支可能要调用多个工具、多次模型、做校验、存日志、失败重试。函数之间传递上下文靠参数共享状态靠全局变量无法可视化无法断点续跑也无法复用子流程。到这一步if-else 就撑不住了。LangGraph 换了一种建模方式把流程画成图状态集中在 State 中节点只做增量更新边决定流转方向。它解决的三个核心问题状态共享每个节点都能读取全局 State不需要层层传参。流程可视化图结构本身就能作为系统设计文档get_graph().draw_ascii()可以直接看到完整流程。可控性节点天然支持继续执行、中断恢复、分支选择这是普通代码结构很难做到的。所以 LangGraph 不是“必须学”的技术而是“复杂 Agent 工程化绕不开”的技术。它更适合需要长期维护、多角色协作、有明确流程节点的业务系统。LangChain 与 LangGraph 的区别也在这里。LangChain 管的是“怎么跟模型、工具对话”的细节比如 Prompt 模板、模型调用、输出解析、工具调用LangGraph 管的是“多个这样的步骤如何编排成一个图”。两者不是替代关系而是互补关系。实际项目里常见组合是用 LangChain 封装模型调用和工具能力用 LangGraph 把这些能力编排成稳定的 Agent 流程。2. 核心概念State、Node、Edge 到底在说什么LangGraph 的基本模型很简单一张图由节点Node和边Edge组成图在执行时维护一个全局状态对象State。下面逐个拆开。State状态是整个图的“数据中枢”。在 Python 里通常用一个TypedDict定义from typing import TypedDict class AgentState(TypedDict): user_input: str intent: str | None answer: str | None每个节点函数的输入都是当前 State 的字典视图输出是一个“部分更新的字典”。也就是说节点只需要返回它要修改的字段其他字段会被 LangGraph 自动保留。这正好回答了很多人第一次接触时的疑问LangGraph 的节点函数里如何改变 State 状态值答案不是直接修改传入的 state 字典而是返回一个包含新字段的 dict让框架帮你合并。例如某个节点想把intent更新为technical只需要return {intent: technical}。Node节点是图上真正干活的单元。一个节点可以是普通的 Python 函数也可以是 LangChain 的 Runnable。函数签名通常是这样def some_node(state: AgentState) - dict: # 读取 state 中的内容 text state[user_input] # 调用模型、工具或做其他处理 result do_something(text) # 返回需要更新的字段 return {answer: result}需要注意节点函数不应该直接修改传入的 state 对象也不应该依赖外部全局变量。保持纯函数风格图才能被可靠地测试、回放和恢复。Edge边决定节点之间的流转。普通边表示“无条件从 A 走到 B”条件边表示“根据当前 State 的内容判断往哪里走”。LangGraph 里还有两个特殊端点START表示图的入口END表示图的终止。创建图的基本流程是from langgraph.graph import StateGraph, START, END graph StateGraph(AgentState) graph.add_node(some_node, some_node) graph.add_edge(START, some_node) graph.add_edge(some_node, END) app graph.compile() result app.invoke({user_input: hello})可以这样理解State 像列车运行中随身携带的“日志本”每个站点都会往本子上追加信息但不会把前一个站点写的内容清空Node 是站点负责处理一件具体的事Edge 是轨道普通边对应固定线路Conditional Edge 对应道岔根据当前状态决定走哪条支线。3. 环境准备与最小项目结构本文示例使用 Python 3.10 及以上版本安装 LangGraph 即可运行。建议先创建一个干净的虚拟环境。mkdir langgraph-demo cd langgraph-demo python -m venv .venv source .venv/bin/activate pip install -U langgraph如果是在 Windows 上激活虚拟环境命令是.venv\Scripts\activate。安装完成后创建一个main.py先写一个最简单的“占位”图验证环境from typing import TypedDict from langgraph.graph import StateGraph, START, END class DemoState(TypedDict): message: str def echo_node(state: DemoState): return {message: state[message] - processed} graph StateGraph(DemoState) graph.add_node(echo, echo_node) graph.add_edge(START, echo) graph.add_edge(echo, END) app graph.compile() print(app.invoke({message: hello langgraph}))运行python main.py如果输出包含processed说明环境没问题。后面所有示例都可以在这个文件上迭代。顺便说明真实项目如果需要接入 OpenAI、通义千问、DeepSeek 等模型通常还要安装对应的模型包例如langchain-openai然后在节点函数里调用chat_model.invoke(prompt)。本文为了让你零配置运行用了一个模拟 LLM 函数代替外部调用逻辑一致。4. 第一个实战用 State 实现多轮信息收集先来看一个最能体现 State 价值的场景多轮信息收集。假设我们要收集会议信息先拿到参会人姓名再拿到会议主题最后生成一份摘要。在没有 State 的情况下这些字段要一层层当作参数传下去非常啰嗦有了 State每个节点只管读取自己需要的字段、更新自己负责的字段。from typing import TypedDict, Optional from langgraph.graph import StateGraph, START, END class MeetingState(TypedDict): name: Optional[str] topic: Optional[str] summary: Optional[str] def input_name(state: MeetingState): # 模拟从用户输入中提取姓名 if not state.get(name): return {name: 张三} return {} def input_topic(state: MeetingState): # 模拟从用户输入中提取会议主题 if not state.get(topic): return {topic: LangGraph 状态管理讲解} return {} def generate_summary(state: MeetingState): return { summary: f会议人{state[name]}主题{state[topic]} } graph StateGraph(MeetingState) graph.add_node(input_name, input_name) graph.add_node(input_topic, input_topic) graph.add_node(generate_summary, generate_summary) graph.add_edge(START, input_name) graph.add_edge(input_name, input_topic) graph.add_edge(input_topic, generate_summary) graph.add_edge(generate_summary, END) app graph.compile() result app.invoke({}) print(result[summary])运行输出会议人张三主题LangGraph 状态管理讲解这段代码里有几个关键点。第一app.invoke({})传入空字典是因为 State 里所有字段都是Optional框架允许从空状态开始节点逐步填充字段。如果定义了必填字段invoke时必须传入对应值。第二input_name在没有 name 时才返回新值否则返回{}。空字典表示“本次不更新任何字段”这在实际工程里很常见节点有时需要短路有时需要跳过。第三generate_summary同时读取了state[name]和state[topic]。虽然它的代码只返回了summary但因为它能访问整个 State所以可以读取前面节点写入的所有内容。这就是“全局状态”带来的便利。第四节点函数的返回值并不是“全量替换 State”而是“增量合并”。LangGraph 会保留 State 中其他字段已有的值只覆盖返回 dict 中出现的同名 key。如果你在节点里返回了 State 中没有定义的新 key在开启严格类型检查时会报错所以在定义 State 时尽量把所有需要持久化的字段都提前规划好。5. 条件分支实战Conditional Edge 让 Agent 学会“走哪条路”线性流程只能解决固定步骤真实 Agent 最核心的能力是“根据情况决定下一步”。LangGraph 里的add_conditional_edges就是为这个场景设计的。这里用一个多语言处理场景先判断输入文本的语言再进入对应语言的处理节点。from typing import TypedDict, Optional from langgraph.graph import StateGraph, START, END class TranslateState(TypedDict): text: str lang: Optional[str] result: Optional[str] def detect_lang(state: TranslateState): text state[text] # 模拟模型做语言识别实际项目可替换为 LLM 分类 if 你好 in text or 世界 in text: return {lang: zh} return {lang: en} def handle_zh(state: TranslateState): return {result: f[中文处理] {state[text]}} def handle_en(state: TranslateState): return {result: f[English] {state[text]}} def route_by_lang(state: TranslateState) - str: return state[lang] graph StateGraph(TranslateState) graph.add_node(detect_lang, detect_lang) graph.add_node(handle_zh, handle_zh) graph.add_node(handle_en, handle_en) graph.add_edge(START, detect_lang) graph.add_conditional_edges( detect_lang, route_by_lang, { zh: handle_zh, en: handle_en, }, ) graph.add_edge(handle_zh, END) graph.add_edge(handle_en, END) app graph.compile() print(app.invoke({text: 你好世界})) print(app.invoke({text: hello world}))运行会看到两种不同的处理结果中文文本进入handle_zh英文文本进入handle_en。这个示例里route_by_lang就是条件边函数。它接收当前 State返回一个字符串LangGraph 拿这个字符串去匹配add_conditional_edges的第三个参数路由表。路由表的 key 是分支名value 是下一个节点的名称。有两个非常容易踩的坑提前说清楚。第一条件边函数的返回值必须能匹配路由表里的某个 key。如果route_by_lang返回了fr而路由表里只有zh和en运行时会报错提示找不到下一个节点。实际项目中路由函数最后一定要有默认兜底例如def route_by_lang(state: TranslateState) - str: lang state.get(lang) if lang in (zh, en): return lang return en第二条件边也可以让流程形成循环。比如“生成方案 - 人工审核 - 不通过则重新生成”这就是条件边指向前面节点形成环。LangGraph 天然支持循环这也是它和 LangChain 线性链条最大的差异之一。只要注意设置合理的结束条件避免死循环即可。如果你把上面的条件分支换成第一节里的 if-else会发现在“只有一个函数”的场景下两者没有本质差别。但 LangGraph 的区别在于每个分支节点是独立可测试、可复用的且整个分支过程会被记录在 State 和 checkpoint 里出现问题时可以定位到具体是哪一步、由哪个条件选择错了分支。6. 并行分支与子图把复杂流程拆小真实 Agent 场景经常需要并行执行多个子任务。LangGraph 支持从同一个节点出发让多个节点并行运行再汇聚到同一个下游节点。并行分支有一个必须注意的细节多个并行节点同时向同一个普通字段写入值会产生覆盖最终谁写入取决于执行顺序。解决办法是给字段绑定 reducer让写入变成“追加合并”。下面用一个商品评论分析场景演示同时做“情感打分”和“关键词提取”最后统一汇总成报告。from typing import TypedDict, Annotated, List, Optional from operator import add from langgraph.graph import StateGraph, START, END class ReviewState(TypedDict): review: str tasks: Annotated[List[str], add] report: Optional[str] def sentiment_score(state: ReviewState): # 模拟情感分析实际项目替换为模型调用 return {tasks: [情感得分8/10]} def keyword_extract(state: ReviewState): # 模拟关键词提取 return {tasks: [关键词安装, 关键词崩溃]} def build_report(state: ReviewState): return {report: | .join(state[tasks])} graph StateGraph(ReviewState) graph.add_node(sentiment_score, sentiment_score) graph.add_node(keyword_extract, keyword_extract) graph.add_node(build_report, build_report) graph.add_edge(START, sentiment_score) graph.add_edge(START, keyword_extract) graph.add_edge(sentiment_score, build_report) graph.add_edge(keyword_extract, build_report) graph.add_edge(build_report, END) app graph.compile() result app.invoke({ review: 软件安装很简单但运行过程中崩溃了一次 }) print(result[report])tasks字段用了Annotated[List[str], add]。这意味着无论有多少个并行节点向这个字段追加值LangGraph 都会用operator.add把列表合并而不是互相覆盖。如果去掉Annotated两个并行节点同时写tasks就很可能出现其中一个节点的结果被另一个节点覆盖的情况。这是并行分支最核心的 Go 细节建议动脑记一次。子图subgraph则是另一种复用手段。当一个流程片段会被多个地方重复使用可以把它封装成一个独立的图再作为节点加入主图。下面演示一个“文本安全检查子图”主图在生成最终回答前先调用它。from langgraph.graph import StateGraph, START, END class SafetyState(TypedDict): text: str safety_result: Optional[str] def check_safety(state: SafetyState): if 危险词 in state[text]: return {safety_result: blocked} return {safety_result: pass} safety_graph StateGraph(SafetyState) safety_graph.add_node(check_safety, check_safety) safety_graph.add_edge(START, check_safety) safety_graph.add_edge(check_safety, END) safety_app safety_graph.compile() class MainState(TypedDict): text: str safety_result: Optional[str] final_answer: Optional[str] def build_answer(state: MainState): if state[safety_result] blocked: return {final_answer: 内容未通过安全检查不生成回答} return {final_answer: 内容安全可继续生成回答} main_graph StateGraph(MainState) main_graph.add_node(safety_check, safety_app) main_graph.add_node(build_answer, build_answer) main_graph.add_edge(START, safety_check) main_graph.add_edge(safety_check, build_answer) main_graph.add_edge(build_answer, END) main_app main_graph.compile() result main_app.invoke({text: 这是一个危险词示例}) print(result[final_answer])子图作为节点加入主图时有一个容易忽略的限制子图只能感知自己 State 中定义的字段。如果子图的SafetyState里没有text字段那么即使主图MainState有text子图也无法读取它。所以在设计子图时要明确这个子图需要哪些输入字段、输出哪些字段再在主图的 State 里保留同名同类型的字段两边才能接通。更复杂的场景可以用映射把父图状态转换为子图状态但基础做法是“字段名对齐”。7. 多 Agent 协作实战主管-工人模式怎么落地多 Agent 应用在 LangGraph 里并不是一个独立的“Agent 类”本质上仍然是多个节点只不过每个节点拥有自己的 Prompt、工具和上下文范围。目前社区和官方文档里最常用的模式是“主管-工人”模式Supervisor-Worker Pattern。这个模式的核心是一个主管节点负责接收用户请求、分析意图、决定把任务分配给哪个领域 Agent领域 Agent 各自负责完成自己的任务把结果写回 State最后再由一个汇总节点统一整理输出。下面是一个客服工单处理的简化示例。为了演示方便用关键词规则代替意图识别真实项目可以换成 LLM 分类。from typing import TypedDict, Optional from langgraph.graph import StateGraph, START, END class TicketState(TypedDict): user_request: str intent: Optional[str] agent_name: Optional[str] response: Optional[str] def triage(state: TicketState): request state[user_request] if 退款 in request or 账单 in request: return {intent: billing} if 安装 in request or 报错 in request: return {intent: technical} return {intent: other} def tech_agent(state: TicketState): # 技术 Agent 只调用与技术支持相关的工具 tool_result f[技术工具] 收集日志并定位问题{state[user_request]} return { agent_name: tech_agent, response: f技术支持处理结果{tool_result}, } def billing_agent(state: TicketState): # 财务 Agent 只调用与账单、退款相关的工具 tool_result f[账单工具] 查询订单和退款规则{state[user_request]} return { agent_name: billing_agent, response: f账单客服处理结果{tool_result}, } def default_agent(state: TicketState): return { agent_name: default_agent, response: 暂时无法自动处理已转接人工客服。, } def route_by_intent(state: TicketState) - str: return state[intent] def final_review(state: TicketState): # 汇总节点记录是由哪个 Agent 处理的 return { response: f【{state[agent_name]}】{state[response]} } graph StateGraph(TicketState) graph.add_node(triage, triage) graph.add_node(tech_agent, tech_agent) graph.add_node(billing_agent, billing_agent) graph.add_node(default_agent, default_agent) graph.add_node(final_review, final_review) graph.add_edge(START, triage) graph.add_conditional_edges( triage, route_by_intent, { technical: tech_agent, billing: billing_agent, other: default_agent, }, ) graph.add_edge(tech_agent, final_review) graph.add_edge(billing_agent, final_review) graph.add_edge(default_agent, final_review) graph.add_edge(final_review, END) app graph.compile() print(app.invoke({user_request: 我的账单出现异常扣款})) print(app.invoke({user_request: 软件安装时提示报错}))这段代码的结构更接近真实的多 Agent 系统triage是主管节点tech_agent、billing_agent、default_agent是不同角色的工人节点final_review是结果汇总节点。这里要强调一个工程安全问题每个领域 Agent 的工具集应当遵循最小权限原则。tech_agent只能访问日志和诊断工具billing_agent只能访问账单查询和退款工具。不要让一个 Agent 拥有整条业务链路的所有权限。尤其在 Agent 需要操作数据库、发送消息、调用外部系统时权限边界必须通过工具注册表、环境变量、API Token 范围等手段严格收敛并且在测试环境验证后再上生产。从实现上看多 Agent 和单 Agent 的差别并没有想象中那么大。LangGraph 没有显式的 Agent 对象所谓多 Agent只是“不同的节点拥有不同的 Prompt 和工具由路由边决定谁被执行”。想要扩展新的 Agent也只需要增加一个新节点、在路由表里多注册一个分支不需要修改已有节点的逻辑。8. 记忆机制短期记忆、长期记忆与可观测性很多人在 LangGraph 里做多轮对话时发现一个问题每次invoke都像新开了一个会话前一轮的 State 完全丢失。这是因为默认情况下图的每一次执行都是独立的State 不会自动保留到下一次。要让 Agent 记住上下文需要给编译后的图挂一个 Checkpointer。最轻量的方式是使用MemorySaver它把每一次执行的 State 快照保存下来并通过thread_id区分不同会话。from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import StateGraph, START, END # 使用之前某个示例的图结构这里只展示编译时挂载 checkpointer MemorySaver() app graph.compile(checkpointercheckpointer) config {configurable: {thread_id: user-001}} result1 app.invoke({user_request: 你好}, config) result2 app.invoke({user_request: 继续, intent: technical}, config)同一个thread_id下的多次invoke会共享 State从而实现会话级记忆。MemorySaver只把数据存在内存中进程重启后记忆就没了。生产环境想要持久化可以使用 LangGraph 提供的可持久化 Checkpointer或者对接自己的存储方案比如 Redis、SQLite、Postgres 等。如果业务需要的是跨会话长期记忆比如“记住用户的产品偏好”“记住用户所在行业”只靠 Checkpointer 是不够的。LangGraph 还提供了 Store 机制用于保存独立于执行周期的长期记忆数据。基础概念是短期记忆存在于某个线程Thread的每个 checkpoint 里长期记忆存在于全局 Store 里可以跨线程读取。不同小版本的 Store API 变化较快使用前建议先核对当前文档。对于刚入门的人来说先理解“Checkpointer 保存单次流程状态Store 保存跨会话业务知识”这个分层比记 API 更重要。可观测性也是 LangGraph 的一大优势。一个编译好的图可以直接导出流程结构# 打印图的文本结构 print(app.get_graph().draw_ascii())也可以输出 Mermaid 格式用于在支持 Mermaid 的文档工具中可视化整张图。这里的建议是画图不是可有可无的装饰而是排查 Agent 运行逻辑的重要手段。一个分布式系统的排查依赖链路追踪一个 Agent 系统的排查则依赖清晰的图结构和每一步的状态记录。在生产项目中建议把每一步节点执行时间、State 关键字段变化、调用模型和工具的结果都写入结构化日志。如果想进一步做精细追踪可以接入 LangSmith 类似的可观测平台它会记录每次节点调用、Prompt、模型输出和工具调用结果对定位 Agent 的“异常行为”非常有帮助。9. 常见问题与排查思路LangGraph 入门最常见的报错和误区集中整理在这里。问题现象可能原因排查方式解决方案节点执行后 State 没更新节点函数没有 return dict或 return 的 key 与 State 定义不一致打印节点返回值对比 State 字段名确保每个节点返回的是字典且 key 已在 State 中定义条件边执行时报错“找不到下一个节点”条件函数返回的分支名不在路由表中打印条件函数返回值检查路由表 key给条件函数加默认兜底分支不要返回未注册的值并行节点结果互相覆盖两个节点同时写同一个普通字段没有使用 reducer检查 State 里字段是否有Annotated[..., add]对需要合并的字段使用 reducer或拆分到不同字段子图拿不到外层 State 的数据子图 State 中没有定义对应字段或字段类型不一致检查子图 State 和父图 State 的字段定义让子图 State 包含它需要的输入字段做好字段对齐把 LCEL 链直接当 Node 用报错LangChain 链输入输出格式与 LangGraph 的 State 格式不匹配确认链的输入是否要求字典、输出是否满足 State 字段用RunnableLambda包装把链的输入输出转换成 State 格式不同用户会话互相串数据所有请求使用了同一个thread_id或没有传 config检查 config 里的thread_id是否按用户区分每次请求根据用户会话生成独立thread_id调用模型时提示缺少 API Key环境变量未配置或依赖包未安装打印环境变量检查依赖安装情况配置API_KEY环境变量安装对应的模型包图一直循环不结束条件边指向了前面节点但缺少终止条件画出图结构检查循环路径增加最大迭代次数或状态字段作为终止条件10. 工程化建议从 Demo 到生产示例能跑通只是第一步。把 LangGraph 应用放到生产环境下面这些建议很重要。第一先画图再写代码。LangGraph 的开发方式天然适合“先设计流程图再实现节点函数”。建议在写代码前用文字把节点、边、条件分支、循环和结束条件列出来。这个习惯能避免“代码越写越乱最后连图都画不出来”的情况。第二State 字段要克制、扁平、语义清晰。State 是图的核心数据模型字段越多节点耦合越重。尽量把一次性中间结果放在局部变量中而不是全部塞进 State。字段类型尽量用Optional表达“可能为空”并给TypedDict定义清楚的注释。不要为了省事把一个“万能 dict”直接作为 State。第三节点函数保持纯函数风格。不要修改传入的 state 字典不要依赖模块级全局变量不要通过闭包偷偷改变外部状态。这样可以保证同一个节点在相同输入下行为一致也方便单元测试。第四工具权限最小化。多 Agent 系统里不同的 Agent 应该持有不同的工具集合。涉及数据库、外部 API、文件系统、支付等敏感操作时务必遵循最小权限原则在测试环境充分验证后再接到生产流程。不要为了演示方便把所有工具一股脑授权给所有 Agent。第五用模拟 LLM 做自动化测试。真实模型输出有随机性不适合作为回归测试的断言依据。更稳妥的做法是像本文示例一样用“规则函数 固定返回”模拟模型先验证图结构、条件分支和状态流转是否正确再在正式环境中接入真实模型用少量真实流量做验证。第六从最小可运行的图开始迭代。不要一开始就把所有功能塞进一张大图。先做一个“能跑通、能看到结果”的最小版本再逐渐加入并行分支、子图、checkpointer、长期记忆。这样每一步出错问题范围都足够小排查速度快很多。11. 结语与下一步实践LangGraph 的学习曲线并不陡峭核心就是 State、Node、Edge 三个概念。理解了“节点函数返回部分更新、条件边根据 State 决定路由、子图通过字段对齐复用流程”你就已经能解决大多数 Agent 开发中的流程编排问题。看完本文后建议你亲自动手做三件事把本文的“翻译路由”示例改成你业务里的意图分发逻辑给你的图加上MemorySaver测试同thread_id下多轮对话的状态保持最后把示例中的模拟 LLM 替换成你真实使用的模型接口感受一下真实语言模型和固定规则返回之间的差距。当你在实际项目中加入分支、循环、并行、子图和多 Agent 协作之后会发现 LangGraph 真正提供的不是某一个具体功能而是一种让复杂 AI 应用变得可控、可观察、可维护的工程框架。下一阶段可以继续研究子图的复杂状态映射、长期记忆的持久化方案以及如何把自己公司的业务系统安全地集成到 Agent 的工具层。
返回列表