第三章 提示词系统——Agent 为什么不会每次都从零开始
核心问题:OpenClaw 是如何组装提示词的?
上一章讲的是 Agent Loop:一件事怎么被一轮一轮推进下去。
但只会“循环推进”还不够。因为如果 Agent 每次开始前都像重新出生一样,不知道自己是谁、不知道该怎么做,也不知道你是谁,那这个循环再聪明,也还是会不断从零开始。
这就是第三章真正要讲的东西:OpenClaw 为什么不会把提示词只当成一段临时文案,而是把它变成了工作区里的长期配置。
这里先提前说一句很重要的话:这一章讲的不是“怎么写一句更厉害的 Prompt”,而是系统怎样决定,哪些信息应该常驻,哪些只在特定时机出现,哪些应该按需读取,哪些干脆不该一直塞在上下文里。
第一节 为什么提示词要写进文件
这一节只回答一个问题:OpenClaw 为什么不把 Agent 的规则继续留在对话里,而是要把它们写成文件。
1.1 对话里的 Prompt,天然是不稳的
把提示词写在对话里,最大的优点是快。你想临时改一下口吻、规则、偏好,打一段话就行。
但它的问题也非常明显:它和这次对话活在同一个生命周期里。 对话结束,它也就结束了。 窗口一关,下次重新开始,系统又会回到默认状态。
这会带来三个直接问题:
| 问题 | 表面现象 | 本质原因 |
|---|---|---|
| 不持久 | 今天调好的风格,明天又没了 | 提示词跟着会话一起消失 |
| 不可维护 | 说不清到底哪句话起了作用 | 一切混在同一段临时文本里 |
| 不可协作 | 团队里很难复用,也很难追溯 | 没有稳定载体,也没有版本历史 |
所以,只要提示词仍然是一次性对话内容,它就很难变成真正可积累、可维护、可复用的系统配置。
1.2 OpenClaw 的解法,是让工作区变成 Agent 的“家”
OpenClaw 官方把这一套叫做 Agent Workspace。 你可以把它理解成:Agent 在磁盘上的固定住处。
它和普通聊天窗口最大的区别在于,很多本来会随会话消失的东西,现在有了落点:
- 角色和性格,不再只靠一段系统提示词临时维持;
- 用户画像和偏好,不再只靠模型从聊天历史里“猜”;
- 做事流程和环境说明,不再每次都要重新交代;
- 长期记忆和日常记录,也不再只是上下文里的一段旧消息。
这也是为什么 OpenClaw 官方文档一直把 workspace 看得很重。
因为从这一刻开始,Agent 的“默认状态”不再只是模型初始化时的一段字符串,而是一组放在磁盘上、可直接编辑、可用 Git 管理的真实文件。
1.3 这些文件,到底各自管什么
第一次看 OpenClaw 的工作区,很多人会觉得文件有点多。
但它的设计重点并不是“多”,而是分层。
更准确地说,它是在回答四个不同的问题:
- 我是谁?
- 我怎么做事?
- 我记住了什么?
- 我在什么时机应该做什么?
可以先把常见文件分成下面几组来看:
| 分层 | 文件 / 目录 | 它更像什么 | 主要负责什么 |
|---|---|---|---|
| 身份层 | SOUL.md IDENTITY.md USER.md | 人设、名片、用户档案 | 定义 Agent 的角色、气质、称呼方式和服务对象 |
| 行为层 | AGENTS.md TOOLS.md HEARTBEAT.md | 操作手册、环境备忘录、短巡检单 | 约束默认工作流,补充本地环境和工具使用说明;HEARTBEAT.md 也属于 workspace file,只是对 heartbeat runs 尤其重要 |
| 记忆层 | MEMORY.md memory/ | 精选笔记、日记本 | 前者更像长期记忆文件,后者更像按天积累的记录层 |
| 生命周期层 | BOOTSTRAP.md BOOT.md / hooks | 首次引导、启动/事件钩子 | 分别对应 brand-new workspace,以及启动或其他事件触发 |
| 能力层 | skills/ | 技能卡片 | 告诉 Agent 遇到某类任务时,该去哪读哪份 SKILL.md |
这里有几个特别容易混淆的点,最好先分清:
AGENTS.md讲的是“我怎么做事”,不是“我是谁”。TOOLS.md讲的是“环境和工具备注”,不是“给工具开权限”。MEMORY.md和memory/都是记忆层,但前者更像整理后的长期知识,后者更像按天积累的原始记录。HEARTBEAT.md名字里虽然有 heartbeat,但它本身仍属于会进入 Project Context 的 workspace file,只是对 heartbeat runs 更关键,而且应该保持很短。BOOTSTRAP.md、BOOT.md/ hooks 都和“时机”有关,但它们面对的是两种完全不同的触发条件。
所以 OpenClaw 的关键,不是“把一段大 Prompt 拆成八九个文件”这么简单,而是:它把原来混成一团的身份、规则、记忆和生命周期,拆成了几种职责完全不同的配置。
这一步一旦完成,Agent 的默认行为才第一次真正变得可定位、可修改、可积累。
第二节 提示词系统运行规则
这一节的核心问题在于:工作区里的这些文件,到底是通过什么机制进入一次 Agent 运行的?
2.1 system prompt 不是一段固定文案,而是每次 run 现拼的
OpenClaw 官方的 System Prompt 文档 和当前源码里的 system-prompt.ts 都在说明同一件事:OpenClaw 的 system prompt 不是一份写死的静态文本,而是每次 run 都会重新构建。
它大致是这样拼起来的:
固定系统区块
→ 工作区上下文(Project Context)
→ 当前会话历史、工具结果、附件
→ 模型看到这一轮真正的完整上下文其中固定系统区块里,官方现在会放很多东西,例如:
- Tooling:有哪些工具、怎么安全使用;
- Safety:高风险操作的边界;
- Skills:当前可用技能的索引;
- Workspace / Documentation:当前工作区与文档提示;
- Sandbox / Time / Runtime:沙箱、时间和运行时信息。
这件事特别重要,因为它告诉我们:
OpenClaw 并不是把“用户发来的那一句话”直接扔给模型,而是会先把这一轮工作的现场搭起来,再让模型开始判断。
也正因为如此,“提示词系统”在 OpenClaw 里其实不是某一段漂亮文案,而是一次运行开始前的上下文装配机制。
2.2 工作区里的东西,不是都走同一条路
这一点是很多初学者最容易搞混的地方。工作区里虽然有很多文件,但它们并不是都以同样的方式进入 prompt。
从官方文档、源码和当前运行时设计来看,至少可以分成四条不同的路径:
| 内容 | 进入方式 | 为什么这样设计 |
|---|---|---|
AGENTS.md SOUL.md USER.md IDENTITY.md TOOLS.md HEARTBEAT.md 等工作区文件 | 作为 Project Context 直接注入 | 这是这轮运行默认就该带着走的规则和身份信息;HEARTBEAT.md 也在其中,只是应该特别短 |
skills/ 里的技能 | 先注入紧凑索引,真正需要时再读 SKILL.md | 减少常驻上下文开销,避免几十个技能全文一起挤爆窗口 |
MEMORY.md | 作为 Project Context 的长期记忆文件直接注入 | 让 Agent 默认带着最重要的长期事实与偏好工作 |
memory/ | 作为 daily notes 和磁盘记忆层参与运行,近邻日期可能自动带入,其余更多依赖 memory 工具召回 | 不把整段历史全文常驻,同时保留按需检索能力 |
BOOTSTRAP.md BOOT.md / hooks | 走不同的生命周期路径 | BOOTSTRAP.md 只在 brand-new workspace 时出现;BOOT.md 或其他 hooks 更像启动 / 事件触发入口,而不是每轮常驻 prompt |
这四条路径背后,其实对应了四种完全不同的问题:
- 哪些东西是每轮都要带着走的;
- 哪些东西是先知道它存在,需要时再展开的;
- 哪些东西是长期存着,等以后召回的;
- 哪些东西是只在某个时刻触发一次的。
这里尤其要强调两点。
第一,memory 不是一股脑都常驻上下文。
OpenClaw 官方的 Memory 文档 把记忆讲得很清楚:
它的“真相”在磁盘上,而不在当前这次对话窗口里。
更稳的理解是:MEMORY.md 负责长期事实;memory/YYYY-MM-DD.md 负责 daily notes。官方概览页强调近邻日期的 notes 可能自动带入,而 system prompt 文档又明确说 daily files 不应被理解成和工作区常驻文件完全等价的“无条件注入全文”。
所以在写教程时,最不容易误导读者的说法是:不要把整个 memory/ 目录当成常驻大 prompt;更远的历史和更细的记录,仍主要依赖 memory 工具和召回机制。
第二,skills 也不是常驻全文。
官方 Skills 文档 和当前源码里的 skills/workspace.ts 都说明了这一点:
系统会先把当前可用技能整理成紧凑列表注入 prompt,模型判断需要某个技能时,再去读那份 SKILL.md。
这其实是一个非常工程化的决定。 因为 skill 的价值本来就是“低频但专业”。如果每次都把所有技能全文塞进 prompt,窗口很快就会被低频内容挤满。
2.3 不是所有 run,看到的都是同一套 prompt
如果 OpenClaw 把所有内容在任何场景下都一股脑塞进去,系统会很快变得又重又慢。
所以官方运行时还做了另一层处理:不同 run 会有不同强度的 prompt。
文档里把它叫做 promptMode,当前正式可见的主要是 full、minimal、none 这几类模式。
你不用死记每一类到底保留了哪些小块,但需要理解它背后的原则:
| 模式 / 场景 | 设计原则 |
|---|---|
full | 给足上下文,让主会话有完整身份、规则和工作现场 |
minimal | 常见于子 Agent,尽量变轻,只保留完成局部任务真正需要的信息 |
none | 极简模式,只保留最基础的身份线 |
这也是为什么你会看到:
- 子 Agent 的 prompt 通常比主会话更轻;
HEARTBEAT.md虽然经常在 heartbeat 场景里被提起,但它本身也是 workspace file;最佳实践一直都是“短”,而在轻量 prompt 或特殊场景里,它可能再被进一步弱化;BOOTSTRAP.md用完就该退场,而不是永远留在默认上下文里;BOOT.md更像开机时的内部检查清单,而不是会话里的常驻人格说明。
从源码角度看,这件事也能对上:当前实现里,workspace 文件、skills 列表和 system prompt 各区块都会根据运行模式和 session 类型做筛选。
具体保留哪些小块,未来版本可能会继续微调,但“主会话更完整、特殊场景更轻量”这个原则是稳定的。
2.4 prompt 的真正成本,常常不是你以为的那一部分
很多人第一次遇到上下文压力时,直觉都会觉得:“是不是我某个 Markdown 文件写太长了?”
这当然有可能,但 OpenClaw 官方的 Context 文档 提醒了另一件更重要的事:
一次 run 的上下文成本,从来不只来自工作区文件。
它至少还包括:
- 当前 session 的历史消息;
- 工具调用和工具结果;
- 附件与外部读入内容;
- skills 列表;
- 工具的文字说明;
- 工具的 schema 开销。
最后这一点尤其容易被低估。因为工具不只是“提示词里那几行说明”,它背后还有给模型看的结构化 schema。很多时候,真正吃上下文的并不是你眼前看到的几句话,而是这些你平时没直接看见的部分。
所以 OpenClaw 才给了几套非常实用的观察工具:
/status:看当前会话和窗口压力;/context list:看这一轮上下文里到底装了哪些东西;/context detail:看更细的拆分,包括不同区块、文件和工具成本;/compact:把旧历史压成摘要,给后面的运行腾位置。
另外,workspace 文件本身也不是毫无限制地注入。官方文档和当前实现里都明确有单文件预算和总量预算;超长内容会被截断,缺失文件会被标记出来。
所以这一节想让大家真正明白的,不是“Prompt 会被注入”,而是:
OpenClaw 一直在做一件更复杂的事:在准确性、可维护性和上下文成本之间,找到一个平衡点。
第三节 知道机制以后,我们应该怎么写这套配置
前两节已经讲清楚了“它为什么存在”和“它怎么运转”。
接下来更实际的问题是:如果你真的要开始写自己的工作区文件,到底应该怎么写,才不会把这套系统用歪。
3.1 第一步,不是多写,而是先分清“这句话该写在哪”
很多初学者第一次配工作区,最容易犯的错误不是写少,而是乱写。
比如:
- 人格和语气写进
AGENTS.md; - 工具路径写进
SOUL.md; - 临时项目任务塞进
USER.md; - 一次性的初始化说明永远留在常驻文件里。
这样做的问题不是“不优雅”,而是你以后根本没法维护。
更稳的思路应该是:先判断这句话属于哪一层,再决定它应该放哪。
可以先用下面这张速查表:
| 你想表达什么 | 更适合写在哪 |
|---|---|
| 这个 Agent 的气质、边界、价值取向 | SOUL.md |
| 名字、身份设定、基础角色信息 | IDENTITY.md |
| 用户偏好、称呼、长期协作习惯 | USER.md |
| 默认工作流、做事顺序、确认规则 | AGENTS.md |
| 路径、项目说明、环境备注、常用命令提示 | TOOLS.md |
| 长期有效的事实和经验 | MEMORY.md |
| 按天记录的原始经历和事件 | memory/ |
| 第一次进入工作区时该完成什么 | BOOTSTRAP.md |
| Gateway 启动时要检查什么 | BOOT.md |
| 定时巡检时要关注什么 | HEARTBEAT.md |
| 某类低频但专业的流程 | skills/某技能/SKILL.md |
这里还要单独提醒一句:
TOOLS.md 不是权限配置文件。
它适合写“在哪里做事、怎么更稳妥地用工具”,但它不负责真正的工具开关、审批和安全边界。
3.2 第二步,把真正长期有效的规则写进去
工作区文件最怕的不是短,而是把短期内容错当成长期规则。
一个很简单的判断方法是:
如果一条信息满足下面两个条件,它才更适合进入常驻配置:
- 它会反复影响未来很多次运行;
- 它不该只存在于某一次具体任务里。
比如下面这种,就很适合写成常驻规则:
执行任何删除、发送、部署类操作前,先列出影响范围并等我确认。因为它长期有效,而且未来很多任务都用得上。
但下面这种,就不应该直接写进常驻文件:
今天先帮我看 chapter3 的 rewrite 文档。因为它只是当前任务,不是未来默认规则。
写这类文件时,还有三个很实用的原则:
- 写具体,不写空话:
请谨慎行事不如删除前先列出文件并等确认。 - 写稳定,不写瞬时状态:今天临时要做的事,不要塞进永远常驻的文件。
- 写必要,不写贪多:尤其是
HEARTBEAT.md、BOOT.md或其他启动/事件 hook 里会被短暂读取的说明,越短越稳。
还有一点非常现实,也非常重要:
不要把密钥和敏感凭证写进工作区文件,更不要顺手提交到 Git。
OpenClaw 官方的 workspace 文档也反复提醒这一点。
配置可以版本化,秘密不该版本化。需要长期保存的敏感信息,应该放进 OpenClaw 的 credentials 体系,而不是 Markdown 文件。
3.3 第三步,用工程的方式维护,而不是靠记忆硬扛
只要你真的开始长期用 OpenClaw,很快就会发现:这套系统的价值,不在于“第一天配置得多漂亮”,而在于能不能越用越稳,而不是越用越乱。
更靠谱的维护方式通常有四条:
第一,把工作区配置当成资产管理。
能进 Git 的进 Git。
尤其是 AGENTS.md、SOUL.md、TOOLS.md、USER.md 这种长期会迭代的文件,最好有提交历史。
第二,不要靠猜,要靠观察。
如果你发现 Agent 开始变慢、变散、变得不听话,不要先怀疑“模型怎么又抽风了”,先去看:
/context list:到底注入了什么;/context detail:到底是谁把窗口撑大了;/compact:是不是该把旧历史压掉了。
很多所谓“人格飘了”“规则没生效”,最后都不是玄学问题,而是上下文分配问题。
第三,改完以后,用新的运行去验证。
OpenClaw 的很多配置都和 session、prompt mode、skills 快照有关。有些变更下一轮就能体现,有些场景下重新开一个新会话会更稳、更容易观察。
所以更工程化的做法往往不是在当前对话里硬拗,而是:
改文件
→ 开一个干净的新会话
→ 看默认行为有没有按预期变化
→ 再决定要不要继续调第四,把低频能力交给 skill,把高频规则留给常驻文件。
如果某个流程只是偶尔需要,但每次都很专业,比如部署、论文检索、代码审查模板,那更适合做成 skill。
如果某条规则是每次做事都该带着的,比如确认边界、输出格式、沟通风格,那才更适合放进 AGENTS.md、SOUL.md 或 USER.md。
一句话说,就是:
不要让所有东西都争着常驻。
真正成熟的提示词系统,不是“尽量塞更多”,而是“让该常驻的常驻,该按需的按需,该退场的退场”。
3.4 第四步,先分清到底是谁在改这些文件
这里还有一个特别容易让初学者误会的问题:
这些 Markdown 文件,到底是我自己改,还是系统会自动改,还是我要命令 Agent 去改?
更准确的答案是:这三种情况都存在,但它们发生的条件完全不一样。
先说最常见、也最稳的一种:你自己手动改。
这些文件本来就是工作区里的普通文件,所以你完全可以像改别的 Markdown 一样,在编辑器里直接修改它们:
- 想改默认流程,就改
AGENTS.md - 想改人格和语气,就改
SOUL.md - 想补环境说明,就改
TOOLS.md - 想整理长期规则和经验,就改
USER.md、MEMORY.md或memory/
这也是最清楚、最可控的维护方式。
第二种是:你把“改配置”本身交给 Agent。
OpenClaw 本来就有读写和编辑文件的能力,所以只要权限允许,你也可以直接说:
把这条规则写进 AGENTS.md。
把我刚才说的偏好补到 USER.md。
把这件事记进 MEMORY.md。这时候本质上不是“系统自己在偷偷改配置”,而是你明确把修改文件当成一个任务交给了 Agent。
第三种才是:系统在少数特定机制里自动写盘。
这一类不是“平时聊天时随时乱改”,而是只发生在几个明确入口:
- 首次 bootstrapping:官方文档明确写着,首次引导会把信息写入
IDENTITY.md、USER.md、SOUL.md,完成后再移除BOOTSTRAP.md; - memory 流程:如果你明确说“记住这件事”,Agent 应该把内容写入记忆;另外 memory 机制在接近压缩或切会话时,也可能通过官方 hook 把值得保留的信息落到磁盘;
- 会话摘要 / memory hooks:如果你的部署启用了把会话摘要写回记忆的 hook,那么切会话、compaction 前后或其他触发点,也可能把内容落到
memory/;具体是否开启要看当前配置; - BOOT.md / 其他 hooks:如果启用了启动或事件钩子,系统会在这些触发点执行对应流程,但会不会真的改文件,还要看那份
BOOT.md或 hook 本身写了什么。
所以更实用的理解可以压成下面这张表:
| 情况 | 会不会改文件 | 更像什么 |
|---|---|---|
| 你在编辑器里直接改 | 会 | 手动维护配置 |
| 你命令 Agent 去改 | 会 | 把“改配置”当成任务交给 Agent |
| 普通聊天但你没要求它改 | 通常不会主动乱改常驻文件 | 正常对话,不等于默认自我改写 |
| bootstrapping / memory / hooks 这类特定机制 | 可能会 | 有明确触发条件的自动写盘 |
如果把这一点收成一句话,就是:
默认把这些文件当成你自己维护的配置;需要时可以让 Agent 帮你改;只有少数官方定义好的流程,系统才会自动写入其中一部分文件。
本章小结
这一章其实就讲清楚了一件事:OpenClaw 的提示词系统,本质上不是“一段更高级的 Prompt”,而是一套围绕工作区、生命周期和上下文预算搭出来的运行机制。
它真正做的事情有三步:
- 把临时提示词搬出对话,变成工作区里的长期配置;
- 按“常驻注入、按需读取、生命周期触发、磁盘召回”几条不同路径,把这些内容带进一次运行;
- 再用 prompt mode、skills 索引、memory 召回和 context 预算,把整套系统控制在可运行的范围里。
所以 OpenClaw 看起来像是在“写很多 Markdown 文件”,但背后真正重要的是:
- 什么该成为默认身份;
- 什么该成为默认规则;
- 什么该留在磁盘上等以后再读;
- 什么只该在特定时机出现;
- 什么根本不该一直占着 prompt。
如果把这一章压成一句话,就是:
OpenClaw 之所以不会每次都从零开始,不是因为它神奇地“记住了对话”,而是因为它把身份、规则、记忆和能力真正落到了文件和运行机制里。
→ 第四章 工具系统