OpenClaw 中有两层 run-loop:

文件

行数

职责

cli/gateway-cli/run-loop.ts

1026

外层循环:gateway 进程的生命周期管理(启动→监听→重启→停止)

agents/embedded-agent-runner/run-loop.ts

669

内层循环:单个 Agent 执行的 LLM↔Tool 闭循环

我们重点讲内层循环的run-loop.ts

这是 OpenClaw 嵌入式 Agent 运行循环(Run Loop) 的核心实现。它负责驱动 Agent 的一次完整推理会话,包括多次尝试、重试、模型切换、错误恢复和资源清理,是整个 Agent 执行引擎的“心脏”。

文件定位与职责

  • 文件路径src/agents/embedded/run-loop.ts

  • 核心导出runPreparedEmbeddedLoop(input: PreparedEmbeddedRunInput): Promise<EmbeddedAgentRunResult>

  • 调用方:由 run-orchestrator.ts 中的 executePreparedEmbeddedRun 调用。

  • 职责

    • 管理一次 Agent 运行的完整生命周期(从开始到结束)。

    • 处理模型调用、工具执行、上下文管理、重试逻辑、错误恢复。

    • 支持认证轮换、模型回退、超时控制、循环检测。

    • 负责清理资源(如 MCP 运行时、上下文引擎)。


整体架构

runPreparedEmbeddedLoop(input)

├── 初始化阶段 ──────────────────────────────

│   ├── prepareEmbeddedRunRuntime()    → 运行时准备(模型/认证/harness)

│   ├── 创建各类状态管理器:

│   │   ├── contextRecoveryState       ← 上下文恢复状态

│   │   ├── usageAccumulator           ← token 用量累加

│   │   ├── terminalRetryState         ← 终端重试

│   │   ├── idleTimeoutBreakerState    ← 空闲超时断路器

│   │   ├── postCompactionGuard        ← 压缩后循环检测

│   │   ├── sessionPromptState         ← 会话提示词状态

│   │   └── failoverRetryController    ← 故障转移重试控制器

│   ├── ensureContextEnginesInitialized()

│   └── resolveContextEngine()          → 初始化上下文引擎(管理 prompt 组装)                    (contextEngine,通过 resolveContextEngine 获取)

└── 主循环 while(true) ─────────────────────

    │

    │  ┌───────────────────────────────────

    │  │  检查:超出最大重试次数?→ 返回失败 L277-311

    │  │  检查:overload 断路器触发?→ 返回失败 L312-320

    │  │

    │  ├─ ① prepareAndDispatchEmbeddedRunAttempt() L321-344

    │  │     → 组装 prompt → 调 LLM → 解析 tool_calls → 执行工具 → 组装下一轮 prompt

    │  │     → 返回 dispatchedAttempt

    │  │

    │  ├─ ② normalizeEmbeddedRunAttempt()

    │  │     → 标准化尝试结果

    │  │     → 判断 action: "complete" | "retry"

    │  │     → 提取 aborted/timedOut/terminalOutcome 等状态

    │  │

    │  ├─ ③ recoverEmbeddedRunAttempt()

    │  │     → 如果需要恢复:rotate profile、fallback model、重试

    │  │     → 判断 action: "complete" | "retry"

    │  │

    │  ├─ ④ handleEmbeddedAssistantFailure()

    │  │     → 处理 LLM 助手故障:

    │  │     │   ├── 超时?→ 重试/切换模型

    │  │     │   ├── rate limit?→ 换 profile/退避重试

    │  │     │   ├── auth error?→ 刷新 token/换 profile

    │  │     │   ├── overload?→ 换 profile

    │  │     │   └── 其他?→ 标记失败

    │  │     → 判断 action: "retry" | proceed

    │  │

    │  ├─ ⑤ prepareEmbeddedRunTerminal()

    │  │     → 组装最终输出(visible text, raw text, tool summary, agentMeta)

    │  │

    │  ├─ ⑥ resolveEmbeddedRunTerminalTimeout()

    │  │     → 超时场景的特殊处理

    │  │

    │  └─ ⑦ resolveEmbeddedRunTerminal()

    │        → 最终判断:完成 OR 重试?

    │        → "complete" → 返回 EmbeddedAgentRunResult

    │        → "retry"   → continue 回到循环顶部

    │

    └── finally 清理:

        ├── 清理 prompt build cache

        ├── 停 runtime auth refresh timer

        ├── contextEngine.dispose()

        └── 清理 bundle MCP runtime


1. 初始化阶段

  • 解析输入参数,构建运行时环境(prepareEmbeddedRunRuntime)。

  • 初始化各种状态管理器:

    • usageAccumulator:累计 token 用量

    • contextRecoveryState:上下文恢复状态

    • idleTimeoutBreakerState:空闲超时断路器

    • postCompactionGuard:压缩后循环检测

    • sessionPromptState:会话提示词状态

    • failoverRetryController:故障转移重试控制器

  • 初始化上下文引擎(contextEngine,通过 resolveContextEngine 获取)。

2. 核心执行流程(while 循环)L275-621l

整个运行循环是一个 while (true) 结构,通过多个状态变量控制循环终止或继续重试。它的职责:

  1. 重试控制:失败了怎么办?换模型?换 profile?退避?
  2. 状态管理:token 用量累计、上下文恢复、session prompt 持久化
  3. 故障处理:rate limit、auth error、overload、timeout、idle timeout
  4. 终端判断:什么时候算"完成"?什么时候继续?

while (未终止) {

  ① 构建 prompt(system + context + tools + history)

  ② 调用 LLM

  ③ 解析响应(text / tool_calls / thinking)

  ④ 如果有 tool_calls → 执行工具 → 把结果塞回上下文 → goto ②

  ⑤ 如果没有 tool_calls → 这是最终回复 → 终止

}

  处理:compaction(上下文超长时压缩)、failover(模型挂了换一个)、重试


2. 循环迭代(每次尝试)

每次迭代执行一次 Agent 推理尝试,步骤包括:

a. 准备与调度尝试
  • 调用 prepareAndDispatchEmbeddedRunAttempt,构造本次尝试所需的参数。

  • 规范化尝试(normalizeEmbeddedRunAttempt),处理可能的重试或完成条件。

  • 如果尝试成功完成,直接返回结果。

b. 尝试恢复与错误处理
  • 调用 recoverEmbeddedRunAttempt,处理超时、中断、认证失败等异常。

  • 如果恢复后决定重试,则更新状态并继续循环。

  • 如果恢复后决定完成,则返回结果。

c. 处理 Assistant 失败
  • 调用 handleEmbeddedAssistantFailure,分析失败原因(如空响应、推理循环、认证失败、超时等)。

  • 根据失败类型决定是否重试、切换模型/认证配置,或最终失败。

d. 终端处理与结果返回
  • 如果无法恢复,则调用 prepareEmbeddedRunTerminal 构建最终结果。

  • 通过 resolveEmbeddedRunTerminalTimeout 和 resolveEmbeddedRunTerminal 判断是否需要重试或返回最终结果。

  • 如果终端决定重试,则继续循环;否则返回最终结果。


 关键点

1. Token 用量控制

const usageAccumulator = createUsageAccumulator();

let lastRunPromptUsage: ReturnType<typeof normalizeUsage> | undefined;

// 每次 LLM 调用后累加 token 使用量 可用于:子 Agent 的 token 预算控制

2. 上下文引擎(Context Engine)

const contextEngine = await resolveContextEngine(params.config, {...});

// 管理 prompt 组装(system prompt + 历史 + tools + attachment) ,压缩时由 compactionRuntime 调用

3. Tool 结果观察

const observeToolOutcome = (observation: ToolOutcomeObservation): void => {

// 观察每个 tool 调用的结果:追踪 tool call ordinal(调用序号) ;检测 tool 循环(post-compaction guard) ;如果检测到死循环 → abort

};

4. 多 Agent 的 session prompt 状态

const sessionPromptState = createEmbeddedRunSessionPromptState({

  runParams: params,

  sessionAgentId,

  resolvedSessionKey,

  lifecycleGeneration,

});

// 管理 session 的 prompt 持久化,子 Agent 执行完成后,prompt 状态更新到 session

5. 故障转移

const failoverRetryController = createEmbeddedRunFailoverRetryController({...});

// 当当前模型/profile不可用时:

//   → rotate profile(换 API key)

//   → fallback model(换备选模型)

//   → rate limit backoff(退避重试)


关键组件与状态管理

组件 作用
preparedRuntime 封装了模型、认证配置、插件信息等运行时上下文。
contextEngine 负责管理对话历史、压缩、上下文窗口等。
sessionPromptState 管理当前会话的提示词、持久化状态。
usageAccumulator 累计 token 消耗,用于计费和限流。
failoverRetryController 控制模型故障转移、重试次数、认证轮换。
postCompactionGuard 检测压缩后是否陷入循环(工具重复调用)。
idleTimeoutBreakerState 检测长时间空闲,防止成本失控。
terminalRetryState 管理终端重试(如空响应、仅推理输出)。
contextRecoveryState 记录上下文恢复尝试,用于智能回退。

重试与容错机制

  • 最大重试次数:由 MAX_RUN_LOOP_ITERATIONS 控制,基于可用认证配置数。

  • 认证轮换:如果认证失败,通过 advanceAttemptAuthProfile 切换到下一个 API Key。

  • 模型回退:如果当前模型持续失败,可触发 fallback 模型(通过 fallbackConfigured 决定)。

  • 超时处理

    • timedOut:整体超时

    • idleTimedOut:空闲超时

    • timedOutDuringCompaction:压缩阶段超时

    • timedOutDuringToolExecution:工具执行超时

  • 空响应/仅推理循环:针对模型返回空文本或仅推理内容,进行有限次重试。

  • 断路器

    • idleTimeoutBreakerState:防止长时间无响应导致成本失控。

    • postCompactionGuard:防止压缩后工具调用循环。


上下文引擎与会话管理

  • 上下文引擎(contextEngine)负责维护对话历史、压缩、截断等。

  • 每次尝试前,会通过 buildEmbeddedContextEngineRuntimeSettings 构建运行时设置(如 token 预算、回退原因)。

  • 会话状态(sessionPromptState)管理当前激活的提示词和持久化控制。


清理与资源回收

  • 在 finally 块中执行:

    • 调用 maybeEmitFastModeAutoResetBestEffort(快速模式自动重置)。

    • 停止认证刷新计时器。

    • 执行 runAgentCleanupStep 清理上下文引擎资源。

    • 如果 cleanupBundleMcpOnRunEnd 为 true,则退出会话的 MCP 运行时。


结果输出

最终返回 EmbeddedAgentRunResult,包含:

  • 回复内容(payloads

  • 元数据(meta):包括会话 ID、模型、耗时、用量、最终可见文本等。

  • 错误信息(如果失败)。


设计亮点

  1. 高度模块化:每个功能模块(认证、上下文、重试、超时)都封装为独立单元。

  2. 弹性设计:支持多级重试、认证切换、模型回退,确保高可用性。

  3. 成本控制:通过空闲超时断路器、循环检测、用量累计,防止失控消耗。

  4. 可观测性:详细日志、阶段标记、进度通知,便于调试和监控。

  5. 资源管理:显式清理上下文引擎和 MCP 运行时,避免资源泄漏。


与其他模块的关系

  • 被 executePreparedEmbeddedRun 调用(位于 run-execution.ts)。

  • 依赖 prepareEmbeddedRunRuntime(位于 run/runtime-preparation.ts)准备运行时。

  • 依赖 handleEmbeddedAssistantFailure(位于 run/assistant-failure.ts)处理失败。

  • 依赖 prepareAndDispatchEmbeddedRunAttempt(位于 run/attempt-dispatch-preparation.ts)调度尝试。


总结

run-loop.ts 是 OpenClaw 嵌入式 Agent 运行循环的核心实现,它协调了尝试执行、错误恢复、重试策略、资源清理等复杂逻辑。通过分层、模块化的设计,它实现了高可用、可扩展、可监控的 Agent 运行时环境。理解这份源码,有助于掌握 OpenClaw 整个 Agent 执行引擎的运作机制和设计哲学。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐