7.1 人机协同(HITL)概述

什么是 HITL

**Human-In-The-Loop(HITL)** 是一种将人工判断嵌入自动化流程的设计模式。在 AI Agent 的语境下,HITL 意味着 Agent 在执行某些操作之前,必须暂停并等待人工操作员的审批。只有获得批准后,操作才会实际执行。

为什么 Agent 需要 HITL

1. **安全性**:Agent 可能被赋予文件系统访问、命令执行等能力,错误的工具调用可能造成不可逆的损害(删除文件、执行危险命令)。 2. **合规性**:金融、医疗等行业要求关键操作必须有审计记录和人工授权。 3. **信任建立**:让用户对 Agent 行为有可见性和控制权,逐步建立信任。 4. **渐进式自动化**:先在低风险场景全自动,高风险场景人工审批,随着系统成熟逐步减少人工介入。

RAF 中的 HITL 场景示例

- Agent 尝试在工作区范围外读写文件 - Agent 尝试执行系统命令(`run_command`) - Agent 在 `ApproveOutside` 策略下访问跨范围路径 - 开发者显式用 `ApprovalRequiredTool` 包装任意工具

RAF 的 HITL 设计

核心三要素

RAF 的 HITL 机制由三个概念紧密配合构成:

1. **`ApprovalRequiredTool`** — 工具包装器。将任意 `Arc<dyn ITool>` 包装后,其 `requires_approval()` 返回 `true`。这个标记在运行时由 `FunctionInvokingChatClient` 检测。

2. **`ToolApprovalRequest` / `ToolApprovalResponse`** — 审批通信协议。当检测到需要审批的工具调用时,`FunctionInvokingChatClient` 发出的 `AgentResponseUpdate::ToolApprovalRequest` 事件包含 `call_id`、`name`、`arguments`、`description`。调用者以 `ToolApprovalResponse`(包含 `call_id`、`approved`、`reason`)回传审批决定。

3. **`FinishReason::AwaitingApproval`** — 流暂停信号。这是一个特殊的结束原因,表示 Agent 执行流已暂停,等待人工审批。会话保留完整的消息上下文(包括 `assistant(tool_calls)` 消息),使得恢复执行时无需重新传递消息。

设计理念:为什么在装饰器层实现

RAF 将 HITL 审批逻辑放在 `FunctionInvokingChatClient`(一个 `IChatClient` 装饰器)而非 Agent 层,这是经过深思熟虑的架构决策:

┌─────────────────────────────────────────────────┐
│               ChatClientAgent (Agent 层)          │
│  职责:指令组装、Provider 调用、压缩、会话管理      │
└────────────────────┬────────────────────────────┘
                     │
         ┌───────────▼───────────┐
         │ FunctionInvoking      │  ← 装饰器层
         │   ChatClient          │    审批 + 工具循环
         │  · 工具调用检测        │    在此实现
         │  · 审批请求发出        │
         │  · 审批响应处理        │
         │  · 工具执行            │
         └───────────┬───────────┘
                     │
         ┌───────────▼───────────┐
         │ DeepSeekChatClient /  │  ← 叶子客户端
         │ OpenAiChatClient      │    HTTP/SSE 传输
         └───────────────────────┘

**选择装饰器层的原因**:

1. **关注点分离**:Agent 层负责"调度"(组装消息、管理会话),装饰器层负责"执行"(工具调用循环、审批)。Agent 不需要知道工具是否需要审批——它只是把消息传给 `IChatClient`。 2. **可组合性**:通过 `ChatClientBuilder` 管道,审批逻辑可以与任何 LLM 提供商结合,不需要修改 Agent 代码。 3. **消息上下文自然保持**:工具调用循环在装饰器内部累积 `assistant(tool_calls)` → `tool(result)` 消息对,审批暂停时这些消息自然地保存在循环状态中,恢复时无需外部重建。

工具包装:ApprovalRequiredTool

`ApprovalRequiredTool` 是一个简单的包装器结构体:

// crates/core/src/tool.rs

pub struct ApprovalRequiredTool {
    pub inner: Arc<dyn ITool>,
}

impl ITool for ApprovalRequiredTool {
    fn name(&self) -> &str { self.inner.name() }
    fn description(&self) -> &str { self.inner.description() }
    fn parameters(&self) -> serde_json::Value { self.inner.parameters() }
    // ... execute 委托给 inner ...

    fn requires_approval(&self) -> bool {
        true  // ← 关键:覆盖默认的 false
    }
}

它实现了完整的 `ITool` trait,将所有方法委托给内部工具,唯独覆盖 `requires_approval()` 返回 `true`。这意味着 `FunctionInvokingChatClient` 在执行前检查此方法时,会触发审批流程。

**使用方式**:

// Agent A:自动执行(开发环境)
builder.with_tool(RunCommand);

// Agent B:需要审批(生产环境)
builder.with_tool(ApprovalRequiredTool::new(Arc::new(RunCommand)));

FinishReason::AwaitingApproval

`FinishReason` 枚举中定义了一个专门的变体:

pub enum FinishReason {
    Stop,           // 正常结束
    Length,         // 达到长度限制
    ToolCalls,      // 包含工具调用(内部使用,对消费者过滤)
    ContentFilter,  // 被内容过滤器拦截
    AwaitingApproval, // ← 审批暂停
    MaxRounds,      // 达到最大轮次
    Other(String),  // 其他
}

当调用者收到 `FinishReason::AwaitingApproval` 时,应该: 1. 从 `ToolApprovalRequest` 事件中获取审批信息 2. 向用户展示审批请求 3. 收集用户的审批决定 4. 构造 `AgentRunOptions` 并携带 `tool_approval_responses` 重新调用 `agent.run()`

与 MAF 的对应关系

RAF 的 HITL 设计直接对齐 Microsoft Agent Framework (MAF):

| RAF | MAF (.NET) | |-----|-----------| | `ApprovalRequiredTool` | `ApprovalRequiredAIFunction` | | `AgentResponseUpdate::ToolApprovalRequest` | `FunctionApprovalRequestContent` | | `ToolApprovalResponse` | `FunctionApprovalResponseContent` | | `FinishReason::AwaitingApproval` | `FinishReason.AwaitingApproval` |

这种对齐确保了熟悉 MAF 的开发者可以快速理解 RAF 的 HITL 机制,同时保持了 Rust 生态的习惯用法(如 `Arc<dyn ITool>` 替代 C# 的接口引用,`mpsc::channel` 替代事件委托)。

本章后续

- [7.2 审批流完整链路](approval-flow.md):用 mermaid 时序图完整展示审批流程 - [7.3 ToolApprovalRequest/Response API](tool-approval-api.md):审批协议的字段详解与代码示例 - [7.4 中断恢复与取消机制](resume-cancel.md):审批暂停后如何恢复执行与主动取消