我花了一天修好自己写的 AI 工具——才发现问题从一开始就设计错了

ai-agentsopenclawskillsmcpdeveloper-tools

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 →