
关键词:LangGraph, LangChain, 状态机, SOP, Human-in-the-loop
大家好,我是飞哥!👋
前两周我们学习了 LlamaIndex (最强大脑) 和 AutoGen (最强团队)。
按理说,有了这两个神器,什么应用做不出来?
但当你真正把 AutoGen 做的客服系统上线后,老板可能会找你谈话:
老板 :"飞哥,昨天有个用户说要退款,你的 AI 也没查订单状态,直接就答应了?而且那个订单金额是 10 万块!AI 居然没问我直接批了?"
你:"呃...那个 Agent 比较热心肠..."
痛点 :🤯
AutoGen 这种"多 Agent 自由讨论"的模式,适合创意型任务 (写研报、写代码)。
但对于业务型任务 (退款、审批、报销),企业需要的是严谨的 SOP (标准作业程序),绝不允许 AI 自由发挥。
💡 解决方案 :
我们需要把 AI 从"自由讨论室"拉到"工厂流水线"上。
这就是 LangGraph 的使命 ------ 构建有状态、可控的图应用。
1. 为什么你需要 LangGraph?(Why) 🤔
生动类比 🍎:
- AutoGen = 头脑风暴会议 🗣️。大家七嘴八舌,最后也能讨论出结果,但过程不可控,适合创意。
- LangGraph = 肯德基后厨流水线 🍔。
- State (托盘):一个托盘在流水线上流动,上面放着汉堡胚(用户需求)。
- Node (工位) :
- 工位 1 (意图识别) 🤖:服务员看单子。是做"香辣鸡腿堡"还是"牛肉堡"?(决定去哪个分支)
- 工位 2 (规则检查) ⚖️:称重员检查。肉饼有没有少于 100g?(金额 > 1000?)
- 工位 3 (人工审批) 🛑:质检员发现肉饼颜色不对,按停传送带!喊店长(人类)过来确认。店长说"行",才能继续包装;店长说"不行",直接扔掉。(Human-in-the-loop)
- 工位 4 (执行) 💸:打包员装袋,递给顾客。
核心价值 💎:
LangGraph 让你能用代码画出这张"流程图",并且强制 AI 沿着线走。最重要的是,它支持 Human-in-the-loop (人在回路) ------ 关键时刻,必须人来拍板,AI 才能继续。
2. 核心概念提炼 (Skeleton) 🦴
LangGraph 的世界里主要有这几个概念:
- State (状态) 📦:
- 流水线上的"包裹"。所有节点都在读写这个包裹里的数据(比如
order_id,status)。
- 流水线上的"包裹"。所有节点都在读写这个包裹里的数据(比如
- Node (节点) 👷:
- 流水线上的"工位"。可以是 LLM,可以是 Python 函数,也可以是 API 调用。
- Edge (边) 🔗:
- 连接工位的"传送带"。
- Conditional Edge (条件边) 🔀:智能分拣机。如果
amount > 1000,传送到"经理室";否则传送到"财务室"。
- START & END 🏁:
- 图的入口 和出口。
3. 实战项目:智能客服"工单自动处理流" 🛠️
我们要实现一个严谨的退款系统,绝不让 AI 乱批款。
📦 安装依赖
bash
pip install langgraph langchain-openai langchain-core
langgraph:本次课程的主角。它负责画图 (定义状态、节点、边),并管理整个流水线的运行(包括暂停、恢复)。langchain-core:LangChain 家族的基础组件。它定义了BaseMessage(消息格式)、PromptTemplate(提示词模版)等通用标准,相当于乐高的"基础积木块"。langchain-openai:连接 OpenAI 模型的桥梁。如果我们的节点需要调用 GPT-4 进行意图识别或生成回复,就得靠它。
📊 流程设计图
是退款
闲聊
金额 < 1000
金额 >= 1000
经理同意
经理拒绝
用户输入
意图识别节点
退款规则检查节点
直接回复
自动批准
经理审批节点
拒绝退款
💻 核心代码拆解
第一步:定义状态 (包裹里有什么?) 📦
python
from typing import TypedDict, Annotated, List, Optional
import operator
from langchain_core.messages import BaseMessage
class AgentState(TypedDict):
messages: Annotated[List[BaseMessage], operator.add] # 聊天记录 (使用 operator.add 追加新消息)
order_id: Optional[str] # 订单号
amount: Optional[float] # 订单金额
# 状态机核心字段:
# pending: 初始状态
# waiting_approval: 等待经理审批 (金额 > 1000)
# approved: 已批准 (金额 < 1000 或 经理同意)
# rejected: 已拒绝 (经理拒绝)
refund_status: str
第二步:定义节点 (工人在干什么?) 👷
python
def node_process_refund(state: AgentState):
"""
退款规则检查节点
输入: state['order_id']
输出: 更新 state['amount'] 和 state['refund_status']
"""
# 模拟查询数据库
def get_order_amount(order_id):
# 假设所有订单都是 1200 元 (大于 1000,触发审批)
return 1200.0
amount = get_order_amount(state['order_id'])
# 规则引擎逻辑
if amount > 1000:
print("⚠️ 金额 > 1000, 需要经理审批")
# 返回的部分数据会自动 merge 到 State 中
return {"amount": amount, "refund_status": "waiting_approval"}
else:
print("✅ 金额 < 1000, 自动批准")
return {"amount": amount, "refund_status": "approved"}
def node_human_approval(state: AgentState):
"""
人工审批节点
注意:这里其实是个空函数,因为我们会在进入这个节点前使用 interrupt_before 暂停。
当人工 update_state 后,会直接跳过这个节点的执行,进入下一个节点。
"""
pass
第三步:构建图与人工介入 (关键!) 🛑
这里最神奇的是 interrupt_before。它告诉 LangGraph:在进入 human_approval 节点之前,把程序暂停,把状态存到数据库里(MemorySaver),然后等待指令!
注意:这里的"暂停"不是进程退出,而是图的执行挂起。如果你用的是 Web 服务,这意味着 API 请求结束,等待下一个 API 请求来唤醒它。
python
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
# 构建图
workflow = StateGraph(AgentState)
# 1. 添加节点 (Add Nodes)
# 注册我们的"工人"和"工位"
workflow.add_node("process", node_process_refund)
workflow.add_node("human_approval", node_human_approval)
# 2. 设置入口 (Set Entry Point)
# 告诉图从哪里开始跑 (这里假设直接进入 process)
workflow.set_entry_point("process")
# 3. 添加条件边 (Conditional Edges)
# 相当于流水线上的"分拣机"
def route_process(state):
# 如果状态是 waiting_approval,就传送到 human_approval 工位
if state['refund_status'] == "waiting_approval":
return "human_approval"
# 否则直接结束 (END)
return END
# 将 process 节点连接到分拣逻辑 (route_process)
# 意味着:process 跑完后,下一步去哪儿?问问 route_process!
workflow.add_conditional_edges("process", route_process)
# 4. 添加普通边 (Edges)
# 经理审批完,直接结束
workflow.add_edge("human_approval", END)
# 5. 编译图 (Compile)
# memory: 用于保存图的运行状态 (Checkpointer)
memory = MemorySaver()
app = workflow.compile(
checkpointer=memory,
# interrupt_before: 在进入这些节点之前,强制暂停!
# 这是实现 Human-in-the-loop 的关键
interrupt_before=["human_approval"]
)
第四步:运行与交互 (Human-in-the-loop) 🤝
当程序暂停时,你可以去喝杯咖啡。回来后,经理输入 yes,程序会从断点处恢复执行。
python
# 1. 配置 (Config)
# thread_id: 每个用户的对话都有唯一的 ID,类似微信号
config = {"configurable": {"thread_id": "thread-1"}}
# 2. 初始运行 (Run)
# 我们只给一个空的输入,因为这里假设从 process 节点开始,它会自己去查 order_id
print("🚀 系统启动...")
for event in app.stream({"order_id": "BIG888"}, config=config):
# stream 会逐步输出每个节点的执行结果
pass
# 3. 检查断点 (Check Interrupt)
# 运行结束后,检查一下图的状态
state = app.get_state(config)
if state.next and "human_approval" in state.next:
# 发现下个节点是 human_approval,说明被暂停了!
print(f"🛑 触发风控!等待经理审批... (金额: {state.values['amount']})")
user_input = input("经理是否批准?(yes/no): ")
if user_input == "yes":
# 4. 人工干预 (Human Intervention)
# 经理说 OK,我们直接修改图里的状态
print("✅ 经理已批准,更新状态为 approved")
app.update_state(config, {"refund_status": "approved"})
else:
print("❌ 经理已拒绝,更新状态为 rejected")
app.update_state(config, {"refund_status": "rejected"})
# 5. 恢复执行 (Resume)
# 再次调用 stream,这次传入 None,LangGraph 会自动从断点处继续跑
print("▶️ 继续执行后续流程...")
for event in app.stream(None, config=config):
pass
🎯 运行结果展示
当你运行这段代码时,你会看到类似下面的交互过程:
text
🚀 系统启动...
⚠️ 金额 > 1000, 需要经理审批
🛑 触发风控!等待经理审批... (金额: 1200.0)
经理是否批准?(yes/no): yes
✅ 经理已批准,更新状态为 approved
▶️ 继续执行后续流程...
✅ 金额 < 1000, 自动批准 (注意:这里可能是后续节点的日志)
看!这就是 Human-in-the-loop 的魅力。
AI 跑得再快,关键时刻也得停下来听你的。这种"暂停-修改-继续"的能力,正是传统自动化脚本做不到的,也是构建负责任 AI (Responsible AI) 的基石。
4. 飞哥总结 📝
- AutoGen 是文科生,擅长创意、协作、发散思维。
- LangGraph 是理科生,擅长逻辑、规则、流程控制。
在真正的企业级 AI 应用(如 ERP、CRM、OA 系统)中,LangGraph 才是那个能让老板放心的"定海神针"。
下期预告 :
做好了功能,怎么知道 AI 到底稳不稳定?会不会哪天突然变笨了?
下周我们将进入 LLMOps (运维与监控) 的世界,教你用 LangSmith 监控 AI 的一举一动!
创作不易,记得👇关注飞哥👇 ,点赞、收藏哦~~,下篇见👋