ai_pipeline/Demo3.md

5.4 KiB
Raw Blame History

你这个点抓得非常准 👍 👉 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”。


如果你愿意,下一步我可以直接帮你做:

1 SKILL.md → JSON Schema 自动生成 2 参数校验Pydantic 3 Skill Planner多技能组合 4 MCP Server 适配(你这套非常合适)

你点一个,我直接继续。