链载Ai

标题: Agent Skills 逆向工程解密,用Strands Agents SDK完美实现Agent Skills [打印本页]

作者: 链载Ai    时间: 4 天前
标题: Agent Skills 逆向工程解密,用Strands Agents SDK完美实现Agent Skills

1.引言

最近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背后的设计哲学和技术细节。

2.技术背景:从Tool Calling到Agent Skills


传统Tool Calling的局限

在Agent Skills出现之前,为LLM Agent赋予特定能力主要依赖Tool Calling机制:

#传统ToolCalling方式tools=[{"name":"pdf_extract","description":"ExtracttextfromPDFfiles","parameters":{...}}]#所有工具定义都需要预先加载到系统提示词中agent=Agent(tools=tools)


这种方式存在三个核心问题:

  1. 上下文窗口压力:所有工具的完整描述必须在启动时加载,占用大量token

  2. 缺乏可组合性:新增能力需要修改代码,无法通过文件系统灵活分发

  3. 专业知识难以封装:复杂的领域知识(如pptx生成的最佳实践)无法有效传递给Agent


Agent Skills的设计哲学

Anthropic提出的Agent Skills基于两个核心设计原则:

  1. 渐进式披露(Progressive Disclosure)

Skills采用三层信息结构:

这种设计让Agent在需要时才加载详细信息,避免了上下文窗口的浪费。

  1. 文件系统作为能力载体

每个Skill本质上是一个包含SKILL.md的文件夹:

skills/├──pdf/│├──SKILL.md#核心技能描述│├──forms.md#表单填写指南│├──reference.md#API参考│└──scripts/│└──extract.py#可执行脚本

这种文件系统结构天然支持:


核心架构解析:Skills的工作机制

三阶段加载流程

通过分析Anthropic的官方文档和实现,我们可以提炼出Skills的完整工作流程:

SKILL.md的标准格式

每个Skill的核心是一个符合特定格式的Markdown文件:

---nameDFProcessingdescription:Extracttext,fillforms,mergePDFs.UsewhenworkingwithPDFfilesorwhentheusermentionsPDFs,forms,ordocumentextraction.---#PDFProcessingSkill##OverviewThisskillprovidescomprehensivePDFmanipulationcapabilities...##Instructions1.ToextracttextfromaPDF,usepdfplumber...2.Forformfilling,referto[forms.md](forms.md)##CodeExamples```pythonimportpdfplumberwithpdfplumber.open("doc.pdf")aspdf:text=pdf.pages[0].extract_text()


关键字段解析:

-name:Skill的标识名称,用于在系统提示词中展示

-description:触发条件描述,必须明确说明"何时使用",这是Agent判断是否加载Skill的核心依据



3.逆向工程:解构Claude Code中Agent Skill的实现

核心技术架构

通过分析Claude Code的行为模式和官方文档,我们可以推断出Skills的核心技术架构:

实现架构设计

在Strands Agents SDK中实现Agent Skills需要解决三个技术点:

1.如何动态生成Skill工具:在Agent启动时扫描Skills并生成tool定义

2.如何注入Skill内容:在tool调用后将Skill内容添加到消息流

3.如何支持Message Prompt Caching:利用缓存优化重复加载的成本

我们的实现方案采用了动态工具生成 + Hook拦截器的架构:

关键技术点

  1. 动态Tool生成

Skills本质上是一个动态生成的Tool。在Agent启动时:

  1. 两阶段响应机制

当模型调用Skill工具时,系统返回两部分内容:

第一部分:命令消息(让模型知道Skill正在加载)

<command-message>The"skill-name"skillisrunning</command-message><command-name>skill-name</command-name>


第二部分:完整的Skill内容(作为额外的Content Block)

Basedirectoryforthisskill:/path/to/skill[SKILL.md的完整Markdown内容]


这种两阶段设计确保了:

  1. Prompt Cache优化

Skills内容通常很长(几千到上万tokens),重复加载会消耗大量资源。Strands Agents SDK中使用Claude模型,默认只能针对System和Tool设置Prompt Cache,如果需要对Message设置Prompt Cache,则需要自行在Messages列表中添加CachePoint,而我们使用了Strands的Hook机制提供的BeforeModelCallEvent拦截,在调用模型API之前,对Messages列表修改,在最末添加cachePoint。


4. 核心代码实现

1. 动态Skill工具生成

skill_tool.py中的generate_skill_tool()函数负责扫描Skills目录并生成动态工具:

defgenerate_skill_tool():"""动态生成Skill工具函数"""skills_desc=init_skills()#扫描并生成<available_skills>列表ifnotskills_desc:returnNone#动态创建函数defdynamic_func(command:str)->str:"""ExecuteaskillwithinthemainconversationArgs:command:Skill名称,如"pdf"、"xlsx""""returnf"Launchingskill:{command}"#设置函数元数据function_name="Skill"dynamic_func.__name__=function_namedynamic_func.__doc__=skill_prompt_template.format(skills=skills_desc)#应用@tool装饰器decorated_func=tool(dynamic_func)returndecorated_func


技术要点

2. Hook拦截器实现Skill内容注入

Strands SDK的Hook机制允许我们拦截Agent的关键事件。我们实现了SkillToolInterceptor来处理Skill的加载和注入:

classSkillToolInterceptor(HookProvider):def__init__(self):super().__init__()self.tooluse_ids={}#存储待注入的Skill内容defregister_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)defadd_skill_content(self,event:AfterToolCallEvent)->None:"""Hook1:工具调用后,加载Skill完整内容"""ifevent.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_contentdefadd_skill_message(self,event:MessageAddedEvent)->None:"""Hook2:消息添加时,注入Skill内容到tool_result"""ifevent.message['role']=='user':forblockinevent.message['content']:if'toolResult'inblock:tooluse_id=block['toolResult']['toolUseId']skill_content=self.tooluse_ids.get(tooluse_id)ifskill_content:#将Skill内容追加到消息content数组event.message['content']+=skill_contentself.tooluse_ids.pop(tooluse_id)breakdefadd_message_cache(self,event:BeforeModelCallEvent)->None:"""Hook3:模型调用前,添加PromptCache标记"""#移除旧的cachePointformessageinevent.agent.messages:content=message['content']ifany(['cachePoint'inblockforblockincontent]):message['content']=content[:-1]#在最后一条消息添加cachePointifevent.agent.messages:event.agent.messages[-1]['content']+=[{"cachePoint":{"type":"default"}}]


技术要点

1.两阶段注入

2.Prompt Caching优化

3. load_skill函数:返回多块内容

defload_skill(command:str)->list:"""加载指定Skill的完整内容返回包含两个contentblock的列表:1.command-message:提示Skill正在加载2.textblock:Skill的完整Markdown内容"""try:path=os.path.join(SKILLS_ROOT,command,"SKILL.md")withopen(path,'r',encoding='utf-8')asf:skill_content=f.read()#解析YAMLfrontmatter,提取Markdown正文frontmatter_match=re.match(r'^---\n(.*?)\n---\n(.*)',skill_content,re.DOTALL)ifnotfrontmatter_match:return[{"text":f"<command-message>The\"{command}\"skillfailedtoload</command-message>"}]markdown_content=frontmatter_match.group(2)#返回两个contentblockreturn[{"text":f"<command-message>The\"{command}\"skillisrunning</command-message>\n<command-name>{command}</command-name>"},{"text":f"Basedirectoryforthisskill:{os.path.join(SKILLS_ROOT,command)}\n\n{markdown_content}"}]exceptExceptionase:return[{"text":f"<command-message>The\"{command}\"skillfailedtoload</command-message>"}]


为什么返回两个block?

4. Agent初始化完整流程

main.py中,我们将Skills集成到Strands Agent:

fromstrandsimportAgentfromstrands.modelsimportBedrockModelfromskill_toolimportgenerate_skill_tool,SkillToolInterceptor#动态生成Skill工具skill_tool=generate_skill_tool()#创建Agent,注入Skill工具和Hookagent=Agent(model=agent_model,system_prompt=f"""YouareahelpfulAIassistantwithaccesstovariousskills.<IMPORTANT>-Yourworkingdirectoryis{WORK_ROOT}-Use'Skill'tooltoloadspecializedcapabilities</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文件。

任务输入

researchaboutClaudeCodeAgentSkills(https://docs.claude.com/en/docs/claude-code/skills),andcreateapptinChinesetointroduceit,saveitaspptxfileinworkingdirectory.


执行流程

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。

alt text

完整生成的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)


6. 总结


Agent Skills代表了Agent 上下文工程的一种新范式,通过文件系统 + 渐进式披露的设计,实现了极高的可扩展性和可维护性。对于开发者而言,深入理解Agent Skills的架构原理,将有助于构建更强大、更灵活的Agentic AI应用。

通过本文的逆向工程Claude Code Agent Skills的设计,我们揭示了其核心技术架构,更展示了如何用Strands Agents SDK通过动态工具生成 + Hook拦截器的架构,完美复刻了这一能力。

Strands Agents SDK实现Skills具有以下优势:

- ✅完全开源:代码透明,可自定义扩展

- ✅跨云兼容:支持AWS Bedrock、Anthropic API等多种部署

- ✅Hook系统强大:可扩展更多增强功能(日志、监控、审计)

- ✅原生异步支持:高性能的流式响应

- ✅企业级特性:完整的对话管理、状态持久化







欢迎光临 链载Ai (http://www.lianzai.com/) Powered by Discuz! X3.5