我花了一天修好自己写的 AI 工具——才发现问题从一开始就设计错了
TL;DR
我发现自己的 AI skills 互相冲突,三个工具抢同一个触发词。追下去才发现更深的问题:有的 skill 依赖本地文件路径,别人装了根本跑不起来。用一天时间重建了整个 skills 架构——核心原则只有一条:Skill 是模式,不是快照。
今天下午两点,我盯着一条错误信息发呆。
firecrawl 这个 skill 的描述写着:"替代一切联网工具,网页抓取、内容提取、全部交给我。"而同一份配置里,research、research-hub、research-pro——三个工具也都在说同样的话。每次我让 agent 去查一个网址,它都得在这四个工具里硬猜一个。有时候它猜对了,有时候它同时调了两个。结果当然是一团乱。
这不是 bug。这是设计问题。
背景说明: 我刚开始用 OpenClaw 的时候,习惯是需要什么功能就随手写一个 skill。每个 skill 单独看都能跑,往 config 里一扔就完事。这套方法在工具少的时候没问题——但当 skill 数量超过十几个,它们开始互相干涉,有些甚至在我不知道的情况下悄悄坏掉了。这篇文章就是记录我意识到这个问题、并花一天时间修好它的过程。
背景:我以为自己在「写工具」,其实在「存快照」
过程中积累了十几个 skill——每次需要某个功能,就随手写一个。写完能跑,就往 config 里一扔。时间长了,这堆工具开始互相打架。
触发词冲突只是表面现象。我今天真正发现的是一个更底层的问题:
我的很多 skill,根本不能被别人用。
以 soul-keeper 为例——这是一个帮助 agent 维护自身记忆的工具。我写它的时候,直接引用了我 workspace 里的一个文档路径 docs/WORKSPACE_FILES_GUIDE.md。在我的机器上完美运行。但如果你把这个 skill 装到你的 OpenClaw 里,它会立刻失败,因为那个文件在你的 workspace 里根本不存在。
这个 skill 不是一个工具。它是我当时环境的一张截图——一个快照。
快照会过期。快照不能分享。快照会在你迁移机器、换账户、换路径的时候悄悄坏掉,而且你通常不会马上发现。
修法一:入口只有一个
回到 firecrawl 和 research 的冲突问题。
解法看起来很简单,但背后是一个设计决策:用户触发的入口只有一个,其他工具内部调用。
我把 research-pro 设定为唯一的联网入口。它的职责是理解用户意图,然后决定:这个任务需要 firecrawl 抓完整页面,还是只需要一个快速搜索?它来判断,用户不用管。
research 和 research-hub 的描述改窄了——从"我能做所有事"变成"我处理 X 类型的请求"。firecrawl 的描述改成了技术层面的能力说明,不再宣称自己是入口。
这个改动花了大约四十分钟。但它要求我想清楚一件事:哪些工具是给用户看的,哪些工具是给 agent 看的? 这两类工具的描述逻辑完全不同。给用户看的工具需要清晰的触发场景;给 agent 看的工具需要精确的能力边界。混在一起写,就会出现我之前的乱象。
修法二:知识必须随 skill 走
soul-keeper 的问题怎么解?
我把它依赖的知识内容直接嵌入 skill 定义里。不再引用外部文件——skill 自身携带它需要的上下文结构,在安装时动态生成所需文件,而不是假设文件已经存在。
这背后是一个原则:一个 skill 能独立运行,意味着它的所有依赖要么内置,要么通过标准接口获取。 用户的本地路径不是标准接口。用户的特定文件名不是标准接口。
我给自己设了一个测试标准,叫 Generic Test:
假设一个全新安装了 OpenClaw 的陌生人,把这个 skill 加进去。不改一行代码,不问我任何问题。它能正常工作吗?
soul-keeper 原版通不过这个测试。修改后的版本通过了。
修法三:维护不是事后的事
技术债有个特点:它会悄悄累积,直到某天突然爆。
我今天踩的坑,其实几周前就埋下了——那时候我往 config 里扔了一堆 skill,每个单独看都没问题,但没人检查它们放在一起会不会打架。
所以今天的最后一件事,是建了一个叫 skills-watchdog 的工具。它的职责很简单:定期扫描所有已安装的 skill,对比本地版本和上游来源(GitHub、clawhub),有新版本就通知我。
发现问题的时候,它会报告,不会沉默。
这是系统设计层面的改变——把维护从"记得的时候才做"变成"设计里就有"。
设计手册:HOW-TO-WRITE-SKILLS.md
做完这些之后,我写了一份 HOW-TO-WRITE-SKILLS.md,记录整套设计原则。基于 Google ADK 的五种 agent 模式,结合我自己的踩坑经验,总结出了写一个可分享 skill 需要遵守的规范。
其中我觉得最重要的一条是这个:
版本号、路径名、用户名——凡是你写死的具体值,都是定时炸弹。 写 skill 时,想的应该是"这个 skill 在任何人的机器上都能描述清楚自己能做什么",而不是"这个 skill 在我这里能跑"。
今天的四条结论
1. Skill 是模式,不是快照。 一旦你写了具体的名字、版本号、路径,skill 就从"工具"变成了"截图"。截图会过期。
2. 入口只有一个,工具按需加载。 不是每个工具都需要被用户直接触发。有些工具是给 agent 用的,它们的描述应该面向 agent 写,不是面向用户写。
3. 维护是设计的一部分,不是事后补救。 skills-watchdog 存在的意义,是把"我记得去检查一下"变成"系统会提醒我"。这是两种完全不同的可靠程度。
4. Generic Test 是发布前的最后一关。 一个全新安装的人,能不能不问任何问题就用这个 skill?能,才算完成。不能,就还没写完。
FAQ
Q:只有 OpenClaw 才有这个问题吗?
不。任何支持 skill/tool/plugin 的 agent 框架都会遇到一样的问题——LangChain、Cursor、Claude Code 都有类似机制。只要你在给 AI 写"工具描述",触发词冲突和路径依赖就是普遍问题。
Q:skill 和 MCP tool 有什么区别?
MCP tool 是更底层的协议层接口,定义了 AI 调用外部工具的通信规范。skill 是建在 MCP 之上的应用层——它包含触发条件、执行逻辑、上下文知识,比单纯的 tool 定义更丰富。本文说的设计问题主要发生在 skill 层。
Q:Generic Test 适用于所有 skill 吗?
适用于打算分享或复用的 skill。如果你写的 skill 只在自己机器上用,硬编码路径没什么问题。但如果你想分享给团队或发布出去,Generic Test 是发布前的最低要求。
Q:skills-watchdog 在哪里?
在这里:github.com/mfang0126/openclaw-skills/tree/main/skills-watchdog,装完直接跑,第一次运行会自动建 baseline。
所有整理好的 skill 现在在这里:github.com/mfang0126/openclaw-skills
如果你也在用 OpenClaw 或者类似的 agent 框架,欢迎试一下 Generic Test——把你自己写的工具拿出来,假装自己是第一次装的人。很可能你也会发现几个悄悄坏掉的快照。
有什么踩坑经历?在 GitHub Issues 或者 X 上找我:@mfang0126
Want to work together?
I'm always happy to connect — whether it's a project, a question, or just to say hi.
Get in Touch →