LangGraph 教程:复杂 Agent 工作流编排与人工审批

约 70 分钟L2L3T3

课程来源

[来源] FDE 实战课(fde-teach)

[收录说明] 本课未随文署名,按课程线署名。

本课目录(16 节)

🎯 本节目标

学完这一节,你能:

  • 用地铁网类比,跟家里人讲清 LangGraph 为什么需要"图"
  • 从最简单的两节点图(A→B) 起步,亲手逐步叠加条件分支、人工审批,跑通一个真 Agent
  • 知道何时用 LangChain 就够、何时必须升级到 LangGraph

🍎 生活化类比:流水线 vs 地铁网

LangChain = 工厂流水线(一条直线,只能往前走)。LangGraph = 城市地铁网(能绕、能停、能回头)。

流水线一路向前,不能回头。但真实业务里:Agent 第一次没读懂想重试(地铁能坐回头)、置信度有高有低要分岔(地铁有换乘)、金额太大要等人审批(地铁每站都能停)。这些直线流水线全做不到——所以我们换一种能绕、能停、能回头的结构:图(Graph)。LangGraph 就是帮我们画这种"地铁网"的工具。

📖 概念讲解

1. 三要素:用"地铁系统"记一辈子

地铁系统LangGraph大白话
乘客(站间流动的人)State(状态)节点之间传递的数据。乘客上车拿行李,下车可能又多一个包——数据会累积、会变
地铁站(每站做一件事)Node(节点)一个处理步骤。拿到乘客,做自己那点活儿,再送走
轨道(连接两站)Edge(边)节点间的流向。有的直通,有的有条件(高峰开、平峰停)

一句话记住:乘客(State)坐着轨道(Edge),从一个站(Node)流到下一个站。

2. 何时从 LangChain 升级到 LangGraph

业务长啥样用什么
单轮问答、A→B→C 一条直线LangChain 够
要循环(没做对就重试)/ 条件岔路LangGraph
要中途停下等人审批LangGraph(杀手锏)
多 Agent 协作LangGraph

四个信号里只要出现一个,直线链就开始别扭。

3. HITL(人工审批):取快递要本人签字

HITL 全称 Human-In-The-Loop(人在环里),听着玄,其实就是有些步骤必须停下来等真人点个头。就像取贵重快递,快递员非要你本人签字——快递不会自动签收,必须在最后一公里停下来等你。Agent 也一样:大部分自动处理,金额超 10 万的必须有个真人点"批准"。这个"停下来等人签字"的站,就是 HITL。

🛠️ 动手实操:三步搭出一个真 Agent

不学那种"一上来甩完整 Agent"的写法——零基础跟不上。我们分三小步,每步都是能跑的最小例子,逐步往上加东西。

⚠️ 下面是示意代码,讲清图的逻辑。LangGraph API 在迭代,跑之前看本节末尾官方文档,以最新写法为准。

Step 1:最朴素的两节点图(感受"站→轨道→站")

先画一条最简单的地铁线:A 站 → B 站,没分支、没回头。

# 伪代码示意,跑之前看官方最新文档
from langgraph.graph import StateGraph, END
from typing import TypedDict

# 1. "乘客长啥样"——一个带字段的字典
class State(TypedDict):
    name: str       # 乘客带个名字
    greeting: str   # A 站会给它加一句问候

# 2. 两个"地铁站",每个做一件小事
def station_a(state):                          # A 站:加问候
    return {"greeting": f"你好,{state['name']}!"}
def station_b(state):                          # B 站:打印后送下车
    print(f"B 站收到:{state['greeting']}"); return {}

# 3. 画地铁线:建图→加两站→连轨道→指定终点
graph = StateGraph(State)
graph.add_node("a", station_a)     # 加 A 站
graph.add_node("b", station_b)     # 加 B 站
graph.set_entry_point("a")         # 列车从 A 站发车(新版官方推荐写法 graph.add_edge(START, "a"),两者等价)
graph.add_edge("a", "b")           # A→B 直通轨道
graph.add_edge("b", END)           # B 站之后是终点(下车)
app = graph.compile()
app.invoke({"name": "张奶奶"})      # 打印:B 站收到:你好,张奶奶!

跑通了?你已经掌握 LangGraph 的骨架。剩下的花活,都是在这条线上加站、加岔路、加签字站。

Step 2:加一条"条件轨道"(岔路口)

业务常有岔路:置信度高直接放行,低转人工。这就是有条件的轨道——高峰开、平峰停。

# 接 Step 1 加东西。先给 State 补一个字段(乘客多带一样东西:置信度)
class State(TypedDict):
    name: str
    greeting: str
    confidence: float   # 提取站打出的"把握分",0 到 1

def route_by_confidence(state):       # "岔路口调度员":只看数据定下一站
    return "high" if state["confidence"] > 0.8 else "low"

graph.add_node("auto_process", ...)      # 自动处理站(略)
graph.add_node("human_review", ...)      # 人工复审站(略)

graph.add_conditional_edges(             # 条件轨道:列车从 b 站开出后,看调度员指向去哪
    "b", route_by_confidence,            # 注意:挂在"上一站"(b)后面
    {"high": "auto_process", "low": "human_review"}
)

条件轨道的精髓:不连死的,而是"看情况"决定下一站。这正是流水线做不到、而图能做到的事。

route_by_confidence 这种路由函数只负责"指路",不注册成节点,也别给它 add_node——节点函数必须返回"State 更新字典"(如 {"greeting": ...}),路由函数返回的是"下一站名字"(如 "high")。两者混用同一个函数,运行时会直接报 InvalidUpdateError。记住:车站干活的,岔路口只指路的。

Step 3:加一个"签字站"(人工审批 HITL)

加上本节的杀手锏:有一个站,列车到了**必须停下来,等真人点"批准"**才继续——就像取贵重快递要本人签字。

# 接 Step 2 加东西
def human_approve(state):           # "签字站":停下来等人输入 y/n
    print(f"待审批:{state['extracted']}")
    approved = input("批准吗?(y/n): ") == "y"   # 生产用 interrupt,别用 input
    return {"approved": approved}

def make_quote(state):              # "开单站":生成询价单(示意)
    ...                             # 真实实现:调 mock ERP 写入询价单(略)
    return {}

graph.add_node("human", human_approve)
graph.add_node("generate", make_quote)   # 先把"开单站"建出来
graph.add_edge("human", "generate")      # 签完字 → 生成询价单
graph.add_edge("generate", END)          # 生成完 → 终点
app = graph.compile()
# 跑的时候,列车开到 human 站会"卡住",等你输入 y/n 才继续

三步拼起来,就是蚂蚁 FDE 访谈里**"询价邮件自动处理 Agent"** 的最小骨架:抽取信息 → 判断置信度 → (低的话)等人签字 → 生成询价单。再复杂的 Agent,本质都是这条地铁线加东西——多几个站、几条岔路、几个签字站而已。别被吓到。

⚠️ 常见错误

问题大白话原因怎么解决
图跑出死循环有轨道绕回去了,但没终止条件确保所有岔路最终通向 END
State 数据没更新节点返回的 key 跟 State 定义对不上返回的 key 必须和 State 字段名一模一样
HITL 卡死不动用了 input(),生产会真死等生产用 LangGraph 的 interrupt,别用 input()
报错找不到节点add_edge 写了站名,add_node 没加站名拼错或忘加,把所有 add_node 检查一遍

✅ 自测题

<summary>1. 用地铁系统类比,说清三要素各是啥?State 为什么重要?</summary>

State = 乘客(站间流动的数据),Node = 地铁站(每站做一件事),Edge = 轨道(连站,有的有条件)。State 重要,因为它是唯一在站之间传递的东西——每个站读它、改它、再传走。没有 State,站和站就断了。

</details> <summary>2. 什么情况必须从 LangChain 升级到 LangGraph?举一个生活化例子。</summary>

四个信号里任何一个:① 循环重试 ② 条件岔路 ③ 停下等人审批(HITL)④ 多 Agent 协作。比如自动处理邮件,大部分自动发,金额超 10 万必须等人签字——这就必须上 LangGraph,因为直线流水线停不下来等人。

</details>

🚀 实战小项目:给询价 Agent 再加一个签字站

在 Step 3 基础上再加一个审批站:除了"金额超 10 万要签字",再加"客户是新客户也要签字"。提示:再 add_node 一个签字站,再用一条 add_conditional_edges 决定去不去。

验收:① 新老客户 × 金额大小 四种组合,都能走到对的终点;② 两个签字站各签一次,流程能跑完;③ 故意输 n 拒绝,流程能优雅结束,不卡死。

📚 总结 & 下一节

  1. LangChain 是流水线(直线),LangGraph 是地铁网(能绕、能停、能回头)——业务一出现循环、岔路、等人,就该升级
  2. 三要素用地铁记一辈子:State=乘客、Node=地铁站、Edge=轨道(有的有条件)
  3. HITL 就是取快递要本人签字——LangGraph 的杀手锏,生产 Agent 几乎都少不了

下一节:MCP 协议——让 Agent 标准化地连接外部工具,不用每个工具都从头写胶水代码。

🏋️ 配套自练

  • LangGraph 官方教程 — 跟着官方 intro 跑一个最简图(免费)
  • LangGraph Studio — 可视化调试图工作流,看节点怎么流动(免费、macOS)
  • 把本节的"询价邮件 Agent"画在纸上,标出哪些是 Node、哪些是 Edge

🔗 延伸阅读


← 上一节:LangChain | 下一节:MCP 协议 →

随堂测

逐题作答,即时反馈

0 / 3 已作答
  1. 1. 在地铁系统类比中,LangGraph 的三要素“乘客、地铁站、轨道”分别对应什么?

  2. 2. 根据讲义,出现哪种业务需求时就该从 LangChain 升级到 LangGraph?

  3. 3. 在 Step 3 添加人工审批签字站时,讲义给出的生产环境正确做法是?