
前段时间 Nacos 3.2 发布了 Skill Registry 能力,官方说这是"企业级 AI 资源治理平台"。我当时心想:不就是个技能管理吗,能有多花哨?直到我真正在项目里落地了一套完整的 Skill 上架、下架、本地加载方案,才发现——真香。
消失了许久这段时间我们最新的AI Agent项目即将完成,并且开源出来。期待许久的TAICHU.ai即将与大家见面。今天我们先来聊聊,我们TAICHU.ai中关于Skill的管理。如何通过Nacos让Skill的管理变得如此优雅!




2025 年底,Anthropic 推出 Claude Skills,Agent Skills 作为开放标准发布,OpenAI、Microsoft、Cursor 等主流工具纷纷跟进。Skill(技能)成了 AI Agent 生态里最火的概念之一。
简单说,Skill 就是给 AI Agent 装的"插件"。比如一个"前端设计"技能,能让 Agent 自动生成符合设计规范的 UI 代码;一个"PPT 生成"技能,能让 Agent 一键输出精美的演示文稿。
但问题来了——
当你的团队有 50 个 Skill,分布在 10 个 Agent 身上,谁来管版本?谁来审核质量?谁决定哪个技能可以上架给所有人用?哪个技能有安全漏洞需要紧急下架?
根据 Snyk 对 ClawHub 中 3984 个 Skill 的采样报告,**存在安全缺陷的 Skill 占 36.82%,CRITICAL 级别问题 13.4%**。这数据看着就让人后背发凉。
所以 Nacos 3.2 推出了 Skill Registry(技能注册中心),核心定位就是解决"Skill 分散、难管理、无法复用"的痛点。它在 Agent 与 Skill 之间构建了一层"验证后信任"的治理体系,把 Skill 的审核、管理、分发与追溯统一纳管。
我们的项目 TaiChu AI 是一个 AI Agent 框架平台,技术栈是 Java 21 + Spring Boot 3.5.7 + Nacos 3.2.0。核心诉求很明确:
把 Skill 当作"App Store 里的应用"来管理。

听起来是不是很像手机上的应用商店?没错,我们就是照着这个思路设计的。
先说设计模式的选择。我们的 Skill 注册中心不是只对接 Nacos,未来可能还要支持 MySQL 存储、本地文件系统等。所以必须做到底层存储可替换,上层业务无感知。
最终选用了 策略模式 + 模板方法 的经典组合:
SkillRegistryService ← 顶层接口("我不管你怎么存,你给我实现这些方法")
└── AbstractSkillRegistry ← 抽象基类("校验、日志、默认值我来,具体存储你来做")
├── NacosSkillRegistry ← Nacos 实现(生产环境主力)
├── LocalSkillRegistry ← 本地实现(开发调试用)
└── MysqlSkillRegistry ← MySQL 实现(预留扩展)
这个设计的好处是什么?一行配置切换注册中心:
taichu-ai:
skill:
type: nacos # 改成 local 或 mysql,整个系统无缝切换
抽象基类 AbstractSkillRegistry 用模板方法模式统一处理了所有实现都需要的横切逻辑:参数校验、命名空间默认值兜底(默认 public)、统一日志。子类只需要实现 doXxx 方法,专注跟底层存储打交道。
这里有个小细节值得说:模板方法用了 final 修饰,防止子类"手滑"把校验逻辑给覆盖了。这种防御性编程在团队协作中特别有用——你永远不知道队友会写出什么神仙代码。
Nacos 3.2 提供了两套 SDK 来操作 Skill:
我们的 NacosSkillRegistry 就是封装了这两套 SDK。比如上架(发布)操作,本质上就是调 Nacos 的一行 API:
// 发布上架:把 Skill 从审核状态变为已发布
maintainerService.skill().publish(namespaceId, skillName, version, true);
下架也类似,调 changeOnlineStatus 接口,online=false 就完事了:
// 下架:online=true 上线,online=false 下线
maintainerService.skill().changeOnlineStatus(namespaceId, skillName, scope, version, online);
而本地加载 Skill 的过程稍微复杂一点,但也很好理解——从 Nacos 拉取 Skill 的完整内容(包含一个 SKILL.md 定义文件和若干资源文件),然后写入本地目录:
// 1. 从 Nacos 获取 Skill 完整详情
Skill skill = maintainerService.skill().getSkillVersionDetail(namespaceId, name, version);
// 2. 创建本地目录,写入 SKILL.md 和资源文件
Path cachePath = Path.of(skillsDir).resolve(skill.getName());
Files.writeString(cachePath.resolve("SKILL.md"), skill.getSkillMd());
// 资源文件按 type/name 分目录存放...
加载完成后,本地目录长这样:
extensions/skills/
├── frontend-design/
│ ├── SKILL.md ← Skill 的"说明书",Agent 靠它知道怎么用
│ └── references/ ← 附带的参考资料
│ └── design-spec.md
├── ppt-generator/
│ ├── SKILL.md
│ ├── assets/
│ │ └── template.html
│ └── references/
└── web-search/
├── SKILL.md
└── scripts/
└── search.py
一个 Skill 从诞生到退役,会经历完整的状态流转。我们设计了两个维度的状态:
生命周期状态(Skill 在注册中心的状态):
草稿 → 审核中 → 审核通过 → 已发布 ⇄ 已下线
↓ ↑
审核拒绝 ── 强制发布 ────┘
↓
已删除
本地安装状态(Skill 在当前节点的状态):
未安装 → 已安装 → 已禁用
↓ ↓
已卸载 安装失败

这两个状态是独立管理的。一个 Skill 在 Nacos 里是"已发布"状态,不代表每个节点都安装了它。就像 App Store 里的 App 上架了,但你手机上可以选择装或不装。
这里有个经典问题:Skill 的元数据存在 Nacos 里,但我们的业务还需要在数据库里记录状态(谁安装了、什么时候安装的、当前版本是什么)。两个地方都存,怎么保证一致性?
我们的方案是:编排层统一管控,先 Nacos 后 DB。
SkillPackageAppService 作为编排核心,所有写操作都走它:
上架操作:
1. 先调 Nacos SDK 执行发布 → 成功
2. 再更新数据库状态为 PUBLISHED → 完成
如果第 1 步失败?直接返回错误,DB 不动。
如果第 2 步失败?Nacos 已经发布了,但 DB 状态没更新——
这种情况通过启动时的全量同步和定时轮询来兜底修复。
这种"主从写"的策略在分布式系统中很常见。核心思想就是:Nacos 是 Source of Truth(唯一真相源),DB 是 View(视图层)。Nacos 说了算,DB 跟着走。

前端展示 Skill 列表时,需要展示的信息来自三个不同的地方:
信息 | 来源 |
|---|---|
Skill 名称、描述、远程版本 | Nacos 注册中心 |
生命周期状态(草稿/已发布/已下线) | 数据库 skill_package 表 |
本地是否安装、安装版本、是否可升级 | 数据库 skill_installation 表 |
如果前端分别调三个接口拿数据再自己拼,那也太不优雅了。我们做了一个 聚合列表接口,后端一次查询把三个维度的数据合并好,前端直接渲染。
其中有个"是否可升级"的计算逻辑挺有意思:
可升级 = 已安装 AND 远程版本 != 本地安装版本
就这一行判断,用户一眼就能看到哪些 Skill 有新版本可以更新。
接口设计遵循"一个 Controller 干一件事"的原则:
SkillPackageController — 基础 CRUD:
POST /skill-packages/create-draft 创建草稿
GET /skill-packages/detail 获取详情
POST /skill-packages/update-skill 更新
DELETE /skill-packages/delete-skill 删除
POST /skill-packages/upload-zip ZIP 上传
SkillLifecycleController — 生命周期管理:
POST /skill-packages/submit 提交审核
POST /skill-packages/publish 发布(上架)
POST /skill-packages/force-publish 强制发布
POST /skill-packages/change-status 上架/下线切换
SkillInstallationController — 本地安装管理:
POST /skill-packages/install 安装到本地
POST /skill-packages/uninstall 从本地卸载
POST /skill-packages/enable 启用
POST /skill-packages/disable 禁用
GET /skill-packages/list-combined 聚合列表
每个 Controller 职责单一,接口语义清晰。新来的同事看一眼就知道该调哪个接口。
最后总结一下这套方案的核心设计决策:
1. 策略模式 + 模板方法扩展新注册中心只需新增一个实现类,不用改任何业务代码。今天用 Nacos,明天想换 Apollo?加个实现类就行。
2. Nacos 作为唯一真相源所有 Skill 的权威数据在 Nacos,本地 DB 只是缓存视图。双写策略保证最终一致性,轮询任务负责兜底修复。
3. 三级缓存策略启动全量 + 按需安装 + 定时轮询,覆盖了冷启动、热更新、长期运行三种场景。
4. 聚合查询设计后端合并多数据源,前端一次请求拿到完整视图。减少网络开销,降低前端复杂度。
5. 完整的状态机生命周期状态和安装状态独立管理,互不干扰。上架/下架不影响本地已安装的 Skill,卸载不影响注册中心的发布状态。
Nacos 从服务发现和配置管理起家,到 3.0 拥抱 AI 支持 MCP 协议,再到 3.2 推出 Skill Registry,一步步从"微服务基础设施"进化为"AI 时代的资源治理平台"。
我们这套方案本质上就是把 Nacos 3.2 的 Skill Registry 能力,通过策略模式和模板方法包装了一层,让它能优雅地融入 Spring Boot 体系。如果你也在做 AI Agent 相关的项目,正在头疼 Skill 怎么管理,希望这篇文章能给你一些启发。
参考链接:
如果觉得有帮助,欢迎点赞、在看、转发三连,你的鼓励是我持续输出的动力。
#AI #Agent #Nacos #AgentSkill