)
李沐深度学习191集课程全解析模块拆解、学习路径吴恩达《面向开发者的提示词工程》吴恩达 MCP 教程Model Context Protocol一多模态大模型教程学习笔记 — ViT · CLIP · SAM · GLIP · Stable Diffusion一句话总结当 Dify 的拖拽节点满足不了精细控制时就下沉到LangGraph——一个独立、MIT 开源、底层基于 FastAPI 的 Agent 编排框架。本模块全课最重23 集约 6.1 小时带你搭起本地服务、理解 agent loop掌握工具的四种创建方式、AgentState 状态管理、Command 更新、短期 checkpointer 与长期 store 记忆并用 PostgreSQL 落地生产级持久化同时讲透 Qwen3 流式工具调用的经典坑。一、可视化导览本模块的 agent 循环、脚手架文件、工具四方式、状态与记忆体系、Qwen3 坑已整理成一张知识图谱模块二 · LangGraph 代码级 Agent · 可视化图谱https://mcnj5s80gkfh.doubaoapps.com/app/app_17fkrqyfemghttps://mcnj5s80gkfh.doubaoapps.com/app/app_17fkrqyfemghttps://mcnj5s80gkfh.doubaoapps.com/app/app_17fkrqyfemghttps://mcnj5s80gkfh.doubaoapps.com/app/app_17fkrqyfemghttps://mcnj5s80gkfh.doubaoapps.com/app/app_17fkrqyfemg1.1 为什么要从 LangChain 迁到 LangGraph早期大家用 LangChain 搭 Agent但它的核心是一条链节点之间顺序执行、很难表达循环和分支。真实的 Agent 偏偏是个循环模型要不断想—做—看—再想还经常需要在某个节点停下来等人审批、或者根据中间结果走不同分支。用链来硬套这种动态流程代码很快就变成一团绕来绕去的回调和状态判断。LangGraph 就是为了解决这个问题而生的它把整个 Agent 抽象成一张图节点是处理步骤边是流转方向状态在节点间显式传递。这样一来循环、分支、暂停、回放都成了图的天然能力而不是你要自己手写的逻辑。理解这个动机你就明白 LangGraph 里那些看似繁琐的概念到底为谁服务。为什么要显式定义状态因为图的每一步都要读上一步的结果。为什么要有 reducer因为多个节点可能同时往一个字段写东西得规定怎么合并。为什么要 checkpointer因为图可能跑一半就停在等人审批下次要能从断点接着跑。这些都不是凭空设计而是把动态 Agent 流程工程化的必然要求。1.2 workflow 与 agent 的本质区别再辨析这个区别是面试高频值得彻底讲透。workflow 的意思是先做什么、再做什么、什么条件下走哪条路这些都是程序员在代码里写死的。大模型在其中只是一个被调用的函数在固定的节点上干活它不能决定下一步去哪。这种方式可控、可预测适合流程明确的自动化任务。agent 则反过来下一步去哪由模型自己根据当前情况决定。你给它一批工具它读了用户问题和工具说明后自己判断要不要调工具、调哪个、传什么参数做完再看结果决定下一步。现实中绝大多数系统是两者的混合主干流程是写死的 workflow但在某个节点内部让 agent 去自由发挥。比如一个客服系统先路由判断问题类型写死再在查订单这个节点里让 agent 自主决定调哪个查询接口动态。学 LangGraph 时要有意识地把你的业务拆成哪些步骤该写死、哪些该交给模型这才是成熟的 Agent 设计思维。记忆钩子workflow 是人开车路线固定agent 是导航自己选路。LangGraph 既能画死路线也能在路口交给模型决定。二、视频清单23 集 · 约 6.1 小时全课最重集标题时长核心内容17最新版 LangGraph 的介绍12:09新版智能体移植到 LangGraph选它的三大理由18Agent 和 WorkFlow18:00workflow 固定路径 vs agent 自主动态agent loop19安装 LangGraph 本地服务一14:19Python3.11、virtualenv、安装 langgraph-cli20安装 LangGraph 本地服务二15:06langgraph new 脚手架、pyproject、pip install -e .21启动 LangGraph 服务器25:40langgraph devREST API / 文档 / Studio 三地址22调用 Agent 发布的 API 接口一16:52REST 调用、消息结构、thread_id23调用 Agent 发布的 API 接口二06:25流式调用与 stream_mode 处理24Tool 的定义一16:18工具四组件、tool 装饰器25Tool 的定义二21:19 工具入参 schema、描述优化本地无素材大纲补全26Tool 的定义三14:29 工具返回值与 return_direct本地无素材大纲补全27Qwen3 大模型工具调用解析器的错误21:09vLLM hermes 解析器漏 b 参数导致 Recursion limit28解决 Qwen3 流式输出的问题14:09 换 SGLang支持 qwen_ip两台机器部署本地无素材大纲补全29根据 Runnable 对象创建工具15:06Runnable.as_tool 把 LCEL 链包成工具30继承 BaseTool 创建工具19:32BaseTool 子类、Pydantic 字段 Annotated[type, Field()]31Configurable 静态配置一21:01不可变配置、thread_id/用户名、prompt 从 config 读32Configurable 静态配置二08:47 配置在节点/工具中的读取实践有视频无字幕大纲补全33AgentState 状态详解一31:09TypedDict、messages add_message reducer34AgentState 状态详解二14:38 自定义 reducer、状态字段设计本地无素材大纲补全35AgentState 状态详解三05:12Command(update) 更新状态、InjectedToolCallId36记忆存储的介绍11:57短期 checkpointer 与长期 store 的分工37开发环境下的短期记忆案例17:49InMemorySaver、thread_id 多轮记忆38PostgreSQL 实现 Agent 短期存储16:59PostgresSaver、with 语句、首次 setup 建表39PostgreSQL 实现长期存储10:10store 跨会话记忆新增 store/migrations 表三、LangGraph 定位与 agent loop新版 Dify 智能体已全面移植到 LangGraph。它是一个独立框架不绑定 Dify、MIT 开源、底层用 FastAPI 提供服务。选它有三个理由① 可靠性、可控性执行路径确定支持人工审批human-in-the-loop关键动作可暂停等人确认不再是黑盒② 低层级、可扩展暴露图、节点、边、状态等底层原语能手撕多智能体协作任意定制③ 一流的流式stream_mode 灵活流式输出体验好LangGraph 的平台版LangGraph Platform不开源、要收费配套可观测平台 LangSmith 在国内基本无法顺畅使用。本课程用的是开源框架 自建本地服务。workflow vs agentAnthropic 把 LLM 应用分成两类workflow是代码预先规定好的固定路径LLM 只在固定节点被调用agent则由模型自主、动态地决定下一步。智能体的三要素是模型 工具 提示词其运行核心是 agent loop模型读取消息并选择工具→ 运行时执行工具→ 把工具结果收回写进 messages → 模型判断还需调工具就继续循环信息足够就输出最终答案。课程强调一切都是图最简单的create_react_agent本质就是一个单节点图。四、本地环境搭建与服务启动准备Python 3.11用 virtualenv 建独立虚拟环境。安装 CLIpip install langgraph-cli。用模板创建项目langgraph new 路径 --template new-langgraph-project-python。安装本包pip install -e .可编辑模式在 PyCharm 中把src目录标记为源代码根目录否则导入报错。启动langgraph dev。生成文件作用graph.py定义 graph 对象是应用入口节点、边、工具都挂在这里langgraph.json配置文件声明图的位置本地服务默认端口2024pyproject.toml依赖与包元信息启动后会同时给出三个地址REST API端点、接口文档Swagger、LangGraph StudioWeb UI。课程模型用 AutoDL 上私有化部署的 Qwen3-8B通过 SSH 隧道把远程端口映射到本地。建议智能体 / MCP 场景优先用 Qwen3 满血版。五、调用 Agent 发布的 API服务启动后Agent 就是一个标准 HTTP 服务。调用时通过thread_id区分不同会话同一个 thread_id 的请求共享上下文依赖记忆配置见第九节不同 thread_id 互相隔离。流式调用时返回结构由stream_mode决定messages-tuple / messages 等处理第一个 chunk 时要用 isinstance(data, list) 做类型保护因为不同模式首帧结构不同。六、Tool 工具系统6.1 工具的四个组件name工具名模型据此选择要调用的工具description工具描述写得越清楚模型越能在正确时机正确调用args_schema用 Pydantic 模型定义入参框架据此自动生成 function schema 并做校验return_direct是否把工具结果直接返回给用户而不再过模型约 80% 场景不设 true6.2 四种创建方式tool 装饰器最常用直接注解一个普通 Python 函数从 docstring 与类型注解生成描述和 schema。Runnable.as_tool把一个已有的 LCEL Runnable / 链包装成工具。继承BaseTool最灵活定义 BaseTool 子类Pydantic 字段需写成Annotated[type, Field()]形式并附带描述。MCP工具第四种方式通过 MCP 协议接入外部标准工具。6.3 Qwen3 流式 function call 经典坑重点现象Agent 一调用工具就触发 Recursion limit递归上限工具被反复调用无法结束但非流式调用却正常。根因vLLM 启动需指定--tool-call-parser。官方对 Qwen 建议用 hermes而 Qwen 自有的解析器叫 qwen_ip对应 qwen2.5 时的解析器名。vLLM 不支持 qwen_ip、只支持 hermes导致 Qwen3 在流式输出 function call 时漏传某个参数如 b 参数工具入参不完整 → 模型反复重试 → 撞递归上限。解决改用SGLang部署它支持 qwen_ip 解析器。注意 vLLM 与 SGLang 装在同一台机器会产生 torch 依赖冲突必须分两台机器部署。6.4 工具描述怎么写模型才会用对很多人抱怨模型乱调工具、不按预期调用其实八成是工具描述写得太差。工具的 description 不是写给人看的注释而是写给模型的使用说明书。写得好要点有三个第一一句话说清这个工具是干什么的让模型在众多工具里能正确地认出来该用它第二说清什么场景下该用、什么场景下不该用边界越清楚越不会误用第三每个入参都写清含义和取值尤其是枚举类参数要告诉模型有哪些合法值。举个反例一个查天气的工具描述只写查天气那模型在用户问今天适合出门吗时可能也来调它其实用户要的是建议而非天气数据。正确写法应该是当用户明确询问某个城市的实时天气或预报时调用不需要用于给出出行建议本身。这种该用/不该用的边界往往比工具本身的实现更影响调用准确率。6.5 四种工具创建方式怎么选方式适用场景取舍tool 装饰器最常用包装一个普通函数简单快捷从类型注解自动生成 schemaRunnable.as_tool已有一条 LCEL 链想复用为工具避免重复实现把现成链变成可被调用的工具继承 BaseTool需要复杂初始化、自定义校验最灵活但样板代码多MCP 工具接入外部标准化能力跨进程、跨语言生态最广日常开发里九成场景用 tool 就够了。只有当你需要在工具初始化时持有连接、配置等资源或者需要更精细的字段描述时才考虑继承 BaseTool。MCP 工具则是当这个能力不是你自己实现、而是别人已经按 MCP 标准提供时才用。6.6 Recursion limit 之外的常见运行时错误除了课程重点讲的 Qwen3 流式坑自己跑 Agent 还常遇到几类错误。工具名冲突两个工具起了一样的名字模型调用时认错。解决办法是保证工具名全局唯一且语义清晰。入参校验失败模型传的参数格式不对Pydantic 直接抛错这时候要看 args_schema 是否定义清楚、错误信息有没有回传给模型让它自我修正。工具执行异常工具内部报错时要把错误信息作为 observation 写回 messages让模型知道这个工具失败了、换个法子而不是整个 Agent 崩掉。最后是死循环模型反复调同一个工具得不到满足这正是课程那个递归上限的本质——要在系统里设定最大步数兜底。排障习惯Agent 行为不对时第一件事是把每一步的 messages 完整打印出来看——模型到底看到了什么、为什么做这个决定。黑盒调试只会瞎猜。七、Configurable 静态配置configurable用来放不可变的静态配置例如 thread_id、当前用户名、模型名、环境开关等。它在一次运行中保持不变与会被节点不断更新的 state 相区分。常见做法调用时在 config 中传入configurable{thread_id: ..., user_id: ...}。系统提示词可以写成一个函数在运行时从 config 里读取用户名等信息动态拼装实现同一套图对不同用户呈现不同人设。八、AgentState 状态与 Command状态用 TypedDict 定义最核心的字段是消息列表from typing import Annotated, TypedDict, Sequence from langchain_core.messages import BaseMessage from langgraph.graph.message import add_message class AgentState(TypedDict): # add_message 是 reducer新消息自动追加/合并而非整体覆盖 messages: Annotated[Sequence[BaseMessage], add_message]reducer如 add_message决定多个节点返回同一字段时如何合并。消息字段用 add_message模型/工具产出的新消息会自动追加。Command(update{...})工具或节点可以返回一个 Command 对象来更新状态而不只是返回字符串。InjectedToolCallId工具需要回写对应那条 tool call 结果时用InjectedToolCallId依赖注入自动拿到 tool_call_id无需模型生成。Command 还支持delete用于删除状态中的某些字段。九、记忆短期 checkpointer 与长期 store类型短期记忆 checkpointer长期记忆 store粒度会话级靠客户端传 thread_id 区分跨会话记住用户长期偏好/事实前提必须先配置 checkpointer必须在短期记忆已配置的基础上才能启用开发态InMemorySaver内存重启即丢对应内存版 store生产态PostgresSaver持久化PostgreSQL store持久化PostgreSQL 落地要点用PostgresSaver.from_conn_string(...)配合with语句获取连接首次运行要调 setup() 创建 4 张 checkpoint_ 开头的表之后切勿再调 setup()避免重复建表/清数据。开启长期 store 后数据库会额外新增 store / migrations 相关表。Redis、SQLite 后端同理。发布限制使用自定义 checkpointer 时无法直接用langgraph dev发布成标准服务出路是自己用 FastAPI 包裹图对外提供接口或部署私有化的 LangSmith。9.1 状态设计的常见误区AgentState 看着就是个 TypedDict其实设计学问很大。第一个误区所有字段都不加 reducer导致后一个节点直接覆盖前一个节点的结果。记住messages 之所以用 add_message就是因为它需要追加语义如果你自己加了个字段想让多个节点累计写入就得自己写对应的 reducer否则每次都会被覆盖。第二个误区把不该放状态的东西塞进状态。状态是会被序列化、持久化的放进去的数据都会存进 checkpoint临时变量、数据库连接这类不该持久化的就别放状态里。第三个误区状态过于庞大。什么都往里塞会让每次更新都要搬运大量数据既慢又难调试。原则是只放跨节点需要共享、且需要被记住的信息。9.2 短期记忆和长期记忆到底差在哪这是另一个高频混淆点。短期记忆checkpointer记的是这一次对话进行到哪了——用户和你一来一回说了什么、Agent 刚才调了哪个工具、中间结果是什么。它靠 thread_id 区分会话换一个 thread_id 就是全新对话。它解决的是多轮对话不遗忘上文。长期记忆store记的是这个用户是谁、他有什么长期偏好——比如他叫什么、他喜欢什么风格、他提过自己是个学生。这些信息不绑定单次会话跨很多次对话都要能用。举个例子用户第一次说我是学生回答请简洁。短期记忆会记住这一次对话里他说过这话但下次开新会话就忘了长期记忆会把他是学生、喜欢简洁存下来以后每次新会话都能自动用上。工程上长期记忆往往是 Agent 在对话中自动提炼出关键事实写进 store下次启动时再从 store 读出来拼进系统提示词。这就是为什么课程强调长期记忆必须建立在短期记忆已经配置好的基础上——它们是两层叠加的关系不是二选一。9.3 生产化时的注意事项从开发态的 InMemorySaver 切到 PostgresSaver不只是换个后端名字这么简单。内存版一重启就丢数据只适合本地调试Postgres 版才能真正持久化。但要记住课程那个关键提醒首次运行必须调一次 setup() 把表建好之后绝不能再调否则可能重复建表甚至清掉已有数据。另外生产环境要考虑连接池配置不要每个请求都新建连接、表的备份checkpoint 里存着所有会话状态丢了就全没了、以及数据隔离不同用户的 thread 要做好隔离别串台。课程最后提到一个现实限制用了自定义 checkpointer 后就不能简单地靠 langgraph dev 直接发布往往需要自己用 FastAPI 把图包一层对外提供服务——这是从 demo 走向产品必须跨过的一步。 补全提示第 25、26、32、34 集本地无素材其工具入参 schema、return_direct、配置读取、自定义 reducer 等细节已分别融入第六、七、八节的补充说明以你安装版本的官方文档为准。十、核心概念速查表概念一句话理解agent loop选工具 → 执行 → 收结果 → 判断终止的循环create_react_agent预置好的 ReAct 单节点图快速建 Agent工具四组件name / description / args_schema / return_direct工具四方式tool、Runnable.as_tool、继承 BaseTool、MCPconfigurable不可变静态配置thread_id、用户等AgentStateTypedDict 状态messages 用 add_message reducerCommand节点/工具更新状态的对象支持 update / deleteInjectedToolCallId依赖注入自动获取 tool_call_idcheckpointer短期会话记忆按 thread_id 存取store跨会话长期记忆依赖短期记忆先配置stream_mode决定流式返回结构首帧需类型保护十一、学习打卡能说清 workflow 与 agent 的区别、agent loop 的每一步能用 langgraph new 脚手架建项目并 langgraph dev 启动拿到三个地址能通过 REST API 调用自己发布的 Agent并用 thread_id 区分会话能用 tool 装饰器定义一个含 Pydantic 入参的工具并被模型正确调用能用继承 BaseTool 的方式定义工具理解 Annotated[type, Field()]理解 return_direct 的作用与默认取值场景能复现/解释 Qwen3 流式漏参数导致 Recursion limit 的原因与 SGLang 解法能用 configurable 传入 thread_id/用户名并让提示词函数读取配置能定义 AgentState理解 add_message reducer 与 Command(update)能用 InMemorySaver 实现多轮短期记忆能用 PostgresSaver 落地短期记忆且只在首次 setup()能用 PostgreSQL store 实现跨会话长期记忆学习建议本模块是全课最硬的部分建议视频 : 敲代码 ≈ 4 : 6每一节都跟着把图跑起来。学习顺序上先把 17-23 的脚手架与 API 跑通建立信心再攻 24-35 的工具与状态这是 Agent 的灵魂最后做 36-39 的记忆持久化Qwen3 坑27-28建议在自己模型上实际复现一次印象最深。十二、本模块常见问题与实战补充12.1 环境搭建踩坑汇总第一次搭 LangGraph 环境最常见的问题集中在三处。第一Python 版本不够langgraph-cli 对版本有要求老版本解释器会装不上或跑不起来务必用 3.11 以上。第二可编辑安装后导入报错pip install -e . 之后如果在 IDE 里跑要把 src 目录标记为源代码根目录否则解释器找不到你写的 graph 包。第三远程模型连不上课程用 AutoDL 上的 Qwen3要通过 SSH 隧道把远程端口映射到本地否则本地服务访问不到模型而且容器或本地访问模型地址时要填对主机别犯把 localhost 当成模型服务器的错。建议把这三条坑在第一次跑通时就记录下来后面换环境还会再遇到。12.2 调试 LangGraph 的几个利器代码级 Agent 最怕不知道它内部在干嘛。好在 LangGraph 提供了几种调试手段。一是 Studio Web UI启动 langgraph dev 后那个网页界面能可视化地看到图的结构和每一步状态强烈建议用起来。二是把状态打印出来在关键节点里 print 一下当前 state一眼看清数据怎么流的。三是利用流式用 stream_mode 边跑边看中间产物而不是等最后结果。四是善用时间旅行有了 checkpointer你可以回到某一步重新跑这对调试为什么这一步走错了极其有用。养成边跑边看状态的习惯比读十遍代码都管用。12.3 面试高频请讲一个你设计的 Agent面试时这道题几乎必问答题框架可以参考。先讲场景你要解决什么业务问题。再讲架构主干是 workflow 还是 agent哪些节点写死、哪些交给模型。然后讲工具给了它哪几个工具工具描述怎么写的。接着讲状态你在 state 里存了哪些字段有没有用 reducer。再讲记忆短期怎么用 thread_id 隔离会话长期记了哪些用户偏好。最后讲踩坑比如遇到过模型乱调工具、递归上限、流式格式不对你是怎么定位和解决的。把课程里这些点串成一个你亲手做过的小项目这个回答就非常完整了。十三、延伸练习学完本模块建议做三个递进练习。练习一给你的 Agent 加一个会查当前日期时间的工具让它回答今天星期几这类需要实时信息的问题体会工具如何补上模型知识截止的短板。练习二用 configurable 传入不同用户名让 Agent 在对话开头自动称呼对方体会静态配置如何区分多用户。练习三用 PostgresSaver 落地后重启服务确认同一 thread_id 的对话还能接着上下文继续体会持久化记忆的实际效果。我能用一句话说清 LangGraph 相比旧式链的核心优势我能在白纸上画出一个 ReAct agent loop 的完整循环我能区分短期记忆与长期记忆并举出各自该记什么我知道生产化时为什么要自己包 FastAPI、要注意哪些数据问题我亲手复现过至少一次工具调用并把 messages 打印出来看过本模块小结你已经从会拖节点进阶到能写图。工具、状态、记忆这三件事是代码级 Agent 的灵魂把它们吃透你就能驾驭那些平台满足不了的复杂场景。下一站模块三回到 RAG看怎么让 Agent 用好私有知识。十四、深入理解 Agent 的工程本质14.1 为什么说一切都是图课程反复强调一切都是图这句话值得展开。在 LangGraph 眼里不管你的应用多复杂最终都能画成一张由节点和边组成的图。最简单的 ReAct Agent 看起来是一个循环但它其实是一张只有两个节点的图一个节点调模型一个节点执行工具边在模型决定继续调工具和模型决定输出答案之间分叉。你以后要做的多 Agent 协作、人在审批、子流程嵌套本质上都是在这张图上加节点、加边、加条件。这种图式思维的好处是可视化和可调试。你可以把整张图画出来一眼看清数据怎么流、在哪分叉、可能在哪卡住。相比之下纯代码写的 Agent 往往是一堆函数互相调用出了问题很难一眼看出执行路径。学 LangGraph 时建议每写一个 Agent 都先在纸上或白板上把图草画一遍——节点有哪些、边怎么连、状态怎么传——画清楚了再写代码事半功倍。14.2 人在审批human-in-the-loop为什么重要这是 LangGraph 相比普通 Agent 框架的一大杀手锏。很多真实场景里Agent 不能一路跑到底关键动作必须先停下来等人确认。比如它要帮你发一封邮件、要执行一个删除操作、要调用一个花钱的接口——这些动作前最好暂停把它打算做什么展示给你你说确认它才继续。在 LangGraph 里这天然支持图可以在某个节点暂停把状态存进 checkpointer等人审批后再从断点恢复。这就是为什么前面说可靠性、可控性是选它的核心理由——没有这种暂停与恢复能力Agent 只能拿来玩玩不敢用在真业务上。14.3 流式输出为什么是刚需用户体验上让用户干等十几秒看 Agent 全部想完再出结果是不可接受的。流式输出让模型一边生成一边把字吐出来体验好得多。但 Agent 的流式比单轮对话复杂它中间还要调工具、看结果这些过程也要能流式地展示给用户——它现在正在查天气它正在计算。LangGraph 的 stream_mode 就是为这件事设计的不同的模式让你既能拿到最终文本流也能拿到中间每个节点的执行事件。课程特别提醒首帧要做类型保护就是因为不同模式返回的数据结构不一样不判一下直接解就会报错。实操要点做流式时不要只关心最终答案流出来也要把正在调用工具这种中间状态透传给前端用户才会觉得系统是活的、有掌控感。十五、从课程到生产还差什么课程带你跑通了一个可调用的 Agent但离真正上线还有几步。一是错误处理工具失败、模型超时、限流都要有兜底不能整个崩掉。二是可观测线上 Agent 行为不对时你得能回溯当时它经历了哪些节点、状态长什么样这就需要日志和轨迹记录。三是成本控制Agent 会反复调模型和工具token 消耗是普通应用的好几倍要监控、要设上限。四是安全工具能调外部接口就得防注入、防越权别让用户诱导 Agent 干坏事。这些课程没展开但都是从 demo 到产品绕不开的功课。我能在纸上画出 ReAct 的图并标出分叉条件我理解了人在审批为什么是 Agent 上生产的关键能力我知道流式不仅要吐答案还要展示中间工具调用我能列出从 demo 到生产还差的至少四项工程工作记住课程让你会写生产化要你写稳。中间隔着的就是错误处理、可观测、成本与安全这四道工序。十六、把课程概念串成一个完整例子为了帮你把零散概念串起来我们构造一个完整例子做一个个人日程助手。用户用自然语言说帮我记一下明天下午三点开会。整个流程在 LangGraph 里是这样跑的。开始节点接收用户消息写进 state 的 messages。模型节点读了消息和可用工具后决定要调用新增日程这个工具——它生成工具调用带上参数明天下午三点、开会。图流转到工具节点执行工具把新增成功的结果写回 messages。模型再读一遍信息够了不需要再调工具于是生成一句确认话输出给用户。这就是一个最小 agent loop。现在叠加本模块学的各个能力。多轮对话用户接着问那我明天一共几个会因为用了 thread_id短期记忆里还存着上一条日程模型据此回答。多用户configurable 里传入了 user_id每个用户只能看自己的日程工具内部按 user_id 隔离数据。长期记忆用户之前说过我喜欢把日程时间写得具体到分钟这个偏好存在 store 里每次新会话自动带上。可靠性新增日程这种关键动作前图暂停一下问用户确认新增吗等人点头才执行。你看课程里每个概念都在这个例子里各司其职——这就是把知识学活的样子。16.1 学习这个模块的正确节奏23 集、6.1 小时是全课最重的部分一口气看完只会更晕。建议分成三段。第一段17-23 集先把脚手架和 API 跑通目标是能启动、能调通不追求理解每个细节先建立它是个 HTTP 服务的体感。第二段24-35 集是核心中的核心工具和状态这部分一定要逐行跟着敲每个概念都用 print 看一眼实际数据。第三段36-39 集做记忆持久化把短期和长期都跑通。Qwen3 那个坑27-28如果你的模型环境复现不出来也没关系理解它的原理——流式 function call 的解析器问题——就够了不必强求一模一样的现象。节奏建议这个模块宁可慢不要飘。看懂了不等于跑通了跑通了不等于理解了。每一节都问自己我能不看笔记复现吗十七、常见误区与澄清误区一Agent 越聪明越好工具挂得越多越好。错。工具太多模型在选择时反而容易迷惑挂工具要精不要多。误区二状态里塞得越全越保险。错冗余状态只会拖慢、搞乱。误区三短期记忆和长期记忆是一回事。前面已辨析一个绑会话、一个跨会话。误区四用了 LangGraph 就不用写错误处理了。错框架管流程不管业务健壮性。误区五create_react_agent 是万能的。它只是个单节点图复杂流程还得自己画。我能复用日程助手这个例子讲清 agent loop 全流程我按三段节奏推进没有一口气硬啃我已跳出工具越多越好等常见误区模块二的主体补充到此。它是全课最硬的部分慢一点、动手多一点都是值得的。十八、代码视角的关键细节补充18.1 add_message reducer 到底做了什么很多人知道 messages 要用 add_message但说不清它具体怎么合并。普通字段如果你返回一个新值它会直接覆盖旧值。而 add_message 是一个 reducer它的合并逻辑是把你这次返回的新消息追加到已有消息列表后面。更细一点它还会处理同一条消息的更新——比如工具结果要回写某条已有的工具调用消息它能识别出来是更新而不是新增。理解这点你就明白为什么模型每生成一条、工具每返回一条messages 列表就自动变长而不需要你手动 append。这就是 reducer 的价值把怎么合并这件事从你的业务代码里抽出来框架替你管。18.2 Command 为什么比直接返回字符串强节点最朴素的写法是返回一个字符串框架默认把它当成对 messages 的更新。但有时候你要做的事更复杂比如工具执行完你不仅要写回结果还想顺便更新状态里另一个字段甚至想直接改变图接下来的走向。这时候返回字符串就不够了得返回一个 Command。Command 可以同时做几件事用 update 指定要更新哪些状态字段用 goto 指定下一步去哪个节点用 delete 删除某些字段。这种一次返回里既改状态又控流转的能力让复杂节点写起来干净利落不用把逻辑拆散到好几个节点里。18.3 InjectedToolCallId 解决了什么痛点工具被模型调用时框架需要知道这个返回值是对应哪一次工具调用才能把结果正确贴回 messages。按理说这个对应 id 应该由模型生成但让模型每次都吐出正确的 tool_call_id 既麻烦又容易错。InjectedToolCallId 的思路是依赖注入你在工具函数参数里声明一下需要这个 id框架在调用工具时自动把当前这次调用的 id 塞进来你直接拿来用就行模型完全不用管。这种框架自动注入运行时上下文的设计是 LangGraph 让工具写起来更省心的典型手法。18.4 configurable 和 state 怎么分工新手最容易把这两个搞混。记住一句话configurable 是运行前就定好、运行中不变的静态配置比如这个会话属于哪个用户、用哪个模型state 是运行中不断变化的动态数据比如对话进行到哪、中间结果是什么。一个是常量一个是变量。为什么要分开因为静态配置不应该被节点误改把它单独放在 config 里既安全又方便在调试时一眼看清。系统提示词写成函数去读 config 里的用户名就是利用了这种运行时动态读静态配置的模式。一句话分工configurable 放这次是谁、用什么state 放聊到哪了、有什么中间结果。十九、模块二收尾走到这里你已经具备了代码级 Agent 的核心能力能搭服务、能调 API、能写工具、能管状态、能落记忆。回头看模块一让你会用平台模块二让你能写底层。这两者不是替代关系而是视野的两层——你既知道平台里那个节点在代码里对应什么也知道什么时候必须离开平台自己写。我能解释 add_message reducer 与普通字段覆盖的区别我知道 Command 相比返回字符串多了哪些能力我能说清 configurable 与 state 的分工我理解了 InjectedToolCallId 依赖注入省去了什么模块二第17-39集到此结束。这是全课最硬的一战撑过来你已经超过大多数只会调平台的人。下一站模块三RAG。十三、补遗状态管理与排错的实战经验写代码级智能体真正让人头大的往往不是把图跑通而是状态为什么没按预期流动。这里补几条实战中特别有用的经验。第一调试时一定要把每一步的状态打印出来看。智能体的执行是一条接一条节点的你光看最终输出根本不知道哪一步偏了把每个节点进出的状态分别打出来一眼就能看出是在哪一步丢了字段、改了值。这个习惯能帮你少走大量弯路。第二关于记忆。很多新手一上来就想存所有对话结果状态越堆越乱、还超出上下文长度。正确做法是想清楚哪些是每次都要带的短期记忆哪些是该提炼后长期留存的该忘的就主动忘别让历史对话把后面的判断带偏。第三工具调用失败要有兜底。真实世界里工具随时可能报错你的图不能一遇到失败就整个崩掉而要设计好失败后是重试、换个工具、还是直接告诉用户。这种鲁棒性才是玩具和能用产品的区别。面试时若被问到你做过的智能体能主动讲出我怎么调试状态、怎么处理工具失败、怎么管理记忆就说明你不是照抄教程而是真正踩过坑、想过设计。再补一句心态上的建议刚接触代码级智能体时别想着一口气把所有高级技巧都用上。先用最朴素的图把一个能跑的最小版本做出来让它至少能调一次工具、记住一点上下文。然后在这个能跑的基础上一次只加一个改进每次都观察行为有没有变好。这种小步快跑的方式比一上来就堆复杂流程要稳得多也更符合真实工程的节奏。