整体架构图
快速导航(TL;DR)
新手必读:
- Layer 7(Workspace Files)- 你能直接编辑的配置文件
- Layer 8(Bootstrap Hook)- 你能写脚本动态注入内容
- 其他层都是框架自动生成的,了解即可
常见需求:
- 想定义 Agent 身份?→ 编辑 Layer 7 的 IDENTITY.md
- 想添加项目文档?→ 使用 Layer 8 的 bootstrap-extra-files Hook
- 想注入实时上下文?→ 使用 Layer 8 的 before_prompt_build Hook
- 想控制文件大小?→ 调整 bootstrapMaxChars 配置
Layer 1: OpenClaw Framework Core(框架核心层)
比喻
就像一本操作手册的"使用说明"部分——告诉 LLM 你是谁、能做什么、应该怎么回应
组成内容
实际示例
你正在以「创意伙伴」身份运行,这是一个 AI 内容创作专家 Agent。 当前时间:2026-03-05 14:37:00 CST 运行环境:agent=creative | host=黄宗宁的MacBook Air === 工具调用规范 === - 使用 XML 风格的工具调用格式 - 每个工具调用必须包含唯一的 tool_call_id - 工具结果通过 <tool_result> 标签返回 - 执行工具时考虑 AbortSignal 以支持取消操作 === 安全边界 === - 严禁执行 destructive 操作(rm -rf、格式化等) - 处理用户敏感信息时必须加密存储 - 禁止向未授权的渠道发送消息
设计权衡
为什么这样设计?
- 权衡: 灵活性 vs 一致性
- 决策: 框架层统一生成,保证所有 Agent 的基础行为一致
- 好处:用户不需要为每个 Agent 重复配置基础规则 框架升级时所有 Agent 自动获得新能力 降低配置错误的风险
- 代价:用户无法修改这些核心规则 如果需要特殊行为,只能通过 Layer 7/8 间接实现
Layer 2: Tool Definitions(工具定义层)
比喻
就像一把瑞士军刀的工具清单——告诉 LLM 你有哪些工具、每个工具是干什么用的、怎么用
组成内容
工具定义示例
{ "name": "read", "description": "读取文件内容。支持文本文件和图片(jpg/png/gif/webp)。图片会作为附件发送。文本文件输出限制为2000行或50KB。", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "文件路径(相对或绝对)" }, "offset": { "type": "number", "description": "起始行号(1-indexed)" }, "limit": { "type": "number", "description": "最大读取行数" } }, "required": ["path"] } }
设计权衡
为什么用 JSON Schema?
- 权衡: 灵活性 vs 类型安全
- 决策: 使用严格的 JSON Schema 定义工具参数
- 好处:LLM 能更准确地理解工具用法 框架可以在调用前验证参数 自动生成文档和类型定义
- 代价:添加新工具需要编写完整的 Schema 无法支持完全动态的参数结构
Layer 3: Skills Registry(技能注册表)
比喻
就像一家餐厅的"特色菜谱"——告诉 LLM 有哪些专业领域的"配方"可以调用
设计权衡
为什么用目录扫描而不是手动注册?
- 权衡: 灵活性 vs 维护成本
- 决策: 自动扫描 ~/development/openclaw/skills/ 目录
- 好处:添加新 Skill 只需放入目录,无需修改配置 所有 Agent 自动获得新 Skill 降低配置错误风险
- 代价:无法精确控制每个 Agent 可用的 Skill 所有 Skill 都会被注入 System Prompt(增加 token 消耗)
组成内容
Layer 4: Model Aliases(模型别名层)
比喻
就像"快捷键"——给复杂的模型路径起个简短的别名,方便调用
设计权衡
为什么需要模型别名?
- 权衡: 灵活性 vs 可读性
- 决策: 允许用户为常用模型定义简短别名
- 好处:简化模型调用(glm-5 代替 zhipu/glm-5) 支持多 Provider 切换(同一别名可映射不同 Provider) 便于 A/B 测试和模型迁移
- 代价:需要维护别名配置文件 可能造成混淆(不同 Agent 的同一别名可能指向不同模型)
组成内容
实际示例
在 System Prompt 中,模型别名会被展示为:
Model Aliases - GLM-5: zhipu/glm-5 - Opus 4.6: xiaowang886/claude-opus-4-6-thinking - Sonnet 4.5: xiaowang886/claude-sonnet-4-5
LLM 可以使用别名来切换模型:/model glm-5
Layer 5: Protocol Specifications(协议规范层)
比喻
就像"交通规则"——定义 Agent 与系统交互的标准协议
设计权衡
为什么需要协议规范?
- 权衡: 自由度 vs 一致性
- 决策: 定义标准化的交互协议(Silent Replies、Heartbeats、Reply Tags 等)
- 好处:保证所有 Agent 行为一致 支持自动化监控和健康检查 简化多 Agent 协作
- 代价:限制了 Agent 的自由表达 需要 LLM 严格遵守协议(可能被忽略)
组成内容
实际示例
Silent Replies 示例:
用户:收到 Agent:NO_REPLY
Heartbeats 示例:
System:[Heartbeat Poll] Agent:HEARTBEAT_OK
Reply Tags 示例:
Agent:[[reply_to_current]] 已完成任务 ✓
Layer 6: Runtime Info(运行时信息层)
比喻
就像"仪表盘"——告诉 LLM 当前运行环境的实时状态
设计权衡
为什么每次都注入运行时信息?
- 权衡: Token 消耗 vs 上下文准确性
- 决策: 每次请求都注入最新的运行时状态
- 好处:LLM 知道当前时间(避免时间错乱) LLM 知道当前模型(避免能力误判) LLM 知道当前环境(避免路径错误)
- 代价:每次请求消耗 ~2KB token 信息可能包含冗余
组成内容
实际示例
Runtime Runtime: agent=thinktank | host=黄宗宁的MacBook Air | repo=/Users/huangzongning/.openclaw/workspace-thinktank | os=Darwin 25.2.0 (arm64) | node=v25.5.0 | model=xiaowang886/claude-opus-4-6-thinking | default_model=xiaowang886/claude-opus-4-6-thinking | shell=zsh | channel=discord | capabilities=none | thinking=off
Layer 7: Workspace Files(工作区文件层)★ 用户可控
比喻
就像"你的工作笔记"——这是你可以直接编辑的静态配置文件
设计权衡
为什么只有这一层是静态可编辑的?
- 权衡: 框架稳定性 vs 用户自由度
- 决策: 把"变"和"不变"分离,框架层保证一致性,用户层允许个性化
- 好处:用户可以定义 Agent 身份、工作规范、记忆 框架升级不会破坏用户配置 配置文件可以版本管理、备份、共享
- 代价:用户无法修改框架核心行为 需要学习 TELOS 框架和文件结构
核心文件
Layer 8: Bootstrap Hook System(动态注入层)★ 用户可控
比喻
就像"可编程的注射器"——你可以写脚本在运行时动态注入内容到 System Prompt
设计权衡
为什么需要 Hook 系统?
- 权衡: 静态配置的简单性 vs 动态注入的灵活性
- 决策: 在静态 Workspace Files 之外,提供动态 Hook 机制
- 好处:可以根据上下文(channel、sender、时间)动态调整注入内容 可以执行 shell 命令并注入输出(如当前天气、Git 状态) 可以读取外部文件并注入(如项目文档、API 文档) 支持条件判断(if/else)
- 代价:需要学习 Hook 系统的语法和触发机制 Hook 脚本错误可能导致 System Prompt 异常 增加了系统复杂度
四种 Hook 机制
- agent:bootstrap Hook(内部 Hook 系统)
触发位置: bootstrap-hooks.ts 的 applyBootstrapHookOverrides()
能力:
- 完全控制 bootstrapFiles 数组
- 可以增删改文件
- 可以重排序
- 可以修改文件内容
谁可以注册:
- OpenClaw 插件
- Workspace Hooks(~/.openclaw/workspace-*/hooks/ 目录)
- 内部模块
代码示例:
registerInternalHook("agent:bootstrap", (event) => { const context = event.context as AgentBootstrapHookContext; // 完全控制 bootstrapFiles 数组 context.bootstrapFiles = [ { path: "CUSTOM.md", content: "自定义内容" } ]; });
- bootstrap-extra-files Hook(Bundled Hook)
触发位置: hooks/bundled/bootstrap-extra-files/handler.ts
能力:
- 只追加文件,不修改现有文件
- 通过配置文件指定额外文件
配置示例:
{ "hooks": { "bootstrap-extra-files": { "enabled": true, "paths": ["extra/*.md", "docs/CONTEXT.md"] } } }
适用场景:
- 需要注入项目特定的上下文文件
- 不想修改默认的 8 个 Bootstrap 文件
- 需要动态加载额外文档
- before_prompt_build Hook(Plugin Hook)
触发位置: attempt.ts 的 runBeforePromptBuild()
能力:
- 修改最终 prompt(在系统提示词构建后、发送给 LLM 前)
- 可以 prepend context(在 prompt 前添加内容)
- 可以覆盖 systemPrompt
事件数据:
{ prompt: string; // 用户输入 messages: unknown[]; // Session 消息历史 }
返回值:
{ prependContext?: string; // 在 prompt 前添加的内容 systemPrompt?: string; // 覆盖系统提示词 }
适用场景:
- 需要根据 session 历史动态调整 prompt
- 需要注入实时上下文(如当前时间、天气)
- 需要完全替换系统提示词
- bootstrapMaxChars / bootstrapTotalMaxChars(配置项)
类型: 配置项(不是 hook)
能力:
- 控制字符预算
- 单文件默认 20K
- 总计默认 150K
- 超出部分按头 70% + 尾 20% 截断
配置位置:
{ "agents": { "defaults": { "bootstrapMaxChars": 20000, "bootstrapTotalMaxChars": 150000 } } }
实战建议
场景 1:我想添加项目文档
推荐方案:bootstrap-extra-files
{ "hooks": { "bootstrap-extra-files": { "enabled": true, "paths": ["docs/API.md", "docs/ARCHITECTURE.md"] } } }
场景 2:我想根据任务类型动态加载文件
推荐方案:自定义 agent:bootstrap Hook
registerInternalHook("agent:bootstrap", (event) => { const context = event.context as AgentBootstrapHookContext; const sessionKey = context.sessionKey; // 根据 session 类型加载不同文件 if (sessionKey.includes("coding")) { context.bootstrapFiles.push({ path: "CODING_GUIDELINES.md", content: fs.readFileSync("...").toString() }); } });
场景 3:我想注入实时上下文(如当前时间)
推荐方案:before_prompt_build Hook
on("before_prompt_build", (event, ctx) => { return { prependContext: 当前时间:${new Date().toISOString()} }; });
Layer 9: Inbound Context(入站上下文层)
比喻
就像"实时路况信息"——每次请求都会动态注入当前对话的上下文信息
设计权衡
为什么每次都注入上下文?
- 权衡: Token 消耗 vs 对话连贯性
- 决策: 每次请求都注入最新的消息元信息、发送者信息、对话历史
- 好处:LLM 知道当前是谁在说话(避免混淆发送者) LLM 知道对话历史(保持上下文连贯) LLM 知道是否被 @(决定是否响应)
- 代价:每次请求消耗 ~3KB token 对话历史可能包含噪音信息
组成内容
完整 System Prompt 组装流程
用户可控层总结
OpenClaw 提供了 3 种用户可控机制:
- Layer 7(Workspace Files) - 静态配置文件适用场景:定义 Agent 身份、工作规范、记忆 优点:简单直观,易于版本管理 缺点:无法动态调整
- Layer 8(Bootstrap Hook System) - 动态注入脚本适用场景:根据上下文动态注入内容、执行命令、读取外部文件 优点:灵活强大,支持条件判断和命令执行 缺点:需要学习 Hook 系统,脚本错误可能导致异常
- 间接控制 Layer 9(Inbound Context) - 通过发送消息影响上下文适用场景:通过对话历史、引用消息影响 LLM 行为 优点:无需配置,自然交互 缺点:无法精确控制
大小对比表
⚠️ 注意:以下数据为估算值,实际大小会因配置和运行时上下文而变化。框架层(Layer 1-6 + 9)理论上应该相同,但实际可能因工具定义、Skills 加载、运行时信息等差异而略有不同。
说明:
- Layer 7 和 Layer 8 是用户可控层,大小因 Agent 配置而异
- 其他层由框架自动生成,理论上所有 Agent 应该相同
- 实际测量时可能因工具可用性、Skills 加载、运行时上下文等因素产生差异
优化建议
- 用户可控部分优化(Layer 7 + 8)
由于 Layer 7 和 8 是用户可以控制的,以下是优化策略:
Layer 7(静态文件)优化:
✅ 推荐的精简策略:
- IDENTITY.md:保留核心 TELOS 框架,删除冗余描述,使用表格代替段落
- AGENTS.md:使用 checklist 代替长段落,用代码块展示命令,删除重复的规则说明
- MEMORY.md:依赖 MemOS 自动导出,不要手动添加内容,让系统自动维护
❌ 避免的做法:
- 不要重复描述 OpenClaw 框架已经知道的事情
- 不要把 Skills 的详细说明复制到 Workspace Files
- 不要使用过多的修辞和装饰性语言
Layer 8(Hook 系统)优化:
✅ 推荐的使用策略:
- 优先使用 bootstrap-extra-files(简单场景)
- 需要条件判断时使用 agent:bootstrap(复杂场景)
- 需要实时上下文时使用 before_prompt_build(动态场景)
❌ 避免的做法:
- 不要在 Hook 中执行耗时操作(会阻塞 System Prompt 生成)
- 不要在 Hook 中注入过多内容(会超出 token 限制)
- 不要在 Hook 中使用不稳定的外部依赖(会导致启动失败)
- 提示词裁剪策略
如果 System Prompt 过大,可以考虑:
总结
OpenClaw 的 System Prompt 不是一个单一的文件,而是 9 层架构的精心编排:
- Layer 1-6:框架自动生成,保证一致性和稳定性
- Layer 7:用户可编辑的静态配置文件(IDENTITY.md、AGENTS.md 等)
- Layer 8:用户可编程的动态注入脚本(Bootstrap Hook System)
- Layer 9:框架自动注入的实时上下文(Inbound Context)
用户可控的层有 2 个(Layer 7 + 8),而不是之前错误说的"只有 Layer 7"。
理解这些层的区别和联系,才能真正掌握 OpenClaw 的配置能力。