<!-- title: Agent审批闸门六栈实测 -->

# Agent审批闸门六栈实测：中断、接管与吞吐税的全账

当自主智能体开始触碰支付、发邮件、删库、提交工单这类**不可逆副作用**时，"它能跑"和"它敢跑"之间隔着一条决定生产成败的鸿沟——人机协作闸门（Human-in-the-Loop Gate）。本期把 2026 年主流的六套 Agent 框架拉到同一条审批流水线上实测：LangGraph、OpenAI Agents SDK、CrewAI、AutoGen/AG2、Microsoft Agent Framework、Pydantic AI。核心结论先行——**只有 LangGraph 的 `interrupt()` 和 OpenAI Agents SDK 的 `RunState` 真正具备"跨进程、跨天"的持久化中断-恢复能力**；CrewAI 与 AutoGen 停留在"会话内征求反馈"；Pydantic AI 则是裸奔基线。而真正决定框架生死的不是"能不能加闸门"，而是**每个闸门要交多少吞吐税**。

## 一、六栈审批闸门对比总表

| 框架 | HITL 原语 | 审批声明方式 | 中断/恢复机制 | 状态持久化 | 审批作用域 | 首闸上手 | 吞吐税 |
|------|----------|-------------|--------------|-----------|-----------|---------|--------|
| **LangGraph** | `interrupt()` + `Command(resume=)` | 命令式（节点内调用） | 检查点回放 | Checkpointer（内存/SQLite/Postgres） | 线程级（`thread_id`） | ~15 min | 中（可异步） |
| **OpenAI Agents SDK** | 工具级审批 + `RunState` | 声明式（工具标记 `needsApproval`） | Run 中断 + 状态序列化 | `RunState` 可序列化 | **运行级（跨 handoff）** | ~10 min | 低 |
| **CrewAI** | `human_input=True` | 任务级标志 | 阻塞式 stdin 征询 | 无原生持久化 | 任务级 | ~5 min | 高（阻塞） |
| **AutoGen / AG2** | `UserProxyAgent` | `human_input_mode` 配置 | 会话轮次等待 | 会话内存（可外挂） | 会话级 | ~10 min | 高（同步） |
| **Microsoft Agent Framework** | `FunctionApprovalRequest` | 函数工具 `approval_required` | 事件回调 + 恢复 | 持久化线程 | 运行级 | ~15 min | 中 |
| **Pydantic AI** | 无内置 | 手动拦截 | 手动暂停/续跑 | 自行实现 | 自定义 | ~20 min | 视实现 |

**一句话读表**：上手最快的 CrewAI 恰恰是吞吐税最高的；声明式的 OpenAI Agents SDK 在"审批作用域跨 handoff"这一项上独一份；LangGraph 在"状态可编辑、可时光旅行"上是唯一答案。

## 二、LangGraph：中断即一等公民

LangGraph 把 HITL 当作核心抽象而非附加功能。它的心智模型是**图执行可暂停**：任意节点里调用 `interrupt(value)`，整张图冻结，`value` 被吐回给客户端；人类决策后，用 `Command(resume=answer)` 续跑，`interrupt()` 那一行原样返回 `answer`，节点逻辑无缝继续。

```python
from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import MemorySaver

def approve_node(state):
    verdict = interrupt({"plan": state["draft"], "risk": state["risk"]})
    # 人类拒绝 → verdict="reject"，分支去重写；批准 → 放行
    return {"approved": verdict == "approve"}

graph = builder.compile(checkpointer=MemorySaver())
cfg = {"configurable": {"thread_id": "order-7721"}}
graph.invoke({"draft": plan}, cfg)            # 跑到 interrupt 停下
graph.invoke(Command(resume="approve"), cfg)  # 从检查点回放续跑
```

强制三件事让它成为长会话首选：①必须挂 `checkpointer`；②必须给 `thread_id`；③状态按线程隔离。代价是这套机制**默认同步阻塞**——节点停在 `interrupt()` 期间，工作线程被占住。生产里通常把"等待人类"这段挪到外部队列（Redis/Postgres），用 `thread_id` 当任务号异步恢复，从而把阻塞变成轮询，吞吐税从"高"压回"中"。它的杀手锏还有 `get_state_history(cfg)`：任意历史检查点都能 fork 出分支重放，相当于给 Agent 装了时光机，这是其余五栈都没有的能力。

## 三、OpenAI Agents SDK：声明式审批与 RunState 序列化

OpenAI Agents SDK 走了一条与 LangGraph 完全相反的路：**审批声明在工具上，不在控制流里**。工具自己声明"我需要审批"，运行结果把待审批项作为中断抛出，`RunState` 让你把整个运行状态序列化、过一会儿再反序列化续跑。

```python
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_sync(agent, "给订单 7721 退 200")
# result.interruptions 暴露待审批工具调用
# 人类 approve 后：Runner.run_sync(agent, "...", state=result.state) 续跑
```

这套设计有两个被低估的优势。第一，**审批作用域是运行级而非当前 Agent 级**——官方文档明确"the approval surface is run-wide, not limited to the current top-level agent"，意味着即便工具调用发生在 handoff 之后的子 Agent 身上，审批照样生效，跨角色协作不漏审。第二，`RunState` 是序列化对象，天然适配"用户下班了、明天早上审批"这种跨天异步场景，无需自己造 checkpointer。短板也明显：强绑 OpenAI 模型与生态，审批回调的 UI 层要自建，且 `needs_approval` 这种布尔粒度偏粗——想"按金额分级审批"得自己在工具内拆条件。综合吞吐税最低，是 OpenAI-only 技术栈的甜点选择。

## 四、CrewAI：human_input 的轻量代价

CrewAI 把人机协作压成了一个布尔标志：给 Task 加 `human_input=True`，任务执行完会停下来向人类征求反馈。上手确实五分钟不到，代价是它**本质是阻塞式 stdin 征询，不是真正的中断-恢复**。

```python
review = Task(
    description="起草退款方案",
    expected_output="退款 JSON",
    human_input=True,        # 任务后弹出人工反馈
    agent=analyst,
)
```

实测三个硬伤。其一，**没有原生持久化**——一旦会话进程退出，等待中的审批请求随之蒸发，撑不过一次容器重启或五小时长会话。其二，作用域锁死在单个 Task，跨 Crew、跨 Agent 的统一审批策略要靠 `@before_kickoff`/`@after_kickoff` 回调自己缝。其三，`human_input` 只能在任务边界触发，无法在"工具调用前"这种更细粒度的点设卡——对一个准备点"发送"的邮件 Agent 来说，任务级闸门粒度太粗。CrewAI 适合做原型演示和内部低风险流程，但凡涉及不可逆副作用，它的吞吐税（全程阻塞）和持久化缺失就是红线。

## 五、AutoGen / AG2：UserProxy 的会话级审批

AutoGen 的人机协作原语是 `UserProxyAgent`，通过 `human_input_mode` 三档配置控制介入力度：`ALWAYS`（每轮问）、`TERMINATE`（终止前问）、`NEVER`（全自动）。

```python
from autogen import ConversableAgent, UserProxyAgent

user = UserProxyAgent("human", human_input_mode="TERMINATE",
                      is_termination_msg=lambda x: "FINAL" in x.get("content",""))
assistant = ConversableAgent("assistant", llm_config=cfg)
user.initiate_chat(assistant, message="分析并下單")
```

这套"人类即一个 Agent"的设计在多智能体对话里很自然——人类就是对话图里的一个节点，逢到该他说话就阻塞等待。但它的审批是**会话级、同步的**：审批请求绑在对话轮次上，进程崩了对话历史得靠外挂记忆恢复，没有像 LangGraph `thread_id` 那样的稳定恢复句柄。2025–2026 年 AutoGen 进入剧烈动荡期——微软把 AutoGen 与 Semantic Kernel 合并进新的 **Microsoft Agent Framework**，社区则 fork 出 **AG2** 续命。这意味着今天选 AutoGen，实质是在"走向 MSAF 的官方线"和"AG2 社区线"之间二选一，迁移成本必须提前计入。

## 六、Microsoft Agent Framework：FunctionApproval 的统一面

微软把 AutoGen + Semantic Kernel 收编后推出的统一框架，在 HITL 上给出了一套更工程化的方案：函数工具声明 `approval_required`，框架在调用前抛出 `FunctionApprovalRequest` 事件，宿主程序拿到事件后回调 approve/reject，运行据此恢复。

```python
# Microsoft Agent Framework（概念示意）
@agent.function_tool(approval_required=True)
def place_trade(symbol: str, qty: int):
    ...

# 运行时订阅
async def on_approval(req: FunctionApprovalRequest):
    if req.args["qty"] < 100:
        return req.approve()
    return await ask_human(req)   # 超阈值转人工
```

它的差异化在于**审批决策可以是程序化的**——金额、风险分、用户等级都能写进回调里做条件放行，不必事事打扰人类。结合其内置的持久化线程，`FunctionApprovalRequest` 能跨进程恢复，作用域是运行级。这套东西对企业特别对味：和 Azure 生态、Copilot Studio、Semantic Kernel 技能复用一脉相承。缺点同样在企业味——抽象层厚、配置项多、学习曲线陡，团队若没有 .NET/Python 双栈和 Azure 基建，首闸上手要 15 分钟起步。

## 七、Pydantic AI：无闸门的裸奔基线

Pydantic AI 把第六栈的位置留给它，不是因为它闸门强，恰恰相反——**它几乎没有内置 HITL**。它的强项是类型安全与结构化输出，Agent 循环很薄，要加审批只能"手动拦截"：在工具调用前自己插入一个 await 人类确认的协程，状态持久化完全自己实现。

```python
# 手动闸门：把审批做成一个普通函数工具前的钩子
async def guarded_tool(ctx, **args):
    if args["amount"] > ctx.deps.threshold:
        if not await ctx.deps.human.approve(args):
            return "rejected"
    return await do_refund(**args)
```

把它列进来是为了立一个对照系：**当你主动拒绝框架提供的闸门抽象、选择裸写时，你拿到了最大灵活性，也接下了全部持久化、超时、重放、审计的债。** 小团队、低风险、短会话场景，这套"依赖注入 + 手动钩子"反而最干净；一旦会话跨小时、审批跨人、需要审计回溯，这些手写的边角代码会迅速膨胀成事实上的私有框架——而那正是其他五栈想替你省掉的。

## 八、闸门设计的三个真相与吞吐税

把六栈摆在一起，会浮现出三条贯穿性规律。

**真相一：闸门粒度决定安全上限，声明方式决定工程体验。** 工具级声明（OpenAI、MSAF）比节点级命令（LangGraph）更贴"副作用在哪、审批挂哪"的直觉，但牺牲了"在任意逻辑点叫停"的灵活性。LangGraph 的 `interrupt()` 可以在条件分支中间、在规划完成后、在校验失败时任意插入，粒度自由度最高。

**真相二：没有持久化的审批都是玩具。** CrewAI 和 AutoGen 的同步征询在"用户去开会了"的现实中直接失效。真正过得了关的是把等待态外置——LangGraph 的 checkpointer 把 `thread_id` 当任务号写进 Postgres，OpenAI 的 `RunState` 序列化进对象存储，MSAF 的持久化线程。**判断一个框架能否上生产，就看它的"等待人类"状态能不能在进程重启后毫秒级恢复。**

**真相三：吞吐税是隐形定价表。** 每个同步阻塞闸门都在序列化整条流水线。一个有四处审批门的 Agent，若全做成阻塞，端到端延迟等于四次人类响应时间之和，且工作线程全程空转。对冲手段只有两个——把闸门异步化（外部队列 + 轮询恢复），或把闸门程序化（MSAF 式条件放行，能自动放行的绝不打扰人）。OpenAI Agents SDK 之所以吞吐税评"低"，正是因为 `RunState` 让异步化几乎零成本。

```mermaid
flowchart TD
    U[用户请求] --> A[Agent Loop 推理]
    A --> T{工具调用?}
    T -->|needsApproval=true| G1[审批门-声明式]
    T -->|高风险节点| G2[中断门-命令式 interrupt]
    G1 --> Q[审批队列-可持久化]
    G2 --> CK[Checkpointer/RunState 持久化]
    Q --> H{人工/规则决策}
    CK --> H
    H -->|approve/timeout放行| EX[执行副作用]
    H -->|reject| A
    H -->|edit state| F[fork历史检查点-时光旅行]
    F --> A
    EX --> D[审计日志-不可抵赖]
    D --> A
    style G1 fill:#ffe0b2
    style G2 fill:#c8e6c9
    style CK fill:#bbdefb
    style Q fill:#bbdefb
```

## 九、分场景推荐

- **金融/交易/退款（不可逆+合规审计）** → **LangGraph**。检查点 + 状态历史 fork 满足审计与回溯，`thread_id` 天然可追踪，配合 Postgres checkpointer 撑得住跨天会话。
- **OpenAI 全家桶 SaaS（OpenAI-only 技术栈）** → **OpenAI Agents SDK**。`needsApproval` 声明式 + `RunState` 序列化 + 审批作用域跨 handoff，三合一且吞吐税最低。
- **企业内 Azure/Copilot 生态** → **Microsoft Agent Framework**。条件化 `FunctionApprovalRequest` + 持久化线程 + Semantic Kernel 技能复用，最贴中大型企业治理要求。
- **快速原型/演示/低风险内部流程** → **CrewAI**。五分钟出活，但别碰不可逆副作用。
- **多角色对话式协作研究** → **AG2**（社区续命版）。注意 AutoGen→AG2/MSAF 的迁移账，别在分叉前夕重资产押注旧 AutoGen。
- **极致定制、强类型、小团队** → **Pydantic AI** + 手动闸门，但提前为持久化与审计预留工程预算。

一句话收口：**2026 年选 Agent 框架，与其问"它能不能加审批"，不如问"它的审批等待态能不能在容器重启后 50 毫秒内恢复、能不能不打扰人就不打扰人"。** 前者是所有六栈都能做到的表象，后者才是区分生产可用与玩具的分水岭。

## 参考来源

1. [Human-in-the-loop — OpenAI Agents SDK 官方文档](https://openai.github.io/openai-agents-python/human_in_the_loop/) — 工具声明审批、`RunState` 序列化与跨 handoff 运行级审批作用域的权威说明。
2. [Interrupts — LangGraph 官方文档](https://docs.langchain.com/oss/python/langgraph/interrupts) — `interrupt()` / `Command(resume=)` 与 checkpointer 回放机制。
3. [How to Gate Agents: LangGraph, AutoGen, CrewAI（2026）](https://www.bestaiweb.ai/how-to-add-human-approval-gates-to-agents-with-langgraph-autogen-and-crewai-in-2026/) — 三栈审批闸门对比与"不杀吞吐量"工程权衡。
4. [CrewAI vs LangGraph vs AutoGen vs OpenAgents — Best AI Agent Framework (2026)](https://openagents.org/blog/posts/2026-02-23-open-source-ai-agent-frameworks-compared) — 四栈架构、多智能体与生产就绪度基准对比。
5. [Function tools with human in the loop approvals — Microsoft Learn](https://learn.microsoft.com/en-us/agent-framework/agents/tools/tool-approval) — Microsoft Agent Framework 的 `FunctionApprovalRequest` 条件放行机制。

> [!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学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
