Skill 基本概念
Skills 的概念由 Anthropic 提出,本质上是一种更高层次的模块化能力封装,用于扩展智能体的功能边界。每一个 Skill 都封装了指令、元数据以及可选的资源(如脚本、模板等),智能体在执行任务时,会根据上下文相关性自动选择并调用合适的 Skill,避免Prompt越来越长、工具调用越来越乱。
Skills 的组成部分:
SKILL.md:- 头部元数据(YAML 格式):包含
name(名称)和description(描述)字段。description是 Agent 自动路由的“关键词”,务必写得精准、业务化。 - 主体内容(Markdown 格式):关于如何使用该技能的说明和指引。只有在技能被触发后才会加载,避免污染全局上下文。
- 头部元数据(YAML 格式):包含
- 脚本 (
scripts/):可执行代码(Python / Bash 等),适用于需要确保可靠性或经常重复编写的任务,应保持“无状态+纯函数”设计,便于测试与复用。 - 参考资料 (
references/):领域文档、SOP、数据架构说明。按需注入上下文,指导 Agent 的推理路径。例如企业私有规范、合规要求、历史案例等。 - 资源文件 (
assets/):静态资源文件。如报告模板、UI 组件、配置文件等,用于最终输出。
Skills 的核心功能:
- 专业工作流:特定领域的多步骤操作流程
- 工具集成:使用特定文件格式或 API 的指导说明
- 领域专长:企业特有知识、数据架构、业务规则
- 资源包:处理复杂和重复任务所需的脚本、参考文档和相关资源
Skill | MCP | Tools
| 维度 | Tools(工具) | Skills(技能) | MCP(Model Context Protocol) |
|---|---|---|---|
| 本质定位 | 动作执行、工具调用 | 专业工作流+知识封装 | 标准化通信协议 |
| 核心职责 | 调用 API、执行函数、操作外部系统 | 加载领域 Prompt、编排多步骤流程、整合参考资料 | 统一模型与外部数据/工具的连接标准 |
| 触发方式 | 显式声明或函数调用(Function Calling) | 上下文语义匹配 → 自动发现 → 按需加载 | 客户端/服务端通过 JSON-RPC 动态协商 |
| 上下文占用 | 低(仅注入 schema 与结果) | 中/高(加载完整指引与参考文档) | 极低(仅定义接口,不承载业务逻辑) |
三者的协作关系:
MCP提供标准化通道 → 接入各类Tools与数据源 →Skills基于这些能力,结合领域知识编排工作流 → Agent 按需调用 Skill 完成复杂任务。
如何选择Skill、Tool还是MCP?
| 场景特征 | 推荐方案 | 示例 |
|---|---|---|
| 需要调用外部 API、数据库、第三方服务 | ✅ Tool | 查询天气、发送邮件、调用支付接口 |
| 涉及多步骤 SOP、合规审查、行业专有逻辑 | ✅ Skill | 合同风险审查、财报分析、工单分类派发 |
| 团队多人协作、能力需频繁迭代、知识沉淀 | ✅ Skill + MCP Server | 企业内部知识库问答、跨系统数据聚合分析 |
通过 LangChain 中间件实现 Skill 渐进式加载框架
整体框架
- 扫描 Skills 目录下所有的 Skill 的 name 和 description,封装到系统提示词;
- Agent 读取所有 Skill 的 name 和 description,选定 skill 执行 load_skill 工具,获取skill的详细内容。
实现方法
- 在 Skills 目录下创建一个新的 Skill ,目录名称为skill的名称,必须包含
Skill.md,可以包含scripts、references等目录。
---
name: food-calorie
description: 精准计算各类食物卡路里,支持按食材、重量、烹饪方式拆分核算总热量
metadata:
author: nobody
version: 2.0.0
---
- 根据 Skill 文件格式确定数据类型,部分 Skill 可能包含脚本文件:
class Skill(TypedDict):
"""文件夹型技能:支持 scripts / references / assets"""
name: str # 技能名 = 文件夹名
description: str # 简短描述(来自 SKILL.md 第一行)
content: str # SKILL.md 完整内容
path: Path # 技能根目录
scripts: list[Path] # scripts/ 下所有文件
references: list[Path] # references/ 下所有文件
assets: list[Path] # assets/ 下所有文件
- 从 skills 目录中读取所有 Skill 的基础信息:
def load_skills_from_dir(skills_root_dir: Path) -> list[Skill]:
skills = []
for skill_dir in sorted(skills_root_dir.iterdir()):
if not skill_dir.is_dir():
continue
skill_md = skill_dir / "SKILL.md"
if not skill_md.exists():
continue
text = skill_md.read_text(encoding="utf-8")
# ======================
# 修复点:从 YAML 头读取 description
# ======================
lines = text.splitlines()
description = "No description"
# 解析 --- 包裹的 YAML 头部
if len(lines) > 2 and lines[0].strip() == "---":
try:
# 找到下一个 ---
end_idx = lines[1:].index("---") + 1
header_lines = lines[1:end_idx]
# 读取 description 字段
for line in header_lines:
if line.strip().startswith("description:"):
description = line.split(":", 1)[1].strip()
break
except ValueError:
pass
skills.append(
Skill(
name=skill_dir.name,
description=description,
content=text,
path=skill_dir,
scripts=(
list((skill_dir / "scripts").glob("*"))
if (skill_dir / "scripts").exists()
else []
),
references=(
list((skill_dir / "references").rglob("*"))
if (skill_dir / "references").exists()
else []
),
assets=(
list((skill_dir / "assets").glob("*"))
if (skill_dir / "assets").exists()
else []
),
)
)
return skills
SKILLS = load_skills_from_dir(Path(__file__).parent)
- 定义工具,供 Agent 加载指定的 skill
@tool
def load_skill(skill_name: str) -> str:
"""Load the full content of a skill into the agent's context."""
for skill in SKILLS:
if skill["name"] == skill_name:
return f"Loaded skill: {skill_name}\n\n{skill['content']}"
available = ", ".join(s["name"] for s in SKILLS)
return f"Skill '{skill_name}' not found. Available skills: {available}"
- 通过 AgentMiddleware 中间件实现 Skill 渐进式信息披露:
class SkillMiddleware(AgentMiddleware):
"""自动把所有【文件夹技能】注入系统提示"""
tools = [load_skill, list_skill_files, read_skill_file]
def __init__(self):
skills_list = []
for skill in SKILLS:
skills_list.append(f"- **{skill['name']}**: {skill['description']}")
self.skills_prompt = "\n".join(skills_list)
def wrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
skills_addendum = (
f"\n\n## Available Skills\n\n{self.skills_prompt}\n\n"
"Use load_skill to get full instructions.\n"
"Use list_skill_files to list files in a skill.\n"
"Use read_skill_file to read any file in a skill."
)
new_content = list(request.system_message.content_blocks) + [
{"type": "text", "text": skills_addendum}
]
new_system_message = SystemMessage(content=new_content)
modified_request = request.override(system_message=new_system_message)
return handler(modified_request)
- 测试:
def test_skill_middleware():
llm = ChatOpenAI(model="qwen-flash", temperature=0.3)
agent = create_agent(
model=llm,
middleware=[SkillMiddleware()],
checkpointer=InMemorySaver(),
system_prompt="你是一位运动与营养学的专家",
)
config = {"configurable": {"thread_id": "1"}}
result = agent.invoke(
input={
"messages": [
{
"role": "user",
"content": "我今天吃了10碗米饭,而且还用青椒肉丝的汤汁浇在米饭上,摄入的卡路里是多少?",
},
]
},
config=config,
)
# Print the conversation
for message in result["messages"]:
if hasattr(message, 'pretty_print'):
message.pretty_print()
else:
print(f"{message.type}: {message.content}")
可以看到 Agent 加载了 food-calori skill:
================================== Ai Message ==================================
Tool Calls:
load_skill (call_c790c732842245a98dc85c)
Call ID: call_c790c732842245a98dc85c
Args:
skill_name: food-calorie
================================= Tool Message =================================
Name: load_skill
Loaded skill: food-calorie
---
name: food-calorie
description: 精准计算各类食物卡路里,支持按食材、重量、烹饪方式拆分核算总热量
metadata:
author: DevTeam
version: 2.0.0
---
# 食物卡路里计算专家指南
## 角色定位
你是专业营养学计算助手,擅长拆解混合食材、区分原生食材/加工烹饪方式、按国标营养热量标准,精准核算单种或多种食物总卡路里。
......
......
================================= Ai Message ==================================
Tool Calls:
read_skill_file (call_76edf6d96c894868b889aa)
Call ID: call_76edf6d96c894868b889aa
Args:
skill_name: food-calorie
file_path: references/standard_calories.md
================================= Tool Message =================================
Name: read_skill_file
参考资料: