任务调度与并行执行——多 Job 并发模型

📑 目录

IronClaw 深度剖析(五):任务调度与并行执行——多 Job 并发模型

系列导读:IronClaw 是一个基于 Rust 的 AI Agent 执行平台,采用多层级 Docker 沙箱架构实现安全隔离。本系列从架构到实现逐步拆解其核心设计。本篇聚焦任务调度系统——从三层调度模型到 Orchestrator/Worker 模式,从 Docker 沙箱生命周期到自修复机制,揭示 IronClaw 如何管理数百个并发 Job 的复杂调度场景。


一、为什么任务调度是 Agent 系统的核心挑战

当 AI Agent 从"单次对话工具"演进为"7×24 小时自主执行的生产系统"时,任务调度成为决定系统可靠性的关键基础设施。一个生产级 Agent 平台需要同时应对以下挑战:

挑战典型场景IronClaw 的解决方案
多租户隔离不同用户的 Agent 不能相互干扰Docker 容器级隔离,每 Job 独立容器
资源竞争某个 Agent 耗尽 CPU/内存cgroup 限制(2GB 内存 / 1024 CPU shares)
迭代安全Agent 陷入无限循环服务器端强制 max_iterations 上限 500
故障恢复容器崩溃后资源泄漏SandboxReaper + AutonomousRecovery 双保险
凭据安全API Key 泄露风险按需获取 /credentials,不注入环境变量

IronClaw 的调度系统通过三层调度模型Orchestrator/Worker 架构解决了这些挑战。让我们从架构全貌开始,逐层深入。


二、三层调度模型:从宏观目标到微观执行

IronClaw 的调度系统采用清晰的三层架构,将"长期目标"、"线程生命周期"和"运行时分发"解耦:

graph TB
    subgraph Layer1["Layer 1: MissionManager
长期目标管理"] M1["Mission
OneShot / Interval / Cron"] M2["MissionStatus
Active / Paused / Completed"] M1 --> M2 end subgraph Layer2["Layer 2: ThreadManager
线程生命周期管理"] T1["Thread
Chat / Background / Routine / Sub"] T2["ThreadState
Created → Running → Waiting / Completed / Failed"] T3["ThreadTree
父子线程关系追踪"] T1 --> T2 --> T3 end subgraph Layer3["Layer 3: Dispatcher
运行时 Lane 分发"] D1["RuntimeAdapterRequest"] D2["ExtensionPackage + CapabilityDescriptor"] D3["ResourceReservation"] D1 --> D2 --> D3 end Layer1 -->|"Mission 触发"| Layer2 Layer2 -->|"Step 执行"| Layer3 Layer3 -->|"ActionResult"| Layer2 Layer2 -->|"MissionCompleted"| Layer1 style Layer1 fill:#e1f5fe style Layer2 fill:#f3e5f5 style Layer3 fill:#e8f5e9

MissionManagercrates/ironclaw_engine/src/runtime/mission.rs)位于最顶层,负责管理长期运行目标。Mission 按照 OneShotIntervalCron 的节奏触发,自动在预定时间点生成 Thread。

ThreadManagercrates/ironclaw_engine/src/runtime/manager.rs)是调度的核心枢纽。它将 v1 版本中分散的 SessionJobRoutineSub-agent 等概念统一为单一的 Thread 原语,通过 ThreadType 区分用途,通过 ThreadState 状态机管理生命周期。

Dispatchercrates/ironclaw_dispatcher/src/lib.rs)是最底层的运行时分发层。它的设计哲学是"组合而非解析"——不自己解析 Extension Manifest、不实施沙箱策略、不预留预算,仅将已经过验证的 Capability Descriptor 路由到对应的运行时 Lane。


三、Orchestrator/Worker 模式:分布式执行的核心架构

IronClaw 采用经典的 Orchestrator-Worker 模式将控制平面与执行平面分离:

graph LR
    subgraph Orchestrator["Orchestrator (src/orchestrator/)"]
        API["api.rs
~800行"] AUTH["auth.rs
~400行"] JM["job_manager.rs
~1542行"] RP["reaper.rs
~969行"] MOD["mod.rs"] end subgraph Worker["Worker (src/worker/)"] WA["api.rs
~300行"] PLLM["proxy_llm.rs
~400行"] WC["container.rs
~350行"] WJ["job.rs
~500行"] ACP["acp_bridge.rs
~400行"] CLB["claude_bridge.rs
~500行"] AR["autonomous_recovery.rs
~300行"] end subgraph Sandbox["Docker Sandbox"] DC["Worker Container
沙箱执行环境"] end Orchestrator -- "HTTP/API" --> Worker Worker -- "沙箱创建请求" --> Sandbox Sandbox -- "日志/结果返回" --> Worker Worker -- "状态/心跳上报" --> Orchestrator style Orchestrator fill:#fff3e0 style Worker fill:#e3f2fd style Sandbox fill:#fce4ec

3.1 Orchestrator 的职责

Orchestrator 是 IronClaw 的主二进制文件,承担中央控制节点的角色:

模块职责规模
api.rsWorker HTTP API 端点(LLM 代理、状态上报、心跳)~800 行
auth.rs凭据授权(OsRng 生成安全令牌)、TokenStore~400 行
job_manager.rs容器生命周期管理、Job 状态机维护~1542 行
reaper.rs后台清理孤儿容器~969 行

3.2 Worker 的三种工作模式

Worker 是实际执行 Agent 逻辑的容器化进程,支持三种 JobMode

// src/orchestrator/job_manager.rs
pub enum JobMode {
    /// 标准 IronClaw Worker,LLM 调用通过 Orchestrator 代理
    Worker,
    /// 直接启动 `claude` CLI 的桥接模式
    ClaudeCode,
    /// 启动任何 ACP(Agent Client Protocol)兼容 Agent
    Acp,
}

三种模式的设计意图非常清晰:Worker 模式是标准 LLM 代理,所有 LLM 请求通过 proxy_llm.rs 转发到 Orchestrator 的 /worker/{id}/llm/complete 端点;ClaudeCode 模式是 Claude CLI 的桥接实现,在容器内安装 @anthropic-ai/claude-code@latestAcp 模式则是开放的协议桥接,可对接任何符合 ACP 规范的第三方 Agent。

3.3 Job 状态定义

每个 Job 的生命周期都在 Orchestrator 的 JobManager 中被精确追踪:

// src/orchestrator/job_manager.rs - Job 状态与创建参数

pub struct JobCreationParams {
    /// Worker 的凭据授权(通过 `/credentials` 端点按需获取)
    pub credential_grants: Vec<CredentialGrant>,
    /// 可选:指定挂载哪些 MCP 服务器
    pub mcp_servers: Option<Vec<String>>,
    /// Worker Agent 循环的最大迭代次数(服务器端限制为 1..=500)
    pub max_iterations: Option<u32>,
    /// ACP 模式下的 Agent 定义
    pub acp_agent: Option<AcpAgentConfig>,
    /// MCP 服务器配置(JSON 格式)
    pub mcp_config_json: Option<serde_json::Value>,
}

// Job 状态机
// Created -> Starting -> Running -> [Completing | Failed | Cancelled] -> Cleaned

max_iterations 的服务器端限制尤其值得关注:const MAX_WORKER_ITERATIONS: u32 = 500;。这个硬上限防止了 Agent 陷入无限循环消耗资源,是生产系统的关键安全阀。


四、Docker Sandbox 架构:四级镜像体系与安全策略

4.1 四级 Docker 镜像

IronClaw 定义了 4 个专门的 Docker 镜像,各司其职:

镜像Dockerfile用途关键特征
主应用Dockerfile云端部署cargo-chef 分层缓存、WASM 目标预装、panic=abort
Worker 沙箱Dockerfile.worker沙箱化 Job 执行非 root 用户(UID 1000)、内置开发工具链、Claude CLI
测试网关Dockerfile.test轻量级 Web 测试libSQL 后端、端口 3003、沙箱禁用
WASM 构建沙箱docker/sandbox.DockerfileWASM 组件构建双目标 wasm32-wasip2 + wasm32-unknown-unknown

4.2 安全策略:纵深防御

Worker 沙箱的安全模型体现了"最小权限原则"的每一层:

stateDiagram-v2
    [*] --> PullingImage : Orchestrator 创建 Job
    PullingImage --> Configuring : 拉取镜像完成
    Configuring --> Running : 配置环境变量与挂载

    Running --> Streaming : 启动容器,流式日志
    Streaming --> Completed : 正常退出
    Streaming --> Failed : 异常退出 / 超时
    Streaming --> Cancelled : 用户取消 / Token 过期

    Completed --> Cleaning : 自动清理
    Failed --> Cleaning : 自动清理
    Cancelled --> Cleaning : 自动清理

    Cleaning --> [*] : 容器移除

    Running --> Orphaned : Orchestrator 崩溃
    Orphaned --> Reaped : SandboxReaper 扫描移除
    Reaped --> [*]

    note right of Configuring
        安全配置:
        - User: sandbox (UID 1000)
        - Memory: 2GB
        - CPU: 1024 shares
        - Privileged: false
        - 无环境变量注入凭据
    end note

4.3 Docker 安全配置代码

以下是 Worker 容器的核心安全配置,使用 bollard crate 与 Docker API 交互:

// src/sandbox/config.rs - 资源限制与沙箱策略

pub struct ResourceLimits {
    pub memory_mb: u64,      // 默认: 2048 MB
    pub cpu_shares: i64,     // 默认: 1024
    pub timeout_secs: u64,   // 默认: 300
}

pub enum SandboxPolicy {
    ReadOnly,   // 只读文件系统访问
    ReadWrite,  // 读写文件系统访问
    FullAccess, // 需要 SANDBOX_ALLOW_FULL_ACCESS 环境变量
}

// 凭据获取方式:Worker 通过 /credentials 端点按需拉取
// 绝不通过环境变量注入敏感信息
const CONTAINER_LABEL: &str = "ironclaw.job_id";

关键安全设计:所有容器以非特权用户 sandbox(UID 1000)运行,Privileged 设为 false,内存限制 2GB,CPU shares 1024。所有出站流量通过 host.docker.internal 上的 HTTP 代理路由,凭据绝不以环境变量形式注入容器——Worker 需通过认证后的 /credentials 端点按需获取授权令牌。

4.4 容器执行输出

// src/sandbox/container.rs - 容器执行结果

pub struct ContainerOutput {
    pub exit_code: i64,      // 进程退出码
    pub stdout: String,      // 标准输出
    pub stderr: String,      // 标准错误
    pub duration: Duration,  // 执行耗时
    pub truncated: bool,     // 输出截断标志(防止日志爆炸)
}

五、Routine/Hooks 系统:声明式生命周期管理

5.1 Hooks 系统

Hooks(src/hooks/)提供了事件驱动的生命周期回调和声明式 Bundle 机制:

文件职责
mod.rsHook 注册表与执行引擎
hook.rs核心 Hook trait 与实现
registry.rsHook 注册与查找
bootstrap.rs引导 Hook Bundle
bundled.rs内置 Hook 集合
session_summary.rs会话摘要生成

Hook 类型覆盖 Agent 全生命周期:启动时、停止时、出错时触发回调;声明式 Bundle 预配置常见场景的 Hook 组合;SessionSummaryHook 以可配置间隔生成周期性洞察。

5.2 Webhooks 入口

Webhooks(src/webhooks/mod.rs)提供通用的主机验证 Webhook 入口,端点为 /webhook/tools/{tool}。在接收 Webhook 之前进行主机验证,然后根据工具名称路由到对应的处理逻辑,全程进行安全校验的负载处理。

5.3 进程管理:ironclaw_processes crate

ironclaw_processes 是 IronClaw 的进程生命周期管理基础设施,提供完整的进程管理能力:

ironclaw_processes/
  src/
    lib.rs              - 公共 API 与 re-export
    types.rs            - 核心 trait: ProcessStore, ProcessExecutor, ProcessManager
    cancellation.rs     - 协作式取消令牌 + 进程级注册表
    host.rs             - ProcessHost: 读取/轮询/等待/取消 接口
    memory_store.rs     - 内存版 ProcessStore / ProcessResultStore
    filesystem_store.rs - 持久化文件系统后端
    wrappers.rs         - 可组合装饰器(Eventing, ResourceManaged)
    services.rs         - BackgroundProcessManager, ProcessServices

核心 trait 设计简洁明了:ProcessStore 负责进程记录的 CRUD,ProcessResultStore 负责执行结果的存取,ProcessExecutor 负责实际执行逻辑,ProcessManager 负责高层编排。ProcessCancellationRegistry 追踪所有活跃进程,ResourceManagedProcessStore 确保失败时的资源清理,EventingProcessStore 则对外发出事件供监控与恢复使用。


六、Heartbeat 心跳系统:活性检测与自动恢复

6.1 心跳时序

Worker 通过定期心跳向 Orchestrator 报告存活状态:

sequenceDiagram
    participant W as Worker Container
    participant O as Orchestrator
    participant DR as Docker Daemon
    participant SD as systemd

    rect rgb(225, 245, 254)
        Note over W,O: 正常心跳循环
        loop 每 30-60 秒
            W->>O: POST /worker/{job_id}/heartbeat
            O-->>W: 200 OK(续期超时计时器)
        end
    end

    rect rgb(255, 243, 224)
        Note over W,O: Worker 无响应场景
        W-xO: 心跳超时(未收到)
        O->>O: 标记 Job 为 Failed
        O->>DR: 强制移除容器
        O->>O: 触发 Reaper 扫描
    end

    rect rgb(232, 245, 233)
        Note over DR,SD: 基础设施级健康检查
        loop 每 5 秒
            DR->>DR: pg_isready 健康检查
        end
        SD->>SD: Restart=always
RestartSec=5 end

6.2 Heartbeat 端点实现

// Worker 端:定期发送心跳
// src/worker/api.rs - WorkerHttpClient

pub struct WorkerHttpClient {
    client: reqwest::Client,
    orchestrator_url: String,
    job_id: Uuid,
}

impl WorkerHttpClient {
    /// 发送心跳信号,防止 Orchestrator 判定 Job 超时
    pub async fn send_heartbeat(&self) -> Result<(), WorkerError> {
        let url = format!(
            "{}/worker/{}/heartbeat",
            self.orchestrator_url, self.job_id
        );
        self.client
            .post(&url)
            .send()
            .await?
            .error_for_status()?;
        Ok(())
    }
}

6.3 多层健康监测

IronClaw 的健康监测是"Defense in Depth"的典范:

  1. 应用层:Worker 定期 POST /worker/{id}/heartbeat,Orchestrator 据此续期 Job 超时计时器
  2. 容器层:Docker Compose 配置 pg_isready 健康检查,每 5 秒检测 PostgreSQL 可用性
  3. 主机层:systemd 服务配置 Restart=always,服务崩溃后 5 秒内自动重启

七、Self-repair 自修复机制:三重重试策略

生产系统中的故障不可避免,IronClaw 通过三重自修复机制构建韧性:

graph TB
    subgraph WorkerSide["Worker 侧:AutonomousRecovery"]
        A1["错误检测"]
        A2["指数退避重试"]
        A3["LLM 代理失败优雅降级"]
        A4["恢复状态保持"]
        A1 --> A2 --> A3 --> A4
    end

    subgraph OrchestratorSide["Orchestrator 侧:SandboxReaper"]
        B1["每 300s 扫描"]
        B2["检测 ironclaw.job_id 标签容器"]
        B3["对比 ContextManager 活跃 Job"]
        B4["容器存活 > 600s 且无活跃 Job?"]
        B5["强制移除孤儿容器"]
        B1 --> B2 --> B3 --> B4 -->|"是"| B5
        B4 -->|"否"| B1
    end

    subgraph RetryLayer["瞬态故障重试:TransientFailureRetry"]
        C1["max_retries: 3"]
        C2["base_delay: 1s"]
        C3["max_delay: 30s"]
        C1 --> C2 --> C3
    end

    style WorkerSide fill:#e8f5e9
    style OrchestratorSide fill:#fff3e0
    style RetryLayer fill:#fce4ec

7.1 SandboxReaper:孤儿容器清理

reaper.rs 解决了 Agent 进程在"容器创建"和"清理"之间崩溃导致的资源泄漏问题:

// src/orchestrator/reaper.rs - SandboxReaper 配置

pub struct ReaperConfig {
    /// 扫描孤儿容器的间隔(默认:300 秒 = 5 分钟)
    pub scan_interval: Duration,
    /// 孤儿判定阈值:容器存活超过此时长且无对应活跃 Job(默认:600 秒 = 10 分钟)
    pub orphan_threshold: Duration,
    /// 用于关联 Job ID 的容器标签键
    pub container_label: String,  // 默认: "ironclaw.job_id"
}

impl Default for ReaperConfig {
    fn default() -> Self {
        Self {
            scan_interval: Duration::from_secs(300),    // 5 min
            orphan_threshold: Duration::from_secs(600), // 10 min
            container_label: "ironclaw.job_id".to_string(),
        }
    }
}

Reaper 算法简洁高效:

  1. 每 300 秒扫描 Docker 中所有带 ironclaw.job_id 标签的容器
  2. 对每个容器,检查 ContextManager 中是否存在对应的活跃 Job
  3. 如果 Job 已不存在(或已完成)且容器存活时间超过 600 秒
  4. 立即强制移除该孤儿容器

7.2 AutonomousRecovery:Worker 侧自主恢复

autonomous_recovery.rs 位于 Worker 端,负责错误检测与恢复:

  • 指数退避重试策略,避免对故障服务造成雪崩效应
  • LLM 代理失败时的优雅降级(切换到备用策略)
  • 恢复过程中保持执行状态,不丢失上下文

7.3 瞬态故障重试配置

// src/sandbox/manager.rs - 瞬态故障重试配置

pub struct RetryConfig {
    pub max_retries: u32,      // 默认: 3 次
    pub base_delay_ms: u64,    // 默认: 1000 ms(1 秒)
    pub max_delay_ms: u64,     // 默认: 30000 ms(30 秒)
}

重试采用指数退避算法:第 1 次等待 1 秒,第 2 次等待 2 秒,第 3 次等待 4 秒……直到达到 30 秒上限。这种策略既不会过早放弃(最多 3 次),也不会对故障服务造成过大压力。


八、资源管理与并发控制

8.1 并发模型

IronClaw 的并发管理采用 Rust 的 Arc<RwLock<...>> 模式,在 Tokio 异步运行时上实现高效的并发控制:

层级机制数据结构
Docker 级每 Job 独立容器HashMap<Uuid, JobState>
运行时Tokio asyncArc<RwLock<...>>
取消协作式取消ProcessCancellationToken
清理Job 完成/超时自动触发drop() + reaper 兜底

8.2 凭据安全架构

IronClaw 的凭据安全设计体现了"零信任"理念:

graph LR
    subgraph OrchestratorSecrets["Orchestrator 安全区"]
        TS["TokenStore"]
        OR["OsRng
密码学安全随机数"] end subgraph WorkerContainer["Worker 容器(最小权限)"] CG["CredentialGrant
限定范围令牌"] API["/credentials 端点
按需获取"] end UserSecrets["用户主密钥"] -->|"生成"| TS OR -->|"派生"| CG TS -->|"响应请求"| API API -->|"拉取(非推送)"| CG style OrchestratorSecrets fill:#e8f5e9 style WorkerContainer fill:#fff3e0

核心原则

  • 主密钥始终存储在 Orchestrator 的 TokenStore 中,永不离开安全区
  • 每个 Job 获得独立生成的 CredentialGrant 令牌,作用域严格限定
  • Worker 通过认证的 /credentials 端点按需拉取凭据(Pull 模式),而非 Orchestrator 主动推送
  • 绝不将凭据注入容器的环境变量

九、总结:IronClaw 调度系统的五大设计哲学

回顾 IronClaw 的任务调度与并行执行架构,可以提炼出五条核心设计哲学:

  1. 分层解耦:MissionManager → ThreadManager → Dispatcher 的三层架构,每一层只关注自己的抽象层次,上层不关心下层的实现细节。

  2. 容器级隔离:每 Job 独占一个 Docker 容器,配合非 root 用户、资源限制、代理隔离,实现真正的多租户安全。

  3. Fail-safe 默认max_iterations 服务器端硬上限、GateDecisionNone 变体(fail-closed)、SandboxReaper 自动兜底清理——系统默认为安全状态。

  4. 按需授权:凭据通过 /credentials 端点按需获取,不注入环境变量,每 Job 独立令牌,最小权限原则贯穿始终。

  5. 韧性设计:瞬态故障重试(指数退避)、AutonomousRecovery 状态保持、SandboxReaper 孤儿清理、systemd 自动重启——多层故障恢复机制确保系统在任何单点故障下都能自动恢复。

这套调度系统使 IronClaw 能够在单节点上可靠地并发执行数十个 Agent Job,同时保持严格的资源隔离和安全边界。对于正在构建 Agent 平台的开发者来说,IronClaw 的 Orchestrator/Worker 模式、Docker 沙箱策略和自修复机制都是极具参考价值的设计范例。


系列文章导航

  • (一)架构总览与核心组件
  • (二)Engine v2:统一执行引擎与 CodeAct 模式
  • (三)Docker 沙箱安全模型深度解析
  • (四)Capability 系统与执行门控
  • (五)任务调度与并行执行——多 Job 并发模型 ← 本文
  • (六)事件驱动架构与状态持久化

参考资料: