11.10 IAgent 统一门面

RAF 的核心设计哲学之一是"编排即 Agent"——任何工作流编排模式都可以通过统一的 `XXXWorkflowBuilder.build() → Workflow.as_agent() → IAgent` 链路收敛为 `IAgent` 接口。前端与编排系统只与 `IAgent` 交互,编排细节完全透明。

设计哲学

graph TB
    subgraph "编排 Builder 层"
        SEQB[SequentialWorkflowBuilder]
        CONB[ConcurrentWorkflowBuilder]
        HOB[HandoffWorkflowBuilder]
        GCB[GroupChatWorkflowBuilder]
        MAGB[MagenticWorkflowBuilder]
        VOTEB[VoteWorkflowBuilder]
    end

    subgraph "Workflow"
        SW[SequentialWorkflow]
        CW[ConcurrentWorkflow]
        HW[HandoffWorkflow]
        GCW[GroupChatWorkflow]
        MW[MagenticWorkflow]
        VW[VoteWorkflow]
    end

    subgraph "适配与门面"
        WAA[WorkflowAsAgent]
        WA[WorkflowAgent]
    end

    subgraph "统一接口"
        IAG[IAgent trait]
    end

    subgraph "消费者"
        UI[UI / CLI]
        NEST[其他编排模式]
        REG[AgentRegistry / AgentClientProtocol]
    end

    SEQB -->|build| SW -->|as_agent| WA --> IAG
    CONB -->|build| CW -->|as_agent| WA --> IAG
    HOB -->|build| HW -->|as_agent| WA --> IAG
    GCB -->|build| GCW -->|as_agent| WA --> IAG
    MAGB -->|build| MW -->|as_agent| WA --> IAG
    VOTEB -->|build| VW -->|as_agent| WA --> IAG
    IAG --> UI
    IAG --> NEST
    IAG --> REG

核心链路

// 所有 Workflow 收敛为 Arc<dyn IAgent>
let agent: Arc<dyn IAgent> = SequentialWorkflowBuilder::new()
    .add_agent(researcher)
    .add_agent(writer)
    .build()?
    .as_agent();

// 前端只与 IAgent 接口交互
agent.id()                        // agent 标识
agent.metadata()                  // 元信息(包含子 agent 列表)
agent.get_subagent(&child_id)     // 发现子 agent
agent.run(messages, session).await? // 流式执行
agent.reset().await?              // 递归重置

适配器实现

WorkflowAsAgent(编排模式门面)

`WorkflowAsAgent` 将编排模式的 `run()` 方法包装为 `IAgent`:

pub struct WorkflowAsAgent {
    id: AgentId,
    metadata: AgentMetadata,
    agents: HashMap<AgentId, Arc<dyn IAgent>>,  // 子 Agent 注册表
    runner: StoredRunner,                        // 工作流的 run() 函数
}

用于 `SequentialWorkflow`、`ConcurrentWorkflow`、`HandoffWorkflow`、`GroupChatWorkflow`、`MagenticWorkflow`、`VoteWorkflow` 的 `as_agent()` 包装。

WorkflowAgent(图引擎门面)

`WorkflowAgent` 将 `WorkflowEngine` 双流输出适配为 `IAgent` 的单流:

#[async_trait]
impl IAgent for WorkflowAgent {
    async fn run(&self, messages, session, _options)
        -> Result<BoxStream<'static, Result<AgentResponseResult>>>
    {
        // 内部:engine.run(initial_message, session)
        //       → (BoxStream<WorkflowEvent>, BoxStream<WorkflowOutput>)

        // 适配:WorkflowEvent::NodeStreaming
        //       → AgentResponseResult (逐事件转换)
        //       WorkflowEvent::NodeInvoking/NodeCompleted
        //       → Event::ExecutorInvoking/Invoked (嵌入事件)
    }
}

关键适配逻辑: - `NodeInvoking` → `Event::ExecutorInvoking` —— 节点开始执行 - `NodeStreaming` → `Content::Text/Reasoning/ToolCall...` —— 流式内容逐帧转换 - `NodeCompleted` → `Event::ExecutorInvoked` —— 节点执行完成 - `NodeFailed` / `WorkflowError` → `AgentError::WorkflowError` —— 错误传播

各编排模式的 as_agent()

| 编排模式 | 生成的 Agent ID | 内部引擎 | |---------|---------------|---------| | SequentialWorkflow | `workflow_seq_agent_0` | WorkflowAgent(Engine) | | ConcurrentWorkflow | `workflow_concurrent_source` | WorkflowAgent(Engine) | | HandoffWorkflow | `workflow_handoff_triage` | WorkflowAgent(Engine) | | GroupChatWorkflow | `workflow_group_chat_...` | WorkflowAgent(Engine) | | MagenticWorkflow | `workflow_magentic_orchestrator` | WorkflowAgent(Engine) | | VoteWorkflow | `workflow_vote_source` | WorkflowAgent(Engine) |

子 Agent 树发现

`get_subagent()` 方法支持递归发现和遍历 Agent 树:

// 通过 ACP 协议查询 Agent 树
let agent: Arc<dyn IAgent> = workflow.as_agent();

// 获取直接子 Agent
let sub = agent.get_subagent(&AgentId::new("code-expert"));

// 子 Agent 可独立运行
if let Some(sub_agent) = sub {
    let stream = sub_agent.run(messages, session, None).await?;
    // 独立消费子 Agent 流
}

`WorkflowAgent` 通过遍历节点的 `executor.as_agent()` 自动提取子 Agent 注册表。

嵌套编排

由于所有编排模式收敛为 `IAgent`,工作流可以递归嵌套:

// 子工作流
let sub_workflow = SequentialWorkflowBuilder::new()
    .add_agent(step1)
    .add_agent(step2)
    .build()?;

let sub_agent: Arc<dyn IAgent> = sub_workflow.as_agent();

// 子工作流作为 Concurrent 编排的一个节点
let main_workflow = ConcurrentWorkflowBuilder::new()
    .add_agent(sub_agent)         // 子工作流作为普通 Agent
    .add_agent(independent_agent)
    .build()?;

let agent: Arc<dyn IAgent> = main_workflow.as_agent();

嵌套工作流的 checkpoint 作为子 scope 管理,嵌套深度无限制。

事件流透传

`WorkflowAgent` 将引擎的全链路事件映射为 `AgentResponseResult`:

Engine 事件流              →  IAgent 响应流
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NodeInvoking               →  Event::ExecutorInvoking
NodeStreaming(TextDelta)   →  Content::Text
NodeStreaming(ToolCall*)   →  Content::ToolCallStart/Args/End/Called
NodeStreaming(Reasoning)   →  Content::Reasoning
NodeStreaming(Usage)       →  Content::Usage
NodeCompleted              →  Event::ExecutorInvoked
NodeFailed                 →  AgentError::WorkflowError
WorkflowError              →  AgentError::WorkflowError

统一接口的优势

| 优势 | 说明 | |------|------| | **前端简化** | UI 无需区分单个 Agent 和工作流,统一交互模型 | | **递归编排** | 工作流可以作为任何需要 IAgent 的上下文中的子组件 | | **统一生命周期** | `reset()` 递归清理所有子 Agent 状态 | | **可发现性** | `get_subagent()` 使前端能够渲染多 Agent 树视图 | | **流式可观测** | 引擎事件全链路映射为 AgentResponseResult,前端逐帧消费 |

注意事项

1. **as_agent() 消耗 self**:`as_agent()` 取所有权,如需复用请先 clone 2. **子 Agent 发现**:`get_subagent()` 需要已知 AgentId,最佳实践是配合 AgentRegistry 使用 3. **reset 递归性**:`reset()` 递归重置所有子 Agent,适合完整的上下文清理 4. **门面开销**:引擎事件到 AgentResponseResult 的转换在独立 tokio task 中进行,不阻塞引擎执行