Skip to content

OpenClaw 源码解读(五)Agent 智能体系统

本文基于 OpenClaw 2026.3.2 源码,深入解读 Agent 智能体系统的架构设计与实现细节。


一、模块概览

Agent 智能体系统是 OpenClaw 的"大脑",负责接收用户消息、调用 AI 模型、执行工具、管理对话上下文,并将回复发送回用户。它是整个系统中最复杂、文件最多的模块,也是连接渠道层和 AI 模型层的核心枢纽。

1.1 核心源码分布

目录/文件行数(约)职责
src/agents/pi-embedded-runner/run.ts~900+核心运行引擎(最重要的文件)
src/agents/pi-embedded-runner/run/attempt.ts~360+单次 AI 调用尝试(工具循环)
src/agents/pi-embedded-runner/runs.ts~400+运行队列管理与并发控制
src/agents/pi-embedded-subscribe.ts~1000+流式输出订阅与事件分发
src/agents/pi-embedded-runner/system-prompt.ts~200+系统提示词组装
src/agents/openclaw-tools.ts~216内置工具集合注册
src/agents/tool-catalog.ts~326工具目录定义与 Profile 管理
src/agents/tools/common.ts~100+工具公共抽象
src/agents/context.ts~120Agent 运行上下文
src/agents/compaction.ts~400+上下文压缩(长对话摘要化)
src/agents/model-auth.ts~350+模型认证(API Key / OAuth / AWS)
src/agents/model-fallback.ts~350+模型故障转移链
src/agents/agent-scope.ts~280+Agent 配置解析与工作区路径
src/agents/defaults.ts~280+Agent 默认配置与模型解析
src/agents/session-write-lock.ts~300+会话文件分布式锁
src/agents/context-window-guard.ts~60+上下文窗口大小保护
src/agents/lanes.ts~4运行车道定义
src/agents/system-prompt.ts~100+系统提示词核心逻辑

1.2 系统全景

┌───────────────────────────────────────────────────────────────────────┐
│                      Agent 智能体系统全景                              │
│                                                                       │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │                    入站消息(来自 Channel 系统)                  │  │
│  └──────────────────────────┬──────────────────────────────────────┘  │
│                             │                                         │
│                             ▼                                         │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │                  Run Queue(运行队列)                            │  │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐                      │  │
│  │  │ Session 1│  │ Session 2│  │ Session 3│  ...                  │  │
│  │  │ (排队中) │  │ (运行中) │  │ (排队中) │                      │  │
│  │  └──────────┘  └──────────┘  └──────────┘                      │  │
│  └──────────────────────────┬──────────────────────────────────────┘  │
│                             │                                         │
│                             ▼                                         │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │                  Run Engine(运行引擎)                           │  │
│  │                                                                   │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │  │
│  │  │ Model Auth  │  │ System      │  │ Context     │             │  │
│  │  │ 模型认证    │  │ Prompt      │  │ Window      │             │  │
│  │  │ Profile轮询 │  │ 系统提示词   │  │ 窗口保护    │             │  │
│  │  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘             │  │
│  │         │                │                │                     │  │
│  │         ▼                ▼                ▼                     │  │
│  │  ┌──────────────────────────────────────────────┐               │  │
│  │  │           Attempt Loop(尝试循环)             │               │  │
│  │  │                                                │               │  │
│  │  │  ┌──────┐  ┌─────────┐  ┌──────┐  ┌───────┐ │               │  │
│  │  │  │Model │→│ Stream  │→│ Tool │→│ Model │ │               │  │
│  │  │  │ Call │  │ Result  │  │ Exec │  │ Call  │ │               │  │
│  │  │  └──────┘  └─────────┘  └──────┘  └───────┘ │               │  │
│  │  │     ↑           │           │          │     │               │  │
│  │  │     └───────────┴───────────┴──────────┘     │               │  │
│  │  │              (工具循环直到无工具调用)            │               │  │
│  │  └──────────────────────────────────────────────┘               │  │
│  │                             │                                     │  │
│  │              ┌──────────────┼──────────────┐                     │  │
│  │              ▼              ▼              ▼                     │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │  │
│  │  │ Compaction  │  │ Fallback    │  │ Auth Profile│             │  │
│  │  │ 上下文压缩   │  │ 模型切换    │  │ Key 轮询    │             │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘             │  │
│  └──────────────────────────┬──────────────────────────────────────┘  │
│                             │                                         │
│                             ▼                                         │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │                Subscribe(流订阅系统)                            │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐          │  │
│  │  │text_delta│ │tool_call │ │reasoning │ │usage     │          │  │
│  │  │ 文本流   │ │ 工具调用  │ │ 推理过程  │ │ 用量统计  │          │  │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘          │  │
│  └──────────────────────────┬──────────────────────────────────────┘  │
│                             │                                         │
│                             ▼                                         │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │              出站回复(返回 Channel 系统)                        │  │
│  └─────────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────────┘

![Agent 智能体系统全景架构](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/01-infographic-agent-system-overview-1775150695696.png)


二、运行引擎——最核心的 run.ts

2.1 函数签名

typescript
// src/agents/pi-embedded-runner/run.ts
export async function runEmbeddedPiAgent(
  params: RunEmbeddedPiAgentParams
): Promise<RunEmbeddedPiAgentResult>

这个函数是整个 Agent 系统的入口。它有 60+ 个参数,是整个项目中参数最多的函数之一。

关键参数分组:

分组参数说明
会话标识sessionId, sessionKey, sessionFile会话文件和键
消息输入prompt, images, trigger用户消息和触发方式
渠道上下文messageChannel, agentAccountId, messageTo, groupId消息来源渠道
模型配置config, thinkLevel, reasoningLevel, verboseLevel模型和推理配置
流回调onPartialReply, onBlockReply, onReasoningStream, onToolResult流式输出回调
控制abortSignal, timeoutMs, disableTools中断和超时控制

2.2 九阶段启动序列

runEmbeddedPiAgent 的执行分为九个阶段:

阶段 1: Workspace 解析
  ├── resolveAgentWorkspaceDir() → 确定工作区路径
  ├── resolveAgentDir() → 确定 Agent 数据目录
  └── ensureAuthProfileStore() → 加载认证 Profile 存储

阶段 2: 模型解析
  ├── resolveRunModel() → 解析模型名称(provider/model)
  ├── resolveContextWindowInfo() → 确定上下文窗口大小
  └── evaluateContextWindowGuard() → 检查窗口是否过小

阶段 3: Auth Profile 候选列表
  ├── resolveAuthProfileCandidates() → 按优先级排列 API Key
  └── isProfileInCooldown() → 跳过冷却中的 Profile

阶段 4: API Key 激活
  ├── resolveApiKeyForProfile() → 获取 Key
  ├── refreshCopilotToken() → GitHub Copilot 特殊处理
  └── applyApiKeyInfo() → 应用到当前运行

阶段 5: 系统提示词构建
  ├── buildSystemPrompt() → 组装系统提示词
  ├── runBeforePromptBuild hook → 插件前置钩子
  └── 注入渠道特化提示 + 工具说明

阶段 6: 主运行循环
  ├── runEmbeddedAttempt() → 执行一次 AI 调用
  ├── 处理工具调用循环
  └── 处理流式输出事件

阶段 7: 错误恢复
  ├── 上下文溢出 → 自动压缩(compaction)
  ├── 认证失败 → 切换 Auth Profile
  ├── thinking 不支持 → 降级 think level
  └── 模型不可用 → 故障转移到 fallback 模型

阶段 8: 用量统计
  ├── 累加 token 使用量
  └── 记录 prompt/completion 分布

阶段 9: 返回结果
  ├── 构建 payloads(回复内容列表)
  ├── 构建 meta(运行元数据)
  └── 记录 session 元信息

![runEmbeddedPiAgent 九阶段启动序列](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/02-infographic-nine-stage-startup-1775150696538.png)

2.3 主运行循环——核心中的核心

typescript
const MAX_RUN_LOOP_ITERATIONS = resolveMaxRunRetryIterations(profileCandidates.length);

while (true) {
  if (runLoopIterations >= MAX_RUN_LOOP_ITERATIONS) {
    // 超过最大重试次数 → 返回错误
    return { payloads: [{ text: "Request failed...", isError: true }], meta: { ... } };
  }
  runLoopIterations += 1;

  // 1. 执行一次 AI 调用尝试
  const attempt = await runEmbeddedAttempt({ ...params });

  // 2. 分析尝试结果
  if (attempt.aborted) break;
  if (attempt.timedOut) break;

  // 3. 检测上下文溢出
  if (contextOverflowError) {
    if (overflowCompactionAttempts < MAX_OVERFLOW_COMPACTION_ATTEMPTS) {
      // 自动压缩上下文 → 重试
      overflowCompactionAttempts++;
      continue;
    }
    // 压缩次数耗尽 → 工具结果截断 → 重试
    if (!toolResultTruncationAttempted) {
      toolResultTruncationAttempted = true;
      continue;
    }
  }

  // 4. 检测认证错误
  if (assistantErrorText && isFailoverErrorMessage(assistantErrorText)) {
    const reason = classifyFailoverReason(assistantErrorText);
    if (reason === "auth") {
      // Copilot Token 刷新 → 重试
      if (await maybeRefreshCopilotForAuthError(assistantErrorText, copilotAuthRetry)) {
        authRetryPending = true;
        continue;
      }
      // 切换 Auth Profile → 重试
      await markAuthProfileFailure({ store, profileId, reason });
      const advanced = await advanceAuthProfile();
      if (advanced) continue;
    }
    if (reason === "thinking") {
      // thinking 不支持 → 降级
      if (downgradeThinking()) continue;
    }
  }

  // 5. 成功 → 返回结果
  return { payloads: [...], meta: { ... } };
}

2.4 自动恢复机制总结

错误类型恢复策略最大尝试次数
上下文溢出自动压缩(Compaction)3 次
工具结果过长截断工具结果1 次
Auth 认证失败切换 Auth ProfileProfile 数量
Copilot Token 过期刷新 Token1 次
Thinking 不支持降级 think level3 级降级
模型不可用故障转移到 fallbackfallback 数量

![主运行循环与六种自动恢复策略](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/03-infographic-run-loop-recovery-1775150697270.png)


三、Attempt(单次 AI 调用尝试)

3.1 尝试流程

typescript
// src/agents/pi-embedded-runner/run/attempt.ts
export async function runEmbeddedAttempt(params: RunEmbeddedAttemptParams)

每次 attempt 代表一次完整的 AI 模型调用,包含工具循环:

调用模型 API(streaming)


接收流式响应
  ├── text_delta → 推送给 subscribe 系统
  ├── tool_call → 执行工具
  │     ├── 工具成功 → 记录结果 → 再次调用模型
  │     └── 工具失败 → 记录错误 → 再次调用模型
  ├── reasoning → 推理过程(thinking)
  └── message_end → 本轮结束

3.2 工具名称标准化

AI 模型返回的工具调用名称可能有空格、大小写不一致等问题。系统在分发前做了三层标准化:

typescript
function normalizeToolCallNameForDispatch(rawName: string, allowedToolNames?: Set<string>): string {
  const trimmed = rawName.trim();
  // 1. 精确匹配
  if (allowedToolNames.has(trimmed)) return trimmed;
  // 2. 标准化后匹配(下划线/连字符统一等)
  const normalized = normalizeToolName(trimmed);
  if (allowedToolNames.has(normalized)) return normalized;
  // 3. 大小写不敏感匹配
  const folded = trimmed.toLowerCase();
  for (const name of allowedToolNames) {
    if (name.toLowerCase() === folded) return name;
  }
  return trimmed;
}

3.3 Tool Call ID 自动修复

某些模型返回的 tool call 可能缺少 ID 或 ID 有空格。系统自动修复:

typescript
function normalizeToolCallIdsInMessage(message: unknown): void {
  const usedIds = new Set<string>();
  // 收集已有 ID
  for (const block of content) {
    if (isToolCallBlockType(block.type) && typeof block.id === "string") {
      usedIds.add(block.id.trim());
    }
  }
  // 为缺失 ID 的 tool call 生成唯一 ID
  let fallbackIndex = 1;
  for (const block of content) {
    if (!block.id?.trim()) {
      let fallbackId = "";
      while (!fallbackId || usedIds.has(fallbackId)) {
        fallbackId = `call_auto_${fallbackIndex++}`;
      }
      block.id = fallbackId;
      usedIds.add(fallbackId);
    }
  }
}

3.4 Ollama 兼容层

本地 Ollama 模型通过 OpenAI 兼容 API 调用时,需要注入 num_ctx 参数:

typescript
function shouldInjectOllamaCompatNumCtx(params): boolean {
  if (params.model.api !== "openai-completions") return false;
  if (!isOllamaCompatProvider(params.model)) return false;
  return resolveOllamaCompatNumCtxEnabled({ ... });
}

// 自动检测 Ollama 端点
function isOllamaCompatProvider(model): boolean {
  if (providerId === "ollama") return true;
  // localhost:11434 → 大概率是 Ollama
  if (isLocalhost && parsed.port === "11434") return true;
  return false;
}

![Attempt 单次 AI 调用与工具名三层标准化](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/04-infographic-attempt-tool-loop-1775150698128.png)


四、流订阅系统——实时事件分发

4.1 架构

pi-embedded-subscribe.ts 是一个巨大的事件处理器工厂(~1000+ 行),它将 AI 模型的流式输出转化为结构化事件:

AI 模型流式输出

  ├── text_delta (文本增量)
  │     ├── stripBlockTags() → 去除 <think>/<final> 标签
  │     ├── shouldSkipAssistantText() → 去重检测
  │     ├── blockChunker.add() → 分块缓冲
  │     └── onPartialReply(text) → 推送到渠道

  ├── text_end (文本结束)
  │     ├── finalizeAssistantTexts() → 确认最终文本
  │     └── blockChunker.drain() → 刷新分块缓冲

  ├── reasoning_delta (推理增量)
  │     └── onReasoningStream(text) → 推送推理过程

  ├── tool_call (工具调用)
  │     ├── emitToolSummary() → 推送工具摘要
  │     └── onAgentEvent("tool_call", ...) → 推送事件

  ├── tool_result (工具结果)
  │     ├── emitToolResultMessage() → 推送结果
  │     └── recordAssistantUsage() → 记录用量

  └── message_end (消息结束)
        ├── recordAssistantUsage() → 最终用量
        └── 状态重置

4.2 去重机制

流式输出中存在已知的重复问题(某些 Provider 的 text_end 会重复完整内容):

typescript
const shouldSkipAssistantText = (text: string) => {
  if (state.lastAssistantTextMessageIndex !== state.assistantMessageIndex) {
    return false;
  }
  const trimmed = text.trimEnd();
  if (trimmed && trimmed === state.lastAssistantTextTrimmed) {
    return true;  // 完全相同 → 跳过
  }
  const normalized = normalizeTextForComparison(text);
  if (normalized.length > 0 && normalized === state.lastAssistantTextNormalized) {
    return true;  // 标准化后相同 → 跳过
  }
  return false;
};

4.3 消息工具去重

当 Agent 通过 messaging tool 主动发送了消息后,最终的 block reply 不应重复发送相同内容:

typescript
const MAX_MESSAGING_SENT_TEXTS = 200;
const MAX_MESSAGING_SENT_TARGETS = 200;
const MAX_MESSAGING_SENT_MEDIA_URLS = 200;

系统维护三个缓冲区跟踪已发送的文本/目标/媒体 URL,并在 block reply 阶段进行对比抑制。

4.4 分块输出(Block Chunking)

对于不支持流式编辑的渠道(如 IRC、Signal),文本按块发送:

typescript
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;

Chunker 在收到足够文本后,按照 onBlockReply 回调分块推送,避免一次性发送过长消息。

4.5 Thinking 标签处理

AI 模型的"思考过程"被 <think> 标签包裹,订阅系统负责剥离:

typescript
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => {
  // 1. 处理 <think> 块(状态化,剥离内容)
  // 2. 处理 <final> 块(提取最终回复)
  // 3. 处理内联代码(避免误剥离代码中的标签)
};

4.6 压缩重试协调

当运行引擎触发上下文压缩时,订阅系统需要等待压缩完成:

typescript
const noteCompactionRetry = () => {
  state.pendingCompactionRetry += 1;
  ensureCompactionPromise();
};

const resolveCompactionRetry = () => {
  state.pendingCompactionRetry -= 1;
  if (state.pendingCompactionRetry === 0 && !state.compactionInFlight) {
    state.compactionRetryResolve?.();  // 释放等待者
  }
};

![流订阅系统事件处理管线](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/05-infographic-subscribe-events-1775150698879.png)


五、工具系统——Agent 的"手脚"

5.1 工具目录

typescript
// src/agents/tool-catalog.ts
const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
  // ── 搜索与文件 ──
  { id: "web_search",       section: "search",     profiles: ["coding", "messaging"] },
  { id: "web_fetch",        section: "search",     profiles: ["coding", "messaging"] },
  { id: "pdf",              section: "search",     profiles: ["coding"] },
  // ── 记忆 ──
  { id: "memory_search",    section: "memory",     profiles: ["coding"] },
  { id: "memory_get",       section: "memory",     profiles: ["coding"] },
  // ── 会话 ──
  { id: "sessions_list",    section: "sessions",   profiles: ["coding", "messaging"] },
  { id: "sessions_history", section: "sessions",   profiles: ["coding", "messaging"] },
  { id: "sessions_send",    section: "sessions",   profiles: ["coding", "messaging"] },
  { id: "sessions_spawn",   section: "sessions",   profiles: ["coding"] },
  { id: "subagents",        section: "sessions",   profiles: ["coding"] },
  { id: "session_status",   section: "sessions",   profiles: ["minimal", "coding", "messaging"] },
  // ── UI ──
  { id: "browser",          section: "ui",         profiles: [] },
  { id: "canvas",           section: "ui",         profiles: [] },
  // ── 消息 ──
  { id: "message",          section: "messaging",  profiles: ["messaging"] },
  // ── 自动化 ──
  { id: "cron",             section: "automation", profiles: ["coding"] },
  { id: "gateway",          section: "automation", profiles: [] },
  // ── 节点 ──
  { id: "nodes",            section: "nodes",      profiles: [] },
  // ── Agent ──
  { id: "agents_list",      section: "agents",     profiles: [] },
  // ── 媒体 ──
  { id: "image",            section: "media",      profiles: ["coding"] },
  { id: "tts",              section: "media",      profiles: [] },
];

5.2 四种 Tool Profile

Profile 控制不同场景下可用的工具集:

Profile工具范围典型场景
minimalsession_status极简模式,纯对话
coding搜索+记忆+会话+定时+图像+PDF编程助手
messaging搜索+会话+消息消息转发助手
full全部工具(无限制)完全自主 Agent

5.3 工具组(Tool Groups)

工具被自动分组,支持配置级别的批量控制:

typescript
const CORE_TOOL_GROUPS = {
  "group:openclaw": [...所有核心工具...],
  "group:search": ["web_search", "web_fetch", "pdf"],
  "group:memory": ["memory_search", "memory_get"],
  "group:sessions": ["sessions_list", "sessions_history", "sessions_send", ...],
  "group:ui": ["browser", "canvas"],
  "group:messaging": ["message"],
  "group:automation": ["cron", "gateway"],
  "group:nodes": ["nodes"],
  "group:agents": ["agents_list"],
  "group:media": ["image", "tts"],
};

5.4 工具注册流程

typescript
// src/agents/openclaw-tools.ts
export function createOpenClawTools(options?: OpenClawToolsOptions): AnyAgentTool[] {
  const tools: AnyAgentTool[] = [
    createBrowserTool({ ... }),
    createCanvasTool({ ... }),
    createNodesTool({ ... }),
    createCronTool({ ... }),
    createTtsTool({ ... }),
    createGatewayTool({ ... }),
    createAgentsListTool({ ... }),
    createSessionsListTool({ ... }),
    createSessionsHistoryTool({ ... }),
    createSessionsSendTool({ ... }),
    createSessionsSpawnTool({ ... }),
    createSubagentsTool({ ... }),
    createSessionStatusTool({ ... }),
    ...(webSearchTool ? [webSearchTool] : []),
    ...(webFetchTool ? [webFetchTool] : []),
    ...(imageTool ? [imageTool] : []),
    ...(pdfTool ? [pdfTool] : []),
  ];

  // 插件工具(来自扩展)
  const pluginTools = resolvePluginTools({
    context: { ... },
    existingToolNames: new Set(tools.map(t => t.name)),  // 去重
    toolAllowlist: options?.pluginToolAllowlist,
  });

  return [...tools, ...pluginTools];
}

关键设计:

  • 条件注册webSearchToolimageTool 等依赖配置是否启用
  • 去重保护existingToolNames 确保同名工具不会重复注册
  • Allowlist 控制pluginToolAllowlist 限制插件可注册的工具

![工具系统:目录、Profile 与注册流程](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/06-infographic-tool-system-1775150699605.png)


六、模型认证——多来源 API Key 解析

6.1 五级认证优先级

typescript
// src/agents/model-auth.ts
export async function resolveApiKeyForProvider(params): Promise<ResolvedProviderAuth> {
  // Level 1: 指定 Profile ID → 直接使用该 Profile
  if (profileId) {
    return await resolveApiKeyForProfile({ store, profileId });
  }

  // Level 2: Provider Auth Override → 配置级覆盖(如 aws-sdk)
  const authOverride = resolveProviderAuthOverride(cfg, provider);
  if (authOverride === "aws-sdk") return resolveAwsSdkAuthInfo();

  // Level 3: Auth Profile Store → 按优先级遍历 Profile
  const order = resolveAuthProfileOrder({ cfg, store, provider, preferredProfile });
  for (const candidate of order) {
    const resolved = await resolveApiKeyForProfile({ store, profileId: candidate });
    if (resolved) return { apiKey: resolved.apiKey, profileId: candidate, ... };
  }

  // Level 4: 环境变量 → ANTHROPIC_API_KEY, OPENAI_API_KEY 等
  const envResolved = resolveEnvApiKey(provider);
  if (envResolved) return { apiKey: envResolved.apiKey, ... };

  // Level 5: 自定义 Provider 配置 → models.json 中的 apiKey
  const customKey = getCustomProviderApiKey(cfg, provider);
  if (customKey) return { apiKey: customKey, ... };

  throw new Error(`No API key found for provider "${provider}".`);
}

6.2 环境变量映射

系统为 30+ 个 Provider 预定义了环境变量映射:

typescript
const envMap: Record<string, string> = {
  openai: "OPENAI_API_KEY",
  anthropic: "ANTHROPIC_API_KEY",     // 也支持 ANTHROPIC_OAUTH_TOKEN
  google: "GEMINI_API_KEY",
  groq: "GROQ_API_KEY",
  xai: "XAI_API_KEY",
  openrouter: "OPENROUTER_API_KEY",
  minimax: "MINIMAX_API_KEY",
  // ... 30+ 个 Provider
};

6.3 认证模式

模式说明典型 Provider
api-key静态 API KeyOpenAI, Anthropic, Google
oauthOAuth Token(可刷新)GitHub Copilot, Anthropic OAuth
tokenBearer TokenAWS, 自定义
aws-sdkAWS SDK 默认链Amazon Bedrock

6.4 Auth Profile 冷却机制

当某个 Profile 认证失败时,系统将其标记为"冷却"状态,避免短时间内重复尝试失败的 Key:

typescript
// 在运行循环中
if (isProfileInCooldown(authStore, candidate)) {
  profileIndex += 1;  // 跳过冷却中的 Profile
  continue;
}

// 失败时标记
await markAuthProfileFailure({
  store: authStore,
  profileId,
  reason: "auth",  // "auth" | "rate_limit" | "timeout"
});

冷却期过后,系统会探测性地重试主 Profile(30s 间隔,每次最多等冷却期结束前 2 分钟才探测)。

![模型认证五级优先级链与冷却机制](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/07-infographic-model-auth-1775150700479.png)


七、模型故障转移——多模型 Fallback 链

7.1 候选模型解析

typescript
// src/agents/model-fallback.ts
function resolveFallbackCandidates(params): ModelCandidate[] {
  const candidates = createModelCandidateCollector(allowlist);

  // 1. 主模型(当前使用的)
  candidates.addExplicitCandidate(normalizedPrimary);

  // 2. 配置的 fallback 列表
  for (const raw of modelFallbacks) {
    candidates.addExplicitCandidate(resolved.ref);
  }

  // 3. 配置的默认模型(如果不同于主模型)
  if (primary?.provider && primary.model) {
    candidates.addExplicitCandidate({ provider: primary.provider, model: primary.model });
  }

  return candidates;
}

7.2 Fallback 执行

typescript
async function runWithModelFallback<T>(params): Promise<ModelFallbackRunResult<T>> {
  const candidates = resolveFallbackCandidates({ ... });
  const attempts: FallbackAttempt[] = [];

  for (const candidate of candidates) {
    const result = await runFallbackAttempt({
      run: params.run,
      provider: candidate.provider,
      model: candidate.model,
      attempts,
    });

    if ("success" in result) return result.success;  // 成功 → 返回

    // 失败 → 记录 → 尝试下一个候选
    attempts.push({ provider: candidate.provider, model: candidate.model, error: result.error });
    params.onFallbackAttempt?.({ ... });
  }

  // 全部失败 → 抛出聚合错误
  throwFallbackFailureSummary({ attempts, candidates, ... });
}

7.3 跨 Provider 智能 Fallback

特殊逻辑:当用户运行的 Provider 与配置的 Provider 不同时,不会盲目使用配置的 fallback:

typescript
if (normalizedPrimary.provider !== configuredPrimary.provider) {
  // 只有当前模型已在 fallback 链中时才使用配置的 fallback
  const isConfiguredFallback = configuredFallbacks.some(fb =>
    sameModelCandidate(resolved.ref, normalizedPrimary)
  );
  return isConfiguredFallback ? configuredFallbacks : [];
}

![模型故障转移 Fallback 链](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/08-infographic-model-fallback-1775150701218.png)


八、上下文压缩(Compaction)

8.1 为什么需要压缩

AI 模型有上下文窗口限制(如 128K tokens)。长对话会超出限制,此时需要将旧消息摘要化。

8.2 压缩流程

检测上下文溢出(context overflow error)


计算自适应分块比例 computeAdaptiveChunkRatio()
  │ → 消息越大,分块越小

安全预处理 stripToolResultDetails()
  │ → 不把工具结果详情送进摘要模型

分块 chunkMessagesByMaxTokens()
  │ → 按 token 上限切割消息序列

逐块摘要 summarizeChunks()
  │ → 每块调用 AI 生成摘要
  │ → 后续块附带前序摘要作为上下文

渐进式降级 summarizeWithFallback()
  │ → 完整摘要失败 → 排除超大消息 → 部分摘要
  │ → 部分摘要也失败 → 纯文字记录

替换旧消息为摘要 → 重试 AI 调用

8.3 关键常量

typescript
const SAFETY_MARGIN = 1.3;                    // Token 估算安全倍率(chars/4 不精确)
const BASE_CHUNK_RATIO = 0.4;                 // 基础分块比例(窗口的 40%)
const MIN_CHUNK_RATIO = 0.15;                 // 最小分块比例
const SUMMARIZATION_OVERHEAD_TOKENS = 4096;   // 摘要化的额外开销

8.4 分阶段摘要

超长对话用 summarizeInStages() 分段处理:

typescript
export async function summarizeInStages(params): Promise<string> {
  const parts = normalizeParts(params.parts ?? DEFAULT_PARTS, messages.length);
  const splits = splitMessagesByTokenShare(messages, parts);

  let summary = params.previousSummary;
  for (const split of splits) {
    summary = await summarizeWithFallback({
      ...params,
      messages: split,
      previousSummary: summary,
    });
  }
  return summary;
}

![上下文压缩 Compaction 流水线](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/09-infographic-context-compaction-1775150702017.png)


九、Agent 配置解析

9.1 多 Agent 架构

OpenClaw 支持配置多个 Agent,每个有独立的模型、工作区和工具集:

json5
{
  agents: {
    default: "main",
    entries: [
      {
        id: "main",
        model: "anthropic/claude-sonnet-4-20250514",
        workspace: "~/projects",
        tools: { profile: "coding" },
      },
      {
        id: "helper",
        model: "openai/gpt-4o",
        workspace: "~/temp",
        tools: { profile: "messaging" },
      }
    ]
  }
}

9.2 Agent 配置解析链

typescript
// src/agents/agent-scope.ts
export function resolveAgentConfig(cfg, agentId): ResolvedAgentConfig | undefined {
  const entry = resolveAgentEntry(cfg, normalizeAgentId(agentId));
  return {
    name: entry.name,
    workspace: entry.workspace,
    agentDir: entry.agentDir,
    model: entry.model,
    skills: entry.skills,
    memorySearch: entry.memorySearch,
    humanDelay: entry.humanDelay,
    heartbeat: entry.heartbeat,
    identity: entry.identity,
    groupChat: entry.groupChat,
    subagents: entry.subagents,
    sandbox: entry.sandbox,
    tools: entry.tools,
  };
}

9.3 模型解析——三级 Fallback

Agent 级别模型 → agents.entries[].model
  │ (未配置)

默认 Agent 模型 → agents.defaults.model
  │ (未配置)

全局默认 → "anthropic/claude-sonnet-4-20250514"

fallback 列表的解析也类似:

typescript
export function resolveEffectiveModelFallbacks(params): string[] | undefined {
  // 1. Agent 级 fallback
  const agentFallbacks = resolveAgentModelFallbacksOverride(cfg, agentId);
  // 2. 如果有 session 模型覆盖,还看默认 fallback
  if (params.hasSessionModelOverride) {
    return agentFallbacks ?? resolveAgentModelFallbackValues(cfg.agents?.defaults?.model);
  }
  return agentFallbacks;
}

9.4 工作区路径解析

typescript
export function resolveAgentWorkspaceDir(cfg, agentId) {
  // 1. Agent 显式配置 → agents.entries[].workspace
  const configured = resolveAgentConfig(cfg, id)?.workspace;
  if (configured) return resolveUserPath(configured);

  // 2. 默认 Agent → agents.defaults.workspace
  if (id === defaultAgentId) {
    const fallback = cfg.agents?.defaults?.workspace;
    if (fallback) return resolveUserPath(fallback);
    return resolveDefaultAgentWorkspaceDir(process.env);  // ~/openclaw-workspace
  }

  // 3. 非默认 Agent → ~/.openclaw/workspace-<agentId>
  return path.join(resolveStateDir(process.env), `workspace-${id}`);
}

![Agent 配置解析:多 Agent 架构与三级解析链](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/10-infographic-agent-config-1775150702974.png)


十、会话写锁——分布式文件锁

10.1 为什么需要锁

同一个 session 可能被多个来源同时访问(Gateway 的多个 Node、CLI 直接调用等)。会话文件(JSONL)需要独占写入保证一致性。

10.2 锁实现

typescript
// src/agents/session-write-lock.ts
type LockFilePayload = {
  pid?: number;
  createdAt?: string;
  starttime?: number;  // /proc/pid/stat field 22(进程启动时间)
};

锁文件包含 PID 和进程启动时间,用于检测"僵尸锁"(进程已死但锁文件残留)。

10.3 三层清理保障

typescript
const CLEANUP_SIGNALS = ["SIGINT", "SIGTERM", "SIGQUIT", "SIGABRT"] as const;
  1. 信号处理器:捕获退出信号 → 释放所有持有的锁
  2. 看门狗:定期检查锁持有时间是否超过最大限制(默认 5 分钟)
  3. 陈旧检测inspectSessionLock() 检查 PID 是否存活 + 进程启动时间是否匹配
typescript
const DEFAULT_STALE_MS = 30 * 60 * 1000;      // 30 分钟无活动 → 视为陈旧
const DEFAULT_MAX_HOLD_MS = 5 * 60 * 1000;     // 单次最长持有 5 分钟
const DEFAULT_WATCHDOG_INTERVAL_MS = 60_000;    // 看门狗每分钟检查一次
const DEFAULT_TIMEOUT_GRACE_MS = 2 * 60 * 1000; // 超时后 2 分钟宽限期

![会话写锁:分布式文件锁与三层清理保障](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/11-infographic-session-lock-1775150703901.png)


十一、上下文窗口保护

typescript
// src/agents/context-window-guard.ts
export const CONTEXT_WINDOW_HARD_MIN_TOKENS = 16_000;
export const CONTEXT_WINDOW_WARN_BELOW_TOKENS = 32_000;

11.1 窗口大小解析——四级优先级

typescript
export function resolveContextWindowInfo(params): ContextWindowInfo {
  // 1. 配置中指定的模型上下文窗口 → models.providers[].models[].contextWindow
  const fromModelsConfig = ...;
  // 2. 模型自报的上下文窗口 → model.contextWindow
  const fromModel = ...;
  // 3. 默认值
  const baseInfo = fromModelsConfig ?? fromModel ?? { tokens: defaultTokens };

  // 4. Agent 级上限帽 → agents.defaults.contextTokens
  const capTokens = cfg?.agents?.defaults?.contextTokens;
  if (capTokens && capTokens < baseInfo.tokens) {
    return { tokens: capTokens, source: "agentContextTokens" };
  }
  return baseInfo;
}

11.2 保护级别

条件行为
tokens < 16KshouldBlock: true — 拒绝运行
tokens < 32KshouldWarn: true — 警告但继续
tokens >= 32K正常运行

十二、插件钩子系统

Agent 支持两个插件钩子,允许插件在 AI 调用前修改提示词和消息:

typescript
type PromptBuildHookRunner = {
  hasHooks: (hookName: "before_prompt_build" | "before_agent_start") => boolean;

  // 钩子 1: 构建提示词之前
  runBeforePromptBuild: (
    event: { prompt: string; messages: unknown[] },
    ctx: PluginHookAgentContext,
  ) => Promise<PluginHookBeforePromptBuildResult | undefined>;

  // 钩子 2: Agent 开始运行之前
  runBeforeAgentStart: (
    event: { prompt: string; messages: unknown[] },
    ctx: PluginHookAgentContext,
  ) => Promise<PluginHookBeforeAgentStartResult | undefined>;
};

钩子可以返回修改后的 promptmessages,但不能改变其他参数。


十三、完整数据流

13.1 消息处理全流程

用户消息到达


Channel 系统构建 MsgContext
  │ → agentId, sessionKey, prompt, images

runEmbeddedPiAgent() 入口

  ├── 阶段 1: 解析工作区和 Agent 目录
  ├── 阶段 2: 解析模型(provider + model)
  ├── 阶段 3: 构建 Auth Profile 候选列表
  ├── 阶段 4: 激活 API Key
  ├── 阶段 5: 构建系统提示词


主运行循环 while(true)

  ├── runEmbeddedAttempt()
  │     │
  │     ├── 构建消息数组(系统提示 + 历史消息 + 用户消息)
  │     ├── 调用 AI 模型 API(streaming)
  │     ├── 订阅流事件(pi-embedded-subscribe)
  │     │     ├── text_delta → onPartialReply() → 渠道流式输出
  │     │     ├── tool_call → 执行工具 → 将结果追加到消息
  │     │     ├── reasoning → onReasoningStream()
  │     │     └── message_end → 本轮结束
  │     └── 返回 attempt 结果

  ├── 成功?
  │     ├── 是 → 跳出循环
  │     └── 否 → 错误恢复
  │           ├── 上下文溢出 → compaction → continue
  │           ├── Auth 失败 → advanceAuthProfile → continue
  │           ├── Thinking 不支持 → 降级 → continue
  │           └── 不可恢复 → 返回错误


构建最终结果
  ├── payloads: [{ text, mediaUrls }]  → 回复内容
  ├── meta: { durationMs, usage, ... }  → 运行元数据
  └── 返回给 Channel 系统

13.2 工具调用循环

AI 模型返回 tool_call


normalizeToolCallNameForDispatch()
  │ → 修正工具名称(空格/大小写)

normalizeToolCallIdsInMessage()
  │ → 修复缺失的 tool call ID

分发到对应工具实现
  │ → createBrowserTool / createWebSearchTool / ...

工具执行
  ├── 成功 → 返回结果文本
  └── 失败 → 返回错误文本


工具结果追加到消息数组


再次调用 AI 模型
  │ → 模型决定是否继续调用工具

  ├── 有工具调用 → 重复循环
  └── 无工具调用 → 返回最终文本回复

十四、设计模式总结

14.1 重试+恢复模式(Retry with Recovery)

主运行循环是一个带有多种恢复策略的重试循环。每种错误类型有专属的恢复路径,全部恢复失败后才返回错误。

14.2 策略模式(Strategy Pattern)

  • thinkLevel: "low" | "medium" | "high" → 推理强度策略
  • toolProfile: "minimal" | "coding" | "messaging" | "full" → 工具集策略
  • deliveryMode → 出站发送策略

14.3 工厂方法模式(Factory Method)

每个工具通过 createXxxTool() 工厂函数创建,接受上下文参数生成具体实现。

14.4 观察者模式(Observer Pattern)

流订阅系统中 onPartialReplyonToolResultonReasoningStream 等回调构成观察者网络。

14.5 责任链模式(Chain of Responsibility)

五级认证优先级链、三级模型解析链、七级路由优先级链。

14.6 状态机模式(State Machine)

订阅系统内部维护复杂状态:

IDLE → STREAMING → TOOL_CALLING → STREAMING → DONE

    COMPACTING → STREAMING (重试)

14.7 渐进式降级模式

  • 摘要化:完整摘要 → 部分摘要 → 纯文字记录
  • Thinking:high → medium → low → disabled
  • Auth Profile:primary → secondary → ... → env fallback

![设计模式与设计洞察总结](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(五)Agent 智能体系统/12-infographic-design-patterns-1775150704880.png)


十五、调试建议

15.1 关键断点位置

文件位置断点目的
pi-embedded-runner/run.tswhile (true) 循环入口观察主运行循环
pi-embedded-runner/run.tsawait runEmbeddedAttempt()观察 AI 调用参数
pi-embedded-runner/run.tscontextOverflowError 判断压缩触发时机
pi-embedded-runner/run.tsadvanceAuthProfile()Auth Profile 切换
pi-embedded-runner/run/attempt.ts函数入口单次尝试参数
pi-embedded-subscribe.tspushAssistantText()文本流推送
pi-embedded-subscribe.tsemitToolResultMessage()工具结果推送
compaction.tssummarizeChunks()压缩过程
model-auth.tsresolveApiKeyForProvider()认证解析
model-fallback.tsrunFallbackAttempt()模型切换
openclaw-tools.tscreateOpenClawTools()工具注册
tool-catalog.tsresolveCoreToolProfilePolicy()Profile 应用

15.2 测试命令

bash
# Agent 核心测试
pnpm test -- --reporter verbose src/agents/pi-embedded-runner/

# 订阅系统测试
pnpm test -- --reporter verbose src/agents/pi-embedded-subscribe.test.ts

# 压缩测试
pnpm test -- --reporter verbose src/agents/compaction.test.ts

# 模型认证测试
pnpm test -- --reporter verbose src/agents/model-auth.test.ts

# 模型故障转移测试
pnpm test -- --reporter verbose src/agents/model-fallback.test.ts

# 工具目录测试
pnpm test -- --reporter verbose src/agents/tool-catalog.test.ts

# Agent 配置解析测试
pnpm test -- --reporter verbose src/agents/agent-scope.test.ts

# 上下文窗口保护测试
pnpm test -- --reporter verbose src/agents/context-window-guard.test.ts

十六、文件依赖图

src/agents/context-window-guard.ts          ← 零外部依赖
src/agents/lanes.ts                        ← 零外部依赖

src/agents/agent-scope.ts                  ← 依赖 config 模块

src/agents/defaults.ts                     ← 依赖 agent-scope

src/agents/model-auth.ts                   ← 依赖 agent-scope + config

src/agents/model-fallback.ts               ← 依赖 model-auth + defaults

src/agents/tool-catalog.ts                 ← 零外部依赖(纯定义)

src/agents/openclaw-tools.ts               ← 依赖 tool-catalog + 各工具实现

src/agents/compaction.ts                   ← 依赖 model 调用

src/agents/pi-embedded-subscribe.ts        ← 依赖 compaction + 工具分发

src/agents/pi-embedded-runner/run/attempt.ts ← 依赖 subscribe + tools

src/agents/pi-embedded-runner/run.ts       ← 依赖一切(主入口)

src/agents/pi-embedded-runner/runs.ts      ← 依赖 run.ts(队列管理)

src/agents/session-write-lock.ts           ← 独立模块(文件锁)
src/agents/system-prompt.ts                ← 依赖 config + agent-scope

十七、设计洞察

17.1 为什么运行引擎如此复杂

run.ts 有 900+ 行,因为它需要处理真实世界中 AI API 的所有"不靠谱"情况:

  • API Key 过期:需要自动切换到备用 Key
  • 模型不可用:需要自动切换到备用模型
  • 上下文爆炸:需要自动压缩历史消息
  • Thinking 不支持:需要自动降级推理级别
  • Copilot Token 刷新:OAuth Token 有有效期
  • 工具调用异常:工具名错误、ID 缺失、结果过长

这些都不是"边界情况"——它们在生产环境中频繁发生。运行引擎的核心价值就是自动处理这些问题,让用户无感知

17.2 为什么工具名需要三层标准化

不同 AI 模型对工具名的处理差异很大:

  • GPT 系列可能返回 webSearch 而注册名是 web_search
  • Claude 可能在工具名前后加空格
  • 某些模型可能全大写 WEB_SEARCH

三层标准化(精确 → 规范化 → 大小写不敏感)以最小侵入方式解决这些兼容性问题。

17.3 订阅系统为什么需要去重

AI 流式 API 有已知问题:

  • text_end 可能重复发送完整内容
  • 迟到的 text_end 可能在 message_end 之后到达
  • 某些 Provider 在重连后重放部分流

订阅系统通过 shouldSkipAssistantText() 的双重去重(原始文本 + 标准化文本)确保用户不会收到重复消息。

17.4 Auth Profile 冷却 vs 探测的平衡

当主 Profile 认证失败后,系统会切换到 fallback Profile。但不能永远不尝试主 Profile——它可能只是临时限流。解决方案:

主 Profile 失败 → 标记冷却(cooldown)

  ▼ 使用 fallback Profile

每 30s 探测主 Profile(throttled probe)

  ├── 探测成功 → 切回主 Profile
  └── 探测失败 → 继续使用 fallback

冷却期结束(距离到期 < 2 分钟时开始探测)

  ▼ 自动切回主 Profile

17.5 上下文压缩的"安全优先"设计

压缩过程有多处安全考虑:

  • stripToolResultDetails() → 工具结果详情不进入摘要模型(防信息泄露)
  • SAFETY_MARGIN = 1.3 → Token 估算加 30% 余量(chars/4 低估多字节字符)
  • isOversizedForSummary() → 超大消息直接标注跳过(防摘要模型溢出)
  • 三级降级 → 完整摘要失败不会直接报错,而是尝试部分摘要

读文档、看源码、写代码,理解 AI Agent 本质 🤖