langchain实现agent skill
这也是为什么它在使用体验上更接近“一个很聪明、但被不断切换工作模式的助理”,而不是一个 Agent 群体。Skill 更像是“临时 persona + 能力集”,而不是独立 Agent。上一轮 Skill 的历史,对下一轮 Skill 可见。Skill 被触发时,其内容直接读入当前上下文窗口。律师把判例、资料放进去,再写一个搜索法条的脚本。不想理解 Planner / Executor。从这个角度
对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 的加载是分层的:
-
第一层:YAML 元信息
只加载名称和描述,用于语义路由 -
第二层:SKILL.md
在确定激活后,才注入完整 policy -
第三层:references / scripts
仅在需要时才进一步加载
这套机制的目的非常明确:
-
控制上下文体积
-
延迟加载非必要信息
-
在 token 成本和能力之间取得平衡
评论里把 references 称为“丐版 RAG”,其实非常贴切——
它并不是为了做细粒度检索,而是为了让背景知识“挂在那儿,但不常驻上下文”。
四、为什么这是一种“正确但不炫技”的产品选择
从工程师视角看,Skill 的确不新:
-
路由 + 专家模型,大家早就在做
-
工具调用、RAG 早已是行业共识
但问题在于:
绝大多数用户并不是工程师。
非开发者在使用 LLM 时,最容易犯的错误并不是“不会写代码”,而是:
-
让 Agent 过度自由
-
不知道如何拆场景边界
-
Prompt 越堆越大,逻辑越来越混乱
Skill 强制用户完成三件事:
-
场景拆分:一个 Skill 只做一件事
-
依赖显式化:工具、资料放到明确位置
-
行为收敛:能力被限制在场景框内
这在工程上看起来“笨”,
但在人类协作层面却非常聪明。
五、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
```
更多推荐




所有评论(0)