【LangGraph】新篇章:LangGraph 持久化的三大应用能力*重点*
-
- 前言
- 一、什么是人机交互?
- 二、四种人机交互应用模式
-
- [2.1 批准或拒绝(Approve / Reject)](#2.1 批准或拒绝(Approve / Reject))
- [2.2 查看和编辑状态(Review & Edit State)](#2.2 查看和编辑状态(Review & Edit State))
- [2.3 在工具中中断(Tool Interrupts)](#2.3 在工具中中断(Tool Interrupts))
- [2.4 验证人类输入(Validating Human Input)](#2.4 验证人类输入(Validating Human Input))
- [三、LangGraph 中断机制核心 API](#三、LangGraph 中断机制核心 API)
- 四、完整工作流示例:结合中断与条件路由
- 五、总结
上一章-> 【LangGraph】持久化实现的三大能力------记忆
在自动化的 AI 工作流中,有时候"让机器全自动"并不是最好的选择。关键操作需要人类确认,敏感数据需要人工审查,用户输入需要反复校验。LangGraph 内置的 中断(Interrupt) 机制,让你可以在任意节点暂停图执行,等待人工干预,然后精确恢复
本文带你掌握四种最实用的人机交互模式
前言
在构建 AI Agent 或自动化系统时,我们常常需要在关键环节暂停执行,等待人工反馈。例如:
- 执行大额转账前,请求人工审批。
- 生成重要文案后,允许编辑修改再发送。
- 调用第三方 API 前,让用户确认参数。
LangGraph 通过 中断(Interrupt) 和 命令(Command) 机制,优雅地实现了这一需求。本文将详细介绍四种典型应用模式,并提供可直接运行的代码示例。
一、什么是人机交互?
人机交互(Human-in-the-loop,简称 HITL)
是指在自动化流程中,系统在特定节点暂停,等待人工输入、审查或决策。
这种模式结合了 AI 的自动化能力和人类的判断力,
特别适合:
- 高风险操作(支付、删除数据、发送邮件)
- 需要人工质量把控的内容生成(报告、文案、代码)
- 用户输入需要格式校验或合规检查的场景
在 LangGraph 中,核心 API 只有两个:
interrupt():暂停图执行,返回人工提供的值。Command(resume=value)或Command(goto=next_node):恢复执行并指定下一步
二、四种人机交互应用模式
2.1 批准或拒绝(Approve / Reject)
典型场景 :
财务 Agent 检测到一笔可疑交易,需要人工确认是否放行
实现思路 :
在关键操作前插入一个审批节点,调用 interrupt() 展示操作信息,等待用户输入"批准"或"拒绝"
根据输入,路由到不同分支
代码示例:
python
from typing import TypedDict, Literal, Optional
from langgraph.types import interrupt, Command
class ApprovalState(TypedDict):
transaction_id: str
amount: float
recipient: str
status: Optional[Literal["pending", "approved", "rejected"]]
def approval_node(state: ApprovalState):
decision = interrupt({
"question": f"是否批准以下交易?",
"transaction": f"ID: {state['transaction_id']}, 金额: {state['amount']}元, 收款方: {state['recipient']}",
"options": "请输入 '批准' 或 '拒绝'"
})
if decision == "批准":
return Command(goto="execute_payment")
else:
return Command(goto="cancel_payment")
def execute_payment(state: ApprovalState):
# 实际执行支付逻辑
print(f" 交易 {state['transaction_id']} 已执行")
return {"status": "approved"}
def cancel_payment(state: ApprovalState):
print(f"交易 {state['transaction_id']} 已取消")
return {"status": "rejected"}
用户恢复执行(通过外部代码调用)
# 当图暂停后,用户输入决定
graph.invoke(Command(resume="批准"), config=config)
2.2 查看和编辑状态(Review & Edit State)
典型场景 :
AI 生成了一封营销邮件草稿,人工可以修改后确认发送
实现思路 :
在生成节点后插入审查节点,让用户修改中间状态(例如文本内容)
修改后的内容会覆盖原有字段,继续后续节点
代码示例:
python
class DraftState(TypedDict):
subject: str
body: str
def generate_draft(state: DraftState):
# 模拟 LLM 生成邮件草稿
return {
"subject": "限时优惠:全场8折",
"body": "亲爱的用户,本周末所有商品8折,快来抢购吧!"
}
def review_draft(state: DraftState):
updated = interrupt({
"instruction": "请修改以下邮件草稿",
"current_subject": state["subject"],
"current_body": state["body"],
"提示": "你可以修改主题和正文,确认后返回 JSON 格式。"
})
# 假设用户返回 {"subject": "...", "body": "..."}
return {
"subject": updated.get("subject", state["subject"]),
"body": updated.get("body", state["body"])
}
def send_email(state: DraftState):
print(f"发送邮件\n主题:{state['subject']}\n正文:{state['body']}")
return state
用户输入示例(恢复时传入修改内容)
python
edited = {"subject": "【紧急】全场8折仅剩2天", "body": "亲爱的用户,折扣即将结束..."}
graph.invoke(Command(resume=edited), config=config)
2.3 在工具中中断(Tool Interrupts)
典型场景 :
一个可以发送邮件的工具,在真正调用邮件 API 之前,请求人类确认收件人、主题和内容
实现思路 :
直接在工具函数内部调用 interrupt()
当图执行到该工具时,会暂停并等待人工输入
人工可以修改参数或取消操作
代码示例:
python
from langchain_core.tools import tool
@tool
def send_email_tool(to: str, subject: str, body: str) -> str:
"""发送邮件给指定收件人"""
confirmation = interrupt({
"action": "即将发送邮件",
"to": to,
"subject": subject,
"body_preview": body[:100] + ("..." if len(body) > 100 else ""),
"message": "请确认或修改邮件信息(输入 '发送' 确认,或输入修改后的 JSON)"
})
# 如果用户返回字典,则覆盖原参数
if isinstance(confirmation, dict):
to = confirmation.get("to", to)
subject = confirmation.get("subject", subject)
body = confirmation.get("body", body)
confirmed = confirmation.get("confirm", False)
else:
confirmed = (confirmation == "发送")
if confirmed:
# 假设真实发送逻辑
print(f" 已发送邮件至 {to}")
return f"邮件已发送至 {to}"
else:
return "用户取消了邮件发送"
效果:当 Agent 决定调用 send_email_tool 时
图会在工具内部暂停,用户返回确认信息后继续
2.4 验证人类输入(Validating Human Input)
典型场景 :
收集用户年龄,但要求必须是int类型并且是正数
如果输入无效,重复提示直到正确
实现思路 :
在一个循环内反复调用 interrupt(),每次检查输入是否合法
不合法则更新提示语,重新 interrupt(),合法则返回并继续
代码示例:
python
class FormState(TypedDict):
age: int | None
retries: int
def ask_age_node(state: FormState):
prompt = "请输入你的年龄(正整数):"
while True:
answer = interrupt(prompt)
if isinstance(answer, int) and answer > 0:
return {"age": answer}
prompt = f"'{answer}' 不是有效的年龄,请重新输入(正整数):"
# 超时或多次失败后,可设置默认值或抛异常
return {"age": 18}
用户体验:每次输入非法,系统会返回更明确的提示,直到正确为止
三、LangGraph 中断机制核心 API
- interrupt(value)
作用:暂停当前图的执行,并等待外部人工输入。value 是一个 JSON 可序列化的对象,用于向人类展示信息(如问题、操作详情、选项等),该函数会返回人工提供的值
示例:
python
confirm = interrupt({"msg": "是否批准此交易?", "details": "转账1000元"})
# 图停在这里,等待外部调用 Command(resume=...)
# 恢复后 confirm 的值就是外部传入的内容
重要:interrupt() 只能在编译图时传入了 checkpointer 的情况下使用,因为图需要将暂停点状态保存到检查点中,否则无法恢复执行。
- Command(resume=value)
作用:从外部的 invoke/stream 调用中恢复一个被 interrupt() 暂停的图。value 会成为 interrupt() 的返回值,图将从暂停点继续执行。
示例:
python
# 在外部代码中
graph.invoke(Command(resume="批准"), config=config)
# 此时图中 interrupt() 会返回 "批准"
- Command(goto=node_name)
作用:在节点函数内部动态决定下一个要执行的节点,覆盖图中预定义的边。通常结合 interrupt() 使用,根据人类输入来路由到不同的处理分支。
示例:
python
def approval_node(state):
decision = interrupt("批准或拒绝?")
if decision == "批准":
return Command(goto="execute_payment")
else:
return Command(goto="cancel_payment")
- 重要注意事项:
-
必须配置 checkpointer:
所有涉及 interrupt() 的图,在编译时都必须传入 checkpointer(如 InMemorySaver、PostgresSaver),否则会抛出异常
-
恢复时需使用同一个 thread_id:Command(resume=...) 必须携带与暂停时相同的 config(尤其是 thread_id),否则无法定位暂停点
-
中断可以嵌套:
你可以在一个节点中多次调用 interrupt(),也可以在多个节点中分别设置中断,LangGraph 会依次等待人类输入
四、完整工作流示例:结合中断与条件路由
以下是一个模拟"人工审批 --> 执行操作 --> 记录日志"的线性流程,展示了如何在实际图中使用中断
python
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
task: str
approved: bool
result: str
def ask_approval(state: State):
decision = interrupt({
"task": state["task"],
"message": "请批准或拒绝该任务(输入 yes/no)"
})
approved = (decision.lower() == "yes")
return {
"approved": approved
}
def execute_task(state: State):
if state["approved"]:
print(f" 执行任务:{state['task']}")
return {"result": "成功"}
else:
print(f" 任务被拒绝:{state['task']}")
return {"result": "已取消"}
builder = StateGraph(State)
builder.add_node("ask_approval", ask_approval)
builder.add_node("execute_task", execute_task)
builder.add_edge(START, "ask_approval")
builder.add_edge("ask_approval", "execute_task")
builder.add_edge("execute_task", END)
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
# 执行图,会在 ask_approval 处暂停
config = {"configurable": {"thread_id": "123"}}
for event in graph.stream({"task": "删除临时文件"}, config):
print(event)
# 用户输入 yes 后恢复
graph.invoke(Command(resume="yes"), config=config)
五、总结
我们可以通过下表进行一个总结:
| 模式 | 核心能力 | 关键机制 | 典型场景 |
|---|---|---|---|
| 批准 / 拒绝 | 在关键操作前进行人工决策控制 | interrupt + Command(resume) |
转账、删除数据、发布内容 |
| 查看 / 编辑状态 | 允许人工修改中间状态数据 | interrupt + 状态更新 |
邮件草稿、报告生成、代码审查 |
| 工具中断 | 在工具调用前插入人工干预 | 工具内部 interrupt |
发送消息、调用外部 API |
| 输入验证 | 循环获取并校验用户输入 | interrupt + 循环校验 |
表单填写、参数配置 |
一句话 :
interrupt()保存现场,Command(resume=...)恢复执行,人机协作无缝衔接
通过合理使用 LangGraph 的中断功能,我们可以让自动化系统在关键节点"停下来问问人",既保留了 AI 的效率,又引入了人类的判断和监督

今天的分享先到这里我们下期再见~
