Files
JARVIS/development-doc/plan/agent-update/phase-9-skills-registry.md
WIN-JHFT4D3SIVT\caoxiaozhu a3fe4d24fc feat(agents): Phase 7-10 hook system, plugins, skills, orchestration
Phase 7: Built-in Hooks (audit_log, dangerous_confirmation, security_scan)
Phase 8: Plugin system (PluginManager, PluginSandbox, PluginManifest)
Phase 9: Skills registry (SkillRegistry, local/plugin/MCP loaders)
Phase 10: TeamLeader, RemoteTransport, BackgroundTaskManager
2026-04-04 22:56:27 +08:00

509 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 9Skills 注册表Skills Registry
日期2026-04-04
状态:待开始
前置依赖Phase 6工具系统重构
Demo参考claw-code-main — skills/loadSkillsDir.ts, bundledSkills.ts, mcpSkillBuilders.ts
---
## 1. 阶段目标
建立完整的 **Skills 动态加载和注册系统**,支持:
- 本地文件 Skills现有
- 插件提供的 Skills
- MCP 动态发现的 Skills
- Skills 注册表和搜索
- Bundled Skills 集
**现有状态**Jarvis 已有基础的 `skill_registry.py`,需要增强为完整系统。
---
## 2. Skills 架构
### 2.1 Skill 结构
```markdown
# SKILL.md
## Skill Metadata
name: my-skill
description: Does something useful
version: 1.0.0
author: Developer
## Triggers
- keywords: ["skill", "my"]
- patterns: ["/my-skill"]
## Capabilities
- tools: ["my_tool"]
- hooks: ["my_hook"]
## Configuration
```json
{
"option1": "value1"
}
```
```
### 2.2 SkillRegistry 增强
```python
# backend/app/agents/skills/registry.py
@dataclass
class SkillMetadata:
"""Skill 元数据"""
name: str
description: str
version: str
author: str | None = None
triggers: list[str] = field(default_factory=list)
patterns: list[str] = field(default_factory=list)
tools: list[str] = field(default_factory=list)
hooks: list[str] = field(default_factory=list)
commands: list[str] = field(default_factory=list)
config_schema: dict | None = None
source: SkillSource = SkillSource.LOCAL
file_path: Path | None = None
class SkillSource(Enum):
"""Skill 来源"""
LOCAL = "local" # 本地文件
PLUGIN = "plugin" # 插件提供
MCP = "mcp" # MCP 动态发现
BUNDLED = "bundled" # 内置
class SkillRegistry:
"""Skills 注册表"""
def __init__(
self,
local_skills_dir: Path,
tool_registry: ToolRegistry,
hook_manager: HookManager
):
self.local_skills_dir = local_skills_dir
self.tool_registry = tool_registry
self.hook_manager = hook_manager
self._skills: dict[str, SkillMetadata] = {}
self._index: dict[str, list[str]] = defaultdict(list) # keyword -> skill names
async def load_all(self):
"""加载所有 Skills"""
await self._load_local_skills()
await self._load_bundled_skills()
async def _load_local_skills(self):
"""加载本地 Skills"""
if not self.local_skills_dir.exists():
return
for skill_file in self.local_skills_dir.rglob("SKILL.md"):
try:
metadata = await self._parse_skill_file(skill_file)
metadata.source = SkillSource.LOCAL
self._register_skill(metadata)
except Exception as e:
logger.warning(f"Failed to load skill from {skill_file}: {e}")
async def _load_bundled_skills(self):
"""加载内置 Skills"""
for skill_def in BUNDLED_SKILLS:
metadata = SkillMetadata(
name=skill_def["name"],
description=skill_def["description"],
version=skill_def["version"],
author="Jarvis Team",
triggers=skill_def.get("triggers", []),
tools=skill_def.get("tools", []),
source=SkillSource.BUNDLED
)
self._register_skill(metadata)
async def _parse_skill_file(self, path: Path) -> SkillMetadata:
"""解析 SKILL.md 文件"""
content = path.read_text(encoding="utf-8")
# 简单的 frontmatter 解析
metadata = SkillMetadata(
name=path.parent.name,
description="",
version="1.0.0"
)
# 解析 triggers
for line in content.split("\n"):
if line.startswith("- keywords:"):
keywords = line.split("[")[1].split("]")[0]
metadata.triggers = [k.strip() for k in keywords.split(",")]
return metadata
def _register_skill(self, metadata: SkillMetadata):
"""注册 Skill"""
self._skills[metadata.name] = metadata
# 更新索引
for keyword in metadata.triggers:
self._index[keyword].append(metadata.name)
# 注册关联的工具
for tool_name in metadata.tools:
self.tool_registry.register_tool_for_skill(metadata.name, tool_name)
async def get_skill(self, name: str) -> SkillMetadata | None:
"""获取 Skill"""
return self._skills.get(name)
async def search(
self,
query: str,
limit: int = 10
) -> list[SkillMetadata]:
"""搜索 Skills"""
results = []
query_lower = query.lower()
# 精确匹配 name
if query_lower in self._skills:
results.append(self._skills[query_lower])
# 关键词匹配
for skill_name in self._index.get(query_lower, []):
if skill_name not in results:
results.append(self._skills[skill_name])
# 描述匹配
for skill in self._skills.values():
if skill in results:
continue
if query_lower in skill.description.lower():
results.append(skill)
return results[:limit]
async def get_skill_context(
self,
skill_name: str,
context: dict
) -> str:
"""获取 Skill 上下文"""
skill = self._skills.get(skill_name)
if not skill:
return ""
# 读取 SKILL.md 内容
if skill.file_path and skill.file_path.exists():
content = skill.file_path.read_text(encoding="utf-8")
# 移除 metadata 部分,只保留 instructions
return content
return ""
```
---
## 3. MCP Skill Builder
### 3.1 MCPSkillBuilder
```python
# backend/app/agents/skills/mcp_builder.py
class MCPSkillBuilder:
"""
MCP Skill Builder
从 MCP 服务器动态构建 Skills
"""
def __init__(
self,
mcp_client: MCPClient,
skill_registry: SkillRegistry
):
self.mcp_client = mcp_client
self.skill_registry = skill_registry
async def discover_skills_from_mcp(
self,
mcp_server_id: str
) -> list[SkillMetadata]:
"""从 MCP 服务器发现 Skills"""
# 获取 MCP 服务器提供的工具
tools = await self.mcp_client.list_tools(mcp_server_id)
skills = []
# 按工具前缀分组
tool_groups: dict[str, list] = defaultdict(list)
for tool in tools:
parts = tool.name.split("_")
if len(parts) > 1:
prefix = parts[0]
tool_groups[prefix].append(tool)
else:
# 单工具,作为独立 skill
skill = self._tool_to_skill(tool, mcp_server_id)
skills.append(skill)
# 创建分组 skill
for prefix, group_tools in tool_groups.items():
skill = self._group_to_skill(prefix, group_tools, mcp_server_id)
skills.append(skill)
return skills
def _tool_to_skill(
self,
tool: MCPProtocolTool,
server_id: str
) -> SkillMetadata:
"""将单个 MCP 工具转换为 Skill"""
return SkillMetadata(
name=f"{server_id}_{tool.name}",
description=tool.description or f"MCP tool: {tool.name}",
version="1.0.0",
tools=[tool.name],
source=SkillSource.MCP,
config_schema={
"server_id": server_id,
"tool_name": tool.name
}
)
def _group_to_skill(
self,
prefix: str,
tools: list[MCPProtocolTool],
server_id: str
) -> SkillMetadata:
"""将一组 MCP 工具转换为 Skill"""
return SkillMetadata(
name=f"{server_id}_{prefix}",
description=f"MCP {prefix} tools: {', '.join(t.name for t in tools)}",
version="1.0.0",
tools=[t.name for t in tools],
source=SkillSource.MCP,
config_schema={
"server_id": server_id,
"prefix": prefix
}
)
```
---
## 4. 内置 Skills
```python
# backend/app/agents/skills/bundled.py
BUNDLED_SKILLS = [
{
"name": "code-analysis",
"description": "代码分析技能 - 分析代码结构、复杂度、依赖关系",
"version": "1.0.0",
"triggers": ["分析代码", "代码分析", "code analysis"],
"tools": ["grep", "glob", "lint"]
},
{
"name": "git-helper",
"description": "Git 操作助手 - 管理 Git 仓库和操作",
"version": "1.0.0",
"triggers": ["git", "版本控制", "commit"],
"tools": ["git_status", "git_log", "git_diff"]
},
{
"name": "web-research",
"description": "网络研究技能 - 搜索和抓取网页内容",
"version": "1.0.0",
"triggers": ["搜索", "研究", "research", "web search"],
"tools": ["web_search", "web_fetch"]
},
{
"name": "file-management",
"description": "文件管理技能 - 组织和管理文件",
"version": "1.0.0",
"triggers": ["文件", "整理", "file management"],
"tools": ["glob", "file_read", "file_write", "organize"]
},
{
"name": "task-planning",
"description": "任务规划技能 - 拆解和规划复杂任务",
"version": "1.0.0",
"triggers": ["规划", "任务拆解", "task planning"],
"tools": ["create_task", "get_tasks", "update_task"]
}
]
```
---
## 5. Skill 与 Agent 集成
### 5.1 修改 AgentService
```python
# backend/app/services/agent_service.py (修改部分)
async def build_skill_context(
self,
skill_names: list[str],
context: dict
) -> str:
"""构建 Skill 上下文"""
parts = []
for skill_name in skill_names:
skill_context = await self.skill_registry.get_skill_context(
skill_name, context
)
if skill_context:
parts.append(f"\n\n=== Skill: {skill_name} ===\n{skill_context}")
return "\n".join(parts)
async def chat(
self,
message: str,
context: dict
) -> AgentResponse:
"""处理聊天消息(集成 Skills"""
# 1. 检测触发的 Skills
triggered_skills = await self.skill_registry.search(message)
# 2. 构建 Skill 上下文
skill_context = await self.build_skill_context(
[s.name for s in triggered_skills],
context
)
# 3. 注入到 system prompt
if skill_context:
context["skill_context"] = skill_context
# 4. 继续正常处理
return await self._process_message(message, context)
```
---
## 6. API 接口
```python
# backend/app/routers/skills.py
@router.get("/api/skills")
async def list_skills(
source: SkillSource | None = None,
current_user: User = Depends(get_current_user)
):
"""列出所有 Skills"""
skills = await skill_registry.list_all()
if source:
skills = [s for s in skills if s.source == source]
return {
"skills": [
{
"name": s.name,
"description": s.description,
"version": s.version,
"source": s.source.value,
"triggers": s.triggers
}
for s in skills
]
}
@router.get("/api/skills/search")
async def search_skills(
q: str,
limit: int = 10
):
"""搜索 Skills"""
results = await skill_registry.search(q, limit)
return {"skills": results}
@router.get("/api/skills/{skill_name}")
async def get_skill(
skill_name: str,
current_user: User = Depends(get_current_user)
):
"""获取 Skill 详情"""
skill = await skill_registry.get_skill(skill_name)
if not skill:
raise HTTPException(404, "Skill not found")
return {
"name": skill.name,
"description": skill.description,
"version": skill.version,
"source": skill.source.value,
"triggers": skill.triggers,
"tools": skill.tools,
"hooks": skill.hooks
}
```
---
## 7. 文件结构
```
backend/app/agents/skills/
├── __init__.py
├── registry.py # Skills 注册表(增强)
├── metadata.py # Skill 元数据
├── mcp_builder.py # MCP Skill Builder
├── bundled.py # 内置 Skills
├── loaders/ # 加载器
│ ├── __init__.py
│ ├── local_loader.py # 本地文件加载
│ ├── plugin_loader.py # 插件 Skill 加载
│ └── mcp_loader.py # MCP Skill 加载
└── context.py # Skill 上下文构建
backend/app/routers/
└── skills.py # Skills API 路由
```
---
## 8. 验收标准
| 检查点 | 标准 |
|--------|------|
| 本地 Skills | 能加载 local_skills_dir 下的所有 SKILL.md |
| MCP Skills | 能从 MCP 服务器发现和加载 Skills |
| Bundled Skills | 内置 Skills 默认加载 |
| Skill 搜索 | 能按关键词搜索 Skills |
| Skill 上下文 | Skill 内容正确注入 Agent prompt |
| API 可用 | Skills API 可用 |
---
## 9. Demo 借鉴
| claw-code | Jarvis 对应 |
|-----------|------------|
| `src/skills/loadSkillsDir.ts` | `skills/loaders/local_loader.py` |
| `src/skills/bundledSkills.ts` | `skills/bundled.py` |
| `src/skills/mcpSkillBuilders.ts` | `skills/mcp_builder.py` |
| `/skills` command | `/api/skills` |
| Skill registry pipeline | `SkillRegistry` |