对SKILL的进一步认知

一、Skill 的本质:一次标准化的 Agent 打包

如果从 Agent 架构的角度解构,Claude Code 的 Skill 几乎可以一一映射到我们熟悉的概念:

Skill 组成 Agent 语义
SKILL.md Agent policy / system prompt
scripts/ tools / executors
references/ coarse-grained RAG
Skill 名称 Agent identity
路由机制 Agent selector / router

从这个角度看,Skill 本质上就是一个场景化 Agent(Single-Context Agent)

它刻意回避了 AutoGPT 式的“自治幻想”:

  • 没有无限 loop

  • 没有自我反思

  • 没有长期规划

取而代之的是一种非常明确的姿态:

“你只在这个场景下,把这一件事做好,别想太多。”

这是一种强约束、弱自治的 Agent 设计。


二、路由 + 多 Skill,但不是传统多智能体

评论中一个非常敏锐的观察是:
Claude Code 的 Skill 并不隔离上下文。

这点非常关键。

在经典多智能体框架(如 CrewAI、AutoGen)中:

  • 每个 Agent 有独立上下文

  • Agent 之间通过消息通信

  • 角色边界清晰、状态隔离

而在 Claude Code 中:

  • 所有 Skill 共享同一个 conversation state

  • Skill 被触发时,其内容直接读入当前上下文窗口

  • 上一轮 Skill 的历史,对下一轮 Skill 可见

这意味着一件事:

Claude Code 的 Skill 不是“多个意识体”,而是“一个意识体的多个视角”。

Skill 更像是“临时 persona + 能力集”,而不是独立 Agent。

这也是为什么它在使用体验上更接近“一个很聪明、但被不断切换工作模式的助理”,而不是一个 Agent 群体。


三、Progressive Disclosure:Skill 背后的真正机制

如果一定要给 Skill 找一个“理论标签”,那么最贴切的其实不是多智能体,而是:

渐进式披露(Progressive Disclosure)

Skill 的加载是分层的:

  1. 第一层:YAML 元信息
    只加载名称和描述,用于语义路由

  2. 第二层:SKILL.md
    在确定激活后,才注入完整 policy

  3. 第三层:references / scripts
    仅在需要时才进一步加载

这套机制的目的非常明确:

  • 控制上下文体积

  • 延迟加载非必要信息

  • 在 token 成本和能力之间取得平衡

评论里把 references 称为“丐版 RAG”,其实非常贴切——
它并不是为了做细粒度检索,而是为了让背景知识“挂在那儿,但不常驻上下文”


四、为什么这是一种“正确但不炫技”的产品选择

从工程师视角看,Skill 的确不新:

  • 路由 + 专家模型,大家早就在做

  • 工具调用、RAG 早已是行业共识

但问题在于:
绝大多数用户并不是工程师。

非开发者在使用 LLM 时,最容易犯的错误并不是“不会写代码”,而是:

  • 让 Agent 过度自由

  • 不知道如何拆场景边界

  • Prompt 越堆越大,逻辑越来越混乱

Skill 强制用户完成三件事:

  1. 场景拆分:一个 Skill 只做一件事

  2. 依赖显式化:工具、资料放到明确位置

  3. 行为收敛:能力被限制在场景框内

这在工程上看起来“笨”,
但在人类协作层面却非常聪明。


五、Skill 真正对标的不是 LangChain,而是“专业模板”

评论中提到的律师例子,几乎是 Claude Code 的“教科书级使用场景”:

律师把判例、资料放进去,再写一个搜索法条的脚本。

这类用户:

  • 不想学 LangChain

  • 不想理解 Planner / Executor

  • 但非常清楚自己在某个场景下要做什么

Skill 的价值正在于此——
它把 Agent 开发,降级成了“整理文件 + 写说明书”。


六、Skill 的真正价值:把隐性经验变成显性结构

如果一定要用一句话总结 Claude Code Skill 的工程定位:

Skill 是把 Agent 设计中的隐性经验,变成显性、可编辑、可组合的配置。

这件事:

  • 对熟悉 Agent 的人来说,并不稀奇

  • 对 90% 的真实用户来说,极其重要

Anthropic 并没有试图用 Skill 去展示技术领先性,而是选择了一条更难被注意、但更难被替代的路线:

限制模型的“自由”,换取用户的“安全感”。

langchain模拟SKILL

安装

pip install langchain langgraph langchain-openai pdfplumber pandas numpy matplotlib

代码

"""
基础测试用例
"""

import pytest
from pathlib import Path
import sys

# 添加项目路径
sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from skill_system.core import SkillRegistry, SkillMetadata, BaseSkill
from skill_system.core.exceptions import SkillNotFoundError, SkillLoadError
from skill_system.config import SkillSystemConfig


class TestSkillRegistry:
    """测试 Skill Registry"""

    def test_registry_init(self):
        """测试 Registry 初始化"""
        registry = SkillRegistry()
        assert len(registry) == 0

    def test_register_skill(self):
        """测试注册 Skill"""
        registry = SkillRegistry()

        # 创建一个简单的测试 Skill
        class TestSkill(BaseSkill):
            @property
            def metadata(self):
                return SkillMetadata(
                    name="test_skill",
                    description="Test skill",
                    version="1.0.0"
                )

            def get_tools(self):
                return []

            def get_loader_tool(self):
                from langchain_core.tools import tool

                @tool
                def test_loader():
                    """Test loader"""
                    return "loaded"

                return test_loader

        skill = TestSkill()
        registry.register(skill)

        assert len(registry) == 1
        assert "test_skill" in registry

    def test_get_skill(self):
        """测试获取 Skill"""
        registry = SkillRegistry()

        class TestSkill(BaseSkill):
            @property
            def metadata(self):
                return SkillMetadata(
                    name="test_skill",
                    description="Test",
                    version="1.0.0"
                )

            def get_tools(self):
                return []

            def get_loader_tool(self):
                from langchain_core.tools import tool

                @tool
                def test_loader():
                    return "loaded"

                return test_loader

        skill = TestSkill()
        registry.register(skill)

        retrieved = registry.get("test_skill")
        assert retrieved.metadata.name == "test_skill"

    def test_get_nonexistent_skill(self):
        """测试获取不存在的 Skill"""
        registry = SkillRegistry()

        with pytest.raises(SkillNotFoundError):
            registry.get("nonexistent")

    def test_list_skills(self):
        """测试列出所有 Skills"""
        registry = SkillRegistry()

        class TestSkill1(BaseSkill):
            @property
            def metadata(self):
                return SkillMetadata(name="skill1", description="", version="1.0.0")

            def get_tools(self):
                return []

            def get_loader_tool(self):
                from langchain_core.tools import tool

                @tool
                def loader():
                    return "loaded"

                return loader

        class TestSkill2(BaseSkill):
            @property
            def metadata(self):
                return SkillMetadata(name="skill2", description="", version="1.0.0")

            def get_tools(self):
                return []

            def get_loader_tool(self):
                from langchain_core.tools import tool

                @tool
                def loader():
                    return "loaded"

                return loader

        registry.register(TestSkill1())
        registry.register(TestSkill2())

        skills = registry.list_skills()
        assert len(skills) == 2
        assert "skill1" in skills
        assert "skill2" in skills


class TestSkillSystemConfig:
    """测试配置类"""

    def test_default_config(self):
        """测试默认配置"""
        config = SkillSystemConfig()

        assert config.state_mode == "replace"
        assert config.verbose is False
        assert config.auto_discover is True

    def test_custom_config(self):
        """测试自定义配置"""
        config = SkillSystemConfig(
            skills_dir=Path("./custom_skills"),
            state_mode="fifo",
            max_concurrent_skills=5,
            verbose=True
        )

        assert config.skills_dir == Path("./custom_skills")
        assert config.state_mode == "fifo"
        assert config.max_concurrent_skills == 5
        assert config.verbose is True

    def test_invalid_state_mode(self):
        """测试无效的状态模式"""
        with pytest.raises(ValueError):
            SkillSystemConfig(state_mode="invalid_mode")

    def test_config_to_dict(self):
        """测试配置转字典"""
        config = SkillSystemConfig(
            skills_dir=Path("./skills"),
            state_mode="accumulate"
        )

        config_dict = config.to_dict()

        assert config_dict["state_mode"] == "accumulate"
        assert "skills_dir" in config_dict

    def test_config_from_dict(self):
        """测试从字典创建配置"""
        config_dict = {
            "skills_dir": "./skills",
            "state_mode": "fifo",
            "max_concurrent_skills": 3
        }

        config = SkillSystemConfig.from_dict(config_dict)

        assert config.state_mode == "fifo"
        assert config.max_concurrent_skills == 3


class TestSkillMetadata:
    """测试 Skill 元数据"""

    def test_metadata_creation(self):
        """测试元数据创建"""
        meta = SkillMetadata(
            name="test_skill",
            description="Test description",
            version="1.0.0",
            tags=["test", "example"],
            visibility="public"
        )

        assert meta.name == "test_skill"
        assert "test" in meta.tags
        assert meta.visibility == "public"

    def test_metadata_to_dict(self):
        """测试元数据转字典"""
        meta = SkillMetadata(
            name="test_skill",
            description="Test",
            version="1.0.0"
        )

        meta_dict = meta.to_dict()

        assert meta_dict["name"] == "test_skill"
        assert meta_dict["version"] == "1.0.0"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
# Claude-Style Skills System for LangChain

一个模块化、可扩展的 Skill 管理系统,为 LangChain/LangGraph 实现类似 Claude Skills 的动态工具加载机制。

## ✨ 核心特性

- 🔄 **动态 Skill 加载**:运行时按需激活能力,减少 token 消耗
- 🎯 **智能工具过滤**:中间件自动过滤无关工具,降低认知负荷
- 📦 **模块化设计**:每个 Skill 独立封装,易于开发和维护
- ⚙️ **灵活状态管理**:支持 Replace/Accumulate/FIFO 三种模式
- 🔐 **权限控制**:基于可见性和权限的访问控制
- 🚀 **高性能**:减少延迟和错误率,提升 Agent 决策质量

## 📦 安装

```bash
# 克隆仓库
cd skill_system

# 安装依赖
pip install langchain langgraph langchain-openai pdfplumber pandas numpy matplotlib
```

## 🚀 快速开始

### 基础用法

```python
from skill_system import create_skill_agent, SkillSystemConfig
from langchain_openai import ChatOpenAI

# 创建 Agent
agent = create_skill_agent(
    model=ChatOpenAI(model="gpt-4")
)

# 使用
result = agent.invoke({
    "messages": [{"role": "user", "content": "帮我处理 PDF 文件"}]
})

print(result["messages"][-1].content)
```

### 自定义配置

```python
# 创建自定义配置
config = SkillSystemConfig(
    skills_dir="./my_skills",
    state_mode="fifo",           # 最多同时加载 N 个 Skill
    max_concurrent_skills=3,     # FIFO 模式下的限制
    verbose=True,                # 启用详细日志
    log_level="DEBUG"
)

agent = create_skill_agent(
    model=ChatOpenAI(model="gpt-4"),
    config=config
)
```



## 🎯 工作原理

### 1. 传统方式 vs Skills 方式

**传统方式(静态加载所有工具):**
```
Agent 启动 → 注册 50 个工具 → 每次调用都看到所有 50 个工具
问题:高 token 消耗、高延迟、高错误率
```

**Skills 方式(动态加载):**
```
Agent 启动 → 注册所有工具 → 中间件过滤 → Agent 只看到 Loaders
需要能力 → 调用 Loader → 更新状态 → 中间件注入对应工具
结果:低 token 消耗、低延迟、低错误率
```

### 2. 核心组件交互

```
┌─────────────┐
│   User      │
└──────┬──────┘
       │ 请求
       ▼
┌─────────────────────────────────────┐
│          Skill Agent                │
│  ┌──────────────────────────────┐  │
│  │   LangGraph Agent            │  │
│  │  • System Prompt             │  │
│  │  • 注册所有工具              │  │
│  └──────────┬───────────────────┘  │
│             │                       │
│  ┌──────────▼───────────────────┐  │
│  │   Skill Middleware           │  │
│  │  • 读取 skills_loaded 状态  │  │
│  │  • 动态过滤工具列表          │  │
│  │  • 只显示相关工具            │  │
│  └──────────┬───────────────────┘  │
│             │                       │
│  ┌──────────▼───────────────────┐  │
│  │   Skill Registry             │  │
│  │  • 管理所有 Skills           │  │
│  │  • 提供工具查询              │  │
│  │  • 自动发现和加载            │  │
│  └──────────────────────────────┘  │
└─────────────────────────────────────┘
```

### 3. 执行流程

```
1. User: "帮我处理 PDF 并分析数据"

2. Agent 初始状态:
   - skills_loaded: []
   - 可见工具: [skill_pdf_processing, skill_data_analysis]

3. Agent 决策: "需要 PDF 处理能力"
   → 调用 skill_pdf_processing()

4. Loader 更新状态:
   - skills_loaded: ["pdf_processing"]
   - 返回使用说明

5. 中间件拦截下次调用:
   - 检测到 skills_loaded = ["pdf_processing"]
   - 注入 pdf_to_csv, extract_pdf_text 等工具

6. Agent 使用工具:
   → pdf_to_csv("report.pdf")

7. Agent 决策: "需要数据分析能力"
   → 调用 skill_data_analysis()

8. 重复流程...
```

## 📝 创建自定义 Skill

### 1. 创建 Skill 目录

```bash
mkdir -p skills/my_skill
cd skills/my_skill
```

### 2. 编写 Skill 类 (skill.py)

```python
from pathlib import Path
from typing import List
from langchain_core.tools import tool, BaseTool
from langchain_core.messages import ToolMessage
from langgraph.types import Command

from skill_system.core.base_skill import BaseSkill, SkillMetadata


class MySkill(BaseSkill):
    """我的自定义 Skill"""

    @property
    def metadata(self) -> SkillMetadata:
        return SkillMetadata(
            name="my_skill",
            description="我的 Skill 功能描述",
            version="1.0.0",
            tags=["custom", "example"],
            visibility="public",
            dependencies=["some_library"],
            author="Your Name"
        )

    def get_loader_tool(self) -> BaseTool:
        """Loader Tool"""
        skill_instance = self

        @tool
        def skill_my_skill(runtime) -> Command:
            """Load my custom skill capabilities."""
            instructions = skill_instance.get_instructions()
            return Command(
                update={
                    "messages": [ToolMessage(
                        content=instructions,
                        tool_call_id=runtime.tool_call_id
                    )],
                    "skills_loaded": ["my_skill"]
                }
            )

        return skill_my_skill

    def get_tools(self) -> List[BaseTool]:
        """实际工具"""
        @tool
        def my_custom_tool(input_text: str) -> str:
            """My custom tool description"""
            # 实现你的功能
            return f"Processed: {input_text}"

        return [my_custom_tool]


def create_skill(skill_dir: Path) -> BaseSkill:
    """工厂函数"""
    return MySkill(skill_dir)
```

### 3. 编写使用说明 (instructions.md)

```markdown
# My Skill

自定义 Skill 已激活!

## 可用工具

### my_custom_tool
工具功能描述

**参数**:
- `input_text`: 输入文本

**示例**:
\```python
my_custom_tool(input_text="hello")
\```
```

### 4. 使用你的 Skill

```python
from skill_system import create_skill_agent
from langchain_openai import ChatOpenAI

agent = create_skill_agent(
    model=ChatOpenAI(model="gpt-4")
)

# Agent 会自动发现并加载你的 Skill
result = agent.invoke({
    "messages": [{"role": "user", "content": "使用我的自定义功能"}]
})
```

## 🔧 配置选项

### SkillSystemConfig 完整参数

```python
config = SkillSystemConfig(
    # 基础配置
    skills_dir=Path("./skills"),          # Skills 目录

    # 状态管理
    state_mode="replace",                 # replace/accumulate/fifo
    max_concurrent_skills=3,              # FIFO 模式限制

    # 日志
    verbose=False,
    log_level="INFO",

    # Agent
    default_model="gpt-4",
    temperature=0.7,
    max_tokens=None,

    # 中间件
    middleware_enabled=True,

    # 自动发现
    auto_discover=True,
    skill_module_name="skill",            # skill.py

    # 过滤
    filter_by_visibility=True,
    allowed_visibilities=["public"],      # public/internal/private

    # 权限
    user_permissions=[],

    # 自定义
    custom_config={}
)
```

## 📊 性能优势

基于原文的测试结果:

| 指标 | 传统方式 (50 工具) | Skills 方式 (5-10 工具) |
|-----|------------------|---------------------|
| Token 消耗 | 高 | **降低 60-80%** |
| 延迟 | 高 | **降低 40-60%** |
| 错误率 | 高 | **降低 50%+** |
| 工具选择准确性 | 中 | **高** |

## 🎓 最佳实践

### 1. Skill 设计原则

- **单一职责**:每个 Skill 专注一个领域
- **独立性**:Skill 之间应该解耦
- **清晰命名**:Skill 名称应描述性强
- **完善文档**:提供详细的 instructions.md

### 2. System Prompt 优化

```python
custom_prompt = """
你是一个专业的 AI 助手。

重要规则:
1. 在使用工具前,先检查是否需要加载对应的 Skill
2. 如果需要 PDF 处理,先调用 skill_pdf_processing
3. 如果需要数据分析,先调用 skill_data_analysis
4. Skill 一旦加载,工具即可使用

工作流程:
分析任务 → 识别所需 Skill → 加载 Skill → 使用工具 → 完成任务
"""

agent = create_skill_agent(
    model=ChatOpenAI(model="gpt-4"),
    custom_system_prompt=custom_prompt
)
```

### 3. 状态模式选择

- **Replace 模式**:简单任务,每次只需一个 Skill
- **Accumulate 模式**:复杂任务,需要多个 Skill 协作
- **FIFO 模式**:控制成本,限制同时加载数量

### 4. 错误处理

```python
from skill_system.core.exceptions import SkillNotFoundError, SkillLoadError

try:
    agent = create_skill_agent(model=ChatOpenAI(model="gpt-4"))
except SkillLoadError as e:
    print(f"Skill 加载失败: {e.skill_name} - {e.reason}")
except Exception as e:
    print(f"创建 Agent 失败: {e}")
```

## 📚 示例

查看 [examples/](./examples/) 目录获取更多示例:

- `basic_usage.py` - 基础使用
- (待添加) `custom_skill.py` - 创建自定义 Skill
- (待添加) `advanced_config.py` - 高级配置
- (待添加) `async_usage.py` - 异步使用

## 🧪 测试

```bash
# 运行测试
pytest tests/

# 运行示例
python examples/basic_usage.py
```

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐