OpenClaw 源码拆解:一个开源 Coding Agent 的架构全景
OpenClaw 源码拆解:一个开源 Coding Agent 的架构全景
OpenClaw 是目前最活跃的开源 AI 助手项目之一,面试中被高频问到"你看过源码吗"。本文基于对 OpenClaw 源码的完整阅读,拆解其核心架构——从 Agent 循环、工具系统、上下文管理、记忆体系到插件机制,逐模块分析设计决策和工程取舍。
项目概览
OpenClaw 是一个基于 TypeScript 的 AI 个人助手框架,采用 pnpm monorepo 结构。它不只是一个 Coding Agent,更是一个支持 25+ 消息通道(Telegram、Discord、Slack、飞书等)、93 个扩展插件、53 个内置 Skill 的通用 Agent 平台。
先看整体架构:
一、Agentic Loop:不只是 ReAct 循环
大多数介绍 Agent 的文章把 Agentic Loop 简化为"Think → Act → Observe → Repeat"。OpenClaw 的实现远比这复杂——它在经典 ReAct 循环外面包了一层弹性重试循环,处理真实生产环境中的各种故障。
双层循环架构
外层循环的核心逻辑在 run.ts(1441 行),内层在 attempt.ts(2072 行)。几个值得注意的设计:
1. Auth Profile 轮转
OpenClaw 支持配置多个 API Key,当一个 Key 被限流或认证失败时,自动切换到下一个。这在个人使用中不常见,但在团队或多通道场景下很实用。
2. 上下文溢出的智能处理
不是简单地报错,而是检测到溢出后自动触发 compaction(压缩),然后重新尝试。最多压缩 3 次,每次都能腾出更多空间。
3. 超时时的启发式判断
超时不一定意味着失败。如果 token 使用率已经超过 65%,说明可能是上下文太长导致推理变慢,此时先压缩再重试,而不是直接放弃。
二、工具系统:多层策略管道
工具目录
OpenClaw 的工具分为 11 个类别:
| 类别 | 核心工具 | 说明 |
|---|---|---|
| Files | read, write, edit, apply_patch |
文件操作 |
| Runtime | exec, process, code_execution |
命令执行 |
| Web | web_search, web_fetch |
网络访问 |
| Memory | memory_search, memory_get |
记忆检索 |
| Sessions | sessions_list, sessions_spawn, subagents |
多会话/子Agent |
| UI | browser, canvas |
界面交互 |
| Messaging | message |
跨通道消息 |
| Automation | cron, gateway |
定时任务/网关 |
| Nodes | - | 节点管理 |
| Agents | - | Agent 管理 |
| Media | image, image_generate, tts |
多媒体 |
多层策略过滤
工具不是一股脑全给模型的。OpenClaw 设计了一条六层策略管道,逐层过滤出当前场景应该暴露的工具:
四种 Profile 预设:
minimal:只有session_status——用于状态检查场景coding:文件、运行时、Web、记忆、会话、自动化、媒体messaging:会话和消息工具——用于纯聊天场景full:所有工具
为什么这样设计? 工具数量直接影响模型的工具选择准确率。研究表明,当可用工具超过 15-20 个时,模型选错工具的概率显著上升。通过策略管道,不同场景下模型看到的工具集是精简且相关的。
MCP 工具集成
除了内置工具,OpenClaw 还通过 materializeBundleMcpToolsForRun 加载 MCP 工具。MCP(Model Context Protocol)是 Anthropic 提出的开放协议,让外部工具通过标准接口接入 Agent。OpenClaw 的 MCP 集成意味着任何实现了 MCP Server 的工具都可以被 OpenClaw 调用。
三、上下文管理:可插拔引擎 + 分块摘要压缩
ContextEngine 接口
这是 OpenClaw 上下文管理的核心抽象。通过定义一套标准接口,允许不同的上下文管理策略以插件形式接入:
接口中有几个精妙的设计:
1. assemble() 返回 systemPromptAddition
引擎不仅组装消息历史,还可以向 System Prompt 注入额外指令。这意味着上下文引擎可以根据当前对话状态,动态调整模型的行为指导。
2. maintain() 的安全重写机制
引擎可以通过 runtimeContext.rewriteTranscriptEntries() 安全地重写会话转录。注意是"安全重写"——引擎决定"改什么",运行时负责"怎么改",避免引擎直接操作底层存储导致数据不一致。
3. ownsCompaction 标志
引擎可以声明自己管理压缩(ownsCompaction: true),此时外层循环不会自动触发压缩,完全由引擎内部决定压缩时机和策略。
压缩算法:三级降级
当上下文超限时,OpenClaw 的压缩算法(compaction.ts,531 行)采用三级降级策略:
几个关键的工程细节:
自适应分块
不是固定大小切分,而是根据平均消息大小动态调整:
当平均消息大小 > 上下文窗口的 10% 时:
reduction = min(avgRatio × 2, BASE_CHUNK_RATIO - MIN_CHUNK_RATIO)
chunkRatio = max(0.15, 0.4 - reduction)
消息越大,分块越小,避免单个分块超出模型处理能力。
标识符保留策略
这是我在源码中看到的一个非常实用的设计:摘要时严格保留 UUID、哈希、API Key、URL、文件路径等不可重构的标识符。因为标识符一旦被摘要改写(比如把一个 UUID 缩写成"之前提到的那个 ID"),后续所有引用都会断链。
20% 安全边际
SAFETY_MARGIN = 1.2——token 估算使用 chars/4 的启发式方法,但这会低估多字节字符(中文、emoji)和特殊 token 的实际消耗。20% 的安全缓冲确保分块不会意外超限。
合并摘要时的信息保留清单
多段摘要合并为最终摘要时,明确要求保留:
- 活跃任务状态和批处理进度
- 用户的最后请求
- 关键决策及其理由
- TODO 和开放问题
- 承诺的后续行动
这不是随便写的 Prompt,而是从大量真实对话中提炼出的"压缩时最容易丢失但最影响后续对话质量的信息类型"。
四、记忆体系:从"做梦"到长期记忆
OpenClaw 的记忆系统是我认为最有创意的模块。它不只是"存储和检索",而是构建了一个记忆生命周期管理系统。
存储层
| 层级 | 存储方式 | 说明 |
|---|---|---|
| 瞬时 | 会话上下文 | 当前对话中的工具结果,可被压缩回收 |
| 短期 | 召回追踪记录 | 每次 memory_search 的查询和命中结果 |
| 长期 | MEMORY.md / memory/*.md |
文件级持久化记忆 |
| 人格 | SOUL.md |
用户定义的 Agent 人格和行为偏好 |
| 向量 | LanceDB(可选) | 语义向量索引,支持高效相似搜索 |
memory_search:强制语义召回
memory_search 工具的描述是 “Mandatory recall step”——在回答关于过去工作、决策、日期、人物、偏好或 TODO 的问题之前,Agent 被强制要求先搜索记忆。这不是建议,是指令。
执行流程:
最后一步是关键:异步记录召回,永远不阻塞主流程。这些召回记录是"做梦"机制的数据来源。
Dreaming:模拟人类睡眠记忆巩固
这是 OpenClaw 记忆系统最有创意的设计——模拟人类"做梦"过程,在空闲时段自动将高频/高权重的短期记忆提升为长期记忆。
三种"做梦"预设:
| 预设 | 触发频率 | 最低分数 | 最少召回次数 | 最少唯一查询数 | 适用场景 |
|---|---|---|---|---|---|
core |
每天凌晨3点 | 默认 | 默认 | 默认 | 日常使用 |
deep |
每12小时 | 0.8 | 3 | 3 | 中度使用 |
rem |
每6小时 | 0.85 | 4 | 3 | 高频使用 |
提升条件的设计逻辑:
不是"被搜索到就提升",而是要同时满足三个条件:
- 分数够高(minScore):语义匹配质量好
- 被召回次数够多(minRecallCount):不是偶然命中
- 被不同查询召回过(minUniqueQueries):不是同一个问题反复问
第三个条件最精妙——它区分了"用户反复问同一个问题"(可能是系统没回答好)和"这个知识在多个不同场景下都有用"(真正值得长期记住的信息)。
SOUL.md:Agent 的人格文件
如果工作空间根目录存在 SOUL.md,OpenClaw 会在 System Prompt 中注入指令,让 Agent 采用其中定义的人格和语气。这不是简单的"角色扮演 Prompt",而是作为 Project Context 的一部分注入,与项目上下文同级。
五、System Prompt:高度结构化的 25+ 章节
OpenClaw 的 System Prompt 构建器(system-prompt.ts,764 行)生成的 Prompt 包含 25+ 个结构化章节。这不是一段自然语言,而是一份精心组织的"操作手册":
缓存边界设计
[CACHE_BOUNDARY] 标记是专门为 Anthropic 的 Prompt Cache 设计的。标记之上的内容变化频率低,可以被缓存复用;标记之下的内容(群聊上下文、子Agent上下文、运行时信息)每次可能不同,不缓存。
这个设计直接影响成本——Anthropic 的缓存命中 token 成本是未命中的 1/10。如果 System Prompt 占输入 token 的 60%(在工具多的场景下很常见),合理的缓存边界能降低约 50% 的输入成本。
三种 Prompt 模式
| 模式 | 包含内容 | 使用场景 |
|---|---|---|
full |
全部 25+ 章节 | 主 Agent |
minimal |
去掉 Memory/Skills/Docs/Silent Replies | 子 Agent(减少 token 消耗) |
none |
仅身份声明一行 | 极简场景 |
子 Agent 使用 minimal 模式是一个务实的取舍:子 Agent 通常执行特定任务,不需要完整的技能目录和记忆系统,精简 Prompt 可以把更多窗口留给任务本身。
六、插件系统:三层扩展机制
OpenClaw 的可扩展性通过三层机制实现:
1. Plugin(插件)
93 个扩展插件,通过 definePluginEntry 注册:
2. Skill(技能)
53 个内置 Skill,每个 Skill 是一个包含 SKILL.md 的目录。Skill 的加载有严格的优先级(低到高):
extra dirs— 配置的额外目录openclaw-bundled— 内置skills/目录openclaw-managed—~/.openclaw/skills/agents-skills-personal—~/.agents/skills/agents-skills-project— 项目.agents/skills/openclaw-workspace— 项目skills/
高优先级覆盖低优先级的同名 Skill。项目级 Skill 优先级最高,意味着每个项目可以定制自己的技能集。
Skill 的加载方式:Skill 目录和描述以 XML 格式注入 System Prompt 的 <available_skills> 标签。Agent 在回答前扫描匹配的 Skill,然后用 read 工具加载对应的 SKILL.md 获取完整指令。
这是一种渐进式加载策略——不是把所有 Skill 的完整内容都塞进 System Prompt(那样会撑爆窗口),而是先给目录,按需加载。
3. MCP(Model Context Protocol)
通过 materializeBundleMcpToolsForRun 加载 MCP 工具。任何实现了 MCP Server 的外部服务都可以被 OpenClaw 作为工具调用,无需编写插件代码。
七、消息规范化与转录修复
流式事件处理
Agent 的流式响应不是简单的文本拼接。OpenClaw 处理以下事件类型:
| 事件 | 处理逻辑 |
|---|---|
text_delta |
增量文本累积 |
text_end |
最终文本处理 + 清洗 |
tool_call_start/end |
工具调用追踪和去重 |
message_end |
消息完成 |
compaction_start/end |
压缩事件 |
文本清洗管线
转录修复
session-transcript-repair.ts 处理一个微妙但重要的问题:tool_use 和 tool_result 的配对修复。
在正常流程中,每个 tool_use 消息都应该有对应的 tool_result。但在上下文压缩、会话恢复、异常中断等场景下,可能出现:
- 孤立的
tool_result(对应的tool_use被压缩掉了) - 缺失的
tool_result(工具执行中断)
OpenClaw 在每次组装上下文前都会运行修复,确保 API 收到的消息序列是格式正确的。
八、多通道架构:一个 Agent 服务所有平台
通道抽象
OpenClaw 支持 25+ 消息通道,通过统一的通道注册表管理:
每个通道声明自己的能力(是否支持 Markdown、内联按钮、表情反应等),Agent 的回复格式会根据通道能力自动适配。比如在 Telegram 上可以用 Markdown 格式化代码块,在 SMS 上就只能发纯文本。
统一消息发送
message 工具提供统一的跨通道消息发送接口,支持:
send:发送消息thread-create:创建线程react:添加表情反应
消息自动路由到源通道,Agent 不需要关心底层是 Telegram 还是 Discord。
九、设计哲学总结
读完 OpenClaw 的源码,我总结出它的几个核心设计哲学:
1. 可插拔优先于深度优化
ContextEngine、Provider、Channel、Memory 全部采用注册表 + 接口模式。这意味着任何模块都可以被替换,但也意味着默认实现可能不如深度定制的方案(比如 Claude Code 的上下文管理)。这是广度 vs 深度的取舍。
2. 弹性优先于性能
外层循环的 auth 轮转、模型降级、自动压缩、三级降级摘要——到处都是 fallback。这反映了一个在 25+ 通道上运行的系统的现实需求:不能因为一个 API Key 过期就整个系统挂掉。
3. 渐进式加载优先于全量注入
Skill 只注入目录不注入全文、记忆只在需要时搜索不全量注入、工具按策略过滤不全量暴露。这是在有限上下文窗口下的务实选择。
4. 记忆是一等公民
"做梦"机制、强制召回、短期到长期的自动提升——OpenClaw 把记忆当作核心特性来建设,而不是事后加的功能。这跟大多数 Coding Agent(包括 Codex CLI)形成鲜明对比。
与 Claude Code 的差异
| 维度 | OpenClaw | Claude Code |
|---|---|---|
| 定位 | 通用 AI 助手(25+ 通道) | 专注编码(终端原生) |
| 上下文管理 | 可插拔接口,实现深度有限 | 工业级精密管理(三路缓存、四级压缩) |
| 记忆 | 做梦机制 + LanceDB 向量搜索 | 五层记忆 + AutoDream + 语义召回 |
| 工具 | 多层策略过滤 | 按语义分类(只读并行/写入独占) |
| 扩展性 | 93 个插件 + 53 个 Skill + MCP | 相对封闭 |
| 核心优势 | 广度(通道 × 插件 × 通道) | 深度(上下文管理精度) |
写在最后
OpenClaw 的源码质量在开源 Agent 项目中属于上乘。它不是一个"能跑就行"的 demo,而是一个经过深思熟虑的生产级系统。特别值得学习的几个点:
- 多层策略管道:工具过滤不是一个 if-else,而是一条可组合的策略链
- 做梦机制:把"记忆管理"从被动存取升级为主动整理,这个思路可以迁移到任何 Agent 系统
- 标识符保留策略:压缩时保留 UUID/URL 等不可重构信息,这个细节在其他系统中很少见
- 弹性重试循环:把 auth 轮转、模型降级、自动压缩统一在外层循环中处理,比分散在各处的 try-catch 优雅得多
如果你正在做自己的 Agent 系统,OpenClaw 的源码值得通读一遍——不是为了复制它的实现,而是理解它在每个设计决策点上的取舍逻辑。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)