首页 › ai-agents › 人在回路中断税:六栈审批闸门横评

生产级智能体落地的头号死法,在 2026 年已经收敛成一句话:智能体执行了一个没有人批准过的副作用。 模型把草稿写得很漂亮、推理链很完整,然后在最后一步调用了 issue_refund、drop_table、send_email、transfer_funds——而这一步恰恰是唯一不可逆、也最该有人盯着的环节。于是"人在回路"(Human-in-the-Loop,HITL)从可选项变成了 2026 年所有主流框架的一等公民原语。问题是:每家框架实现审批闸门的方式天差地别,而加一道闸门从来不是免费的。本文把 LangGraph、OpenAI Agents SDK、CrewAI、AutoGen、LlamaIndex Workflows、裸 SDK 自研这六条路线放在一起,从"中断粒度—状态持久化—恢复机制—阻塞模型"四个轴实测,算清那笔被普遍低估的中断税账。

一、先看清"中断税"的四张账单

在比框架之前,必须先承认一个事实:审批闸门不是"加个 if ask_user"那么轻量。它会从四个方向同时向你收税。

第一张账单是延迟税。 智能体一旦在某个节点挂起等待人类,整个 run 的尾延迟就从"模型推理的十几秒"被拉长到"人响应的几分钟甚至几小时"。一个本来 12 秒跑完的退款流程,加上人工复核,端到端可能变成 4 分钟起步。吞吐和延迟在这里是同一个旋钮:闸门越严,单位时间能完成的 run 越少。

第二张账单是状态持久化税。 想在几小时后恢复执行,框架必须在挂起点把完整状态序列化落盘——LangGraph 的 checkpointer、OpenAI 的 RunState、CrewAI 的 Memory,本质都是干这件事。每一次中断都意味着一次 DB 写入、一次状态 schema 绑定,以及"恢复时环境已变"的时序漂移风险。

第三张账单是粒度税。 审批可以挂在 run 级(最粗,要么全程没人管、要么全程堵死)、节点级、工具级、事件级(最细)。粒度太粗,闸门沦为橡皮图章;粒度太细,操作员被淹没在"批准/批准/批准"的疲劳里,照样橡皮图章。正确的粒度,应当对齐"副作用的可逆性边界"——只有真正不可逆、不可补偿的操作才需要拦。

第四张账单是恢复正确性税。 从序列化快照恢复时,工具签名可能已经变了、外部系统状态可能已经漂移、被批准的草稿参数可能已经过期。能优雅恢复,是 2026 年区分"玩具 demo"和"生产 agent"的分水岭。下面六条路线,差的正是这四张账单上的得分。

二、六栈审批闸门多维度对比

框架 中断粒度 状态持久化 恢复机制 阻塞模型 审批面范围 到首个闸门成本
LangGraph 节点级 / 任意 interrupt() 点 内置 checkpointer(Postgres/SQLite/Memory) Command(resume=) 沿 thread_id 异步,可在任意节点挂起 节点级,细到单次 interrupt 调用点 中(须懂 graph + state schema)
OpenAI Agents SDK 工具级 needs_approval RunState 可序列化(run-wide) Runner.resume_from_state 流式 run_streamed,事件驱动 run-wide,覆盖子 agent 工具调用 低(装饰器一行声明)
CrewAI 任务级 human_input=True 依赖 Memory / Flow state kickoff + Flows 重入 同步阻塞,等标准输入 单任务级,粒度粗 低(一个布尔位)
AutoGen 回合级 human_input_mode 对话历史(GroupChat) UserProxy 重发接续 同步阻塞,常驻 user proxy 回合级,proxy 拦截 中(须配 proxy + 终止条件)
LlamaIndex Workflows 事件级 wait_for_event Context.data + 检查点 事件重投递 异步事件循环 事件级,挂在任意 step 中高(须自定义 Event 类型)
裸 SDK 自研 任意点(写哪停哪) 自建(Redis / DB) 自建 webhook 回调 完全自控 完全自定义 高(全手写)

三、LangGraph:interrupt() + Command(resume=)——最可审计的一等公民

LangGraph 在 2026 年把 interrupt() 升级为一等公民原语,取代了早期只能用 interrupt_before / interrupt_after 在节点边界挂起的笨重写法。它的核心机制是:当图执行到 interrupt(payload) 这一行时,立即暂停,用 checkpointer 把当前完整状态按 thread_id 落盘,把 payload 抛给调用方;人类决策回来后,用 Command(resume=decision) 沿同一个 thread_id 把图从精确挂起点接续跑下去。

from langgraph.graph import StateGraph
from langgraph.types import interrupt, Command
from langgraph.checkpoint.postgres import PostgresSaver

def review_node(state):
    decision = interrupt({"pending_action": state["draft_action"]})  # 在此挂起
    return {"action": state["draft_action"]} if decision["approved"] else {"action": None}

graph = builder.compile(checkpointer=PostgresSaver(...))
cfg = {"configurable": {"thread_id": "run-42"}}
graph.invoke(user_input, cfg)                                  # 暂停在 interrupt
graph.invoke(Command(resume={"approved": True}), cfg)          # 人工决策后恢复

优点在于审计性极强:每一次挂起/恢复都是一条带 thread_id 的可回放轨迹,配合 time-travel 调试能精确复现"当时为什么要做这个操作"。缺点是延迟税和状态税都不低——Postgres checkpointer 每次中断都要写一行状态,state schema 一旦演进,老 thread 的恢复就会踩兼容坑。变现上,LangGraph 的 HITL 模式最适合金融风控、运维变更审批这类强合规场景,是"训练营卖合规故事"的最佳素材,但教学门槛偏高,学员得先吃透 graph 与 channel。

四、OpenAI Agents SDK:needs_approval + RunState——声明式、最轻量的闸门

OpenAI Agents SDK 在 2026 年 2 月 5 日发布的 v0.8 里,把 HITL 做成了整个 SDK 最重要的更新之一。官方文档的原话点明了它的设计哲学:"Use the HITL flow to pause agent execution until a person approves or rejects sensitive tool calls. Tools declare when they need approval, run results surface pending approvals as interruptions, and RunState lets you serialize and resume runs."——工具自己声明何时需要审批,run 结果把待审批项作为中断抛出,RunState 负责序列化与恢复。最关键的一句是审批面 "run-wide, not limited to the current top-level agent":即使是 handoff 到子 agent 后触发的工具调用,同一个 run 内的审批状态依然统一管控。

from agents import Agent, Runner, function_tool

@function_tool(needs_approval=True)        # 一行声明:此工具需人工放行
def issue_refund(order_id: str, amount: float) -> str:
    return f"refunded {amount} for {order_id}"

agent = Agent(name="Billing", tools=[issue_refund])
result = Runner.run_streamed(agent, input="refund order 8821")
async for ev in result.stream_events():
    if ev.type == "approval_request":
        snapshot = result.to_state()        # 序列化 run-wide 状态
        result = Runner.resume_from_state(snapshot, approved=True)

它的杀手锏是声明式与低门槛:在工具上加 needs_approval=True 就完成了审批接入,不必手写状态机。工具级粒度天然对齐"副作用可逆性"——只有真正会改世界的工具才声明需要审批,普通只读工具一路放行,延迟税压到最低。缺点是粒度被钉死在"工具级",想要"同一工具、看参数决定要不要审"就得在 needs_approval 里塞一个返回布尔值的判断函数,灵活性不如 LangGraph 的任意点中断。变现上它是独立开发者做 SaaS 包装最快的一条路:上手成本最低,配 MCP 工具能快速搭出带审批的垂直 agent,适合做"48 小时复刻一个客服/退款 bot"的快闪训练营。

五、CrewAI:human_input=True——简单粗暴,但粒度是硬伤

CrewAI 的 HITL 是六栈里最直白的:给 Task 传一个 human_input=True,该任务跑完就停下来、把结果丢给操作员,由人在终端敲 yes/no 或补一段反馈。配合 2026 年成熟的 Flows(用 @start / @listen 编排的事件流),可以在 Flow 层做更结构化的等待与重入。

from crewai import Agent, Task, Crew
refund_agent = Agent(role="Billing specialist", ...)
approve_task = Task(
    description="Draft the refund action for {order}",
    expected_output="refund draft",
    human_input=True,          # 阻塞,等待操作员输入
    agent=refund_agent,
)
Crew(agents=[refund_agent], tasks=[approve_task]).kickoff()

优点是门槛极低、心智负担最小,一个布尔位就接入了 HITL,非常适合做"角色化团队 + 人工质检"的内部工具。但它的粒度是任务级——粗到要么整段任务没人管、要么整段任务都阻塞,正好踩在"粒度税"最痛的区间。而且它默认走标准输入阻塞,天生不适合跨进程、跨机器的分布式审批场景(比如人不在终端、审批要等到明天)。恢复机制也较弱,CrewAI 的状态持久化依赖 Memory 和 Flow,没有 LangGraph 那种"精确 thread_id 复活"的强保证。变现上,CrewAI 的 HITL 适合面向小团队、非技术运营人员的内容审核、文案发布类训练课,卖点是好懂;但凡客户要强审计、要恢复轨迹,CrewAI 就得让位给 LangGraph。

六、AutoGen:UserProxyAgent——回合级拦截,常驻代理成本高

AutoGen 走的是"对话即控制"的路子。它用一个常驻的 UserProxyAgent 充当人类代理,靠 human_input_mode(NEVER / TERMINATE / ALWAYS)决定每个回合是否打断,再用 is_termination_msg 判定结束。2026 年的 AutoGen 0.5+ 还补上了异步的 interrupt 与用户响应回调,能更细地控制"何时回弹给人"。

import autogen
proxy = autogen.UserProxyAgent(
    "approver",
    human_input_mode="ALWAYS",      # 每次回复都回弹给人
    is_termination_msg=lambda x: "APPROVED" in (x.get("content", "")),
    max_consecutive_auto_reply=0,
)
# GroupChat(proxy, assistant) —— proxy 在副作用回合前拦截

优点是与多智能体对话模型天然契合:任何 agent 想动作,都得先过 proxy 这一关,等于把审批焊进了对话协议。缺点同样明显——常驻代理是资源黑洞:proxy 要在线等着,ALWAYS 模式下每个回合都阻塞,对话一长,延迟税和连接维护成本都直线上升。回合级粒度也比工具级粗:模型可能在一个回合里既查询又退款,proxy 只能在回合边界拦,想只拦退款那一动作就力不从心。变现上,AutoGen 的 HITL 最适合研究型、探索型多智能体场景(科研、数据分析需人指点方向),但做成面向企业的产品化审批,体验和成本都不如 OpenAI SDK / LangGraph。

七、LlamaIndex Workflows:事件级闸门——最灵活,代价是自己造轮子

LlamaIndex 的 Workflows 把一切抽象成事件(Event),HITL 在这里自然落成"在某一步 wait_for_event 等一个人类决策事件"。它的粒度是事件级——比节点级、工具级都更细,能精确到"在 step A 和 step B 之间插入一段等待"。

from llama_index.core.workflow import Workflow, step, Event, Context

class ApprovalEvent(Event): action: dict

class BillingFlow(Workflow):
    @step
    async def draft(self, ctx: Context, ev: StartEvent) -> ApprovalEvent:
        ctx.data["draft"] = make_draft(ev.input)
        return ApprovalEvent(action=ctx.data["draft"])

    @step
    async def gate(self, ctx: Context, ev: ApprovalEvent) -> StopEvent:
        decision = await ctx.wait_for_event(HumanDecisionEvent, timeout=3600)
        return StopEvent(result=execute(ev.action) if decision.ok else None)

优点是表达力最强:异步事件循环让它天生适合跨服务、长时延的分布式审批(人几小时后通过 webhook 推回一个 HumanDecisionEvent)。缺点是所有结构都要自己造——ApprovalEvent、HumanDecisionEvent 都得自定义,状态靠 Context.data 手工管理,持久化和恢复没有开箱即用的强保证,本质是把中断税的大头转嫁给了开发者。上手门槛在六栈里偏高,适合愿意为灵活性付工程税的团队。变现上,它适合做成"工作流即代码"的高级训练营,但门槛劝退大多数初学者,更适合面向有经验工程师的进阶课。

八、裸 SDK 自研:把控制权 100% 拿在自己手里

最后一栈不是某个框架,而是"不要框架"——直接拿 Anthropic Claude / OpenAI 原生 SDK,在 agent loop 里手写挂起与恢复。模型每抛出一个 tool_use,执行前你先判断这个工具是不是副作用型;是,就把当前对话 + 工具调用塞进 Redis,返回一个 approve:<id> 的 key,外部系统(IM、工单、审批平台)拿 key 去找人,人批了走 webhook 把 key 标记放行,循环接续。

import anthropic, json, redis
cl = anthropic.Anthropic(); r = redis.Redis()

def checkpoint_pending(conv, tool_use):
    key = f"approve:{r.incr('aid')}"
    r.setex(key, 3600, json.dumps({"conv": conv, "tool": tool_use.model_dump()}))
    return key        # 外部系统展示 key 给审批人;webhook 回调后恢复

它的全部价值就是零抽象税、零锁定:中断粒度、审批面、持久化后端、超时与重试策略,完全由你定义,最适合已经有一套工单/审批基建的企业把 agent"嵌"进既有治理流程。代价是工程税最高——你要自己处理状态序列化、幂等恢复、并发 run 的隔离、token 上下文在恢复时的重建。六栈里延迟税和状态税可控性最强,但代码量也最大。 变现上它不是训练营产品,而是咨询/外包项目的最佳形态:帮企业把 agent 审批焊进既有 SOX/合规流程,客单价远高于卖课。

九、审批闸门:挂起—恢复的生命周期

flowchart TD
    A[智能体 run 启动] --> B{执行下一步}
    B -->|只读工具| B
    B -->|副作用工具需审批| C[框架触发 interrupt]
    C --> D[Checkpointer/RunState 序列化完整状态]
    D --> E[挂起 run, 抛出 approval_request]
    E --> F{人类决策}
    F -->|批准| G[沿 thread_id/RunState 恢复]
    F -->|拒绝| H[短路退出 / 改写草稿]
    F -->|超时未决| I[补偿事务或告警]
    G --> J[执行真实副作用]
    H --> J
    J --> K[run 结束]
    I --> K

这张图藏着中断税的全部成本点:C→D 是状态持久化税(一次写盘),E→F 是延迟税(挂起时长不可控),C/D 的恢复点是恢复正确性税(环境漂移),而 F 的"批准/拒绝/超时"三分支,决定了闸门是真治理还是橡皮图章——没有超时补偿分支的 HITL,在 2026 年等于没做。

十、按场景的推荐方案

一句话收束:2026 年选 HITL 框架,本质是在"上手成本、审计强度、粒度精度、恢复保证"四个轴上做取舍。 真正决定闸门价值的,从来不是用了哪个框架,而是你把审批挂在副作用的可逆性边界上、并为"人类永远会拖到最后一刻才点批准"这件事预留了超时与补偿分支。

参考来源

  1. Interrupts — LangGraph Docs — 官方 interrupt() / Command(resume=) / checkpointer 持久化机制说明。
  2. Human-in-the-loop — OpenAI Agents SDK — needs_approval 声明、approval_request 中断与 run-wide RunState 序列化/恢复。
  3. OpenAI Agents SDK v0.8 Deep Dive: HITL & Codex Tool — 2026-02-05 v0.8 发布,needs_approval + RunState 序列化解析。
  4. Adding Human Approval Gates to AI Agents Without Killing Throughput — LangGraph/AutoGen/CrewAI 三栈审批闸门与吞吐权衡。
  5. LangGraph vs CrewAI vs AutoGen in 2026: Pick the Right AI Agent Framework — HITL 粒度对比与"何时该跳过框架"的判断。
  6. Guardrails and human review | OpenAI API — guardrails 自动校验与 human review 双机制的官方指引。

[!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 等场景全覆盖。

51学通信知识星球海报-扫码加入