微信扫码
添加专属顾问
我要投稿
揭秘Agent Skills的创新设计,教你用Strands SDK完美复刻这一革命性技术。核心内容: 1. Agent Skills与传统Tool Calling的对比与优势 2. 渐进式信息披露的三层架构设计解析 3. 使用Strands Agents SDK实现技能动态加载的实战指南
最近Anthropic正式发布了Claude Code Skills功能,这是继MCP之后,Anthropic在Agent工具化领域的又一重要创新。不同于传统的硬编码工具调用(Tool Calling),Agent Skills通过文件和文件夹的组织方式,让Agent能够动态发现和加载技能文件,就像为新员工准备一份完整的入职操作手册。
与传统的工具调用(Tool Use)不同,Skills采用了渐进式信息披露(Progressive Disclosure)的设计理念,通过多层次的上下文加载机制,既保证了Agent的响应效率,又提供了深度的领域知识注入能力。这种设计让Agent能够在需要时"即时学习"专业技能,而不是一开始就加载所有可能需要的知识。
本文将深入剖析Agent Skills的技术架构,并展示如何使用Strands Agents SDK完美复刻这一能力。通过逆向工程Anthropic的官方实现,我们将揭开Skills背后的设计哲学和技术细节。
在Agent Skills出现之前,为LLM Agent赋予特定能力主要依赖Tool Calling机制:
-
-
-
-
-
-
-
-
-
-
# 传统Tool Calling方式
tools = [
{
"name": "pdf_extract",
"description": "Extract text from PDF files",
"parameters": {...}
}
]
# 所有工具定义都需要预先加载到系统提示词中
agent = Agent(tools=tools)
这种方式存在三个核心问题:
上下文窗口压力:所有工具的完整描述必须在启动时加载,占用大量token
缺乏可组合性:新增能力需要修改代码,无法通过文件系统灵活分发
专业知识难以封装:复杂的领域知识(如pptx生成的最佳实践)无法有效传递给Agent
Anthropic提出的Agent Skills基于两个核心设计原则:
渐进式披露(Progressive Disclosure)
Skills采用三层信息结构:
第一层:Skill的name
和description
(启动时预加载)
第二层:SKILL.md
的完整内容(Agent判断相关时加载)
第三层:额外的参考文件如reference.md
、forms.md
(按需加载)
这种设计让Agent在需要时才加载详细信息,避免了上下文窗口的浪费。
文件系统作为能力载体
每个Skill本质上是一个包含SKILL.md
的文件夹:
-
-
-
-
-
-
-
skills/
├── pdf/
│ ├── SKILL.md # 核心技能描述
│ ├── forms.md # 表单填写指南
│ ├── reference.md # API参考
│ └── scripts/
│ └── extract.py # 可执行脚本
这种文件系统结构天然支持:
版本控制:通过Git分发和管理
模块化组合:多个Skills可以共存
跨项目复用:个人Skills(~/.claude/skills/
)和项目Skills(.claude/skills/
)分离
通过分析Anthropic的官方文档和实现,我们可以提炼出Skills的完整工作流程:
每个Skill的核心是一个符合特定格式的Markdown文件:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
---
name: PDF Processing
description: Extract text, fill forms, merge PDFs. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.
---
This skill provides comprehensive PDF manipulation capabilities...
1. To extract text from a PDF, use pdfplumber...
2. For form filling, refer to [forms.md](
forms.md
)
```python
import pdfplumber
with pdfplumber.open("doc.pdf") as pdf:
text = pdf.pages[0].extract_text()
关键字段解析:
- name:Skill的标识名称,用于在系统提示词中展示
- description:触发条件描述,必须明确说明"何时使用",这是Agent判断是否加载Skill的核心依据
通过分析Claude Code的行为模式和官方文档,我们可以推断出Skills的核心技术架构:
在Strands Agents SDK中实现Agent Skills需要解决三个技术点:
1. 如何动态生成Skill工具:在Agent启动时扫描Skills并生成tool定义
2. 如何注入Skill内容:在tool调用后将Skill内容添加到消息流
3. 如何支持Message Prompt Caching:利用缓存优化重复加载的成本
我们的实现方案采用了动态工具生成 + Hook拦截器的架构:
动态Tool生成
Skills本质上是一个动态生成的Tool。在Agent启动时:
扫描技能目录
解析所有SKILL.md的frontmatter
动态构建Tool的description(包含所有可用技能列表)
注册为名为Skill
的工具
两阶段响应机制
当模型调用Skill工具时,系统返回两部分内容:
第一部分:命令消息(让模型知道Skill正在加载)
-
-
<command-message>The "skill-name" skill is running</command-message>
<command-name>skill-name</command-name>
第二部分:完整的Skill内容(作为额外的Content Block)
-
-
Base directory for this skill: /path/to/skill
[SKILL.md的完整Markdown内容]
这种两阶段设计确保了:
模型先收到确认消息
然后获得完整的Skill指令
最后重新推理,应用Skill知识完成任务
Prompt Cache优化
Skills内容通常很长(几千到上万tokens),重复加载会消耗大量资源。Strands Agents SDK中使用Claude模型,默认只能针对System和Tool设置Prompt Cache,如果需要对Message设置Prompt Cache,则需要自行在Messages列表中添加CachePoint,而我们使用了Strands的Hook机制提供的BeforeModelCallEvent拦截,在调用模型API之前,对Messages列表修改,在最末添加cachePoint。
skill_tool.py
中的generate_skill_tool()
函数负责扫描Skills目录并生成动态工具:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
def generate_skill_tool():
"""动态生成Skill工具函数"""
skills_desc = init_skills() # 扫描并生成<available_skills>列表
if not skills_desc:
return None
# 动态创建函数
def dynamic_func(command: str) -> str:
"""
Execute a skill within the main conversation
Args:
command: Skill名称,如"pdf"、"xlsx"
"""
return f"Launching skill: {command}"
# 设置函数元数据
function_name = "Skill"
dynamic_func.__name__ = function_name
dynamic_func.__doc__ = skill_prompt_template.format(skills=skills_desc)
# 应用@tool装饰器
decorated_func = tool(dynamic_func)
return decorated_func
技术要点:
将每个Skill的name和description格式化为XML片段,注入到系统提示词
动态函数通过修改__name__
和__doc__
属性,让@tool
装饰器识别
Strands SDK的Hook机制允许我们拦截Agent的关键事件。我们实现了SkillToolInterceptor
来处理Skill的加载和注入:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
class SkillToolInterceptor(HookProvider):
def __init__(self):
super().__init__()
self.tooluse_ids = {} # 存储待注入的Skill内容
def register_hooks(self, registry: HookRegistry) -> None:
"""注册三个关键Hook"""
registry.add_callback(AfterToolCallEvent, self.add_skill_content)
registry.add_callback(MessageAddedEvent, self.add_skill_message)
registry.add_callback(BeforeModelCallEvent, self.add_message_cache)
def add_skill_content(self, event: AfterToolCallEvent) -> None:
"""Hook 1: 工具调用后,加载Skill完整内容"""
if event.tool_use['name'] == 'Skill':
command = event.tool_use['input']['command']
# 读取SKILL.md完整内容
skill_content = load_skill(command)
# 暂存到tooluse_ids映射
self.tooluse_ids[event.tool_use['toolUseId']] = skill_content
def add_skill_message(self, event: MessageAddedEvent) -> None:
"""Hook 2: 消息添加时,注入Skill内容到tool_result"""
if event.message['role'] == 'user':
for block in event.message['content']:
if 'toolResult' in block:
tooluse_id = block['toolResult']['toolUseId']
skill_content = self.tooluse_ids.get(tooluse_id)
if skill_content:
# 将Skill内容追加到消息content数组
event.message['content'] += skill_content
self.tooluse_ids.pop(tooluse_id)
break
def add_message_cache(self, event: BeforeModelCallEvent) -> None:
"""Hook 3: 模型调用前,添加Prompt Cache标记"""
# 移除旧的cachePoint
for message in event.agent.messages:
content = message['content']
if any(['cachePoint' in block for block in content]):
message['content'] = content[:-1]
# 在最后一条消息添加cachePoint
if event.agent.messages:
event.agent.messages[-1]['content'] += [{
"cachePoint": {"type": "default"}
}]
技术要点:
1. 两阶段注入:
AfterToolCallEvent
:工具调用完成后立即读取Skill内容,但不直接修改消息
MessageAddedEvent
:等消息正式添加到历史记录时,再将Skill内容注入到tool_result
的后续content块中
2. Prompt Caching优化:
BeforeModelCallEvent
:在每次调用模型前,将最后一条消息标记为缓存点
Claude会缓存包括Skill内容在内的完整上下文,避免重复加载同一Skill时的token消耗
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
def load_skill(command: str) -> list:
"""
加载指定Skill的完整内容
返回包含两个content block的列表:
1. command-message: 提示Skill正在加载
2. text block: Skill的完整Markdown内容
"""
try:
path = os.path.join(SKILLS_ROOT, command, "SKILL.md")
with open(path, 'r', encoding='utf-8') as f:
skill_content = f.read()
# 解析YAML frontmatter,提取Markdown正文
frontmatter_match = re.match(
r'^---\n(.*?)\n---\n(.*)',
skill_content,
re.DOTALL
)
if not frontmatter_match:
return [{
"text": f"<command-message>The \"{command}\" skill failed to load</command-message>"
}]
markdown_content = frontmatter_match.group(2)
# 返回两个content block
return [
{
"text": f"<command-message>The \"{command}\" skill is running</command-message>\n<command-name>{command}</command-name>"
},
{
"text": f"Base directory for this skill: {os.path.join(SKILLS_ROOT, command)}\n\n{markdown_content}"
}
]
except Exception as e:
return [{
"text": f"<command-message>The \"{command}\" skill failed to load</command-message>"
}]
为什么返回两个block?
第一个block提供元信息(Skill名称、加载状态),使用特殊的<command-message>
标签
第二个block包含实际的Skill内容,并告知Agent Skill的基础路径,方便引用相对路径文件
在main.py
中,我们将Skills集成到Strands Agent:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
from strands import Agent
from strands.models import BedrockModel
from skill_tool import generate_skill_tool, SkillToolInterceptor
# 动态生成Skill工具
skill_tool = generate_skill_tool()
# 创建Agent,注入Skill工具和Hook
agent = Agent(
model=agent_model,
system_prompt=f"""You are a helpful AI assistant with access to various skills.
<IMPORTANT>
- Your working directory is {WORK_ROOT}
- Use 'Skill' tool to load specialized capabilities
</IMPORTANT>
""",
tools=[file_read, shell, editor, skill_tool], # 包含动态生成的Skill工具
hooks=[SkillToolInterceptor()] # 注入Hook拦截器
)
5.实战效果演示
让我们看一个实际运行的例子,市面上很多Agent应用如生成ppt,一般都是HTML格式,往往最终生成pptx文件时,实际效果会差很多,英文从html转换成pptx的难度非常大,一般Agent走到最后这一步都会丢失格式,样式等。我们用Anthropic官方Skills库中的pptx来验证,用Strands Agent+ ppx Skill是否能完美的生成一份pptx文件。
任务输入:
-
-
-
-
research about Claude Code Agent Skills
(https://docs.claude.com/en/docs/claude-code/skills),
and create a ppt in Chinese to introduce it,
save it as pptx file in working directory.
执行流程:
1. 模型推理:识别到需要pptx处理能力
2. 调用工具:`Skill(command="pptx")`
3. Hook拦截:`SkillToolInterceptor`捕获调用
4. 加载内容:读取`skills/pptx/SKILL.md`
5. 注入消息:追加完整Skill指令到工具结果
6. 推理-执行各种工具-循环:基于pptx Skill的完整指令,按页生成html格式的ppt文件,然后再生成nodejs代码,调用pptxgenjs这个库,把html内容转换成pptx格式文件。
实际效果如下图,几乎完美的复刻了原有html格式的ppt,并生成了一个完全可编辑的pptx。


完整生成的PPT文件下载 Claude-Code-Agent-Skills-介绍.pptx(https://github.com/xiehust/strands_demos/blob/main/strands_skills_demo/assets/Claude-Code-Agent-Skills-%E4%BB%8B%E7%BB%8D.pptx)
Agent Skills代表了Agent 上下文工程的一种新范式,通过文件系统 + 渐进式披露的设计,实现了极高的可扩展性和可维护性。对于开发者而言,深入理解Agent Skills的架构原理,将有助于构建更强大、更灵活的Agentic AI应用。
通过本文的逆向工程Claude Code Agent Skills的设计,我们揭示了其核心技术架构,更展示了如何用Strands Agents SDK通过动态工具生成 + Hook拦截器的架构,完美复刻了这一能力。
Strands Agents SDK实现Skills具有以下优势:
- ✅ 完全开源:代码透明,可自定义扩展
- ✅ 跨云兼容:支持AWS Bedrock、Anthropic API等多种部署
- ✅ Hook系统强大:可扩展更多增强功能(日志、监控、审计)
- ✅ 原生异步支持:高性能的流式响应
- ✅ 企业级特性:完整的对话管理、状态持久化
Anthropic Engineering Blog. Equipping agents for the real world with Agent Skills. https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills
Claude Documentation. Agent Skills. https://docs.claude.com/en/docs/claude-code/skills
Strands Agents SDK. https://strandsagents.com/latest/
作者注:本文所有代码示例均来自实际项目实现,经过测试验证。完整代码可在项目仓库中查看。
https://github.com/xiehust/strands_demos/tree/main/strands_skills_demo
(如要转载或者引用其中内容或代码,请先联系作者)
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-10-23
Claude Agents Skills vs MCP:AI 扩展的两条路径
2025-10-23
Dify Agent 核心解密:三模双驱,如何选对策略让你的AI应用“开挂”?
2025-10-23
《AI大模型时代老板必修课》
2025-10-23
AI时代,GEO的探索、痛点和方法|AI透镜研究系列
2025-10-23
冷启动策略:没有数据,我的第一个AI功能如何从0到1?
2025-10-23
从 SEO 到 GEO:当搜索变成“被AI引用的权力游戏”
2025-10-22
给PM的DeepSeek-OCR深度解析,智能体上下文工程的范式转变
2025-10-22
AI Agent的未来之争:任务规划,该由人主导还是AI自主?——阿里云RDS AI助手的最佳实践
2025-08-21
2025-08-21
2025-08-19
2025-09-16
2025-07-29
2025-09-08
2025-09-17
2025-08-19
2025-10-02
2025-09-29
2025-10-23
2025-10-23
2025-10-22
2025-10-22
2025-10-20
2025-10-20
2025-10-19
2025-10-18