LangGraph智能体开发入门:从零搭建带条件分支与工具调用的AI Agent

发布时间:2026/10/20 17:41:13

LangGraph智能体开发入门:从零搭建带条件分支与工具调用的AI Agent 1. 从会聊天的模型到能办事的智能体中间差了什么很多人第一次接触AI应用开发都是从调用一次对话接口开始的给模型一段提示词它回一段话任务就算完成了。这种模式在问答、润色、翻译这类一问一答的场景里够用但只要需求稍微复杂一点比如帮我查一下资料整理成表格再根据表格写一份周报单次调用就立刻露怯了——模型没有记忆、不会分步骤、更不会在中间调用外部工具。这就是**AI智能体Agent**要解决的问题。智能体的本质是把一次模型调用升级成一个有状态、能循环、可调用工具、能根据结果决定下一步的执行流程。而LangGraph就是目前把这件事做得最直观、最工程化的框架之一。这篇内容面向的是完全没有LangGraph基础、但想快速跑通第一个智能体的开发者。我会从为什么需要它讲起把核心概念拆开揉碎再带你从零搭一个能跑的最小智能体最后聊聊新手最容易踩的几个坑。全程不堆术语能上手抄的代码我都给全。读完你应该能做到理解LangGraph的图结构思维独立写出一个带条件分支和工具调用的智能体并且知道出问题时该往哪儿查。需要先说明一点LangGraph的版本迭代比较快本文的代码基于较稳定的API风格编写如果你安装的是更新的版本个别参数名可能有微调遇到报错优先查官方文档的迁移说明这是这类快速演进框架的常态。2. 为什么单靠提示词链搞不定复杂任务2.1 链式调用的天花板在哪里早期的做法是把多个模型调用串成一条链第一步的输出喂给第二步第二步的输出喂给第三步。这种线性结构在流程固定时很好用但真实任务往往不是直线。举个典型例子一个客服智能体收到用户消息后需要先判断意图是查询订单、是投诉、还是闲聊不同意图要走完全不同的处理路径有的还要回头再问用户一句。线性链没法表达根据中间结果决定走哪条路也没法表达这一步做完再回到上一步。更麻烦的是状态管理。链式结构里每一步的输入输出靠手动传递一旦步骤多了变量满天飞调试时根本不知道是哪一步把数据弄丢了。你可能会想那我用一个全局字典存着不就行了可以但随之而来的是并发、重试、中断恢复这些工程问题手写会非常痛苦。2.2 图结构为什么更贴合智能体的思维方式LangGraph的核心洞察很简单把智能体的执行过程建模成一张图。图里有节点Node和边Edge。节点代表一个具体的处理步骤比如调用模型执行工具判断意图边代表步骤之间的流转关系可以是固定的也可以是根据当前状态动态决定的。这个模型的好处在于它天然支持三件线性链做不到的事循环边可以从后面的节点指回前面的节点实现反复尝试直到满足条件这正是智能体自我修正的基础。分支一个节点可以有多条出边走哪条由当前状态决定这就是条件路由。状态共享整张图共享一份状态对象每个节点读取它、修改它不用手动传来传去。打个比方链式调用像一条流水线产品只能往前走图结构像一张地铁线路图有换乘、有环线、有分叉能表达复杂得多的路径。智能体要处理的任务越复杂图结构的优势就越明显。2.3 LangGraph在生态里的位置需要澄清一个常见误解LangGraph不是用来替代模型本身的它管的是编排。模型负责思考和生成LangGraph负责决定什么时候调用模型、调用完做什么、下一步去哪。它和提示词模板、工具定义、记忆存储这些组件是配合关系不是竞争关系。对新手来说理解这一点很重要因为它决定了你学习时的关注点不要指望LangGraph帮你写出更好的提示词它解决的是流程控制问题。你的提示词写得烂图搭得再漂亮智能体照样答非所问。3. 动手前必须搞懂的四个核心概念3.1 State整张图共享的那份记忆State是LangGraph里最重要的概念没有之一。它是一份贯穿整个执行过程的数据结构通常用带类型注解的字典或数据类来定义。每个节点函数接收当前State返回对State的更新框架负责把更新合并进去。定义State时最关键的是想清楚哪些字段需要跨节点传递。比如一个研究型智能体State里通常要有用户原始问题、当前对话历史、已经收集到的资料、下一步要做什么的标记。字段设计得好节点逻辑就清爽设计得乱后面全是补丁。这里有个新手常忽略的点State的更新默认是覆盖还是追加取决于你用的合并策略。对话历史这种需要不断累加的字段必须显式声明用追加的方式合并否则每轮都会把历史冲掉。这个坑我在第5节会详细讲。3.2 Node一个节点只干一件事节点就是一个普通函数输入State输出State的更新。它的设计原则是职责单一。一个节点里既判断意图又调用工具又生成回复看起来省事实际上会让调试变成噩梦——出了问题你根本不知道是哪部分逻辑错了。我习惯把节点分成几类模型调用节点、工具执行节点、路由判断节点、结果整理节点。每类节点逻辑清晰复用性也好。比如调用模型这个节点在多个智能体里几乎可以原样搬。3.3 Edge决定下一步去哪边分两种。普通边是固定的A节点执行完必然去B节点。条件边则挂一个判断函数函数读取当前State返回一个字符串框架根据这个字符串决定走哪条出边。条件边是智能体智能的体现。比如判断用户意图后返回查订单就去订单节点返回投诉就去投诉节点。写条件边函数时一定要保证所有可能的返回值都有对应的目标节点否则运行时会直接报错这是新手最常见的运行时错误之一。3.4 编译与执行把图跑起来定义完节点和边需要调用编译方法把图编译成一个可执行对象。编译过程会做校验比如检查有没有孤立节点、条件边的分支是否完整。编译通过后就可以传入初始State来执行了。执行模式上新手先用最简单的同步调用跑通即可。等熟悉了再去看流式输出、中断恢复这些进阶能力。一上来就追求花哨的功能很容易在基础概念没打牢时迷失方向。4. 从零搭一个能查资料会算数的智能体4.1 环境准备与依赖安装先把环境弄干净。强烈建议用虚拟环境避免和你机器上其他项目的依赖打架。Python版本建议3.10以上因为LangGraph用到了较新的类型注解特性。python -m venv venv source venv/bin/activate # Windows下用 venv\Scripts\activate pip install langgraph langchain-core如果你打算调用真实的模型还需要装对应厂商的集成包并配置好API密钥。为了让你能先跑通流程、不被密钥问题卡住下面的示例我会用一个假模型函数代替真实调用逻辑完全一样你把它换成真实调用即可。提示依赖装完后先跑一个import测试确认没有版本冲突。LangGraph和LangChain的版本耦合比较紧混装旧版本很容易出莫名其妙的导入错误。4.2 定义State想清楚要传什么我们做一个简单的研究助手用户提问智能体判断是否需要计算需要就调用计算工具不需要就直接回答。from typing import Annotated, TypedDict from operator import add class AgentState(TypedDict): question: str messages: Annotated[list, add] needs_calc: bool calc_result: str final_answer: str这里messages字段用了Annotated[list, add]意思是每次更新都往列表里追加而不是覆盖。这就是前面提到的合并策略对话历史、日志这类字段必须这么写。其他字段默认是覆盖语义适合存当前值。4.3 写节点每个函数只做一件事第一个节点负责判断意图。真实场景里这一步应该调用模型这里我用关键词匹配模拟方便你理解流程。def decide_node(state: AgentState): q state[question] need any(k in q for k in [计算, 算一下, 等于多少, , -, *, /]) return {needs_calc: need, messages: [f判断结果: 需要计算{need}]}第二个节点执行计算。真实项目里这里会调用一个计算工具我这里用受限的表达式求值演示。def calc_node(state: AgentState): expr state[question] # 实际项目请用安全的表达式解析库不要直接eval用户输入 try: result str(eval(expr, {__builtins__: {}}, {})) except Exception as e: result f计算失败: {e} return {calc_result: result, messages: [f计算结果: {result}]}第三个节点生成最终回答。def answer_node(state: AgentState): if state.get(calc_result): ans f计算结果是 {state[calc_result]} else: ans f关于「{state[question]}」这是一个直接回答。 return {final_answer: ans, messages: [已生成回答]}注意每个节点都只返回需要更新的字段不需要把整个State都返回。框架会自动合并这也是为什么State设计要清晰——你返回什么框架就更新什么。4.4 连边与条件路由现在把节点连起来。判断节点之后要分叉所以用条件边。from langgraph.graph import StateGraph, START, END def route_after_decide(state: AgentState): return calc if state[needs_calc] else answer builder StateGraph(AgentState) builder.add_node(decide, decide_node) builder.add_node(calc, calc_node) builder.add_node(answer, answer_node) builder.add_edge(START, decide) builder.add_conditional_edges(decide, route_after_decide, { calc: calc, answer: answer, }) builder.add_edge(calc, answer) builder.add_edge(answer, END) graph builder.compile()add_conditional_edges的第三个参数是映射表把路由函数的返回值映射到实际节点名。这个映射表一定要写全路由函数返回了映射表里没有的值运行时会直接抛错。4.5 跑起来并观察状态流转result graph.invoke({question: 帮我计算 12 * 8 5, messages: []}) print(result[final_answer]) print(result[messages])跑完你会看到messages里按顺序记录了每一步的动作这就是状态流转的痕迹。调试智能体时打印messages是最有效的排查手段它能告诉你流程到底走到了哪一步、在哪一步偏离了预期。把问题换成今天天气怎么样你会发现它跳过了计算节点直接回答。这就是条件路由在起作用。到这里一个最小可用的智能体就跑通了。5. 新手最容易栽的五个坑5.1 状态字段被意外覆盖最常见的翻车现场对话历史每轮都被清空。原因就是定义State时忘了给历史字段加追加语义默认的覆盖行为把上一轮的内容冲掉了。判断方法很简单跑两轮对话看第二轮还能不能记得第一轮的内容。记不住八成是这个问题。5.2 条件边返回值对不上路由函数返回了calc但映射表里写的是calculation运行时直接报找不到目标节点。这类错误信息通常比较直白看到unknown node之类的提示先去核对路由函数的所有返回值和映射表的键是否一一对应。建议把路由函数的返回值定义成常量避免手写字符串拼错。5.3 节点里做了太多事一个节点里塞了判断、调用、整理三件事跑通没问题但一旦结果不对你没法定位是哪部分逻辑出的错。我的经验是只要一个节点里出现了两个以上的决策点就该考虑拆开。拆开之后每个节点都能单独测试整体调试效率反而更高。5.4 忘了处理异常分支工具调用失败、模型返回格式不对、外部接口超时这些在真实环境里天天发生。新手往往只写顺利路径一遇到异常整个图就崩了。正确做法是在容易出错的节点里做兜底把错误信息写进State让后续节点能感知到并走降级路径。智能体的健壮性很大程度上取决于你对异常路径的覆盖程度。5.5 一上来就追求复杂图看到别人画的智能体图有十几个节点自己也想一步到位。结果图越画越乱调试成本指数级上升。我的建议是从三个节点起步一个入口判断、一个核心处理、一个输出。跑通之后再逐步加分支、加循环、加工具。图结构的复杂度应该跟着需求长而不是跟着想象长。6. 让智能体真正好用的几个进阶思路6.1 循环与自我修正智能体真正强大的地方在于能试错。你可以加一个校验节点检查上一步的输出是否满足要求不满足就把边指回生成节点重来同时把失败原因写进State供下一轮参考。这就是所谓的反思循环。但要注意设置最大重试次数否则遇到模型死活改不对的情况图会无限循环下去白白烧掉调用额度。6.2 工具调用的组织方式当工具有好几个时不要让模型直接决定调用哪个具体函数而是让它输出一个结构化的意图再由你的代码去分发。这样做的好处是可控——你可以在分发前做权限校验、参数清洗、日志记录。把模型当成决策者而不是执行者是工程上更稳妥的分工。6.3 状态持久化与中断恢复LangGraph支持把执行过程中的状态存下来这样长任务可以中断后继续也方便做人工审核环节——比如智能体生成内容后暂停等人确认再继续。这个能力在真实业务里非常实用但前提是你的State设计得足够完整能完整还原现场。所以回到最开始那句话State设计是重中之重。6.4 可观测性不能省智能体跑起来之后你最需要的是看得见。每一步的输入输出、耗时、走了哪条分支都应该记录下来。前期可以用简单的日志规模大了再上专门的追踪工具。没有可观测性的智能体出了问题只能靠猜这在生产环境里是不可接受的。7. 我踩过这些坑之后的一点体会刚接触LangGraph时我最大的误区是把它当成一个更高级的提示词工具花了很多时间琢磨怎么把提示词写得更花哨却忽略了图结构本身的设计。后来才明白智能体的能力上限一半来自模型一半来自你给它设计的流程。流程设计得好普通模型也能稳定完成任务流程一团糟再强的模型也救不回来。另一个体会是关于克制。新手容易陷入功能越多越好的陷阱恨不得一个智能体什么都能干。实际上一个职责清晰、边界明确的小智能体远比一个什么都沾一点的大杂烩好用。需要复杂能力时用多个小智能体协作比堆在一个图里清晰得多。最后分享一个实用习惯每次改图结构之前先在纸上把节点和边画一遍标清楚每个节点的输入输出和分支条件。画得出来代码基本不会写错画不出来说明你还没想清楚这时候写代码纯属浪费时间。这个习惯帮我省下的调试时间比我学任何技巧都多。
延伸阅读

更多相关文章

2026/10/20 17:41:13

从人类操作视频到自我进化:GUI智能体训练与医疗场景实践

1. 项目整体拆解:三件事串起一条线看到“3.28-1”这个版本号时,我第一反应是:这大概率是一个把数据集、训练范式和应用场景捏在一起的阶段性交付。标题里三块内容——大规模人类标注的计算机使用视频数据、从失败经验中学习的自我进化GUI智能…

2026/10/20 17:41:13

物理规律导向视频生成:技术原理、验证方法与工程落地实践

1. 物理规律导向到底在解决什么痛点 第一次看到"物理规律导向视频模型"这个说法,很多人会下意识觉得又是营销包装。但如果你真正动手做过视频生成,就会明白"物理规律"这四个字背后藏着多少血泪。过去两年我陆续试过七八个主流视频生…

2026/10/20 18:26:50

大模型程序员转型指南:应用开发、微调训练与推理部署全解析

记得去年这个时候,圈子里聊的还是“要不要学大模型”,今年再聊,已经变成“怎么学、从哪儿切入”了。程序员群体对大模型的焦虑,说实话比一般行业更早也更真实——不是因为怕被替代,而是因为我们的工具箱正在被重构&…

2026/10/20 18:26:50

多智能体辩论与红蓝对抗:Agent决策质控的工程实践

我们内部开始认真研究多智能体辩论机制,是因为在模拟项目X里碰到了一次很典型的失败。当时产品负责人提了个需求:把客服售后判责流程交给 Agent 来做。我给一个争议样本跑了一遍,单 Agent 很快给出"不符合换新条件"的结论&#xff…

2026/10/20 18:26:50

C++相场模型模拟金属凝固枝晶生长:方程离散、并行加速与可视化

枝晶这个字,只要搞过材料成型或凝固模拟的工科生都绕不开。焊池冷却、铸锭结晶、增材制造的熔池里,金属从液态到固态那短短一瞬间,微观尺度上会长出一种像蕨类植物一样的树枝状组织——这就是枝晶。很多团队做工艺优化时都想把这玩意算出来&a…

2026/10/20 18:26:50

AI重塑内容生产:从视频号切片到GPT-4商业化的底层逻辑

这两天圈子里最热闹的信息,基本被三件事包了:视频号被曝出在内测一个和内容变现有关的功能,外围讨论直接把它定性为重磅;TikTok那边则悄悄在开发AI头像;微软交出了接入GPT-4之后的首份成绩单。单拎任何一条都能写一篇分…

2026/10/20 18:21:49

Rust供应链攻击的五种隐蔽路径:从名字仿冒到build.rs投毒

先讲一件我最近处理的小事故。某公司一位开发者往内部工具链里加了一个解析JSON的小库,只因为把包名里的连字符写成了下划线,CI里的构建过程就开始向一个从未见过的域名发起外连。幸好该环境配了网络白名单,编译直接失败,才没让那…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑