OpenClaw 深度解析系列 · 第8篇:Learning & Adaptation(学习与自适应)

摘要 :很多 Agent 系统把"学习"简化成一句话:多聊几次就更懂你了。但工程上更关键的是:学到哪里、谁来写、如何不翻车。本文从 OpenClaw 的源码与运行时机制出发,把 Learning & Adaptation 拆成三条可验证的主线:文件化的学习载体(八类人格/工作区文件)系统提示词注入让学习默认生效 、以及 显式更新 / Heartbeat / Compaction 三类触发器如何把"经验"蒸馏成稳定规则。你将看到 OpenClaw 如何把学习变成可审计、可回滚、可控成本的工程系统。

关键词:OpenClaw;Learning & Adaptation;SOUL.mdAGENTS.md;System Prompt;Workspace Bootstrap;Heartbeat;Compaction;Distillation;Self-modification

系列文章

源码版本说明 :本文引用路径基于 openclaw/openclaw 仓库;本地阅读使用的 commit 为 0dd4958bc8a78d26b3b526b1f2e63b15110c64a2(2026-04-11)。GitHub 上可按该 SHA 查看对应版本的源码。


0. 这篇要解决什么问题?

很多 Agent 系统把"学习"讲成一句口号:多聊几次就更懂你了。但工程上你会立刻追问三件事:

  • 学到哪里:是写进数据库?写进文件?还是只存在模型幻觉里?
  • 谁来写:模型自己写?工具写?人写?有没有边界与审批?
  • 怎么不翻车:一旦"学错了",会不会把系统 prompt 写崩、把安全规则改没?

OpenClaw 的回答非常工程化:它把"学习(Learning)"落在一套可读、可写、可审计的 Markdown 文件上,并通过运行时把这些文件"注入"到系统提示词(System Prompt)里,让学习结果对模型有稳定约束力。

💡 理解要点:OpenClaw 的 Learning 更像"把经验沉淀成可控配置",而不是"让模型在脑子里记住"。


1. Learning 的核心心智模型:把 Agent 变成"可迭代的软件"

想象一下你在带一个新人同事:

  • 你不会指望他"凭感觉记住一切"
  • 你会给他:工作手册(规则)沟通风格(语气)项目背景(上下文)值班清单(巡检)
  • 每次踩坑后,你会更新手册:把"教训"写进去,下一次就不再犯

OpenClaw 把这套做成了八类文件(下文简称"人格/工作区文件"),并在每次 agent run 时自动注入。

1.1 架构图:Learning & Adaptation 的闭环总览(从输入到"写回文件")

学习载体(Artifacts / Files)
运行时(Runtime)
输入(Inputs)
写入/更新
写入/更新
写入/更新
写入/更新
写入/更新
写入/更新
flush/index
落盘
注入
注入
注入
注入
注入
按需/动态注入
用户消息 / Channel 消息 / 系统事件
System Prompt 构建

注入工作区文件
Brain / LLM 推理与工具调用
Hands / Tools

read/write/edit/exec...
AGENTS.md

顶层规则/工具策略
SOUL.md

语气/表达风格
USER.md

用户稳定偏好/约束
TOOLS.md

外部工具使用指南
MEMORY.md

记忆策略/检索策略
HEARTBEAT.md

周期清单/tasks
memory corpus/*.md

事实/偏好/项目上下文
sessions/*

messages.jsonl/summary.md

💡 理解要点:Learning 在 OpenClaw 里不是"记在模型里",而是写回文件 → 下次注入 prompt → 行为稳定改变

对应源码与文档线索:

  • 系统提示词如何拼装openclaw/src/agents/system-prompt.ts + 文档 openclaw/docs/concepts/system-prompt.md
  • 工作区如何创建与加载openclaw/src/agents/workspace.ts
  • 心跳如何驱动周期性维护openclaw/src/infra/heartbeat-runner.ts + 文档 openclaw/docs/gateway/heartbeat.md

1.2 八类文件在 System Prompt 中的位置

很多读者会误以为:"既然这 8 个文件这么重要,那 system prompt 就是它们组成的吧?"

不是。 8 个文件只是 system prompt 的一部分(称为 "Project Context"),而非全部。

System Prompt 的完整组成

根据 openclaw/src/agents/system-prompt.ts 的构建逻辑,每次 agent run 的 system prompt 由以下部分拼接而成:

部分 内容 来源
基础身份 "You are a personal assistant running inside OpenClaw." 硬编码
Tooling 可用工具列表(read/write/edit/exec...)、工具调用风格 硬编码
Safety 安全规则(Anthropic constitution 风格) 硬编码
Skills <available_skills> 列表及读取逻辑 硬编码 + 运行时技能扫描
Memory 记忆工具提示(memory_search/get/flush) 硬编码
OpenClaw 运维 CLI 快速参考、自我更新规则、模型别名 硬编码
8 个文件(Project Context) AGENTS.md / SOUL.md / ... 作为用户自定义层 工作区文件注入
动态运行时信息 当前时间、运行时状态、心跳提示 运行时注入
关键理解:固定部分 vs 可变部分

可以把 system prompt 想象成一份分层配置

  • 固定部分(OpenClaw 硬编码) :告诉 Agent"你能做什么、不能做什么、怎么做"------这是基础设施层,用户无法直接修改
  • 可变部分(8 个文件注入) :告诉 Agent"你是谁、为谁服务、有什么偏好"------这是用户定制层,通过文件写入实现 Learning & Adaptation
为什么这样设计?

这种分层架构解决了两个工程问题:

  1. 安全隔离:底层规则(如"不要执行 rm -rf /")无法被用户文件意外覆盖
  2. 可控学习:用户/Agent 只能在"可变部分"写入,而不会破坏系统基础设施

💡 一句话:8 个文件是 system prompt 的"用户可写层",存放学习产物;而 system prompt 的"系统固定层"由 OpenClaw 源码定义,提供执行框架和安全边界。


2. "学习"写入到哪里:八类文件的分工(语义层)

OpenClaw 的一条重要设计原则是:把不同性质的信息放在不同文件里,让"学习"可控、可审计、可回滚。

2.1 八类文件的优先级:谁覆盖谁?

系统提示词会对注入的工作区文件排序,顺序在源码里是硬编码的(数字越小优先级越高):

优先级 文件 核心作用(一句话)
10 AGENTS.md 顶层行为规则------定义 Agent 的工具策略、安全红线、失败处理与群体聊天规范
20 SOUL.md 人格与语气------塑造 Agent 的表达方式、立场态度、沟通风格(不是安全策略)
30 IDENTITY.md 身份标识------Agent 的名称、版本、形象(emoji/avatar)等元信息
40 USER.md 用户画像------记录用户的称呼、偏好、项目背景、长期目标
50 TOOLS.md 工具使用指南------外部工具的本地配置、环境特定参数、快捷别名
60 BOOTSTRAP.md 首次启动清单------新工作区初始化时的自我认知流程(用后即删)
70 MEMORY.md 长期记忆策略------记忆 flush/检索策略,以及用户/项目的稳定长期结论
动态 HEARTBEAT.md 周期巡检清单 ------定义心跳回合要执行的周期性检查任务(tasks:

🔍 实际例子:你在 USER.md 里写"尽量简洁",但 AGENTS.md 里写"必须先给步骤并附带测试计划",最终会以 AGENTS.md 为准。

2.2 学习(Learning)与记忆(Memory)的边界

为了避免混乱,可以用一句话划分:

  • Memory:偏"事实与素材库"(可检索、可引用、可丢弃噪音)
  • Learning & Adaptation:偏"行为与策略"(影响 agent 的长期工作方式)

在 OpenClaw 的系统 prompt 文档里也能看到类似取舍:工作区 bootstrap 文件会自动注入,但 memory/YYYY-MM-DD.md 这类日记不会自动注入,而是通过 memory_search/memory_get 按需读取(减少上下文开销)。

💡 理解要点:Learning 的产物要短、稳、能长期注入;Memory 的产物可以大、杂,但要按需召回。


3. "学习"如何生效:系统提示词注入(Prompt-level Enforcement)

OpenClaw 的 system prompt 文档明确说了:每次 agent run 都会构建一个 OpenClaw-owned 的 system prompt,并把工作区文件作为 Project Context 注入进去(同时有 cache boundary 的稳定性设计)。

openclaw/src/agents/system-prompt.ts 里,你能看到三个特别关键的机制:

3.1 注入是"默认行为",不是"模型自觉"

工作区文件会在每次构建 prompt 时被加载,并作为 Project Context 拼进去(文档里叫 Workspace bootstrap injection)。这意味着:

  • 你不需要每次对话都提醒"按这个风格写"
  • 你只要把规则写进文件,运行时就会持续施加约束

🔍 实际例子:当你把"不要用套话开场"写进 SOUL.md,之后每一轮回复都受它影响(除非更高优先级覆盖)。

3.2 Cache Boundary:把"高频变更"放到动态区(以 HEARTBEAT.md 为例)

system-prompt.tsheartbeat.md 视作动态上下文文件(DYNAMIC_CONTEXT_FILE_BASENAMES),并把它尽可能放在 cache boundary 之后。

直觉上:心跳清单可能经常改,如果放在稳定区,会导致缓存失效、token 成本上涨。

💡 理解要点:Learning 不是"写越多越好",而是"写在合适的层、控制变更频率"。

与其画图,你可以把它理解成:系统提示词被切成"尽量稳定的前缀(prefix)"与"容易变化的后缀(suffix)"两段 ,中间用 SYSTEM_PROMPT_CACHE_BOUNDARY 作为"缓存分界线(cache boundary)"。运行时会尽量让经常变化的内容落在分界线之后,减少缓存失效与 token 成本。

下面用一张表把"稳定区/动态区"说清楚(对应逻辑见 openclaw/src/agents/system-prompt.tsDYNAMIC_CONTEXT_FILE_BASENAMES 与 boundary 拼装)。

区域 含义 典型内容(OpenClaw 注入) 代表文件 建议更新频率(经验值) 写作建议
稳定注入区(Cache-stable prefix) 尽量保持不变的"规则/画像/策略"集合。变化会导致更大范围的缓存失效。 按优先级注入的工作区文件(除动态文件外) AGENTS.mdSOUL.mdIDENTITY.mdUSER.mdTOOLS.mdBOOTSTRAP.mdMEMORY.md 低频:通常"按里程碑/按周/按发现明显长期规律时"更新;避免"每天多次改" 内容要短、稳定、可长期复用;避免把流水账写进来
动态注入区(Cache-volatile suffix) 允许频繁变化的"运行时状态/周期任务/短期提示"。尽量放在分界线后以降低成本。 动态上下文文件 + 运行时临时信息(时间、状态、心跳提示等) HEARTBEAT.md(动态文件);以及运行时附加的临时块 可高频:按任务需要调整;同一日多次变更也可以,但应保持很短 只放"会变的、短期有效的";用 tasks: 控制频率与 no-tasks-due 跳过逻辑

再强调两点容易误解的地方:

  • "稳定/动态"不是说文件本身永远不改:它描述的是"对缓存与成本的影响"。稳定区文件当然可以改,但要把它当成"配置/规则"来改,避免频繁小改动。
  • 哪些文件会被当作动态文件是硬编码的 :OpenClaw 目前把 heartbeat.md 标为动态(DYNAMIC_CONTEXT_FILE_BASENAMES = new Set(["heartbeat.md"])),因此会尽量放在 boundary 之后;其它文件按 CONTEXT_FILE_ORDER 排序注入。

🔍 实际例子:你频繁改 HEARTBEAT.md 不应该导致"整段系统提示词都失效重算",因此它会被尽量放在 boundary 之后。


4. "学习"如何被触发:从对话回合到周期性自维护

OpenClaw 的 Learning/Adaptation 不是单一开关,更像三种触发来源的组合:

4.1 用户在对话中显式更新(Human-in-the-loop)

这是最保守、最可控的更新方式,特点是用户作为最终决策者,明确指示 Agent 修改某个文件。

典型场景
用户输入 Agent 行为 后续影响
"请把我的偏好写进 SOUL.md" 调用 write 工具写入 后续对话自动体现该偏好
"更新 AGENTS.md,加入'修改前先读文件'的规则" 调用 edit 工具追加内容 所有后续会话都遵守此规则
"HEARTBEAT.md 里加一个新任务,每天检查邮件" 更新 tasks: 区块 下一周期自动触发
源码视角:没有硬性保护,但有行为惯性

openclaw/src/agents/pi-tools.read.ts 可以看到,Agent 的 writeedit 工具技术上可以修改 workspace 内任何文件 ,包括 8 个 bootstrap 文件。但 Agent 不会无缘无故修改它们------通常需要明确的上下文触发(用户指令、Heartbeat 任务、或模板暗示的"学到教训"场景)。

与"自主更新"的边界

很多读者误以为"人工显式"是更新 8 个文件的唯一方式。实际上,Agent 在以下场景也会自主决定写入:

  • 用户说"记住这个" → 可能更新 USER.mdMEMORY.md
  • 发现重复犯错 → 可能更新 AGENTS.md 加入新规则
  • Heartbeat 任务到期 → 按 HEARTBEAT.md 定义执行文件更新

核心区别 在于:这些自主写入通常是对明确上下文的响应(用户说"记住"、模板说"学到教训要写入"),而非 Agent 的"自发行为"。

💡 一句话:人工显式更新 = 用户主动 push;自主更新 = 上下文触发后 Agent 主动执行。两者都是 Learning & Adaptation 的合法来源。

安全边界:系统配置 vs 用户配置

system-prompt.ts 中有明确红线:

复制代码
Do not change system prompts, safety rules, or tool policies unless explicitly requested.

这意味着:底层系统配置 (如工具策略、安全规则)确实需要用户明确要求才能修改;但用户配置文件(8 个 bootstrap 文件)Agent 有更大的自主权,尤其是在模板明确鼓励更新的场景下。

4.2 心跳驱动的"软学习"(Heartbeat-driven Soft Adaptation)

如果说"显式更新"像提交一条 PR,那么 Heartbeat 更像定时提醒你做 maintenance:它会周期性触发一次"主会话回合",让 agent 有机会:

  • 检查是否有未完成事项需要提醒
  • 扫描近期的"日记/状态文件"
  • 把值得长期保留的经验**蒸馏(distill)**到更稳定的文件里(例如 MEMORY.mdTOOLS.md、甚至 AGENTS.md

OpenClaw 的实现细节很值得学:

  • Heartbeat 是"定期主会话回合" ,不是后台 task:文档明确说 heartbeat 不创建 task record,而是直接跑一次 agent turn(docs/gateway/heartbeat.md)。
  • 能省 token 的两个关键开关
    • isolatedSession: true:每次 heartbeat 用全新 session,避免把整个历史对话都送进模型(文档里明确说能从 ~100K tokens 降到 ~2-5K)。
    • lightContext: true:只注入 HEARTBEAT.md,不要把其它 bootstrap 文件都塞进来(适合纯巡检/提醒型心跳)。

在源码里,这两个开关直接影响 heartbeat 的执行方式:

  • openclaw/src/infra/heartbeat-runner.ts
    • 读取 HEARTBEAT.md,如果内容"等效为空"会跳过(empty-heartbeat-file
    • 支持 tasks: block,只把"到期任务(due tasks)"拼进 prompt;如果没有任务到期会直接跳过(no-tasks-due
    • 支持 isolatedSession:把 heartbeat 运行绑定到 :heartbeat 兄弟 session,避免污染主会话 transcript
    • 支持重复内容去重:同样的 heartbeat payload 在 24h 内重复会被跳过,避免"nagging"

💡 理解要点:Heartbeat 的价值不在"更频繁地打扰你",而在"把维护工作自动化、把模型调用变成可控的周期成本"。

🔍 例子:一个"可直接用"的 HEARTBEAT.md

下面这个例子刻意覆盖三类常见任务:提醒型 (每小时)、蒸馏型 (每天/每周)、清理型(每周)。你可以把它当作"把 Learning 变成周期维护"的最小落地模板。

markdown 复制代码
# HEARTBEAT.md
#
# 说明:
# - 这个文件支持 tasks: block。OpenClaw 会只执行"到期(due)"的任务;没到期会跳过,省 token(no-tasks-due)。
# - 如果你希望心跳"只维护不打扰",把 agents.defaults.heartbeat.target 设为 "none"(见下文 6.2 的配置示例)。

tasks:
  # 1) 提醒型:每小时检查一次是否有"该提醒"的未完成事项
  - name: remind-open-items
    interval: 1h
    prompt: |
      Check the workspace for open items that should be reminded.
      - Look for TODO markers in recent notes, or obvious "pending" sections in MEMORY.md / project docs.
      - If there's something actionable, write a short alert in Chinese with the next step.
      - If nothing needs attention, reply HEARTBEAT_OK.

  # 2) 蒸馏型:每天把近期流水账提炼成"可长期注入"的稳定条目
  - name: distill-daily-notes
    interval: 24h
    prompt: |
      Distill the last 1-2 days of notes into stable learnings.
      - Read memory/YYYY-MM-DD.md (today + yesterday) if present.
      - Update MEMORY.md with concise, durable items (preferences, decisions, constraints).
      - Do not copy raw logs into MEMORY.md; keep it short.
      - If you made changes, summarize what changed in 3 bullets; otherwise HEARTBEAT_OK.

  # 3) 清理型:每周清理过期/重复规则,避免"稳定区"膨胀导致 token burn
  - name: prune-memory
    interval: 7d
    prompt: |
      Review MEMORY.md and remove stale, duplicate, or one-off items.
      - Keep only durable facts/policies.
      - If anything is ambiguous, leave it and flag it as "needs confirmation" instead of deleting.
      - If no changes are needed, reply HEARTBEAT_OK.

这个例子和前文的两个关键开关是配套的:

  • isolatedSession: true :让上述任务在 :heartbeat 隔离会话里跑,避免把主会话历史越跑越长。
  • lightContext: true :适合这类"巡检/蒸馏/清理"任务------通常不需要把全部 bootstrap 文件都注入,只要 HEARTBEAT.md 的任务定义即可。
4.2.1 架构图:Heartbeat 的执行回路(Preflight → Due Tasks → Isolated Session → 去重)

Channel Delivery Brain/LLM Session Store Workspace(HEARTBEAT.md) heartbeat-runner.ts Scheduler/Timer Channel Delivery Brain/LLM Session Store Workspace(HEARTBEAT.md) heartbeat-runner.ts Scheduler/Timer alt [no tasks due] [due tasks 存在] alt [file 空 + 无 tasks] [有 tasks] [无 tasks] tick / wake(reason=interval|wake|cron|exec) Preflight(quiet-hours / requests-in-flight 等) 读取 HEARTBEAT.md skip(empty-heartbeat-file) parse tasks + 过滤 due tasks skip(no-tasks-due) 可选 isolatedSession(:heartbeat session) 发送 batched prompt(仅 due tasks + directives) reply(HEARTBEAT_OK 或 alert) 去重(24h 内重复 payload → skip duplicate) 可选投递(target=none/last/指定渠道) 更新 task last-run timestamps fallback heartbeat prompt HEARTBEAT_OK / alert 按可见性 showOk/showAlerts 投递或吞掉

下面按图中的顺序,把 Heartbeat(心跳回合)的真实执行流程讲清楚(对应实现主要在 openclaw/src/infra/heartbeat-runner.ts,并调用 openclaw/src/auto-reply/heartbeat.ts 的解析/过滤逻辑)。

A) 触发:是谁把 Heartbeat"叫醒"的?

图里第一步是:

  • Timer -> HB: tick / wake(reason=interval|wake|cron|exec)

这里的 reason 本质是在标注"这次心跳为什么发生",常见来源包括:

  • interval :到点了(按 agents.defaults.heartbeat.every 的周期)
  • wake/hook:系统事件/完成事件唤醒(例如后台任务完成、或需要主会话注意)
  • cron:和 cron 事件相关的唤醒
  • exec:和 exec completion 事件相关的唤醒

💡 理解要点:Heartbeat 并不等于"定时发消息",它首先是"定时跑一轮 agent turn";是否对外发消息是后面的 delivery 决策。

B) Preflight:在真正调用 LLM 之前,先过滤"是否值得跑"

图里第二步:

  • HB -> HB: Preflight(quiet-hours / requests-in-flight 等)

对应源码里会做一串"前置跳过(skip)"判断,典型包括:

  • disabled :心跳未启用(every=0m 或全局禁用)
  • quiet-hours:不在 active hours(静默时段)
  • requests-in-flight:主队列或 session lane 正在忙,避免打断流式对话;这类情况会跳过并由 wake 层稍后重试

🔍 实际例子:你正在和 agent 进行一次长回复/长工具执行时,Heartbeat 到点了也不会硬插队。

C) 读取 HEARTBEAT.md:有无任务清单决定了"batched tasks 模式"是否生效

图里第三步:

  • HB -> WS: 读取 HEARTBEAT.md

读取后分两种大分支:

  1. 文件内容等效为空 + 没有 tasks
    直接跳过,图里对应:
  • skip(empty-heartbeat-file)
  1. 文件里有 tasks: block
    进入 tasks 模式(batched tasks),图里对应:
  • parse tasks + 过滤 due tasks

其中 "due tasks(到期任务)" 的意思是:每个 task 都有自己的 interval,OpenClaw 会用 session 里保存的 heartbeatTaskState(上次执行时间戳)来判断是否到期。

D) no-tasks-due:如果没有任何任务到期,这一轮心跳会被直接跳过(省钱)

图里这个分支是:

  • alt no tasks due -> skip(no-tasks-due)

这一步非常关键:它让你可以把多个周期检查都写进同一个 HEARTBEAT.md,但每次 tick 只执行到期的那几个,而不是"每 30 分钟全做一遍"。

💡 理解要点:tasks: 的意义不是"让心跳更忙",而是"让心跳更可控、更便宜"。

E) isolatedSession:把心跳跑在 :heartbeat 会话里,避免把主会话越跑越长

图里这一步是:

  • HB -> Store: 可选 isolatedSession(:heartbeat session)

当你配置 isolatedSession: true 时,Heartbeat 会创建/复用一个 :heartbeat 后缀的隔离 session 来执行本次 LLM 回合。

这带来两个直接收益:

  • 降低 token 成本:隔离 session 没有主会话的长历史(尤其适合"巡检型心跳")
  • 避免污染主会话 transcript:心跳的"自言自语式维护"不会把主会话变成流水账

F) 组装 prompt 并调用 LLM:tasks 模式 vs fallback 模式

图里两条路径分别是:

  • tasks 模式发送 batched prompt(仅 due tasks + directives)
  • fallback 模式 (没有 tasks):fallback heartbeat prompt

二者的共同点是:这一步才会真正调用 LLM,并得到两类结果:

  • HEARTBEAT_OK:表示"没事可汇报"(ack)
  • alert 文本:表示"有事需要你知道"

G) 去重(dedupe):24h 内相同 payload 会被抑制,避免 nagging

图里是:

  • 去重(24h 内重复 payload → skip duplicate)

它的效果很直观:如果模型连续两次给出几乎相同的提醒(例如"你有一个未读邮件"但实际上状态没变),系统会把重复内容跳过,避免"每 30 分钟提醒一次"的骚扰体验。

H) 投递(Delivery):是否对外发消息取决于 target 与可见性策略

最后是:

  • 可选投递(target=none/last/指定渠道)
  • 按可见性 showOk/showAlerts 投递或吞掉

你可以把 delivery 看成两个开关叠加:

  • target 路由 :发到哪里(none 表示只跑不发)
  • 可见性策略:OK 是否展示(showOk)、Alert 是否展示(showAlerts)、是否发 indicator(useIndicator)

🔍 实际例子:你把 target: "none" 打开,Heartbeat 仍然会"维护文件/更新状态",但不会在聊天里刷屏。

4.3 上下文压缩驱动的"蒸馏"(Compaction-driven Distillation)

当对话变长、token 接近上限时,系统触发 Compaction------把旧消息摘要化。这不仅是"省 token",更是一次"蒸馏":把长对话压成结构化摘要,保留关键任务、决策与标识符。

OpenClaw 的**压缩护栏(Safeguard)**是关键防翻车设计:

  • AGENTS.md 抽取 "Session Startup" / "Red Lines" 塞进摘要尾部
  • 即使摘要被截断,规则锚点优先保留,避免"越聊越跑偏"

💡 一句话:Compaction 不只是压缩,更是"被迫缩短上下文时,把行为约束固化下来"的自适应手段。

4.3.1 常见疑问:8 个文件会被"蒸馏"吗?

不会。

对象 是否被 Compaction 处理 说明
会话消息历史messagesToSummarize 被摘要 长对话分段 → 摘要 → 写入 summary / compaction entry
8 个 workspace 文件AGENTS.md...) 不被改写 按原有规则存在于 system prompt;唯一例外:AGENTS.md 的关键段落会作为 <workspace-critical-rules> 附加进摘要尾部当锚点

结论:Compaction 让"对话历史"变短;Safeguard 让"关键规则"不丢;Learning 文件更新仍靠显式写入或 Heartbeat。

4.3.2 蒸馏用的 Prompt 长什么样?

不是一段固定长文本,而是由三块指令拼成 (源码位置:compaction-safeguard-quality.ts + compaction-instructions.ts):

1) 结构化骨架(强制标题)

复制代码
## Decisions
## Open TODOs  
## Constraints/Rules
## Pending user asks
## Exact identifiers

要求严格保留这些段落,且标识符保真(strict/off/custom policy)。

2) 默认附加要求

  • 保持语言一致、不翻译代码与路径、不改动标识符

3) 分块合并约束MERGE_SUMMARIES_INSTRUCTIONS

  • MUST PRESERVE: active tasks / batch progress / last user ask / decisions / constraints
4.3.3 架构:Compaction 五阶段链路

产物
护栏:保规则
摘要:两条路径
准备:整理素材
触发条件
fallback
上下文接近上限 / 需腾挪 token
选择消息段,保留最近 turns
修复 tool call/result 配对
收集 read/modified files
收集 Tool Failures
可选:外部 Provider 摘要
LLM summarizeInStages:分块+合并
可选:质量审计/重试
AGENTS.md 抽取关键规则
无内容时仍写 boundary 防循环
截断优先保留 suffix
写入 summary / compaction entry
继续对话:更小上下文+规则锚点

A) Trigger:何时触发?

上下文接近上限,下一轮会超窗时强制触发。

B) Preparation:整理什么?
  • Pick:保留最近若干轮,防止关键信息被过度摘要
  • Repair:修复 tool 配对,避免 provider 报错
  • FileOps/ToolFail:记录文件操作与失败信息,方便后续排错
C) Summarize:怎么摘要?
  • Provider 路径:配置外部摘要服务时优先走(一次处理全部)
  • LLM 路径(summarizeInStages):分块 → 摘要 → 递归合并
  • Quality Guard:检查是否丢了关键标识符、结构是否完整、是否覆盖最新需求;不合格可重试

💡 关键:标识符丢了 = "记得有文件但不知道叫什么"的灾难

D) Safeguard:护栏保什么?
  • RulesAGENTS.md 关键规则作为 suffix 附加,防止行为漂移
  • Boundary:无内容时仍写最小 entry,避免 compaction 死循环
  • Cap:截断时优先保留 suffix(规则、诊断),因为这些对"继续工作"更关键
E) Outputs:产物怎么用?
  • Summary:结构化摘要 + 诊断/规则后缀,写入会话记录
  • Continue:下一轮携带"摘要 + 最近消息",规则锚点仍在

🔍 例子:你在 AGENTS.md 写了"修改前必须先读文件",即使对话被压缩,后续回合仍会在摘要尾部看到这条规则。


5. Learning & Adaptation 的"安全边界":允许自改,但不允许乱改

Learning 的本质是"写文件"。而"写文件"在 Agent 系统里既强大又危险。

OpenClaw 的思路是把风险拆成两类,并分别治理:

5.1 让"可改的地方"更可控:文件分层 + 注入顺序

把不同性质的信息放在不同文件里(并按优先级注入),天然降低了误改的破坏面:

  • 想改语气 :改 SOUL.md(不会影响工具权限)
  • 想改工具策略 :改 AGENTS.md / TOOLS.md(可以审计)
  • 想改巡检节奏 :改 HEARTBEAT.md(动态区,成本可控)
  • 想改长期记忆 :改 MEMORY.md(但注意它会被注入,太长会涨 token)

这类"软边界"靠的是结构化与优先级,而不是强制执行。

5.2 让"关键阶段"更不容易崩:护栏与降级路径

以 compaction safeguard 为例,它准备了多种降级策略:

  • provider 失败 → 回退到 LLM 分阶段摘要(summarizeInStages
  • 摘要质量不合格 → 允许重试(quality guard)
  • 根本没有可摘要内容 → 仍写入一个最小 boundary,避免进入"反复触发 compaction 的死循环"

🔍 实际例子:在长时间运行的 gateway/cron/heartbeat 场景里,如果 compaction 进入 cancel loop,会直接拖垮自动化;safeguard 的目标就是"宁可保守,也别把系统卡死"。


6. 端到端:从"用户一句话"到"长期行为改变"

下面用一个具体场景把 Learning & Adaptation 走一遍。

6.1 场景:用户想要"更直接、更少套话"的回答风格

Round 1(用户反馈)

User:以后别用"很好的问题/我很乐意帮助",直接回答重点就行。

Round 2(显式学习写入)

Agent:收到。我会把这个偏好写进 SOUL.md,以后都按这个风格回复。

落地动作(你可以把它理解成"让 agent 自己给自己改配置"):

  • 更新 SOUL.md 的 Tone/Interaction 规则,例如加入:
    • "不要用套话开场"
    • "结论先行"

Round 3(效果)

User:Builtin 的 indexing 到底有没有?

Agent:默认没有;只有启用 indexing 才会建立 SQLite FTS 索引。(然后给清晰解释)
💡 理解要点:Learning 的"完成标志"不是模型说"我记住了",而是文件被更新 + 后续行为稳定一致

6.2 场景:把"日常维护"变成系统能力(Heartbeat)

你希望 agent 每天自动做三件事:

  • 检查未完成事项(来自 agent-knowledge.md 或任务清单)
  • 更新 MEMORY.md(蒸馏长期经验)
  • 如果 HEARTBEAT.md 太长就自动收缩(避免 token burn)

你可以用一段配置把心跳变成"低成本巡检回合":

json5 复制代码
{
  "agents": {
    "defaults": {
      "heartbeat": {
        "every": "30m",
        "target": "none",
        "isolatedSession": true,
        "lightContext": true
      }
    }
  }
}

再配合一个很小的 HEARTBEAT.md(上面示例的 tasks:),就形成了一个闭环:

复制代码
定期触发(heartbeat-runner.ts)
  ↓
只跑到期 tasks(parseHeartbeatTasks + isTaskDue)
  ↓
必要时改文件(MEMORY.md / HEARTBEAT.md / TOOLS.md)
  ↓
无事则 HEARTBEAT_OK(并可被系统吞掉,不打扰用户)

🔍 实际例子:当你把 target: "none" 打开,heartbeat 仍然运行,但不会对外发消息。你得到的是"后台维护能力",而不是"每 30 分钟来一句 OK"。


7. 与业界对照:OpenClaw 的 Learning 取舍在哪里?

这一套"文件化学习"看起来朴素,但它解决的是工程世界里最难的三件事:可控、可审计、可回滚

7.1 优势:学习结果变成"可代码审查的差异"

当学习落在 Markdown 文件上时,你天然获得:

  • diff:知道改了什么
  • history:知道什么时候改的
  • rollback:能回滚到上一版

这类特性在任何严肃工程里都非常关键,尤其当你开始做多 agent、做自动化、做长周期运行。

7.2 代价:需要你像维护配置一样维护人格文件

Learning 不再是"免费魔法",而是"需要 maintenance 的系统":

  • MEMORY.md 写太长会增加注入 token,导致 compaction 更频繁
  • AGENTS.md 写得太泛会让模型"口号化",反而不稳定
  • HEARTBEAT.md 任务太多会让心跳成本不可控

💡 理解要点:把学习做成"可控系统",就意味着你要像维护服务一样维护它------但回报是稳定性与可预测性。


8. 最佳实践清单(拿来就能用)

8.1 文件怎么写才"有用"?

  • AGENTS.md:写"硬规则 + 工具策略 + 失败策略",避免写情绪化口号
  • SOUL.md:写"语气/立场/表达习惯",避免写安全策略堆砌(官方也明确提醒不要把它变成 security policy dump)
  • MEMORY.md :写"稳定的长期结论",不要把流水账堆进去(流水账放 memory/YYYY-MM-DD.md 或 corpus)
  • HEARTBEAT.md :保持极短,用 tasks: 控制频率,能 no-tasks-due 就跳过

8.2 "学习"触发该怎么选?

  • 要强一致 :用显式更新(用户确认后写 AGENTS.md/SOUL.md
  • 要低成本维护 :用 heartbeat(isolatedSession + lightContext
  • 要自动收敛上下文:让 compaction 做蒸馏,但要依赖 safeguard(避免质量问题/死循环)

9. 小结:Learning & Adaptation 是"把经验写进系统"

这篇我们把 OpenClaw 的 Learning & Adaptation 拆成了三个层次:

  • 语义层:八类文件分工(哪些属于规则、哪些属于语气、哪些属于巡检)
  • 注入层:系统提示词自动注入,让学习"默认生效"
  • 触发层:显式更新 / Heartbeat / Compaction,让学习"有节奏、可维护"

如果你只记住一句话:

OpenClaw 的 Learning 不是让模型记住你,而是让系统持续读到你写下的规则。


参考资料(外部)

  • OpenAI Prompt Engineering Guide(关于高优先级指令层与迭代 prompt 的建议)[参考:https://developers.openai.com/api/docs/guides/prompt-engineering]
  • OpenClaw Docs:System Prompt / SOUL / Heartbeat(本文以本地 openclaw/docs 为准,线上镜像可能有漂移)[参考:https://docs.openclaw.ai]

附录 A:8 类文件在 OpenClaw 源码中的 Prompt 内容(模板与注入行为)

目的:把"八类文件到底在 prompt 里长什么样"说清楚,方便你对照源码快速定位。

版本基线:0dd4958bc8a78d26b3b526b1f2e63b15110c64a2

A.1 先看"如何进入 Prompt":源码装配规则

这 8 类文件并不是"模型自己会去找",而是运行时按固定规则注入。

规则 1:默认文件名定义(workspace 层)

源码 openclaw/src/agents/workspace.ts 中定义了默认文件名常量:

  • AGENTS.md
  • SOUL.md
  • TOOLS.md
  • IDENTITY.md
  • USER.md
  • HEARTBEAT.md
  • BOOTSTRAP.md
  • MEMORY.md(并兼容小写 memory.md 作为 fallback)

规则 2:注入优先级(prompt 层)

源码 openclaw/src/agents/system-prompt.tsCONTEXT_FILE_ORDER

  • agents.md(10) > soul.md(20) > identity.md(30) > user.md(40) > tools.md(50) > bootstrap.md(60) > memory.md(70)

并且 heartbeat.md 被标记为动态上下文文件(DYNAMIC_CONTEXT_FILE_BASENAMES),尽量放在 cache boundary 之后。

规则 3:子会话/定时会话会被裁剪

workspace.tsfilterBootstrapFilesForSession() 显示:在 subagent/cron 场景会走最小 allowlist(AGENTS/SOUL/TOOLS/IDENTITY/USER),不是所有文件都无条件注入。


A.2 八类文件的"源码 Prompt 内容"速查表

文件 源码模板路径 在 Prompt 中的角色 模板现状
AGENTS.md docs/reference/templates/AGENTS.md 顶层行为规则与会话启动约束 有默认模板
SOUL.md docs/reference/templates/SOUL.md 语气、人格、风格边界 有默认模板
IDENTITY.md docs/reference/templates/IDENTITY.md 身份元数据(name/vibe 等) 有默认模板
USER.md docs/reference/templates/USER.md 用户画像与长期偏好 有默认模板
TOOLS.md docs/reference/templates/TOOLS.md 本地工具环境/偏好笔记 有默认模板
BOOTSTRAP.md docs/reference/templates/BOOTSTRAP.md 首次启动引导(完成后可删除) 有默认模板
MEMORY.md (无模板文件) 长期记忆策略/沉淀内容 无默认模板,存在即注入
HEARTBEAT.md docs/reference/templates/HEARTBEAT.md 周期任务入口(tasks/检查清单) 有默认模板(默认近空)

A.3 逐文件核心片段(来自源码模板)

下面是每个文件在源码模板中的"关键 prompt 语句/结构",用于理解它们如何影响 agent。

1) AGENTS.md(顶层规则)

来源:openclaw/docs/reference/templates/AGENTS.md

核心内容要点:

  • Session Startup 明确要求:
    • 先读 SOUL.md
    • USER.md
    • 读最近 memory/YYYY-MM-DD.md
    • 主会话还要读 MEMORY.md
  • Red Lines 明确红线:
    • 不外泄隐私
    • 破坏性命令先确认
  • Heartbeat 使用策略:
    • 何时应 HEARTBEAT_OK
    • 心跳与 cron 的分工

这意味着:AGENTS.md 不只是"风格建议",而是 agent 的高优先级操作手册。

2) SOUL.md(人格与语气)

来源:openclaw/docs/reference/templates/SOUL.md

核心内容要点:

  • Core Truths:强调"真帮助、少套话、有观点"
  • Boundaries:隐私与外部动作边界
  • Vibe:沟通语气基调
  • Continuity:强调会话重启后依赖文件保持连续性

这类内容会直接影响"回复口吻与表达姿态"。

3) IDENTITY.md(身份)

来源:openclaw/docs/reference/templates/IDENTITY.md

核心内容要点:

  • Name / Creature / Vibe / Emoji / Avatar 等身份字段
  • 本质上是"自我标识层",帮助 agent 在长期会话中保持角色一致性
4) USER.md(用户画像)

来源:openclaw/docs/reference/templates/USER.md

核心内容要点:

  • Name / Pronouns / Timezone / Notes
  • Context(用户关心什么、正在做什么、偏好与雷区)

它是"对谁服务"的稳定背景层。

5) TOOLS.md(本地工具笔记)

来源:openclaw/docs/reference/templates/TOOLS.md

核心内容要点:

  • 明确"技能定义工具通用能力,TOOLS.md 记录你本地环境特有信息"
  • 典型内容:摄像头别名、SSH 主机、TTS 偏好、设备昵称等

它解决的是"同一技能在不同环境如何落地"的问题。

6) BOOTSTRAP.md(首次引导)

来源:openclaw/docs/reference/templates/BOOTSTRAP.md

核心内容要点:

  • 只在初次启动引导时高频使用
  • 引导建立 IDENTITY.md / USER.md / SOUL.md
  • 模板中明确建议"初始化完成后删除 BOOTSTRAP.md"

它更像 onboarding 脚本,而非长期规则文件。

7) MEMORY.md(长期记忆)

来源:workspace.ts + system-prompt.ts 注入逻辑(无默认模板文件)

关键事实(很重要):

  • 在当前源码中,docs/reference/templates/ 下没有内置 MEMORY.md 模板
  • 但运行时会尝试加载并注入:
    • 优先 MEMORY.md
    • 不存在则 fallback memory.md
  • 因此 MEMORY.md 的内容由你/agent 在工作区中逐步沉淀,不是"固定官方文案"

这也是它最像"学习产物"的原因:存在即生效,内容随实践演化

8) HEARTBEAT.md(周期任务)

来源:openclaw/docs/reference/templates/HEARTBEAT.md

模板核心非常简短,默认倾向"空文件以避免无效调用":

  • 保持空(或仅注释)可跳过 heartbeat API 调用
  • 需要周期检查时再添加 tasks

这和 heartbeat-runner.ts 的执行逻辑一致:空内容/无到期任务时会直接 skip,控制成本。


A.4 一句话总结附录

OpenClaw 的"八类文件 prompt 内容"并不是一段固定大 prompt 文本,而是:

  1. 模板层docs/reference/templates/*.md)给出默认结构;
  2. 工作区层workspace.ts)把文件创建/加载出来;
  3. 系统提示词层system-prompt.ts)按优先级与动态规则注入到每轮 agent run。

所以你真正在"训练"agent 的地方,不是某个黑箱参数,而是这套可读、可改、可审计的文件体系。

相关推荐
紫微AI1 小时前
前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了
前端·人工智能·typescript
码途漫谈1 小时前
Easy-Vibe开发篇阅读笔记(四)——前端开发之结合 Agent Skills 美化界面
人工智能·笔记·ai·开源·ai编程
易连EDI—EasyLink1 小时前
易连EDI–EasyLink实现OCR智能数据采集
网络·人工智能·安全·汽车·ocr·edi
冬奇Lab2 小时前
RAG 系列(二):用 LangChain 搭建你的第一个 RAG Pipeline
人工智能·langchain·llm
学习论之费曼学习法2 小时前
多模态大模型实战:用 GPT-4o API 打造 AI 助手,能看、能听、能说!
人工智能
昨夜见军贴06162 小时前
IACheck与AI报告审核,开启供应商资质核验报告审核新篇章
人工智能
m0_726365832 小时前
Ai漫剧系统 几分钟,让AI 把一篇小说变成了一部漫剧成片:从剧本到视频的全流程系统实现
人工智能·语言模型·ai作画·音视频
AIwenIPgeolocation2 小时前
出海应用合规与风控平衡术:可信ID的全球安全实践
人工智能·安全
WordPress学习笔记3 小时前
镌刻中式美学的高端WordPress主题
大数据·人工智能·wordpress