OpenClaw Agents 模块 — 超深度架构分析

分析范围:penclaw/src/agents
代码规模:686个非测试TypeScript文件,135,591行代码


图表目录

编号 图表名称 类型
01 系统整体架构图 Architecture Diagram
02 PI Embedded Runner核心执行流程 Flowchart
03 子代理生命周期流程 Flowchart
04 模型选择与认证流程 Flowchart
05 Tool Policy Pipeline流程 Flowchart

一、模块定位

1.1 业务职责与功能定位

OpenClaw Agents模块是整个OpenClaw系统的AI代理核心引擎,负责:

  1. LLM交互编排:管理与大语言模型(Anthropic Claude、OpenAI GPT、Google Gemini等)的完整交互循环
  2. 工具执行框架:提供安全的工具注册、调用、策略过滤与结果处理机制
  3. 多代理编排:支持子代理(Sub-Agent)的生成、注册、生命周期管理与结果回收
  4. 会话状态管理:维护对话上下文、压缩(Compaction)、缓存与持久化
  5. 安全沙箱隔离:通过Docker/SSH沙箱实现代理的执行隔离与权限控制
  6. 模型供应商适配:统一多模型供应商的认证、流式传输与降级策略

1.2 在系统中的位置

┌─────────────────────────────────────────┐
│           用户界面层 (UI/CLI/Web)         │
├─────────────────────────────────────────┤
│      通道插件 (Channel Plugins)          │  ← Signal/Telegram/Discord/WhatsApp
├─────────────────────────────────────────┤
│  ★★★  AGENTS 模块 (本次分析范围)  ★★★  │  ← 核心AI引擎
├─────────────────────────────────────────┤
│      基础设施层 (Gateway/Config/DB)      │
└─────────────────────────────────────────┘

Agents模块位于通道插件与基础设施层之间,是请求处理的核心枢纽

  • 向上接收来自各种通道(Signal/Telegram/Discord等)的用户消息
  • 向下调用Gateway进行LLM API通信、会话存储与配置管理
  • 横向协调子代理、工具系统与沙箱环境

1.3 核心业务价值

  • 智能对话引擎:将LLM能力转化为可编程、可控、可观测的对话代理
  • 安全执行边界:通过多层Policy Pipeline确保代理行为在安全范围内
  • 多模型容错:认证轮转、模型降级、Profile冷却等机制保障服务可用性
  • 可扩展架构:Skills系统、工具注册、Provider适配器等支持灵活扩展

二、模块整体结构

在这里插入图片描述

2.1 六层架构分层

层级 名称 核心组件 文件数 代码行数
L1 Interface Layer CLI Runner、Command、Channel Tools、ACP Spawn、Embedded Agent ~30 ~4,000
L2 Core Engine PI Embedded Runner、Subscribe、System Prompt、Agent Scope ~100 ~24,000
L3 Model & Provider Model Selection、Models Config、Fallback、Auth Profiles、Transport ~60 ~12,000
L4 Tool System Bash Tools、PI Tools、Catalog、Individual Tools、Policy Pipeline ~80 ~18,000
L5 Sub-Agent & Session Spawn、Registry、Announce、Session Mgmt、Context、Compaction ~50 ~10,000
L6 Sandbox & Skills Sandbox、Path Policy、Skills、Identity、Bootstrap ~70 ~12,000

2.2 核心依赖关系

Interface Layer
    ↓ 调用
Core Engine ←→ Model & Provider
    ↓ 调用         ↑ 认证
Tool System ←→ Sandbox & Security
    ↓ 回调
Sub-Agent & Session
    ↓ 持久化
Skills & Identity

2.3 关键接口定义

2.3.1 SpawnSubagentParams — 子代理生成参数接口
export type SpawnSubagentParams = {
  task: string;              // 子代理任务描述
  label?: string;            // 可选标签(用于标识)
  agentId?: string;          // 目标代理ID
  model?: string;            // 模型覆盖
  thinking?: string;         // 思考级别覆盖
  runTimeoutSeconds?: number; // 运行超时
  thread?: boolean;          // 是否绑定线程
  mode?: SpawnSubagentMode;  // "run" | "session"
  cleanup?: "delete" | "keep"; // 清理策略
  sandbox?: SpawnSubagentSandboxMode; // "inherit" | "require"
  lightContext?: boolean;    // 轻量上下文模式
  attachments?: Array<{...}>; // 附带文件
};

设计目的:将子代理生成的所有可配置参数统一在一个类型中,支持灵活的代理间任务委派。

2.3.2 ResolvedAgentConfig — 解析后的代理配置
export type ResolvedAgentConfig = {
  name?: string;
  workspace?: string;
  model?: AgentEntry["model"];
  thinkingDefault?: ...;
  reasoningDefault?: ...;
  skills?: string[];
  memorySearch?: ...;
  heartbeat?: ...;
  identity?: ...;
  subagents?: ...;
  embeddedPi?: ...;
  sandbox?: ...;
  tools?: ...;
};

设计目的:将分散在OpenClawConfig中的代理配置整合为一个类型安全的解析结果,消除配置查找的运行时不确定性。

2.3.3 AuthProfileCredential — 认证凭据联合类型
export type AuthProfileCredential = 
  | ApiKeyCredential     // { type: "api_key", provider, key?, keyRef? }
  | TokenCredential      // { type: "token", provider, token?, expires? }
  | OAuthCredential;     // { type: "oauth", provider, access, refresh, expires }

设计目的:使用TypeScript联合类型实现类型安全的凭据多态,每种凭据类型有独立的字段结构。

2.3.4 SandboxConfig — 沙箱配置
export type SandboxConfig = {
  mode: "off" | "non-main" | "all";
  backend: SandboxBackendId;
  scope: SandboxScope;     // "session" | "agent" | "shared"
  workspaceAccess: SandboxWorkspaceAccess; // "none" | "ro" | "rw"
  docker: SandboxDockerConfig;
  ssh: SandboxSshConfig;
  browser: SandboxBrowserConfig;
  tools: SandboxToolPolicy;
  prune: SandboxPruneConfig;
};

三、核心业务逻辑深度解析

3.1 PI Embedded Runner — 核心执行引擎

在这里插入图片描述

3.1.1 模块概述

PI Embedded Runner是整个Agents模块的心脏,包含94个文件、21,988行代码。它负责完整的LLM交互循环:从消息输入到响应输出的全链路处理。

关键子模块

子模块 文件 职责
run.ts 主运行编排 协调一次完整的LLM交互轮次
attempt.ts 单次尝试 一次API调用的完整处理
model.ts 模型解析 将配置解析为可用的模型引用
compact.ts 上下文压缩 对话历史的智能压缩
payloads.ts 请求构建 构建发往LLM的请求载荷
history.ts 历史管理 对话历史的加载与限制
system-prompt.ts 系统提示词 构建系统级提示词
tool-split.ts 工具拆分 内置工具与SDK工具的拆分
3.1.2 pi-embedded.ts — 入口文件逐行解析
// 类型重导出:为旧代码提供向后兼容的别名
export type {
  EmbeddedAgentCompactResult,     // 压缩结果类型(别名)
  EmbeddedAgentMeta,              // 代理元数据类型(别名)
  EmbeddedAgentRunMeta,           // 运行元数据类型(别名)
  EmbeddedAgentRunResult,         // 运行结果类型(别名)
  EmbeddedPiAgentMeta,            // PI代理元数据(原类型)
  EmbeddedPiCompactResult,        // PI压缩结果(原类型)
  EmbeddedPiRunMeta,              // PI运行元数据(原类型)
  EmbeddedPiRunResult,            // PI运行结果(原类型)
} from "./pi-embedded-runner.js";

设计意图:使用TypeScript的export type实现类型的显式重导出,同时提供EmbeddedAgent*EmbeddedPi*两套命名。这是因为历史演进中命名从"EmbeddedAgent"变更为"EmbeddedPi",为了保持向后兼容而保留旧名称作为别名。

export {
  // 核心运行函数 — 启动一次PI代理交互
  runEmbeddedPiAgent,
  runEmbeddedPiAgent as runEmbeddedAgent,  // 向后兼容别名
  
  // 中止函数 — 取消正在进行的运行
  abortEmbeddedPiRun,
  abortEmbeddedPiRun as abortEmbeddedAgentRun,
  
  // 状态查询函数
  isEmbeddedPiRunActive,          // 检查运行是否活跃
  isEmbeddedPiRunStreaming,       // 检查是否正在流式输出
  // ... 对应的Agent别名
  
  // 消息队列函数 — 向运行中的代理追加消息
  queueEmbeddedPiMessage,
  queueEmbeddedPiMessage as queueEmbeddedAgentMessage,
  
  // 会话解析函数
  resolveActiveEmbeddedRunSessionId,   // 解析当前活跃运行的会话ID
  resolveEmbeddedSessionLane,          // 解析会话所属Lane
} from "./pi-embedded-runner.js";

设计意图:所有函数导出都采用as别名模式,确保调用者可以使用更直观的runEmbeddedAgent而非必须使用runEmbeddedPiAgent。这是一种API平滑演进策略。

3.1.3 完整执行流程(从入口到出口)
  1. 消息输入queueEmbeddedPiMessage()将用户消息加入队列
  2. 系统提示词构建buildAgentSystemPrompt()组装完整的系统提示词
  3. 模型解析resolveConfiguredModelRef()确定要使用的模型
  4. 认证解析resolveAuthProfileOrder()查找可用的认证Profile
  5. 上下文检查:如果Token超出窗口,触发compactEmbeddedPiSession()
  6. LLM API调用:通过Provider Transport(Anthropic/OpenAI/Google)发送流式请求
  7. 流式响应处理:解析SSE事件流,提取工具调用和文本内容
  8. 工具执行:如有工具调用,经过Policy Pipeline后执行
  9. 循环判定:工具执行结果追加到对话,回到步骤6继续
  10. 响应交付:最终文本通过Channel路由发送给用户
  11. 会话持久化:更新Transcript、Usage统计、Cache状态

3.2 System Prompt — 系统提示词构建器

文件system-prompt.ts(971行)

3.2.1 核心函数 buildAgentSystemPrompt()

这是整个系统提示词构建的唯一入口,接收一个庞大的参数对象,返回完整的系统提示词字符串。

参数解析

export function buildAgentSystemPrompt(params: {
  workspaceDir: string;          // 工作目录路径
  defaultThinkLevel?: ThinkLevel; // 默认思考级别
  reasoningLevel?: ReasoningLevel; // 推理级别
  extraSystemPrompt?: string;    // 额外系统提示词
  ownerNumbers?: string[];       // 授权发送者列表
  ownerDisplay?: OwnerIdDisplay; // ID显示模式("raw"|"hash")
  reasoningTagHint?: boolean;    // 是否启用推理标签提示
  toolNames?: string[];          // 可用工具名称列表
  toolSummaries?: Record<string, string>; // 工具摘要映射
  contextFiles?: EmbeddedContextFile[]; // 上下文文件列表
  skillsPrompt?: string;         // Skills提示词
  heartbeatPrompt?: string;      // 心跳提示词
  promptMode?: PromptMode;       // "full"|"minimal"|"none"
  runtimeInfo?: {...};           // 运行时信息
  sandboxInfo?: EmbeddedSandboxInfo; // 沙箱信息
  messageToolHints?: string[];   // 消息工具提示
  // ... 更多参数
})

核心构建逻辑

  1. PromptMode分级

    • "full":完整系统提示词(主代理使用)
    • "minimal":精简版(子代理使用,仅保留Tooling/Workspace/Runtime段)
    • "none":仅保留基本身份行
  2. 段落构建顺序(按代码出现顺序):

    • 身份声明 → 工具列表 → 安全规则 → CLI参考 → Skills → Memory → 自更新 → 模型别名 → 工作区 → 沙箱 → 授权发送者 → 时间 → 上下文文件 → 输出指令 → Canvas → 消息 → 语音 → 反应 → 推理格式 → 项目上下文 → 静默回复 → Cache Boundary → 动态上下文 → 子代理/群聊上下文 → 心跳 → Runtime行
  3. Cache Boundary机制

    const SYSTEM_PROMPT_CACHE_BOUNDARY = "<!-- OPENCLAW_CACHE_BOUNDARY -->";
    

    这是一个关键的Anthropic提示缓存优化:将稳定内容放在Cache Boundary上方,动态内容放在下方。这样Anthropic可以在多次请求间复用缓存,减少Token消耗。

  4. 上下文文件排序

    const CONTEXT_FILE_ORDER = new Map([
      ["agents.md", 10], ["soul.md", 20], ["identity.md", 30],
      ["user.md", 40], ["tools.md", 50], ["bootstrap.md", 60], ["memory.md", 70],
    ]);
    

    使用Map定义上下文文件的优先级排序,确保最重要的文件(AGENTS.md、SOUL.md)始终出现在前面。

  5. 动态/静态文件分离

    • 静态文件(AGENTS.md、SOUL.md等):放在Cache Boundary上方
    • 动态文件(HEARTBEAT.md等):放在Cache Boundary下方,每次请求都可变

设计精妙之处

  • 使用函数式组合:每个段落有独立的build*Section()函数,便于维护和测试
  • Provider可覆盖:promptContribution参数允许Provider注入自定义段
  • 安全沙箱感知:根据sandboxInfo动态调整路径提示和权限说明

3.3 Model Selection — 模型选择系统

在这里插入图片描述

文件model-selection.ts(823行)

3.3.1 核心类型
export type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive";
export type ModelRef = { provider: string; model: string; };
export type ModelAliasIndex = {
  byAlias: Map<string, { alias: string; ref: ModelRef }>;
  byKey: Map<string, string[]>;
};
  • ThinkLevel:7级思考深度控制,从"off"到"xhigh","adaptive"为自适应
  • ModelRef:模型引用的最小单元,包含provider和model两个标识符
  • ModelAliasIndex:别名索引,支持双向查找(别名→模型,模型→别名列表)
3.3.2 核心函数 resolveConfiguredModelRef()

这是模型解析的最终仲裁者,按照以下优先级链确定模型:

  1. 代理级覆盖resolveAgentModelPrimaryValue() → 代理配置中的model字段
  2. 别名解析buildModelAliasIndex().byAlias → 查找配置的别名映射
  3. Provider推断inferUniqueProviderFromConfiguredModels() → 当模型名无provider前缀时,从配置中推断唯一provider
  4. 默认Provider回退:使用DEFAULT_PROVIDER(“openai”)作为provider前缀
  5. 硬编码默认DEFAULT_MODEL(“gpt-5.4”)作为最终兜底
  6. 配置Provider回退resolveConfiguredProviderFallback() → 如果默认provider不可用,使用第一个配置的provider

降级链Agent Override → Alias → Inferred Provider → Default Provider → Hardcoded Default → First Configured Provider

3.3.3 Allowlist机制

buildAllowedModelSet()实现了模型白名单控制:

// 如果agents.defaults.models为空 → 允许所有模型(allowAny: true)
// 如果agents.defaults.models非空 → 仅允许其中列出的模型
// Fallback模型自动加入允许列表
// 默认模型自动加入允许列表

安全设计:即使白名单配置错误,默认模型和Fallback模型仍可用,确保系统不会因配置问题而完全不可用。


3.4 Agent Scope — 代理作用域解析

文件agent-scope.ts + agent-scope-config.ts

3.4.1 核心函数 resolveSessionAgentIds()
export function resolveSessionAgentIds(params: {
  sessionKey?: string;
  config?: OpenClawConfig;
  agentId?: string;
}): {
  defaultAgentId: string;
  sessionAgentId: string;
}

解析逻辑

  1. 从配置获取默认代理ID:resolveDefaultAgentId()
  2. 尝试从显式参数获取代理ID:normalizeAgentId(agentId)
  3. 尝试从会话Key解析代理ID:parseAgentSessionKey(sessionKey)
  4. 按优先级:显式agentId > 会话Key中的agentId > 默认agentId
3.4.2 resolveAgentConfig() — 代理配置解析

此函数将OpenClawConfig中的代理条目解析为ResolvedAgentConfig,关键在于配置合并策略:

contextLimits: typeof entry.contextLimits === "object" && entry.contextLimits
  ? { ...agentDefaults?.contextLimits, ...entry.contextLimits }  // 代理级覆盖默认级
  : agentDefaults?.contextLimits,  // 无代理级配置则使用默认

设计模式:Spread运算符实现浅合并,代理级配置优先于全局默认配置。

3.4.3 resolveAgentIdsByWorkspacePath() — 路径匹配代理
export function resolveAgentIdsByWorkspacePath(
  cfg: OpenClawConfig,
  workspacePath: string,
): string[]

此函数实现了通过工作目录路径反向查找代理ID的逻辑:

  1. 规范化输入路径(解析符号链接、处理平台差异)
  2. 检查每个代理的workspace目录是否包含输入路径
  3. 按路径长度降序排序(最长匹配优先)
  4. 返回匹配的代理ID列表

使用场景:当用户在某个目录下启动OpenClaw时,自动识别该目录属于哪个代理。


3.5 Sub-Agent System — 子代理系统

在这里插入图片描述

3.5.1 spawnSubagentDirect() — 子代理生成(907行核心函数)

这是子代理系统的最关键函数,负责从零创建一个子代理会话。完整执行流程:

第一阶段:参数验证与初始化

// 1. agentId合法性检查
if (requestedAgentId && !isValidAgentId(requestedAgentId)) {
  return { status: "error", error: `Invalid agentId "${requestedAgentId}"...` };
}

设计意图:在normalizeAgentId处理前先验证,防止错误消息字符串(如"Agent not found: xyz")被normalize后变成合法ID,导致幽灵工作空间目录。

// 2. 深度检查
const callerDepth = getSubagentDepthFromSessionStore(requesterInternalKey, { cfg });
const maxSpawnDepth = cfg.agents?.defaults?.subagents?.maxSpawnDepth ?? DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH;
if (callerDepth >= maxSpawnDepth) {
  return { status: "forbidden", error: `sessions_spawn is not allowed at this depth...` };
}

设计意图:防止无限递归的代理嵌套,默认最大深度为5。

// 3. 并发子代理数量检查
const maxChildren = cfg.agents?.defaults?.subagents?.maxChildrenPerAgent ?? 5;
const activeChildren = countActiveRunsForSession(requesterInternalKey);
if (activeChildren >= maxChildren) {
  return { status: "forbidden", error: `sessions_spawn has reached max active children...` };
}

设计意图:限制每个代理的并发子代理数量,防止资源耗尽。

第二阶段:安全与权限检查

// 4. agentId白名单
const allowAgents = resolveAgentConfig(cfg, requesterAgentId)?.subagents?.allowAgents ?? [];
const allowAny = allowAgents.some((value) => value.trim() === "*");
if (!allowAny && !allowSet.has(normalizedTargetId)) {
  return { status: "forbidden", error: `agentId is not allowed for sessions_spawn...` };
}
// 5. 沙箱兼容性检查
if (!childRuntime.sandboxed && (requesterRuntime.sandboxed || sandboxMode === "require")) {
  return { status: "forbidden", error: "Sandboxed sessions cannot spawn unsandboxed subagents..." };
}

设计意图:沙箱代理只能生成沙箱子代理,防止通过子代理逃逸沙箱。

第三阶段:会话准备

// 6. 生成子会话Key
const childSessionKey = `agent:${targetAgentId}:subagent:${crypto.randomUUID()}`;
// 7. Gateway Session Patch
await callSubagentGateway({
  method: "sessions.patch",
  params: { key: childSessionKey, spawnDepth: childDepth, ... },
});
// 8. 模型持久化
await persistInitialChildSessionRuntimeModel({ cfg, childSessionKey, resolvedModel });

第四阶段:线程绑定(条件执行)

if (requestThreadBinding) {
  const bindResult = await ensureThreadBindingForSubagentSpawn({
    hookRunner, childSessionKey, agentId: targetAgentId, label, mode: spawnMode, ...
  });
  // 失败时清理:删除provisional session
}

第五阶段:附件物化

const materializedAttachments = await materializeSubagentAttachments({
  config: cfg, targetAgentId, attachments: params.attachments, mountPathHint,
});
// 附件写入磁盘,路径信息追加到系统提示词

第六阶段:启动代理运行

const response = await callSubagentGateway({
  method: "agent",
  params: {
    message: childTaskMessage,
    sessionKey: childSessionKey,
    deliver: false,
    lane: AGENT_LANE_SUBAGENT,
    extraSystemPrompt: childSystemPrompt,
    thinking: thinkingOverride,
    ...
  },
});

第七阶段:注册与通知

registerSubagentRun({ runId, childSessionKey, controllerSessionKey, ... });
// 触发lifecycle hooks: subagent_spawned
// 发出session lifecycle event: sessions.changed
3.5.2 SubagentRegistry — 子代理注册表(947行)

核心数据结构

// 内存中的运行记录
const subagentRuns: Map<string, SubagentRunRecord> = new Map();

关键子系统

  1. Sweeper:每60秒扫描一次,清理过期运行记录

    • Session-mode运行:5分钟TTL(SESSION_RUN_TTL_MS
    • 归档运行:根据archiveAtMs判断过期
    • 清理时通知Context Engine
  2. Lifecycle Listener:监听代理事件流

    • phase: "start" → 更新startedAt时间戳
    • phase: "end" → 触发completeSubagentRun()
    • phase: "error" → 延迟15秒后确认(LIFECYCLE_ERROR_RETRY_GRACE_MS),防止临时错误导致过早清理
  3. Orphan Recovery:恢复重启后的孤儿运行

    • 冷启动时从磁盘恢复运行记录
    • 识别孤儿(resolveSubagentRunOrphanReason()
    • 自动重试Announce流程
  4. Resume机制:处理恢复后的运行

    • 检查重试预算(MAX_ANNOUNCE_RETRY_COUNT
    • 检查过期时间(ANNOUNCE_EXPIRY_MS = 120s
    • 指数退避重试(resolveAnnounceRetryDelayMs()

3.6 Auth Profiles — 认证Profile系统

目录auth-profiles/(26文件,4,203行)

3.6.1 核心类型体系
// 三种凭据类型
ApiKeyCredential  → { type: "api_key", provider, key?, keyRef? }
TokenCredential   → { type: "token", provider, token?, expires? }
OAuthCredential   → { type: "oauth", provider, access, refresh, expires, clientId? }

// Profile使用统计
ProfileUsageStats → { lastUsed?, cooldownUntil?, cooldownReason?, disabledUntil? }

// Profile失败原因分类
AuthProfileFailureReason = 
  "auth" | "auth_permanent" | "format" | "overloaded" | 
  "rate_limit" | "billing" | "timeout" | "model_not_found" | "session_expired" | "unknown"
3.6.2 子模块职责
文件 职责
constants.ts 常量定义(冷却时间、重试策略等)
types.ts 类型定义
profiles.ts Profile加载与排序
order.ts Profile选择顺序(轮转、权重等)
store.ts Profile持久化存储
oauth.ts OAuth令牌刷新
policy.ts 认证策略(允许/拒绝/冷却)
credential-state.ts 凭据状态机
display.ts 认证状态展示
doctor.ts 认证诊断工具
repair.ts Profile修复
external-auth.ts 外部认证集成
path-resolve.ts Profile文件路径解析

3.7 Tool Policy Pipeline — 工具策略管线

在这里插入图片描述

核心文件

文件 行数 职责
tool-policy.ts - 主策略入口
tool-policy-match.ts - 策略匹配逻辑
tool-policy-pipeline.ts - 多阶段管线编排
tool-policy.conformance.ts - 合规性检查
tool-fs-policy.ts - 文件系统策略
bash-tools.exec-approval-request.ts - 执行审批请求
bash-tools.exec-approval-followup.ts - 审批后续处理
tool-loop-detection.ts 689 工具循环检测
payload-redaction.ts - 载荷脱敏

6道关卡

  1. Before-Tool-Call Hook:插件级前置拦截,可完全阻止或修改工具调用
  2. Tool Policy Match:基于工具名称的策略匹配(allow/deny列表)
  3. Filesystem Policy:路径访问控制,限制可读写的目录范围
  4. Sandbox Tool Policy:沙箱模式下的工具可用性控制
  5. Exec Approval:危险命令的人工审批机制(/approve命令)
  6. Loop Detection:检测重复工具调用模式,防止无限循环

3.8 Sandbox System — 沙箱系统

目录sandbox/(40文件,6,566行)

3.8.1 架构设计
┌─────────────────────────────────────┐
│         Sandbox Manager              │
│  (manage.ts — 生命周期管理)          │
├─────────────────────────────────────┤
│     Backend Abstraction              │
│  (backend.ts — 统一后端接口)          │
├──────────┬──────────┬───────────────┤
│  Docker  │   SSH    │   Host        │
│ Backend  │ Backend  │  (Passthrough) │
├──────────┴──────────┴───────────────┤
│         FS Bridge                     │
│  (fs-bridge.ts — 文件系统桥接)        │
├─────────────────────────────────────┤
│      Tool Policy + Validation         │
│  (tool-policy.ts, validate-*.ts)      │
└─────────────────────────────────────┘
3.8.2 核心类型
export type SandboxScope = "session" | "agent" | "shared";
export type SandboxWorkspaceAccess = "none" | "ro" | "rw";
export type SandboxConfig = {
  mode: "off" | "non-main" | "all";  // 沙箱启用范围
  backend: SandboxBackendId;           // "docker" | "ssh" | "host"
  scope: SandboxScope;                 // 沙箱作用域
  workspaceAccess: SandboxWorkspaceAccess; // 工作区访问权限
  docker: SandboxDockerConfig;         // Docker后端配置
  ssh: SandboxSshConfig;              // SSH后端配置
  browser: SandboxBrowserConfig;       // 浏览器配置
  tools: SandboxToolPolicy;            // 工具策略
  prune: SandboxPruneConfig;           // 清理策略
};
3.8.3 FS Bridge — 文件系统桥接

这是沙箱最关键的安全组件,它解决了容器内路径与宿主路径的映射问题:

  • 写入操作:通过Shell命令计划(fs-bridge-shell-command-plans.ts)在容器内执行
  • 读取操作:通过宿主路径映射(host-paths.ts)直接读取
  • 路径安全fs-bridge-path-safety.ts防止路径遍历攻击
  • 重命名操作fs-bridge-rename-targets.ts处理跨边界的文件移动

3.9 Skills System — 技能系统

目录skills/(20文件,2,778行)

3.9.1 核心架构
// skills/types.ts — 核心类型
export type SkillDefinition = {
  name: string;
  description: string;
  location: string;  // SKILL.md文件路径
  // ... 更多元数据
};
3.9.2 关键流程
  1. 加载local-loader.ts从磁盘加载SKILL.md文件
  2. 过滤filter.ts + agent-filter.ts根据代理配置过滤可用技能
  3. Frontmatter解析frontmatter.ts解析SKILL.md的YAML前置数据
  4. 刷新refresh.ts监控文件变更,自动重新加载
  5. 配置runtime-config.ts处理运行时技能配置
  6. 环境覆盖env-overrides.ts支持环境变量覆盖技能参数

3.10 Context & Compaction — 上下文管理

关键文件

文件 职责
context.ts 上下文窗口计算与缓存
context-cache.ts 上下文Token缓存
context-window-guard.ts 上下文溢出保护
compaction.ts 对话压缩入口
pi-hooks/compaction-safeguard.ts 压缩安全守卫
pi-hooks/context-pruning.ts 上下文裁剪

压缩策略

  • 当对话Token接近上下文窗口限制时,自动触发压缩
  • 压缩保留最近的对话轮次,将早期内容摘要化
  • compaction-safeguard确保压缩不会丢失关键信息
  • context-pruning可按配置策略裁剪不重要的上下文

四、关键设计模式总结

4.1 依赖注入模式

整个模块广泛使用构造函数注入+可选运行时替换模式:

type SubagentSpawnDeps = {
  callGateway: typeof callGateway;
  getGlobalHookRunner: () => SubagentLifecycleHookRunner | null;
  loadConfig: typeof loadConfig;
  updateSessionStore: typeof updateSessionStore;
};

const defaultSubagentSpawnDeps: SubagentSpawnDeps = { ... };
let subagentSpawnDeps: SubagentSpawnDeps = defaultSubagentSpawnDeps;

// 测试时替换
export const __testing = {
  setDepsForTest(overrides?: Partial<SubagentSpawnDeps>) {
    subagentSpawnDeps = overrides ? { ...defaultSubagentSpawnDeps, ...overrides } : defaultSubagentSpawnDeps;
  },
};

优点:无需DI框架,测试友好,类型安全。

4.2 运行时模块懒加载

const SUBAGENT_REGISTRY_RUNTIME_SPEC = ["./subagent-registry.runtime", ".js"] as const;

function loadContextEngineInitModule(): Promise<ContextEngineInitModule> {
  contextEngineInitPromise ??= importRuntimeModule<ContextEngineInitModule>(
    import.meta.url,
    SUBAGENT_REGISTRY_RUNTIME_SPEC,
  );
  return contextEngineInitPromise;
}

设计意图:使用??=确保只加载一次,importRuntimeModule处理ESM动态导入的路径解析问题。

4.3 幂等性保护

announce-idempotency.ts确保子代理完成通知不会被重复发送:

  • 使用endedHookEmittedAt时间戳标记已发送的通知
  • endedHookInFlightRunIds集合跟踪正在发送的Hook
  • once语义确保每个生命周期事件只触发一次

4.4 优雅降级链

模型降级链(model-fallback.ts,968行)实现了多层容错:

Profile 1 (冷却中) → Profile 2 (认证失败) → Profile 3 (成功)
                    ↓                         ↓
              cooldownUntil             下次冷却

每个Profile有独立的冷却状态和失败计数,确保单点失败不会影响整体可用性。

4.5 安全纵深防御

Tool Policy Pipeline的6道关卡构成了纵深防御策略:

  • 每道关卡可独立拒绝请求
  • 拒绝后立即返回错误,不继续执行
  • 审批机制(/approve)为危险操作增加人工确认环节

五、代码质量评估

5.1 优点

  1. 类型安全:全面使用TypeScript联合类型和泛型,运行时类型错误极少
  2. 向后兼容:大量使用as别名和export type重导出,API演进平滑
  3. 错误处理:所有异步操作都有try-catch,清理操作为best-effort
  4. 可测试性:依赖注入+__testing对象+mock模块支持单元测试
  5. 安全意识:Null字节剥离、路径遍历防护、XSS防护等贯穿始终

5.2 可改进点

  1. 文件过大bash-tools.exec.ts(1786行)、subagent-registry.ts(947行)等应进一步拆分
  2. 循环依赖pi-embedded-runner/pi-embedded-subscribe/间存在循环引用,通过运行时导入缓解
  3. 配置分散:模型配置分散在model-selection.tsmodels-config*.tsmodel-auth.ts等多个文件中
  4. 错误消息国际化:所有错误消息硬编码英文,不利于国际化

六、模块间调用关系矩阵

调用方 ↓ / 被调用方 → PI Runner Model Sel Auth Sub-Agent Sandbox Skills Tool Policy
CLI Runner - - -
PI Runner 自身
PI Subscribe - - - - -
Sub-Agent Spawn 自身 - -
Tool Exec - - - - 自身
System Prompt - - -

报告生成时间:2026-04-18

*代码版本:openclaw 2026-04-15

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐