拆解 OpenClaw Agent 设计
摘要:
OpenClaw Agent 设计如何通过 Pi 集成、系统提示词三层架构、智能体引导、会话裁剪、上下文压缩和记忆概览,构建完整的智能体上下文管理体系。

做智能体开发的人,大概都经历过这种崩溃:
上下文爆了、提示词混乱、对话历史越积越厚,模型开始「胡言乱语」,或者 Token 费用烧得让人心惊肉跳。
问题出在哪?
不是模型不够强,是架构没搭对。
最近完整拆解了 OpenClaw 的源码,它的 Agent 设计很有意思,没有花哨的概念,全是工程化实践。
下面是完整拆解。
01
Pi 集成:不启动子进程,直接嵌入会话
核心思路:OpenClaw 使用 Pi-Agent 作为它的底层 Agent 框架,Pi-Agent 底层也是一个 while(true) 循环,它会在循环中发布事件,例如:循环开始、工具调用开始、循环结束等等。OpenClaw 基于 PiAgent,在其基础上监听事件,做工程化。其不使用子进程或 RPC 模式调用 Pi,而是通过 createAgentSession() 直接导入并实例化 Pi 的 AgentSession。这种嵌入式架构带来了对会话的完全控制权。
嵌入式架构的 6 大优势
-
对会话生命周期和事件处理的完全控制 -
自定义工具注入(消息、沙箱、渠道特定操作) -
按渠道 / 上下文自定义系统提示词 -
支持分支 / 压缩的会话持久化 -
支持故障转移的多账号认证配置轮换 -
与提供商无关的模型切换
事件订阅机制
OpenClaw 通过 subscribeEmbeddedPiSession() 订阅 Pi 的 AgentSession 事件,完整覆盖智能体运行的全生命周期:
const subscription = subscribeEmbeddedPiSession({session: activeSession,runId: params.runId,verboseLevel: params.verboseLevel,reasoningMode: params.reasoningLevel,toolResultFormat: params.toolResultFormat,onToolResult: params.onToolResult,onReasoningStream: params.onReasoningStream,onBlockReply: params.onBlockReply,onPartialReply: params.onPartialReply,onAgentEvent: params.onAgentEvent,});
处理的事件全景:
|
|
|
|---|---|
message_start/message_end/message_update |
|
tool_execution_start/tool_execution_update/tool_execution_end |
|
turn_start/turn_end |
|
agent_start/agent_end |
|
compaction_start/compaction_end |
|
工具设计
OpenClaw 的工具不是简单的「传给模型」,而是做了完整的分层和过滤:
工具分层:
- 基础工具:
- Pi 原生 codingTools(
read、bash、edit、write) - 自定义替换:
- OpenClaw 用
exec/process替换原生bash,并为沙箱环境自定义read/edit/write - OpenClaw 扩展工具:
- 消息、浏览器、画布、会话、cron、Gateway 网关等
- 渠道专属工具:
- Discord / Telegram / Slack / WhatsApp 特定操作工具
管线处理:
- 策略过滤:
- 按配置、提供商、智能体、群组、沙箱策略多维度过滤可用工具
- Schema 标准化:
- 针对 Gemini / OpenAI 等不同提供商的 API 细节清理和统一工具 schema
- AbortSignal 包装:
- 所有工具包装以遵守中止信号,确保超时和取消能正确传播
系统提示词构造
系统提示词在 buildAgentSystemPrompt()(system-prompt.ts)中构建。它不是简单拼接字符串,而是组装一个完整的运行时上下文,包含以下模块:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
其中有意思的点是它把文档也加进了其系统提示词里,这使得它能自主解决各种安装配置问题,例如和它说下帮我对接 Claude Code,它会自己找文档自己去对接。
启用时还包含 Memory 和 Reactions 模块,以及可选的上下文文件和额外系统提示词内容。用于子智能体的最小提示词模式会智能裁剪这些部分。
提示词在会话创建后通过 applySystemPromptOverrideToSession() 动态应用。
会话文件与缓存
会话存储:
会话是带有树形结构(id / parentId 链接)的 JSONL 文件,由 Pi 的 SessionManager 负责持久化。~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl
会话缓存:
session-manager-cache.ts 缓存 SessionManager 实例,避免重复解析文件,提升响应速度。
错误分类与故障转移
pi-embedded-helpers.ts 提供了一套完整的错误分类体系:
|
|
|
|---|---|
isContextOverflowError(errorText) |
|
isCompactionFailureError(errorText) |
|
isAuthAssistantError(lastAssistant) |
|
isRateLimitAssistantError(...) |
|
isFailoverAssistantError(...) |
|
classifyFailoverReason(errorText) |
"auth" | "rate_limit" | "quota" | "timeout" | … |
思考级别回退:如果当前模型不支持某个思考级别,系统会自动降级回退,不会中断流程。
02
系统提示词:三层架构,动态渲染
OpenClaw Agent 设计中,提示词不是静态模板,而是运行时动态组装的完整指令集。
提示词组装三层架构
|
|
|
|
|---|---|---|
buildAgentSystemPrompt |
|
|
resolveAgentSystemPromptConfig |
|
|
| 运行时适配器 |
|
|
运行时适配器负责收集实时事实:工具列表、沙箱状态、渠道能力、上下文文件、提供商提示词贡献,然后调用已配置的提示词门面。
提示词结构全景
|
|
|
|---|---|
| 工具 |
|
| 执行倾向 |
|
| 安全 |
|
| Skills |
|
| OpenClaw 控制 |
|
| OpenClaw 自更新 |
config.schema.lookup 安全检查配置,用 config.patch 修补配置,使用 config.apply 替换完整配置,并且仅在用户明确请求时运行 update.run。仅所有者可用的 gateway 工具也会拒绝重写 tools.exec.ask / tools.exec.security,包括会规范化到这些受保护执行路径的旧版 tools.bash.* 别名 |
| 工作区 |
agents.defaults.workspace) |
| 文档 |
|
| 工作区文件(已注入) |
|
| 沙箱 |
|
| 当前日期和时间 |
session_status) |
| 助手输出指令 |
|
| Heartbeat |
|
| 运行时 |
|
| 推理 |
/reasoning 切换提示 |
设计亮点:
提示词是「活」的。每次智能体运行,提示词都会根据当前运行时状态重新组装,而不是复用固定模板。这确保了模型始终拿到最新、最准确的上下文。
03
智能体启动引导(Bootstrapping):一次运行,终身记忆
Bootstrapping 是 OpenClaw Agent 设计中最容易被忽略但最关键的环节。它发生在新手引导之后,智能体第一次启动时。
Bootstrapping 会做什么
在首次运行智能体时,OpenClaw 会 bootstrap 工作区(默认 ~/.openclaw/workspace):
- 1生成身份文件:
AGENTS.md、BOOTSTRAP.md、IDENTITY.md、USER.md- 2交互式问答:
- 运行一段简短的问答流程(一次一个问题)
- 3持久化偏好:
- 将身份 + 偏好写入
IDENTITY.md、USER.md、SOUL.md - 4一次性触发:
- 完成后移除
BOOTSTRAP.md,因此它只会运行一次
特殊场景处理
- 嵌入式 / 本地模型运行:
- OpenClaw 会将
BOOTSTRAP.md排除在特权系统上下文之外 - 主要交互式首次运行:
BOOTSTRAP.md仍会在用户提示中传入文件内容,确保那些不能可靠调用read工具的模型也能完成该流程- 工作区访问受限:
- 如果当前运行无法安全访问工作区,智能体会收到一条受限的 bootstrap 说明,而不是通用问候语
04
会话裁剪(Context Trimming):不写磁盘,只裁内存
上下文膨胀是智能体开发的隐形杀手。OpenClaw 的会话裁剪方案,在不丢失历史的前提下,精准控制每次调用送给模型的 token 量。
⚠️ 裁剪仅发生在内存中——不会修改磁盘上的会话记录。你的完整历史始终会被保留。
工作原理
- 1等待缓存 TTL 过期
(默认 5 分钟) - 2识别目标:
找出适合常规裁剪的旧工具结果(对话文本保持不变) - 3软裁剪:
对超大的结果保留开头和结尾,中间插入 ... - 4硬清除:
对其余内容替换为占位符 - 5重置 TTL:
以便后续请求复用新的缓存
旧图片清理机制
对于在历史中保留了原始图片块或提示词注入媒体标记的会话,OpenClaw 构建了一个独立的、幂等的重放视图:
-
逐字节保留最近 3 个已完成轮次,确保最近后续请求的提示缓存前缀保持稳定 -
重放视图中,来自 user或toolResult历史记录里、较早且已处理的图片块,替换为[image data removed - already processed by model] -
较早的文本媒体引用(如 [media attached: ...]、[Image: source: ...]和media://inbound/...)替换为[media reference removed - already processed by model] -
当前轮次的附件标记保持不变,视觉模型仍可为新图片注入内容 -
原始会话记录不重写,历史查看器仍可渲染原始消息条目及图片 -
与常规缓存 TTL 裁剪分离,防止重复图片负载或过期媒体引用破坏提示缓存
裁剪 vs 压缩:互补而非替代
|
|
|
|
|---|---|---|
| 是什么 |
|
|
| 会保存吗? |
|
|
| 范围 |
|
|
它们彼此互补——裁剪可以在压缩周期之间保持工具输出精简。
05
压缩(Compaction):上下文满了怎么办?
每个模型都有上下文窗口上限。当对话接近该限制时,OpenClaw 会将较早的消息压缩成摘要,让聊天可以继续。
工作原理
-
较早的对话轮次被摘要成一条精简条目 -
摘要保存在会话转录中 -
最近的消息保持完整 - 智能边界处理:
当 OpenClaw 将历史拆分为压缩块时,会让智能体工具调用与其匹配的 toolResult条目保持配对。如果拆分点落在工具块内部,OpenClaw 会移动边界,让这对条目保持在一起,并保留当前未摘要的尾部 -
完整对话历史仍保留在磁盘上。压缩只改变模型在下一轮看到的内容
自动压缩
自动压缩默认开启。触发条件:
- 1
会话接近上下文限制时自动运行 - 2
模型返回上下文溢出错误时,OpenClaw 压缩并重试
防丢失机制:压缩前,OpenClaw 会自动提醒智能体将重要笔记保存到 memory 文件。这可以防止上下文丢失。
可识别的上下文溢出错误模式:
request_too_largecontext length exceededinput exceeds the maximum number of tokensinput token count exceeds the maximum number of input tokensinput is too long for the modelollama error: context length exceeded
手动压缩
在任何聊天中输入 /compact 可强制压缩,给用户完全的控制权。
06
记忆概览(Memory):模型「记住」的唯一方式
OpenClaw Agent 设计中,记忆不是黑盒。模型只会「记住」保存到磁盘的内容,没有隐藏状态。
三个记忆文件
|
|
|
|
|---|---|---|
MEMORY.md |
长期记忆
|
|
memory/YYYY-MM-DD.md |
每日笔记
|
|
DREAMS.md
|
Dream Diary
|
|
这些文件位于智能体工作区(默认 ~/.openclaw/workspace)。
内容分层策略
MEMORY.md —— 紧凑、经过整理的长期层
- 存放
:持久事实、偏好、长期决策、应在主私密会话开始时可用的简短摘要 - 不是
:原始记录、每日日志、详尽归档
memory/YYYY-MM-DD.md —— 详细的工作层
- 存放
:详细的每日笔记、观察记录、会话摘要、以后可能仍有用的原始上下文 - 索引
:用于 memory_search和memory_get,但不会在每一轮都注入常规启动提示
最佳实践:
随着时间推移,智能体应从每日笔记中提炼有用材料写入 MEMORY.md,并移除过时的长期条目。生成的工作区说明和 Heartbeat 流程可以定期完成这件事,不需要为每个记住的细节手动编辑MEMORY.md。
启动预算保护
如果 MEMORY.md 超过启动文件预算,OpenClaw 会保持磁盘上的文件完整,但会截断注入到模型上下文中的副本。
处理信号:
-
将详细材料移回 memory/*.md -
只在 MEMORY.md中保留持久摘要 -
或在明确想花费更多提示预算时提高启动限制
使用 /context list、/context detail 或 openclaw doctor 查看原始大小与注入大小,以及截断状态。
记忆指令
如果你想让智能体记住某件事,直接告诉它即可:「记住我偏好 TypeScript。」它会把内容写入适当的文件。
推断式跟进承诺
有些未来跟进不是持久事实。如果你提到明天有面试,有用的记忆可能是「面试后跟进一下」,而不是「永久存进 MEMORY.md」。
跟进承诺机制:
-
选择启用、短期跟进记忆 -
OpenClaw 在隐藏后台流程中推断它们 -
限定到同一个智能体和渠道 -
通过 Heartbeat 发送到期的跟进 -
显式提醒仍使用定时任务
记忆工具
|
|
|
|---|---|
memory_search |
|
memory_get |
|
这两个工具都由主动记忆插件提供(默认:memory-core)。
记忆搜索
配置嵌入提供商后,memory_search 使用混合搜索:
- 向量相似度:
- 语义含义匹配
- 关键词匹配:
ID 和代码符号等精确术语匹配
OpenClaw 根据可用 API key 自动检测嵌入提供商。配置了 OpenAI、Gemini、Voyage 或 Mistral key,记忆搜索自动启用。
记忆后端
|
|
|
|---|---|
| SQLite(内置,默认) |
|
| QMD |
|
| Honcho |
|
| LanceDB |
|
Dreaming:记忆的后台整合
Dreaming 是记忆的可选后台整合流程。它会收集短期信号、为候选项评分,并且只将符合条件的项目提升到长期记忆(MEMORY.md)。
设计目标:让长期记忆保持高信号
- 选择启用:
默认禁用 - 定时:
启用后, memory-core自动管理用于完整 Dreaming 扫描的定期 cron 任务 - 阈值控制:
提升必须通过分数、召回频率和查询多样性门槛 - 可审阅:
阶段摘要和日记条目写入 DREAMS.md,供人工审阅
写在最后
OpenClaw 的 Agent 设计把智能体开发中遇到的问题逐一解决:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这套 Agent 设计值得每一个做智能体开发的人细读。不是因为它用了什么新技术,而是因为它把工程实践中那些「脏活累活」都做了标准化处理。
夜雨聆风