用 OpenClaw 有一段时间了,体验确实不错。但用久了之后发现一个痛点:它每次对话都是从零开始。
什么意思呢?就是我这次花半小时解决了一个很复杂的问题,下次遇到类似情况,OpenClaw 依然要从头摸索。它不会"记住"这个经验。
这和 Hermes Agent 不太一样——后者据说能从经验中自动创建技能(skills)。我当时就想,能不能让 OpenClaw 也具备类似的能力?
今天就把整个研究和实现过程分享出来,不是什么高大上的黑科技,就是一个接地气的工程方案。
1. 需求分析:我们要解决什么问题?
首先得想清楚,我们想让 AI "记住"哪些东西?
复杂任务的解决路径:比如我要一次性完成"创建飞书多维表格 + 批量写入数据 + 设置视图"这一套操作,下次再用到时不用重新摸索。
错误恢复的经验:有些坑踩过一次就记住了,比如某个 API 要先创建字段才能写入数据,这个"先决条件"值得沉淀下来。
用户的纠正反馈:有时候用户会说"不是这样,应该是…",这种纠正本身就是宝贵的学习素材。
重复操作的模式:如果同一个操作出现 3 次以上,说明这可能是个高频操作,值得固化成技能。
2. 技术方案:两条路的权衡
一开始我考虑了两种实现路径。
方案一:定时轮询(Cron)
用 cron 定期扫描日志,提取模式。优点是简单,缺点是实时性差,而且 Cron 无法感知"当前正在发生"的事件。
方案二:持续运行的 Daemon
后台进程实时监听 session 事件。优点是实时,缺点是进程管理麻烦。
我最终选了两者结合——用 Daemon 做后台服务,但不需要它时时刻刻盯着,而是每 30 秒轮询一次。这样既保证了实时性(最多 30 秒延迟),又不会浪费资源。
3. 核心设计:三个关键洞察
洞察一:OpenClaw 是 Stateless 的
这是理解整个方案的前提。每次 session 结束后,OpenClaw 不会保留任何状态。但是——session 日志是文件。
OpenClaw 把每次会话的记录写到 JSONL 文件里,而这个文件是 append-only 的,意味着即使 session 还在进行中,我们也可以读取它。
所以自我改进的载体就变成了:
SKILL.md文件 → 可复用的技能 memory日志 → 历史上下文 auto-generated目录 → 自动沉淀的经验
洞察二:触发条件要精准
不是所有 session 都值得记住。我定义了四类触发条件:
洞察三:被动学习 + 主动获取
这是我觉得最有意思的洞见。
被动学习:Daemon 负责"经验沉淀",从实际操作中学习。这是慢功夫,但积累的都是实战经验。
主动获取:ClawHub/agentskills.io 负责"技能补给",需要什么马上有。
两者互补,形成完整的能力增长闭环。就像人一样,既要靠经历积累经验,也要主动学习新知识。
4. 系统架构
┌─────────────────────────────────────────────────────┐
│ OpenClaw 主 Session │
│ (用户交互入口) │
└─────────────────────┬───────────────────────────────┘
│ 工具调用写入 session JSONL
▼
┌─────────────────────────────────────────────────────┐
│ learning-daemon (Node.js 后台进程) │
│ • 每 30 秒轮询最新 session JSONL │
│ • 检测触发条件 │
│ • 生成标准 SKILL.md │
└─────────────────────┬───────────────────────────────┘
│ 写入
▼
┌─────────────────────────────────────────────────────┐
│ ~/.openclaw/workspace/skills/auto-generated/ │
│ (自动生成的 skills 存放目录) │
└─────────────────────────────────────────────────────┘
整个流程跑起来就是这样:用户和 OpenClaw 交互 → 交互记录写入 JSONL → Daemon 每 30 秒扫一次 → 发现值得记住的瞬间 → 生成 SKILL.md → 下次遇到类似情况直接调用。
5. 踩坑记录
实现过程中踩了几个坑,分享出来让大家少走弯路。
坑一:exec 审批弹窗
OpenClaw 默认对危险操作有审批机制,exec 命令会弹窗。这对于需要后台长期运行的 daemon 来说是个麻烦。
解决方案:修改 exec-approvals.json 配置文件,放行需要的命令路径。注意不要过度开放,只放行必要的最小权限。
坑二:nohup 启动
Daemon 要在后台长期运行,直接 node script.js 会在终端关闭后被杀掉。正确姿势是:
nohup /opt/homebrew/bin/node ~/.openclaw/scripts/learning-daemon.js > /tmp/openclaw-learning-daemon.log 2>&1&
这样即使终端关闭,进程也会继续运行。
坑三:launchd 开机启动
如果想让 daemon 开机自启,可以用 launchd 管理。但我建议暂时不用,因为 OpenClaw 本身启动时有一些初始化工作,Daemon 和它同时启动可能会有竞争。先手动启动,等稳定了再加自启。
6. 核心代码
Daemon 主循环
asyncfunctionmain(){
// 初始化:记录所有现有文件位置
const files = fs.readdirSync(SESSIONS_DIR)
.filter(f=> f.endsWith('.jsonl'));
for(const file of files){
filePositions.set(file, fs.statSync(file).size);
}
while(true){
const activeSession =getActiveSession();
if(activeSession){
awaitanalyzeSession(activeSession);
}
awaitnewPromise(r=>setTimeout(r,POLL_INTERVAL));
}
}
检测逻辑
functiondetectComplexTask(messages){
let toolCalls =0;
for(const msg of messages.slice(-RECENT_WINDOW)){
if(msg.role ==='assistant'&& Array.isArray(msg.content)){
for(const block of msg.content){
if(block.type ==='toolCall') toolCalls++;
}
}
}
return toolCalls >=TOOL_CALL_THRESHOLD;
}
SKILL.md 生成
生成的 SKILL.md 符合 OpenClaw 的标准格式:
---
name: "auto-[任务类型]-[日期]"
description: "自动生成的 skill"
version: 1.0.0
category: auto-generated
trigger: "触发条件"
---
# 自动生成 Skill
## 何时使用
## 解决什么问题
## 关键工具链
## 注意事项
7. 部署步骤
创建 daemon 脚本
~/.openclaw/scripts/learning-daemon.js启动 daemon
nohup /opt/homebrew/bin/node ~/.openclaw/scripts/learning-daemon.js > /tmp/openclaw-learning-daemon.log 2>&1&验证运行
ps aux |grep learning-daemon
tail /tmp/openclaw-learning-daemon.log
8. 效果对比
说实话,OpenClaw 的实现比 Hermes 原生方案多了 30 秒延迟,但胜在不改动核心架构。这很重要——保持架构稳定,能力通过外层扩展,这符合良好的工程实践。
9. 心得
回顾整个实现过程,我最大的感悟是:AI 的"自我改进"不一定是多么高大上的概念。
核心就是四步:
- 感知:检测值得记住的瞬间
- 抽象:把具体经验提取成通用模式
- 固化:写成可复用的载体(skill)
- 应用:下次遇到类似情况直接调用
就这么简单。在 OpenClaw 的架构下,这套机制完全可以通过一个后台 daemon + 文件系统实现,不需要改造核心架构。
有时候最好的解决方案不是最"聪明"的方案,而是最契合现有架构的方案。
如需转载,请注明出处
夜雨聆风