Agent 如何给自己写新 Skill——从 OpenClaw 到 Pitfall Registry
March 10, 2026 · Tech Blog
大多数 agent 系统的工具集是静态的——启动时确定,运行时不变。但真正实用的 agent 需要在运行中积累经验、扩展能力。这篇文章对比两种不同架构下的"agent 自我扩展"实现:OpenClaw 的 file watcher 机制,以及我在 Game V0 项目里基于 MCP 实现的 Pitfall Registry。
不需要重新编译 - 打包。
Session Agent & Persistent Agent
一、OpenClaw:文件系统即扩展接口
Skill 是什么
OpenClaw 的 skill 本质上是一个 markdown 文件(SKILL.md),放在特定目录下,内容会在每次构建 system prompt 时被注入进去:
function buildSkillsSection(params: { skillsPrompt?: string; readToolName: string }) {
return [
"## Skills (mandatory)",
"Before replying: scan <available_skills> <description> entries.",
"- If exactly one skill clearly applies: read its SKILL.md with `read`, then follow it.",
trimmed, // ← 所有 skill 的描述列表注入到这里
];
}
所以"新增一个 skill"在文件系统层面就是"写一个新文件"。agent 本身有 Write 工具,可以创建文件。那么只要 agent 把新 skill 写进 ~/.openclaw/skills/my-new-tool/SKILL.md,理论上下次构建 system prompt 时就能感知到它。
问题变成:系统怎么知道文件变了?
chokidar + 事件驱动
暴力解法是轮询——每隔 1 秒扫一遍目录。但这浪费 CPU,延迟高。
更好的解法是让操作系统来告诉你。现代操作系统内核都提供了文件系统事件 API:
macOS → FSEvents
Linux → inotify
Windows → ReadDirectoryChangesW
这些 API 的工作方式是推而不是拉——文件变化时,内核主动发一个事件给监听进程,进程不需要反复去问"变了吗?"。
chokidar 是 Node.js 生态对这些原生 API 的统一封装。OpenClaw 用它监听所有 skill 目录:
watcher.on("add", (p) => schedule(p)); // 新文件
watcher.on("change", (p) => schedule(p)); // 修改
watcher.on("unlink", (p) => schedule(p)); // 删除
完整的自我扩展链路
agent 调用 Write 工具
→ 创建 ~/.openclaw/skills/new-tool/SKILL.md
→ chokidar 检测到文件变更
→ 触发 bumpSkillsSnapshotVersion()(版本号递增)
→ 通知所有注册的 listener
→ 下次构建 system prompt 时重新加载所有 skill
→ 新 skill 的 markdown 内容注入 system prompt
→ agent 下次回复前扫描到新 skill,开始使用它
整个循环不需要重启进程,不需要人工干预。
为什么这是 Node.js 的典型模式
Node.js 的 event loop 不断检查"有没有事件发生"——文件变化、网络请求到来、定时器触发,都是事件。事件到来时对应的 callback 被放进队列执行;没有事件时进程安静等待,不占 CPU。
chokidar 的 .on("change", callback) 和 WebSocket 的 .on("message", callback) 在抽象层面是同一件事:注册 callback,等事件触发。监听文件系统和监听网络连接,在 Node.js 里没有本质区别。
多来源 Skill 的优先级合并
OpenClaw 支持多个来源的 skill(系统内置、社区管理、用户个人、项目级、工作区级),用一个简单但清晰的 Map 覆盖机制处理优先级冲突:
const merged = new Map<string, Skill>();
// 优先级:extra < bundled < managed < personal < project < workspace
for (const skill of extraSkills) merged.set(skill.name, skill);
for (const skill of bundledSkills) merged.set(skill.name, skill);
for (const skill of managedSkills) merged.set(skill.name, skill);
for (const skill of personalSkills) merged.set(skill.name, skill);
for (const skill of projectSkills) merged.set(skill.name, skill);
for (const skill of workspaceSkills) merged.set(skill.name, skill);
Map 的 set 在 key 相同时直接覆盖——后写入的优先级更高。越具体的配置优先级越高,和 CSS specificity、webpack loader 优先级是同一个设计哲学。
二、Pitfall Registry:Session Agent 的记忆方案
为什么 OpenClaw 的方案在 OpenCode 里不适用
OpenClaw 能做到运行时热重载,根本原因是它是一个持久运行的进程——agent 写文件和 chokidar 检测到变化,发生在同一个进程生命周期里。
OpenCode 是 session 级别的 agent。每次对话是一个独立 session,system prompt 在 session 开始时构建,中途不变。agent 这次用 Write 工具写了一个新文件,这次 session 感知不到——要等下次启动新 session 才能读到。
这是两种架构的根本差异:
OpenClaw 持久进程 → 文件变化 → 立即感知 → 当次 session 生效
OpenCode session → 文件变化 → 下次启动才感知
Pitfall Registry 的设计目标
在 Game V0 项目里,OpenCode 驱动 Claude 生成 Babylon.js 游戏代码。agent 在执行任务时会反复踩同样的坑——比如某个 API 的错误用法、某类场景下会 crash 的操作。
这些失败 pattern 如果只存在于当次 session 的上下文里,下次启动新 session 就全部丢失。Pitfall Registry 的目标是把这些跨 session 的负向经验持久化,让后续 session 能直接从中受益。
实现:MCP Tool 作为记忆读写接口
OpenCode 支持 MCP(Model Context Protocol)——agent 可以通过 tool call 和外部服务通信。Pitfall Registry 就是一个 MCP server,暴露两个工具:
// 写入:agent 执行失败时记录 pitfall
tool: "record_pitfall"
input: {
context: string, // 触发场景:e.g. "Babylon.js mesh disposal"
pattern: string, // 失败 pattern:e.g. "直接调用 mesh.dispose() 不移除子节点"
fix: string // 正确做法
}
// 读取:session 启动时主动拉取相关 pitfall
tool: "query_pitfalls"
input: {
context: string // 当前任务上下文,语义检索相关 pitfall
}
完整的跨 session 记忆链路
Session N:agent 执行任务,遇到错误
→ 调用 record_pitfall 写入 registry(持久化到文件/DB)
Session N+1:新 session 启动
→ system prompt 中注入 MCP tool 描述
→ agent 开始任务前,主动调用 query_pitfalls
→ registry 返回相关历史 pitfall
→ agent 将 pitfall 内容纳入上下文,规避已知错误
关键在于:MCP server 是持久运行的,跨 session 存活。session 本身是无状态的,但通过 MCP 这个外部接口,agent 获得了跨 session 的记忆能力。
和 OpenClaw 机制的对比
OpenClaw Skill Pitfall Registry (MCP)
─────────────────────────────────────────────────────────────────
扩展内容 工具/能力(正向) 失败经验(负向)
触发方式 文件系统事件(推) tool call(拉)
生效时机 当次 session 实时生效 下次 session 读取生效
持久化方式 文件系统 MCP server(文件/DB)
agent 感知方式 system prompt 注入 主动 query
适用架构 persistent agent session agent
两者都是"agent 运行时自我扩展"的方案,只是方向不同——一个扩展正向能力,一个积累负向经验;一个靠文件系统事件驱动,一个靠显式 tool call 读写。
三、更深一层:两种记忆观
OpenClaw 的 skill 系统本质上是在扩展 agent 的能力边界——它能做什么。
Pitfall Registry 扩展的是 agent 的经验边界——它知道什么不该做。
前者是"学会新技能",后者是"记住踩过的坑"。一个成熟的 agent 系统需要两者兼备:既能动态获取新工具,又能跨任务积累负向经验,形成闭环。
这也是为什么 session 级 agent 在复杂长期任务上始终有天花板——每次从零开始,前人踩过的坑后人还要再踩。打破这个限制,需要在 session 外部维护一个持久化的经验层,而 MCP 提供了一个干净的接口来做这件事。