【openclaw】OpenClaw Agents 模块 — 超深度架构分析
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代理核心引擎,负责:
- LLM交互编排:管理与大语言模型(Anthropic Claude、OpenAI GPT、Google Gemini等)的完整交互循环
- 工具执行框架:提供安全的工具注册、调用、策略过滤与结果处理机制
- 多代理编排:支持子代理(Sub-Agent)的生成、注册、生命周期管理与结果回收
- 会话状态管理:维护对话上下文、压缩(Compaction)、缓存与持久化
- 安全沙箱隔离:通过Docker/SSH沙箱实现代理的执行隔离与权限控制
- 模型供应商适配:统一多模型供应商的认证、流式传输与降级策略
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 完整执行流程(从入口到出口)
- 消息输入:
queueEmbeddedPiMessage()将用户消息加入队列 - 系统提示词构建:
buildAgentSystemPrompt()组装完整的系统提示词 - 模型解析:
resolveConfiguredModelRef()确定要使用的模型 - 认证解析:
resolveAuthProfileOrder()查找可用的认证Profile - 上下文检查:如果Token超出窗口,触发
compactEmbeddedPiSession() - LLM API调用:通过Provider Transport(Anthropic/OpenAI/Google)发送流式请求
- 流式响应处理:解析SSE事件流,提取工具调用和文本内容
- 工具执行:如有工具调用,经过Policy Pipeline后执行
- 循环判定:工具执行结果追加到对话,回到步骤6继续
- 响应交付:最终文本通过Channel路由发送给用户
- 会话持久化:更新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[]; // 消息工具提示
// ... 更多参数
})
核心构建逻辑:
-
PromptMode分级:
"full":完整系统提示词(主代理使用)"minimal":精简版(子代理使用,仅保留Tooling/Workspace/Runtime段)"none":仅保留基本身份行
-
段落构建顺序(按代码出现顺序):
- 身份声明 → 工具列表 → 安全规则 → CLI参考 → Skills → Memory → 自更新 → 模型别名 → 工作区 → 沙箱 → 授权发送者 → 时间 → 上下文文件 → 输出指令 → Canvas → 消息 → 语音 → 反应 → 推理格式 → 项目上下文 → 静默回复 → Cache Boundary → 动态上下文 → 子代理/群聊上下文 → 心跳 → Runtime行
-
Cache Boundary机制:
const SYSTEM_PROMPT_CACHE_BOUNDARY = "<!-- OPENCLAW_CACHE_BOUNDARY -->";这是一个关键的Anthropic提示缓存优化:将稳定内容放在Cache Boundary上方,动态内容放在下方。这样Anthropic可以在多次请求间复用缓存,减少Token消耗。
-
上下文文件排序:
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)始终出现在前面。
-
动态/静态文件分离:
- 静态文件(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()
这是模型解析的最终仲裁者,按照以下优先级链确定模型:
- 代理级覆盖:
resolveAgentModelPrimaryValue()→ 代理配置中的model字段 - 别名解析:
buildModelAliasIndex().byAlias→ 查找配置的别名映射 - Provider推断:
inferUniqueProviderFromConfiguredModels()→ 当模型名无provider前缀时,从配置中推断唯一provider - 默认Provider回退:使用
DEFAULT_PROVIDER(“openai”)作为provider前缀 - 硬编码默认:
DEFAULT_MODEL(“gpt-5.4”)作为最终兜底 - 配置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;
}
解析逻辑:
- 从配置获取默认代理ID:
resolveDefaultAgentId() - 尝试从显式参数获取代理ID:
normalizeAgentId(agentId) - 尝试从会话Key解析代理ID:
parseAgentSessionKey(sessionKey) - 按优先级:
显式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的逻辑:
- 规范化输入路径(解析符号链接、处理平台差异)
- 检查每个代理的workspace目录是否包含输入路径
- 按路径长度降序排序(最长匹配优先)
- 返回匹配的代理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();
关键子系统:
-
Sweeper:每60秒扫描一次,清理过期运行记录
- Session-mode运行:5分钟TTL(
SESSION_RUN_TTL_MS) - 归档运行:根据
archiveAtMs判断过期 - 清理时通知Context Engine
- Session-mode运行:5分钟TTL(
-
Lifecycle Listener:监听代理事件流
phase: "start"→ 更新startedAt时间戳phase: "end"→ 触发completeSubagentRun()phase: "error"→ 延迟15秒后确认(LIFECYCLE_ERROR_RETRY_GRACE_MS),防止临时错误导致过早清理
-
Orphan Recovery:恢复重启后的孤儿运行
- 冷启动时从磁盘恢复运行记录
- 识别孤儿(
resolveSubagentRunOrphanReason()) - 自动重试Announce流程
-
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道关卡:
- Before-Tool-Call Hook:插件级前置拦截,可完全阻止或修改工具调用
- Tool Policy Match:基于工具名称的策略匹配(allow/deny列表)
- Filesystem Policy:路径访问控制,限制可读写的目录范围
- Sandbox Tool Policy:沙箱模式下的工具可用性控制
- Exec Approval:危险命令的人工审批机制(
/approve命令) - 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 关键流程
- 加载:
local-loader.ts从磁盘加载SKILL.md文件 - 过滤:
filter.ts+agent-filter.ts根据代理配置过滤可用技能 - Frontmatter解析:
frontmatter.ts解析SKILL.md的YAML前置数据 - 刷新:
refresh.ts监控文件变更,自动重新加载 - 配置:
runtime-config.ts处理运行时技能配置 - 环境覆盖:
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集合跟踪正在发送的Hookonce语义确保每个生命周期事件只触发一次
4.4 优雅降级链
模型降级链(model-fallback.ts,968行)实现了多层容错:
Profile 1 (冷却中) → Profile 2 (认证失败) → Profile 3 (成功)
↓ ↓
cooldownUntil 下次冷却
每个Profile有独立的冷却状态和失败计数,确保单点失败不会影响整体可用性。
4.5 安全纵深防御
Tool Policy Pipeline的6道关卡构成了纵深防御策略:
- 每道关卡可独立拒绝请求
- 拒绝后立即返回错误,不继续执行
- 审批机制(
/approve)为危险操作增加人工确认环节
五、代码质量评估
5.1 优点
- 类型安全:全面使用TypeScript联合类型和泛型,运行时类型错误极少
- 向后兼容:大量使用
as别名和export type重导出,API演进平滑 - 错误处理:所有异步操作都有try-catch,清理操作为best-effort
- 可测试性:依赖注入+
__testing对象+mock模块支持单元测试 - 安全意识:Null字节剥离、路径遍历防护、XSS防护等贯穿始终
5.2 可改进点
- 文件过大:
bash-tools.exec.ts(1786行)、subagent-registry.ts(947行)等应进一步拆分 - 循环依赖:
pi-embedded-runner/与pi-embedded-subscribe/间存在循环引用,通过运行时导入缓解 - 配置分散:模型配置分散在
model-selection.ts、models-config*.ts、model-auth.ts等多个文件中 - 错误消息国际化:所有错误消息硬编码英文,不利于国际化
六、模块间调用关系矩阵
| 调用方 ↓ / 被调用方 → | 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
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)