老周的工具笔记

老周的工具笔记

已发布 24 篇文章

技能加载顺序与同名冲突:为什么你明明装了,agent 却当它不存在

技能加载顺序与同名冲突:为什么你明明装了,agent 却当它不存在
技能加载顺序与同名冲突:为什么你明明装了,agent 却当它不存在

一、先说个场景

你在 ~/.openclaw/skills/ 全局目录里装了一个 weather,又在某个 workspace 的 skills/ 目录里自己写了一个也叫 weather 的 skill。结果聊天里让 OpenClaw 查天气,它一直用 workspace 那个半成品,而不是你精心装的官方版。

这就是 OpenClaw 的技能加载顺序在起作用。同名冲突时,高优先级的来源会遮住低优先级的来源。不理解这个优先级,你会反复遇到”明明装了,agent 却不用”的诡异问题。

二、为什么是这样

OpenClaw 启动时会扫描多个 skill root,每个 root 下出现 SKILL.md 的地方就会被发现。不同 root 对应不同生命周期和可见范围:

  • workspace 目录只影响当前 agent;
  • 全局目录影响本机所有 agent;
  • project-agent 目录影响某个 workspace 的 agent;
  • 共享的 managed 目录影响所有使用该状态目录的 agent;
  • Workshop 目录是提案队列,只对某个 agent 可见。

当同一个 name 出现在多个 root 时,OpenClaw 不能同时用两个。它按优先级取最高的那个。这个规则让 workspace 可以覆盖全局,让个人设置可以覆盖共享设置,也让 Workshop 里的提案不会污染正式发布。

为什么是”静默覆盖”而不是弹窗报错?因为如果每次冲突都报错,你在本地实验 skill 时会被满屏警告淹没;而 OpenClaw 假定”更靠近当前工作环境”的来源就是用户此刻想要的那份。代价是排查更难——你没法靠报错来发现冲突,只能靠主动检查。这也就是为什么这篇文章反复强调要用 openclaw skills list 去确认,而不是凭感觉判断。

另外要理解”快照”的概念。OpenClaw 在会话启动或 Gateway 启动时,会把当时发现的技能拍成一份快照(snapshot)。之后你增删磁盘上的 skill,并不会立刻反映到已经运行的会话里,除非触发刷新。这解释了为什么有时候”明明文件都删了,agent 还在用旧 skill”——它读的是快照,不是实时磁盘。刷新方式无非两种:开新会话,或者 openclaw gateway restart。

三、跟着做:分步操作

1. 记住优先级表(高 → 低)

优先级 来源 路径
1 Workspace skills <workspace>/skills
2 Project agent skills <workspace>/.agents/skills
3 Personal agent skills ~/.agents/skills
4 Managed / local skills <state-dir>/skills
5 Workshop skills <state-dir>/agents/<agentId>/agent/workshop-skills
6 Bundled skills 随安装包发布
6 Custodian skills 仅配置了 Custodian agent 时
7 Extra dirs + plugin skills skills.load.extraDirs + 插件技能

2. 理解命名规则

技能名来自 SKILL.md frontmatter 里的 name。如果缺失,才回退到目录名或仓库名。同名冲突只看最终 name,不看目录路径。

一个小提醒:决定胜负的是最终解析出来的 name 字段,而这个字段是大小写敏感的。如果你在 A 处写 Weather、在 B 处写 weather,它们会被当成两个不同的技能,不会互相覆盖——这往往又制造了新的困惑:你以为在覆盖,其实是两个技能并存。所以团队内部最好约定统一的命名风格,避免这种”假冲突”或”假并存”。

3. 检查当前哪个来源获胜

openclaw skills list

列表里只显示当前会话加载的技能。如果某个来源的技能没出现:

  • 要么被更高优先级同名 skill 遮住;
  • 要么被 agents.entries.*.skills 白名单过滤;
  • 要么需要刷新会话或重启 Gateway。

如果 openclaw skills list 里没有出现你期望的技能,先别急着改代码。按这个顺序排查:第一,确认它是否被更高优先级的同名技能遮住了;第二,确认它是否被某个 agent 的 skills 白名单过滤掉了;第三,确认你是否改完磁盘后忘了刷新会话。这三步能解决绝大多数”看不到技能”的问题,而且都不需要打开 SKILL.md。

4. 分组布局不影响名字

mkdir -p ~/.openclaw/workspace/skills/productivity/reminders

只要 ~/.openclaw/workspace/skills/productivity/reminders/SKILL.md 里的 name: reminders 存在,调用名就是 /reminders,不是 /productivity/reminders。

分组目录(如 productivity/、dev/ 这样的中间层)纯粹是为了人类整理方便,OpenClaw 在解析时只认最终的 SKILL.md 和它的 name,中间路径全部忽略。所以你大可以把几十个技能按主题分门别类地放,调用时依然是扁平的 /名字,不会变长,也不会因为嵌套深了就改变优先级。

四、可以复制的指令

# 查看当前加载的技能
openclaw skills list

# 查看 workspace 技能目录
ls ~/.openclaw/workspace/skills/

# 查看全局技能目录
ls ~/.openclaw/skills/

# 查看 project agent 技能目录
ls <workspace>/.agents/skills/

# 重启 Gateway 刷新
openclaw gateway restart

# 显式调用某个 skill,绕过自动选择
/skill weather

这几条命令各有分工:openclaw skills list 看的是”当前会话实际加载了什么”,是排查的第一抓手;两个 ls 看的是”磁盘上到底有什么”,用来确认文件是否真的在对应目录;openclaw gateway restart 用来让修改生效、重建快照;/skill weather 则是绕过自动选择、强行指定调用某个技能。养成”先 list、再 ls、最后 restart”的顺序,能少走很多弯路,也能最快定位是”没装”还是”装了但被遮住”。

五、实战案例

你在全局装了官方的 weather,又在 workspace 写了一个实验性的 weather:

~/.openclaw/skills/weather/SKILL.md          # 官方版
~/.openclaw/workspace/skills/weather/SKILL.md # 你的实验版

当你当前 workspace 的 agent 启动时,优先级 1 的 workspace 版会遮住优先级 4 的全局版。

排查步骤:

  1. openclaw skills list 确认列表里是哪个 weather。
  2. 如果 workspace 版不要了,删除 ~/.openclaw/workspace/skills/weather/。
  3. 重新 /new 或 openclaw gateway restart。
  4. 再次 openclaw skills list,确认官方版出现。

补充一点排查心法:当你用 openclaw skills list 看到的是 workspace 版而不是官方版时,不要急着删文件。先想清楚你到底想要哪个版本——也许你的实验版本就该优先,问题只是你忘了自己写过它。确认不要之后,删除或改名实验版,再重启刷新,最后再次 list 验证。整个过程其实就是在”确认胜出者 → 移除干扰项 → 重建快照 → 复核”的循环里走一遍。如果重启后还是不对,八成是目录放错了位置,回去用 ls 核对路径最稳妥,比反复改 SKILL.md 高效得多。

六、常见误区

  • 误区 1:同名冲突会报错。不会。OpenClaw 静默取高优先级的那个,不报任何警告。
  • 误区 2:目录层级会出现在 skill 名里。不会。技能名只由 SKILL.md 的 name 决定。
  • 误区 3:删掉低优先级的就会加载高优先级的。还要刷新会话,否则快照还是旧的。
  • 误区 4:openclaw skills list 展示所有磁盘 skill。它展示的是当前会话可见的技能,已被遮住的不会显示。
  • 误区 5:Bundled 技能优先级高于 managed。实际上 bundled 和 custodian 同处于第 6 层,extra dirs 和 plugin 在第 7 层。

七、进阶玩法

用白名单显式隔离:在 openclaw.json 里给某个 agent 指定最终技能集。

{
  agents: {
    defaults: { skills: ["github", "weather"] },
    entries: {
      writer: { default: true },
      docs: { skills: ["docs-search"] },
    },
  },
}

这里 docs agent 只看 docs-search,不会看到 workspace 或全局里的 weather。注意 entries.*.skills 是替换,不是合并。

关于白名单,最容易踩的坑就是忘记它是”替换”语义。很多人以为在 docs agent 里写了 skills: ["docs-search"],它会”额外”获得这个技能、同时保留全局默认。事实恰恰相反——一旦给某个 agent 指定了 skills,它就只看列表里的那几个,默认集被整体替换掉。所以如果你希望 docs 既能用 docs-search 又能用默认的 github,必须两个都写进去,写成 ["docs-search", "github"]。

用 extraDirs 做公共库:

{
  skills: {
    load: {
      extraDirs: ["~/company-skills"],
    },
  },
}

这是最低优先级的来源,适合放一些全公司共享但不会覆盖 workspace 的 baseline skill。

至于 extraDirs,它名字里带个”extra”,定位就是”锦上添花的最低优先级来源”。把它当成公司的 baseline 公共库非常合适:里面放一些人人都该有、但又绝不该覆盖任何人本地自定义的通用技能。只要记住它永远排在最后,你就不会被”我放了怎么没生效”困惑——因为它很可能被更高优先级的同名技能悄悄遮住了,这也是为什么公共库里的技能命名要格外谨慎,避免和别人本地的技能撞名。

八、小结

加载顺序和同名冲突是 OpenClaw 技能系统最容易踩的坑之一。记住这张优先级表,就能解释 90% 的”为什么没生效”。核心要点:workspace 最高,全局次之,Workshop 和 bundled 靠后;同名时高优先级静默覆盖;openclaw skills list 只显示当前会话可见的技能。遇到奇怪行为时,先 list,再 ls 各来源目录,最后 gateway restart。别一上来就怀疑 skill 写错了。