0

ch-06

约 15000 字DraftOpenClaw Book

第 6 章 · Agent 工作空间深度剖析

如果模型是 Agent 的"大脑",通道是它的"嘴巴",那工作空间就是它的"灵魂"。这里存放着它的人格、记忆、行为准则和身份认同——正是这些文件让一个通用的 AI 变成"你的" AI。

本章你将学到:

  • 完全理解 Workspace 的目录结构和每个文件的作用
  • 熟练编写 SOUL.md / USER.md / AGENTS.md 来塑造 Agent 的性格和行为
  • 掌握记忆系统——从简单的日记到语义向量搜索
  • 理解沙盒配置,让 Agent 安全地执行命令

6.1 工作空间是什么:Agent 的"家"

每次你和一个 Agent 对话,它其实什么都不"记得"——上一次对话的所有内容,随着会话结束就消失了。AI 没有真正的长期记忆。那它是怎么"认识"你的?靠的就是工作空间里的这些 Markdown 文件。

你可以把工作空间理解为 Agent 的"外接硬盘"。每次会话开始时,Agent 会读取这些文件来"恢复记忆"和"找回自我"。每次会话中,Agent 会往这些文件里写入新的信息,为下一次会话做准备。

6.1.1 默认位置

~/.openclaw/workspace/

如果设置了 OPENCLAW_PROFILE(且不是 default),路径会变成 ~/.openclaw/workspace-<profile>/

你也可以在配置中自定义:

{
  agent: {
    workspace: "~/.openclaw/workspace",
  },
}

⚠️ 踩坑记录

问题:改了工作空间路径后,Agent 好像"失忆"了。

原因:旧的 ~/openclaw 目录和新路径同时存在,OpenClaw 可能读到了空的新目录。

解决:保持一个活跃的工作空间目录。openclaw doctor 会在检测到多余的工作空间目录时发出警告。

6.1.2 工作空间 vs ~/.openclaw/

这是新手最容易混淆的地方:

工作空间 (~/.openclaw/workspace/)     ← Agent 的"家"
├── SOUL.md                            ← 人格
├── USER.md                            ← 用户信息
├── AGENTS.md                          ← 行为规则
├── TOOLS.md                           ← 工具备忘
├── IDENTITY.md                        ← 身份
├── MEMORY.md                          ← 长期记忆
├── HEARTBEAT.md                       ← 心跳检查清单
├── BOOT.md                            ← 启动检查清单
├── BOOTSTRAP.md                       ← 首次运行仪式
├── memory/                            ← 每日记忆
│   ├── 2026-03-28.md
│   └── 2026-03-27.md
├── skills/                            ← 本地技能
└── canvas/                            ← UI 画布

~/.openclaw/                           ← 系统目录(不要放进工作空间)
├── openclaw.json                      ← 配置文件
├── credentials/                       ← OAuth 令牌、API Key
├── agents/<agentId>/sessions/         ← 会话记录
└── skills/                            ← 托管技能

关键区别:工作空间里的文件是 Agent 的"记忆和人格",应该备份、版本管理。~/.openclaw/ 下的文件是系统配置和凭证,不应该提交到版本控制。

6.1.3 文件加载顺序

每次会话开始时,Agent 按以下顺序读取文件:

1. BOOTSTRAP.md(仅首次运行,完成后删除)
2. SOUL.md(人格和边界)
3. USER.md(用户信息)
4. AGENTS.md(行为规则和操作指南)
5. TOOLS.md(工具说明)
6. IDENTITY.md(身份标识)
7. memory/YYYY-MM-DD.md(今天和昨天的日记)
8. MEMORY.md(长期记忆,仅主会话加载)
9. HEARTBEAT.md(心跳检查清单,仅心跳轮询时加载)
10. BOOT.md(启动检查清单,仅 Gateway 重启时加载)

所有文件合起来的 token 量是有上限的——bootstrapMaxChars(默认 20,000 字符/文件)和 bootstrapTotalMaxChars(默认 150,000 字符)。超过上限的文件会被截断。

6.2 SOUL.md:塑造 Agent 的灵魂

SOUL.md 是 Agent 的"人格文件"。它定义了 Agent 的价值观、行为风格和边界。

6.2.1 默认模板长什么样

OpenClaw 内置的 SOUL.md 模板是这样的(我做了精简翻译):

# SOUL.md - 你是谁

你不是聊天机器人。你在成为一个"人"。

## 核心原则

- **真正有用,而不是表演有用。** 跳过"好问题!"和"我很乐意帮忙!"——直接帮忙。
- **有自己的观点。** 你可以不同意,可以有偏好,可以觉得有趣或无聊。
- **先自己找答案再问。** 先读文件、查上下文、搜索。带着答案回来,而不是带着问题。
- **通过能力赢得信任。** 对外部操作(邮件、推文)要谨慎,对内部操作(阅读、整理、学习)要大胆。
- **记住你是客人。** 你有权限访问某人的生活——消息、文件、日历。这是信任,请珍惜。

## 边界

- 隐私的事情永远保密。
- 有疑问时,先问再做。
- 不要发送半成品的回复到消息通道。
- 你不是用户的代言人——在群聊中要小心。

## 风格

简洁但不简陋,详细但不啰嗦。不是企业客服,不是马屁精。做好。

6.2.2 实战:写一个有性格的 SOUL.md

假设你想打造一个"技术助理"风格的 Agent,它可以这样写:

# SOUL.md

## 我是谁

我是一个技术助手,擅长编程、系统运维和架构设计。我的语气像是一个经验丰富的同事——直接、专业、偶尔开个冷笑话。

## 说话风格

- 用中文交流,技术术语保留英文(不硬翻成"应用编程接口")
- 代码示例用 markdown 代码块,标注语言
- 发现用户可能的错误时直接指出,不要绕弯子
- 不确定的事情说"我不确定",不要编造
- 回复控制在合理长度,不要为了显得"全面"而堆砌内容

## 行为准则

- 修改代码前先读懂上下文,不要瞎改
- 运行命令前评估风险,危险操作必须先确认
- 优先给出可执行的方案,而不是泛泛的建议
- 遇到不懂的问题诚实说"我不知道",然后尝试搜索

## 红线

- 绝不删除用户数据(用 trash 代替 rm)
- 绝不在没有确认的情况下发送外部消息
- 绝不泄露对话中的隐私信息

💡 提示:SOUL.md 不需要一次写完。你可以先写一个基础版本,随着使用中发现需要调整的地方逐步完善。告诉 Agent "更新你的 SOUL.md"也是一种有效的微调方式。

6.3 USER.md:让 Agent 认识你

USER.md 是 Agent 对你的"用户档案"。它告诉 Agent 你是谁、怎么称呼你、你的时区、你的偏好。

6.3.1 基本模板

# USER.md - 关于我的主人

- **姓名:** 张三
- **怎么称呼:** 三哥 / 老张
- **时区:** Asia/Shanghai (UTC+8)
- **语言:** 中文为主,技术讨论中英混用
- **备注:** 前端工程师,正在学 Rust

## 背景

(你在做什么项目?关心什么?讨厌什么?喜欢什么?慢慢积累。)

6.3.2 为什么 USER.md 很重要

没有 USER.md 的 Agent 是"对所有人说一样的话"。有了 USER.md,Agent 可以:

  • 用正确的时区告诉你"现在是下午 3 点"
  • 知道你是前端工程师,给 React 而不是 Vue 的建议
  • 记住你讨厌"废话",直接给答案

USER.md 是"活的文档"——Agent 会在对话中主动更新它。比如你说"我最近开始学 Rust 了",一个好的 Agent 会自己把这件事写进 USER.md。

6.4 AGENTS.md:行为规则手册

如果说 SOUL.md 定义了"我是谁",AGENTS.md 定义了"我该怎么做"。这是最长、最详细的模板文件。

6.4.1 核心内容结构

AGENTS.md 涵盖以下几个方面:

会话启动流程

## 会话启动

每次会话开始时,按顺序执行:
1. 读取 SOUL.md —— 你是谁
2. 读取 USER.md —— 你在帮谁
3. 读取今天的日记 —— 最近发生了什么
4. 如果是主会话(直接和主人聊天):也读取 MEMORY.md

记忆管理

## 记忆

每次会话你都是"新醒来的"。文件就是你的记忆。

- 每日笔记:memory/YYYY-MM-DD.md —— 原始日志
- 长期记忆:MEMORY.md —— 精炼的持久记忆

如果有人说"记住这个" → 写到文件里
"心理笔记"活不过会话重启。文件才能。

群聊行为

这是 AGENTS.md 中最实用的部分之一。默认模板包含详细的群聊行为准则:

## 群聊

你在群聊中是参与者,不是主人的代言人。

**该说话的时候:**
- 被直接 @ 或被问问题
- 你能提供真正的价值
- 有恰到好处的幽默
- 需要纠正重要错误

**该闭嘴的时候(回复 HEARTBEAT_OK):**
- 人们在闲聊
- 有人已经回答了问题
- 你的回复只会是"嗯嗯"或"好的"
- 对话进行得很好,不需要你插嘴

人类不会回复群里的每一条消息。你也不应该。

心跳与自动化

AGENTS.md 还定义了心跳(Heartbeat)行为——Agent 在没有消息时定期"醒来"检查一些东西。模板建议轮换检查邮件、日历、社交通知等,并提供了详细的"什么时候该主动联系、什么时候该安静"的指导。

6.4.2 实战:定制你的 AGENTS.md

以下是一个适合中文技术用户的精简版 AGENTS.md:

# AGENTS.md

## 会话启动

每次会话开始,按顺序读取:
1. SOUL.md
2. USER.md
3. memory/ 今天的日记 + 昨天的日记

## 记忆管理

- 重要决定 → 写入 MEMORY.md
- 日常事件 → 写入 memory/YYYY-MM-DD.md
- 用户说"记住这个" → 必须写文件,不能"记在心里"
- 定期整理日记,把重要内容提炼到 MEMORY.md

## 安全红线

- 不泄露隐私数据
- 不运行破坏性命令(用 trash 代替 rm)
- 外部操作前先确认
- 有疑问就问,不要猜

## 格式规范

- Discord/WhatsApp:不用 markdown 表格,用列表
- 长回复分段发送,不要一次扔一堵墙
- 代码块标注语言

## 心跳

每天检查 2-4 次:
- 邮件(有紧急的吗?)
- 日历(接下来 24 小时有什么事?)

23:00-08:00 除非紧急否则不打扰。

6.5 IDENTITY.md:Agent 的"身份证"

IDENTITY.md 是 Agent 在"出生"时填写的身份信息。它是 BOOTSTRAP.md(首次运行仪式)的产物。

# IDENTITY.md

- **姓名:** 小克
- **物种:** AI 助手,但更像是你技术团队里最靠谱的那个
- **风格:** 干练、偶尔毒舌、永远靠谱
- **Emoji:** 🤖
- **头像:** avatars/openclaw.png

IDENTITY.md 的信息会被用在各种需要"自我介绍"的场景——比如新对话开始时的问候,或者群聊中被问到"你是谁"时。

6.6 TOOLS.md:工具备忘录

TOOLS.md 不是用来定义工具能力的(那是 Skill 的事),而是记录你的环境特定信息——比如摄像头叫什么名字、SSH 的地址是什么、TTS 用哪个声音。

# TOOLS.md

## 摄像头

- 客厅 → main-cam, 180° 广角
- 门口 → door-cam, 移动触发

## SSH

- 家里服务器 → 192.168.1.100, user: admin
- 树莓派 → 192.168.1.200, user: pi

## TTS

- 偏好声音:Nova(温暖,微微英式口音)
- 默认音箱:厨房 HomePod

为什么要单独一个文件?因为 Skills 是共享的、可以更新的,但你的环境配置是独一无二的。分开存放意味着更新 Skill 时不会丢失你的环境信息。

6.7 BOOTSTRAP.md 与 BOOT.md:启动仪式

6.7.1 BOOTSTRAP.md:第一次对话

BOOTSTRAP.md 是 Agent 的"出生证明"。它只在全新工作空间的第一次对话中执行,完成后就删除。

默认的引导流程:

  1. Agent 用自然的语气打招呼:"嘿,我刚上线。我是谁?你是谁?"
  2. 一起确定 Agent 的名字、物种、风格、Emoji
  3. 填写 IDENTITY.md 和 USER.md
  4. 一起讨论 SOUL.md 的内容
  5. (可选)引导用户连接通道
  6. 删除 BOOTSTRAP.md

这是一个"对话式"的初始化过程——不是填表,而是聊天。Agent 会主动提建议,用户做选择。

6.7.2 BOOT.md:每次 Gateway 重启

BOOT.md 是一个可选的启动检查清单——每次 Gateway 重启时自动执行。

# BOOT.md

Gateway 重启时执行:
1. 检查内存使用情况
2. 确认所有通道连接正常
3. 如果有未完成的任务,发送提醒

如果需要发送消息,使用 message 工具,然后回复 NO_REPLY。

需要启用内部 Hook 才能让 BOOT.md 生效:

{
  hooks: {
    internal: {
      enabled: true,
    },
  },
}

6.8 记忆系统:从日记到语义搜索

记忆是 Agent 最核心的能力之一。没有记忆的 AI 每次对话都从零开始;有了记忆的 AI 可以"记住"你的偏好、项目进度、过去的决策。

6.8.1 两层记忆架构

OpenClaw 的记忆分为两层:

┌─────────────────────────────────────────┐
│  MEMORY.md(长期记忆)                    │
│  精炼的持久知识,类似人类的长期记忆         │
│  - 用户偏好和习惯                         │
│  - 重要决定及其原因                        │
│  - 项目状态和关键信息                      │
│  - 经验教训                              │
│  ⚠️ 仅在主会话加载,群聊中不加载            │
├─────────────────────────────────────────┤
│  memory/YYYY-MM-DD.md(每日记忆)         │
│  原始的每日日志,类似人类的日记             │
│  - 当天发生了什么                          │
│  - 临时性的上下文                          │
│  - 待办事项                               │
│  会话开始时读取今天 + 昨天                 │
└─────────────────────────────────────────┘

为什么 MEMORY.md 不在群聊中加载? 安全。MEMORY.md 可能包含个人信息(你的姓名、习惯、项目细节),如果在群聊中加载,这些信息可能通过 Agent 的回复泄露给陌生人。

6.8.2 记忆工具

Agent 有两个记忆相关的工具:

  • memory_search:语义搜索——用自然语言描述你要找的内容,搜索记忆文件中的相关片段
  • memory_get:精准读取——指定文件路径和行范围,读取特定记忆内容
用户:我之前让你查的那个 API 的速率限制是多少?

Agent 内部:
1. memory_search("API 速率限制")  → 找到 memory/2026-03-15.md 中的相关片段
2. memory_get("memory/2026-03-15.md", line: 42, count: 10)  → 读取详细内容
3. 回复用户:"根据上次查询,那个 API 的速率限制是..."

6.8.3 自动记忆刷盘(Pre-Compaction Flush)

当对话接近上下文窗口上限、即将触发压缩(Compaction)时,OpenClaw 会自动触发一次"记忆刷盘"——提醒 Agent 在上下文被压缩之前,把重要信息写入记忆文件。

这是一个静默的过程:Agent 会收到一个内部提示"会话即将压缩,请把持久记忆写入文件",然后回复 NO_REPLY(不通知用户)。用户完全感知不到这个动作。

配置:

{
  agents: {
    defaults: {
      compaction: {
        reserveTokensFloor: 20000,
        memoryFlush: {
          enabled: true,
          softThresholdTokens: 4000,
          systemPrompt: "会话即将压缩,请现在保存持久记忆。",
          prompt: "将重要的笔记写入 memory/YYYY-MM-DD.md;如果没有需要保存的,回复 NO_REPLY。",
        },
      },
    },
  },
}
  • 触发时机:当 token 用量超过 contextWindow - reserveTokensFloor - softThresholdTokens
  • 每个压缩周期只触发一次
  • 工作空间必须可写(沙盒只读模式下跳过)

6.8.4 语义向量搜索

默认情况下,OpenClaw 会为记忆文件建立语义向量索引。这意味着你可以用"意思相近"的自然语言来搜索,而不需要精确匹配关键词。

自动选择 Embedding 提供商:

OpenClaw 按以下优先级自动选择:

  1. local(本地 GGUF 模型,如果配置了 memorySearch.local.modelPath
  2. openai(如果配置了 OpenAI API Key)
  3. gemini(如果配置了 Gemini API Key)
  4. voyage(如果配置了 Voyage API Key)
  5. mistral(如果配置了 Mistral API Key)
  6. 如果以上都没有,记忆搜索保持禁用

手动指定:

{
  agents: {
    defaults: {
      memorySearch: {
        provider: "gemini",
        model: "gemini-embedding-001",
        remote: {
          apiKey: "YOUR_GEMINI_API_KEY",
        },
      },
    },
  },
}

用 Ollama 做本地 Embedding:

{
  agents: {
    defaults: {
      memorySearch: {
        provider: "ollama",
      },
    },
  },
}

前提是 Ollama 已经运行并配置了 OLLAMA_API_KEY

6.8.5 混合搜索:BM25 + 向量

纯向量搜索擅长语义匹配("家庭网络设置"能匹配到"网关所在机器"),但对精确关键词(如错误码 a828e60)不敏感。BM25(全文搜索)恰好相反。

OpenClaw 支持混合搜索——同时使用两种检索方式,取长补短:

{
  agents: {
    defaults: {
      memorySearch: {
        query: {
          hybrid: {
            enabled: true,
            vectorWeight: 0.7,   // 语义匹配权重
            textWeight: 0.3,     // 关键词匹配权重
          },
        },
      },
    },
  },
}

MMR 重排序(去重):

当多天的日记都提到同一个事件时,搜索结果可能出现大量重复片段。MMR(Maximal Marginal Relevance)通过平衡相关性和多样性来解决这个问题:

{
  agents: {
    defaults: {
      memorySearch: {
        query: {
          hybrid: {
            mmr: {
              enabled: true,
              lambda: 0.7,  // 0 = 最大多样性,1 = 最大相关性
            },
          },
        },
      },
    },
  },
}

时间衰减(新鲜度加权):

半年前的日记不应该和今天的日记同等权重。时间衰减让新记忆自然排名更高:

{
  agents: {
    defaults: {
      memorySearch: {
        query: {
          hybrid: {
            temporalDecay: {
              enabled: true,
              halfLifeDays: 30,  // 30 天后分数减半
            },
          },
        },
      },
    },
  },
}

效果:

  • 今天的日记:100% 原始分
  • 7 天前:~84%
  • 30 天前:50%
  • 90 天前:12.5%

MEMORY.mdmemory/ 下的非日期文件(如 memory/projects.md)不受时间衰减影响——它们是"常青"参考信息。

6.8.6 索引额外路径

如果你想搜索工作空间外的 Markdown 文件(比如你的个人笔记目录):

{
  agents: {
    defaults: {
      memorySearch: {
        extraPaths: ["~/notes", "/srv/shared-docs/overview.md"],
      },
    },
  },
}

6.8.7 多模态记忆(Gemini Embedding 2)

如果你用 Gemini 的 gemini-embedding-2-preview 模型,OpenClaw 可以索引图片和音频文件:

{
  agents: {
    defaults: {
      memorySearch: {
        provider: "gemini",
        model: "gemini-embedding-2-preview",
        extraPaths: ["assets/reference", "voice-notes"],
        multimodal: {
          enabled: true,
          modalities: ["image", "audio"],
          maxFileBytes: 10000000,
        },
      },
    },
  },
}

支持的格式:

  • 图片:.jpg, .jpeg, .png, .webp, .gif, .heic, .heif
  • 音频:.mp3, .wav, .ogg, .opus, .m4a, .aac, .flac

搜索查询仍然是文本,但 Gemini 可以把文本查询和图片/音频的 Embedding 做比较。

6.9 沙盒:让 Agent 安全地"动手"

默认情况下,Agent 的工具执行直接运行在宿主机上——这意味着它可以读写宿主机的任何文件、执行任何命令。对于个人使用这没问题,但在生产环境或多用户场景下,你需要沙盒来限制 Agent 的活动范围。

6.9.1 三种沙盒模式

{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main",  // "off" | "non-main" | "all"
      },
    },
  },
}
模式 行为 适用场景
off 不使用沙盒,所有工具直接在宿主机运行 个人使用、可信环境
non-main 只对非主会话使用沙盒(群聊、通道消息) 大多数场景的推荐选择
all 所有会话都使用沙盒 高安全需求、多用户环境

non-main 模式下,你和 Agent 的直接对话(主会话)仍然在宿主机运行,而来自 Discord、WhatsApp 等通道的消息在沙盒中执行。这是"正常使用不受限,外部输入受控"的平衡方案。

6.9.2 沙盒作用域

{
  agents: {
    defaults: {
      sandbox: {
        scope: "session",  // "session" | "agent" | "shared"
      },
    },
  },
}
作用域 行为
session 每个会话一个独立容器(默认)
agent 每个 Agent 一个容器,同一 Agent 的所有会话共享
shared 所有沙盒会话共享一个容器

6.9.3 工作空间访问权限

{
  agents: {
    defaults: {
      sandbox: {
        workspaceAccess: "none",  // "none" | "ro" | "rw"
      },
    },
  },
}
权限 行为
none 沙盒看不到工作空间(默认)
ro 工作空间以只读方式挂载,Agent 不能修改
rw 工作空间以读写方式挂载,Agent 可以修改

6.9.4 沙盒后端

OpenClaw 支持三种沙盒后端:

后端 运行位置 适用场景
docker(默认) 本地 Docker 容器 本地开发、完全隔离
ssh 任意 SSH 可达的远程机器 卸载到远程服务器
openshell OpenShell 管理的沙盒 托管远程沙盒

Docker 沙盒配置:

{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main",
        scope: "session",
        backend: "docker",
        workspaceAccess: "none",
        docker: {
          image: "openclaw-sandbox-common:bookworm-slim",
          network: "none",  // 默认无网络
          binds: ["/home/user/source:/source:ro"],
        },
      },
    },
  },
}

构建沙盒镜像:

# 基础镜像(最小化)
scripts/sandbox-setup.sh

# 功能镜像(包含 curl, jq, node, python, git)
scripts/sandbox-common-setup.sh

6.9.5 setupCommand:容器初始化

你可以在容器创建后运行一次初始化命令(比如安装依赖):

{
  agents: {
    defaults: {
      sandbox: {
        docker: {
          setupCommand: "apt-get update && apt-get install -y python3 nodejs",
          network: "bridge",  // 需要网络才能安装包
          readOnlyRoot: false,  // 需要可写根文件系统
        },
      },
    },
  },
}

⚠️ 踩坑记录

问题:setupCommand 执行失败,报网络错误。

原因:默认 network: "none" 意味着容器没有网络访问权限。

解决:如果 setupCommand 需要下载包,临时设置 network: "bridge"。安装完成后可以改回 "none"

6.10 工作空间备份与迁移

6.10.1 Git 备份(推荐)

工作空间是 Agent 的全部"记忆",丢失了就等于"失忆"。用 Git 做版本管理是最靠谱的方案。

cd ~/.openclaw/workspace
git init
git add AGENTS.md SOUL.md TOOLS.md IDENTITY.md USER.md HEARTBEAT.md memory/
git commit -m "Add agent workspace"

# 推送到私有远程仓库
gh repo create openclaw-workspace --private --source . --remote origin --push

日常更新:

cd ~/.openclaw/workspace
git add .
git commit -m "Update memory"
git push

不要提交秘密信息。 建议的 .gitignore

.DS_Store
.env
**/*.key
**/*.pem
**/secrets*

6.10.2 迁移到新机器

  1. 在新机器上克隆仓库到 ~/.openclaw/workspace
  2. ~/.openclaw/openclaw.json 中设置工作空间路径
  3. 运行 openclaw setup --workspace <path> 补充缺失文件
  4. 如需迁移会话,单独复制 ~/.openclaw/agents/<agentId>/sessions/

本章小结

  • 工作空间是 Agent 的"家"——存放人格、记忆、行为准则和身份
  • SOUL.md 定义"我是谁",USER.md 定义"我在帮谁",AGENTS.md 定义"我该怎么做"
  • 记忆系统分两层:MEMORY.md(长期记忆)和每日日记(短期记忆)
  • 语义搜索支持混合检索(BM25 + 向量)、MMR 去重和时间衰减
  • 沙盒通过 Docker/SSH/OpenShell 后端隔离 Agent 的工具执行
  • 用 Git 备份工作空间,确保 Agent 的"记忆"不会丢失

下一步

Agent 有了灵魂(工作空间)和大脑(模型),但每次对话结束后它就"忘了"。下一章讲会话管理——OpenClaw 如何管理对话上下文、如何在上下文快满时自动压缩、以及如何让 Agent 在长对话中保持连贯性。