OpenClaw 拆解③丨20 段 System Prompt 的模块化组装,以及为什么安全规则不能被覆盖
这是 OpenClaw 架构拆解系列的第三篇。
上一篇聊了 Agent Loop 的三层嵌套循环。今天聊一个更底层的东西,System Prompt。
说真的,在我读 OpenClaw 的 system-prompt.ts 之前,我一直觉得 System Prompt 就是一段固定的文字。你写好了,贴到 API 调用里,完事。
但 OpenClaw 的 System Prompt 有 500 多行构建代码,接收 50+ 个参数,输出一个由 20+ 独立段落 组装而成的完整提示词。
这不是写文案,这是在做编译器。
buildAgentSystemPrompt() 函数(L453-960)是 System Prompt 的「组装工厂」。
它把提示词拆成了 20 多个独立的段落(section),每个段落负责一个特定的行为维度。
| 段落 | 职责 | 示例内容 |
|---|---|---|
| identity | 身份声明 | “You are a personal assistant…” |
| interaction_style | 交互风格 | 回复长度、语气、格式偏好 |
| tool_call_style | 工具调用方式 | 并行/串行、错误处理策略 |
| execution_bias | 执行偏好 | 偏向快速执行还是谨慎确认 |
| safety | 安全规则 | “Inspired by Anthropic’s constitution” |
| messaging | 消息指导 | Discord/Slack 特定格式 |
| voice | 语音模式 | 语音交互的特殊约束 |
| heartbeat | 心跳指导 | 定期汇报进度 |
| tools | 工具列表 | 26 个工具的一行描述 |
这些段落按照固定顺序拼接。顺序不能乱,因为 LLM 对提示词位置敏感,靠前的内容权重更高。
💡 这是一种「组合优于继承」的设计。如果用继承,你会有
BasePrompt → VoicePrompt → DiscordVoicePrompt这样的类层级。用组合,你只需要拼接identity + voice + messaging三个段落。维护成本天差地别。
🔒 三个可以被覆盖的段落,和一个不能被触碰的
这是 System Prompt 设计中我觉得最精妙的部分。
OpenClaw 支持 Provider Overlay,也就是说,不同的 LLM Provider(比如 Anthropic、OpenAI)可以定制 System Prompt 中的某些段落。
但不是所有段落都能被定制。
只有 3 个段落允许被 Override
- 1.
interaction_style,交互风格 - 2.
tool_call_style,工具调用方式 - 3.
execution_bias,执行偏好
绝对不能被覆盖的
safety 段落。
💡 安全段声明 “Inspired by Anthropic’s constitution”,不可被 Provider Override、不可被 SOUL.md 覆盖、不可被用户指令绕过。这是 OpenClaw 的「Ring 0」规则,硬件级别的不可变。
这是一个「声明式白名单」的设计模式。不是「除了安全段其他都可以覆盖」,而是「只有这三个段落可以覆盖,其他一律不行」。
你想想这和什么很像?
Android 的 <uses-permission> 声明。App 不是「默认拥有所有权限,系统禁止某些」,而是「默认没有任何权限,App 显式声明需要哪些」。OpenClaw 的 Provider Overlay 是同样的设计,Provider 只能在被显式授权的 3 个段落范围内定制行为。
📌 SOUL.md,优先级 20 的人设文件
SOUL.md 是 OpenClaw 的人设定义文件。但它不是什么特殊格式,就是一个普通的 Markdown 文件。
源码在 system-prompt.ts L47-55, L98-127。
Context File 优先级排序
| 优先级 | 文件 | 作用 |
|---|---|---|
| 20 | soul.md |
人格定义 |
| 30 | identity.md |
身份信息 |
| 40 | user.md |
用户偏好 |
| 50 | tools.md |
工具指南 |
| 60 | bootstrap.md |
启动配置 |
| 70 | memory.md |
记忆指导 |
SOUL.md 优先级 20,在所有 context file 中最先被加载。
加载后会追加一条明确指令(L117-120)
“embody its persona and tone… follow its guidance unless higher-priority instructions override it”
注意最后那句 “unless higher-priority instructions override it”。
所以 SOUL.md 虽然优先级最高(在 context files 中),但 System Prompt 本体和安全规则仍然可以覆盖它。
💡 三层权限模型
System Prompt 安全规则 > SOUL.md 人格 > 其他 context files。像 CSS 的选择器权重,SOUL.md 是 class 级别的高优先级,但 safety 是 !important,永远赢。
🧱 Cache Boundary,应用层的缓存分割点
在 System Prompt 中间,有一个叫 SYSTEM_PROMPT_CACHE_BOUNDARY 的标记。
它把 System Prompt 分成两半。
[上半部分 - 稳定内容]
identity 段
interaction_style 段
tool_call_style 段
safety 段
tools 段
Provider stablePrefix
═══ CACHE BOUNDARY ═══
[下半部分 - 动态内容]
heartbeat 段(含时间戳)
messaging 段
voice 段
output directives
Provider dynamicSuffix
上半部分在同一个会话内基本不变。下半部分每轮都可能变化(heartbeat 有时间戳,messaging 可能根据平台变化)。
这条线的目的是让 LLM API Provider(比如 Anthropic)能够缓存上半部分。如果每次请求的前缀都一样,Provider 可以跳过对这部分的重新计算,直接复用之前的 KV Cache。
反直觉 OpenClaw 没有 cache hit rate 指标。它不测量缓存命中率,而是通过结构性设计保证「发出去的前缀是字节级稳定的」。就像 CPU 设计者不测量你的程序 L1 cache miss 率,而是通过 Cache Line 大小和关联度来保证大多数访问模式能命中。
这是一种「结构性保证」思路。不依赖运行时度量,而是通过架构设计从源头上保证效果。
🎛️ Provider promptContribution 三件套
每个 Provider 可以注册一个 promptContribution 对象,通过三个维度定制 System Prompt。

| 维度 | 位置 | 缓存行为 |
|---|---|---|
stablePrefix |
Cache Boundary 之前 | ✅ 参与缓存 |
dynamicSuffix |
运行时段 之后 | ❌ 不参与缓存 |
sectionOverrides |
替换 3 个白名单段 | 取决于位置 |
stablePrefix 和 dynamicSuffix 看起来像对称的概念,但缓存行为完全不同。prefix 是 public, max-age=forever,suffix 是 no-cache。
这个设计的含义是,Provider 可以在 System Prompt 的头部和尾部各加一段自己的内容。头部的内容必须是稳定的(因为要参与缓存),尾部的可以是动态的。
💡
stablePrefix参与缓存,意味着 Provider 的定制化内容享受了缓存加速。代价是一旦 prefix 改变,整个缓存失效。OpenClaw 通过命名约束(”stable”)来确保 Provider 不会频繁修改这部分。
🔐 ACP 协议,Agent 世界的 gRPC
System Prompt 中还有一个有意思的部分,ACP(Agent Communication Protocol)指导。
当用户说「在 Claude Code 里做这件事」或者「用 Cursor 处理一下」时,OpenClaw 需要把任务路由到其他 Agent Harness。这就是 ACP。
System Prompt 中的 ACP 指导(L747-754)告诉 LLM 几个规则
- 1. 识别到「在 XXX 中做」这类意图时,调用
sessions_spawn - 2. 指定
runtime: "acp" - 3. Discord 环境下默认
thread: true, mode: "session" - 4. 必须显式设置
agentId - 5. 不能走本地的
subagents/agents_list路径
安全约束在 acp-spawn.ts 中实现
| 规则 | 含义 |
|---|---|
| 沙箱会话禁止生成 ACP | 安全隔离 |
mode:"session" 要求 thread:true |
强制线程隔离 |
Agent 必须在 allowedAgents 白名单 |
权限控制 |
💡 ACP 从架构上看就是 Agent 世界的 RPC 框架。
sessions_spawn≈ gRPC 的stub.Call(),runtime:"acp"≈ 指定传输协议,allowedAgents≈ mTLS 客户端证书白名单。
这让我意识到,Agent 之间的通信协议正在走分布式系统的老路。从非结构化调用(直接调 API)到结构化协议(ACP/A2A/MCP),这和 HTTP → REST → gRPC 的演进路径几乎一模一样。
系统提示词这个东西,看着像文案,拆开来看是一个编译器。
20+ 模块化段落的组装顺序、3 个白名单段的覆盖权限、Cache Boundary 的缓存切分、Provider 三件套的注入机制、SOUL.md 的优先级排序、ACP 的路由规则……每一个设计决策背后都有明确的 trade-off。
说真的,我觉得 System Prompt 是 OpenClaw 整个架构中设计感最强的部分。不是因为它最复杂,而是因为它最克制。
20 多个段落,但只有 3 个允许被覆盖。
SOUL.md 优先级最高,但安全规则可以覆盖它。
Provider 可以定制行为,但只能在白名单范围内。
克制,是好架构的标志。
以上,既然看到这里了,如果觉得不错,随手点个赞、在看、转发三连吧,如果想第一时间收到推送,也可以给我个星标⭐~
谢谢你看我的文章,我们,下次再见。

夜雨聆风