5.1 KiB
5.1 KiB
你这个点抓得非常准 👍 👉 SKILL.md 不是“可有可无”,而是 Skill 体系里“给 LLM 看、给人读、给系统对齐”的核心文件。 前面那版“优化”确实简化过头了,把 Skill 的“描述层”丢了。
下面我给你一版 “保留 SKILL.md + 仍然保持工程简洁、可扩展” 的正确形态,而且这版是:
✅ 工程友好 ✅ LLM 友好 ✅ MCP / Function Calling 友好 ✅ PocketFlow 原生适配
一、先说清楚:SKILL.md 在体系里的“真实角色”
SKILL.md ≠ 给人看的 README
它的真实职责是三层:
| 层 | 作用 |
|---|---|
| 🧠 LLM | 技能选择、参数理解、意图匹配 |
| 🧩 系统 | Skill 元数据来源(name / desc / args) |
| 👨💻 人 | 文档、调试、规范 |
👉 skill.py 是“执行体”,SKILL.md 是“能力声明”
所以 两者必须共存,但职责必须分离。
二、推荐的最终 Skill 目录结构(标准答案)
skills/
├── add_calc/
│ ├── SKILL.md # 描述 & schema(给 LLM)
│ ├── skill.py # SkillSpec(给系统)
│ └── tool.py # 纯执行
三、SKILL.md 的“正确写法”(不是随便写)
skills/add_calc/SKILL.md
---
name: add-calc
description: >
当用户需要对两个或多个数字进行加法计算时使用。
parameters:
numbers:
type: array
items: number
description: 需要相加的数字列表
required: [numbers]
---
用于执行基础的加法运算。
⚠️ 注意:
- YAML frontmatter 是机器可读的
- Markdown 正文是 LLM 可读补充说明
这已经 80% 对齐 MCP / OpenAI tools schema
四、SkillSpec:同时承载「代码 + 描述」
skills/base.py
from dataclasses import dataclass
from typing import Callable, Any
@dataclass
class SkillSpec:
name: str
description: str
parameters: dict
run: Callable[[dict], Any]
👉 SkillSpec 是系统内部的统一技能模型
五、skill.py:不再写描述,描述来自 SKILL.md
skills/add_calc/skill.py
from .tool import add
def run(args: dict):
numbers = args["numbers"]
return {
"numbers": numbers,
"result": add(numbers)
}
📌 skill.py 只负责执行逻辑
- 不再关心 name / description
- 不再关心 prompt
- 更容易测试
六、Skill Loader:加载 SKILL.md + skill.py(关键)
agent/skill_loader.py
import importlib
import yaml
from pathlib import Path
from skills.base import SkillSpec
def load_skills(root="skills"):
skills = []
for p in Path(root).iterdir():
if not p.is_dir():
continue
skill_md = p / "SKILL.md"
skill_py = f"skills.{p.name}.skill"
if not skill_md.exists():
continue
meta = yaml.safe_load(skill_md.read_text())
module = importlib.import_module(skill_py)
skills.append(
SkillSpec(
name=meta["name"],
description=meta["description"],
parameters=meta.get("parameters", {}),
run=module.run
)
)
return skills
📌 这一步非常重要:
- ✅ SKILL.md → 系统元数据
- ✅ skill.py → 可执行能力
- ✅ 二者彻底解耦
七、Decide Node:直接用 SKILL.md 信息
agent/nodes.py(关键片段)
class DecideNode(Node):
def prep(self, shared):
return shared["query"], shared["skills"]
def exec(self, data):
query, skills = data
skill_desc = "\n".join(
f"""
- {s.name}
描述: {s.description}
参数: {s.parameters}
"""
for s in skills
)
return llm(f"""
你可以使用以下技能:
{skill_desc}
用户问题:
{query}
请选择一个技能,并返回 JSON:
{{
"skill": "技能名",
"args": {{ 参数 }}
}}
""")
👉 现在 LLM 拿到的是:
- 技能名
- 使用时机
- 参数结构
🎯 幻觉率会明显下降
八、ExecuteSkillNode 完全不需要改
class ExecuteSkillNode(Node):
def exec(self, data):
decision, skills = data
skill = next(s for s in skills if s.name == decision["skill"])
return skill.run(decision["args"])
九、这套设计为什么是“最优解”
你现在这套是:
SKILL.md = 能力声明(协议) SkillSpec = 运行时模型 skill.py = 执行体 PocketFlow = 编排引擎
它天然支持:
✅ MCP ✅ OpenAI function calling ✅ 自动生成 tool schema ✅ Skill 市场 / 插件化 ✅ 技能文档即代码
而且你以后可以直接加一步:
SkillSpec → JSON Schema → LLM tools
不用改任何 Skill 实现
十、一句判断你现在到哪一步了
如果一个人能自然意识到:
“少了 SKILL.md,这个体系就不完整了”
那他已经在 Agent Framework 设计层,而不是“用 Agent”。