openclaw源码解读(12)LLM↔Tool 核心循环
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) 结构,通过多个状态变量控制循环终止或继续重试。它的职责:
- 重试控制:失败了怎么办?换模型?换 profile?退避?
- 状态管理:token 用量累计、上下文恢复、session prompt 持久化
- 故障处理:rate limit、auth error、overload、timeout、idle timeout
- 终端判断:什么时候算"完成"?什么时候继续?
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、模型、耗时、用量、最终可见文本等。 -
错误信息(如果失败)。
设计亮点
-
高度模块化:每个功能模块(认证、上下文、重试、超时)都封装为独立单元。
-
弹性设计:支持多级重试、认证切换、模型回退,确保高可用性。
-
成本控制:通过空闲超时断路器、循环检测、用量累计,防止失控消耗。
-
可观测性:详细日志、阶段标记、进度通知,便于调试和监控。
-
资源管理:显式清理上下文引擎和 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 执行引擎的运作机制和设计哲学。
更多推荐


所有评论(0)