四大主流 Agent 框架——LangGraph、AutoGen、CrewAI、OpenAI Agents SDK——在 2026 年的官方文档里无一例外都写着"支持人机协作(Human-in-the-Loop)"。但实测下来,"停一下等人批复"这件看似简单的事,在四个栈里是四种完全不同的东西:LangGraph 靠检查点(Checkpoint)把整张图冻在内存里再续命;AutoGen 靠 UserProxyAgent 的对话轮次代理;CrewAI 靠 Task 上的 human_input=True 一个布尔开关;OpenAI Agents SDK 靠 handoff 转交 + guardrail 拦截。一句话概括四个栈的共同宿命:只要中断,上下文和状态就有丢失风险。谁来存、存在哪、能不能跨进程恢复、并发任务会不会撕裂——这才是 HITL 真正的战场。本文用同一个"退款审批"用例,把四栈的中断语义、恢复成本、并发安全拆给你看。
| 维度 | LangGraph | AutoGen | CrewAI | OpenAI Agents SDK |
|---|---|---|---|---|
| 中断原语 | interrupt(value) 抛出 |
human_input_mode 轮询 |
human_input=True 布尔 |
handoff + input_guardrail |
| 恢复机制 | Command(resume=...) |
下一条人工消息 | 弹窗输入 | 重新触发 run |
| 状态持久化 | 检查点强持久化 | 对话历史 | Crew 状态对象 | 运行上下文 |
| 粒度 | 任意节点任意行 | 整条消息 | 单个 Task 完成点 | Agent 切换点 |
| 跨会话恢复 | ✅ 天然支持 | ⚠️ 需自管 | ❌ 进程内 | ⚠️ 需 session |
| 并发中断 | 线程隔离,稳 | 易串话 | ⚠️ 实测撕裂 | 需 session_id |
| 上手成本 | 中(需懂图/checkpoint) | 低(一个参数) | 极低(一行) | 中(需懂 guardrail) |
| 变现包装度 | ★★★★★(最像产品) | ★★★ | ★★ | ★★★★ |
LangGraph 是四个栈里唯一把暂停/恢复提升为语言级原语的。interrupt(value) 不是 sleep、不是阻塞,而是把当前图的状态整个序列化进 Checkpointer(MemorySaver 或 Postgres/Redis 后端),然后向调用方"抛"出一个值。人工批复后,调用方再 graph.invoke(Command(resume="批准"), config),图会从 interrupt 那一行精确续行,feedback 变量直接拿到批复内容。
from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import MemorySaver
def review_node(state):
draft = state["draft"]
feedback = interrupt({"need_review": draft}) # 冻结整图,抛给上层
return {"draft": apply_feedback(draft, feedback)} # resume 后从这里继续
graph = build_graph().compile(checkpointer=MemorySaver())
cfg = {"configurable": {"thread_id": "refund-42"}}
graph.invoke({"draft": "..."}, cfg) # 跑到 interrupt 停下
graph.invoke(Command(resume="批准,加一句致歉"), cfg) # 精确续行
优点:粒度最细,可在任意节点的任意一行打断;检查点是强持久化,进程崩了也能恢复;thread_id 天然隔离并发会话。缺点:心智负担最重——开发者必须理解"图是有状态的、检查点是可重放的、纯函数才有可重放性",节点里写个 print 到外部副作用都会让重放出问题。变现评估:最适合包装成"企业级审批流 Agent"训练营,因为它最像真正的 BPMS(业务流程引擎),能讲清楚"为什么比 Zapier 强"。
AutoGen 的 HITL 不是图的节点属性,而是 Agent 实例的属性。核心是 UserProxyAgent + 三态开关 human_input_mode:ALWAYS 每条消息都问人、TERMINATE 只在终止条件命中时问人、NEVER 完全自治。人工介入的本质是"在对话流里插入一条来自人类的消息",框架用 input() 或自定义回调把流卡住。
from autogen import UserProxyAgent, ConversableAgent
user_proxy = UserProxyAgent(
"approver",
human_input_mode="TERMINATE",
is_termination_msg=lambda m: m.get("content","").strip().endswith("APPROVE"),
)
analyst = ConversableAgent("analyst", llm_config=cfg)
user_proxy.initiate_chat(analyst, message="起草退款意见,等我批复")
优点:上手极快,一个参数就有人机协作;对话式交互对"代码评审、问答澄清"这类场景自然。缺点:状态就是对话历史,没有独立的检查点层——进程一挂,未保存的对话就没了;并发多任务时 UserProxy 容易把不同会话的消息串到一起。AutoGen 0.4 改成事件驱动后语义有迁移税。变现评估:适合做"对话式助手"产品,不适合做合规审批——因为它无法证明"批复发生在流程的哪一步、状态是否可重放"。
CrewAI 把 HITL 压缩成了一个布尔参数 human_input=True,挂在 Task 上。当某个 Task 执行完,框架在控制台弹出提示,让人工批准/拒绝/修改输出,Task 才算完成。另有 Flows 抽象支持条件路由,但 HITL 仍主要靠 Task 级开关。
from crewai import Agent, Task, Crew
analyst = Agent(role="退款分析员", goal="给出意见", llm=llm)
task = Task(
description="撰写退款审批意见",
expected_output="审批结论",
agent=analyst,
human_input=True, # 完成前弹出人工确认
)
Crew(agents=[analyst], tasks=[task]).kickoff()
优点:学习成本最低,"加一个参数就有人工审批"的卖点极强,训练营第一课首选。缺点:控制台弹窗本质是 input() 阻塞,无法跨进程、无法 Web 化、无法持久化;中断点被钉死在 Task 完成时,不能在工具调用前拦;并发 Crew 实测会因为共享 stdin 互相打断。变现评估:做"快速原型"和"AI 训练营入门演示"极佳,但交付给企业生产环境会立刻露馅——没法对接审批系统的回调 webhook。
OpenAI Agents SDK(2025 年 3 月发布,2026 年已成生产主流之一)没有 interrupt() 这种原语,它的 HITL 由三个零件拼装:handoff(把控制权转给另一个 Agent,可转给"人工 Agent")、input_guardrail(在 run 真正开始前拦截输入,可用来强制人工确认)、工具调用钩子(在副作用工具执行前后插入审批回调)。中断的恢复靠重新触发一次 Runner.run() 并传入 session 上下文。
from agents import Agent, Runner, InputGuardrail, GuardrailFunctionOutput
async def must_approve(ctx, agent, input):
ok = await ask_human(input) # 自行实现:弹审批
return GuardrailFunctionOutput(output_info={"ok": ok},
tripwire_triggered=not ok)
guard = InputGuardrail(guardrail_function=must_approve)
agent = Agent(name="退款审批", handoffs=[human_agent], input_guardrails=[guard])
优点:与 OpenAI 模型/可观测性生态深度绑定,tripwire 语义清晰(触发即拦截);handoff 让"转人工"成为一等路径,适合客服/工单。缺点:没有图的检查点,中断后恢复全靠你自己管 session 状态;多步审批要手工编排 handoff 链;和厂商绑定深,迁移到 Anthropic/自托管模型代价大。变现评估:包装成"OpenAI 原生客服 Agent"产品力强,但要说"框架中立"就站不住。
把四栈放在"进程崩溃后能否恢复未完成审批"这个判据下,差距立刻拉开。LangGraph 把 Checkpointer 指向 Postgres,重启后用同一个 thread_id invoke Command(resume=...) 即可精确续行——这是唯一开箱即用的强持久化。AutoGen 的状态是对话历史,需要你自己写快照逻辑存进数据库。CrewAI 的 input() 阻塞在内存里,进程一死审批上下文全丢,只能靠重跑。OpenAI Agents SDK 提供 session 概念但默认是内存的,生产必须接外部存储。结论:如果你的审批流必须满足审计合规("停在哪一步、谁批复的、何时恢复"都可追溯),只有 LangGraph 能直接交付,其余三个都要补一层自研持久化——而这层自研往往就是项目从一周拖到三个月的真凶。
真实业务里一个客服同时处理五个工单是常态。LangGraph 靠 thread_id 给每个审批流独立的状态轨,并发安全。AutoGen 的 GroupChat 在多会话并发时消息路由容易串——A 会话的批复被 B 会话的 UserProxy 吞掉是经典 bug。CrewAI 多 Crew 并发时,多个 input() 抢同一个 stdin,实测会出现"弹窗内容串到另一个 Task"的撕裂。OpenAI Agents SDK 要求每次 run 显式传 session_id,传错就串、不传就单线。实战建议:除了 LangGraph,其余三栈做并发审批都必须自己加一层"会话锁 + 消息队列"把人工输入精确路由,否则越并发越乱。
审批最大的现实问题不是"批准",而是"人没回"。四栈对"超时/拒绝"的处理差别巨大:LangGraph 可在 interrupt 后由调度器判断超时再走拒绝分支,语义完整。AutoGen 的 TERMINATE 模式若人一直不回,对话会无限挂起,需要自己加超时回调。CrewAI 的 input() 没有原生超时,必须用外层 signal.alarm 之类的 hack。OpenAI Agents SDK 的 guardrail 是同步的,适合"批准/拒绝"二选一,但"等待超时"要靠你自己的异步编排。变现洞察:把"超时降级为自动驳回/升级上级"做成一个通用中间件,是横跨四栈都能复用的训练营王牌模块——因为它戳中每个做审批流的人都踩过的坑。
flowchart TD
U[用户触发任务] --> R[Agent 运行]
R --> D{需要人工?}
D -- 否 --> X[自动执行完成]
D -- 是 --> I[发起中断 interrupt]
I --> S[状态持久化]
S --> P{持久层}
P -- LangGraph --> CK[Checkpointer: thread_id 隔离]
P -- AutoGen --> H[对话历史快照-自管]
P -- CrewAI --> M[内存 input-易丢]
P -- OpenAI --> SS[Session-需外部存储]
CK --> W[等待人工批复]
H --> W
M --> W
SS --> W
W -->|批准| RES[恢复执行]
W -->|拒绝| RJ[走拒绝分支]
W -->|超时| TO[降级/升级]
RES --> X
RJ --> X
TO --> X
thread_id 给你"可重放、可追溯、可并发"的全部三件套。UserProxyAgent 最自然,ALWAYS/TERMINATE 两态够用。human_input=True,一行参数、当场见效。interrupt() / Command(resume) / Checkpointer 原语与审批工作流模式[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
