1. 基础知识

Skills(技能)Spring AI Alibaba可复用的指令与上下文封装单元,智能体在处理相关任务时可自动发现、按需加载并使用技能。

Spring AI Alibaba 通过 SkillRegistry 统一管理技能资源,配合 SkillsAgentHook 实现技能列表注入、read_skill 工具注册,模型可按照渐进式披露策略,仅在需要时加载完整技能内容,大幅提升提示词效率与智能体能力复用性。

1.1 Skill 目录结构规范

每个技能对应独立子目录,目录结构标准化,SKILL.md 为强制必需文件

技能根目录/
├── pdf-extractor/          # 技能1:PDF提取
│   ├── SKILL.md            # ✅ 必需:技能核心描述与使用说明
│   ├── references/         # 可选:参考文档、API说明
│   ├── examples/           # 可选:使用示例
│   └── scripts/            # 可选:Python/Shell脚本
│
├── code-analyzer/          # 技能2:代码分析
│   ├── SKILL.md
│   └── ...

1.2 SKILL.md 格式规范

SKILL.md 采用 YAML 头 + Markdown 正文 格式,是技能的核心描述文件。

基础格式:

---
name: pdf-extractor
description: 用于从PDF文件中提取文本、表格、图片与关键信息,支持本地PDF路径解析
---

# PDF 提取技能
本技能支持对本地PDF文件执行:
1. 文本全文提取
2. 表格结构化解析
3. 关键信息自动抽取
4. 配合Python脚本实现PDF加密破解

## 可用资源
- scripts/extract_pdf.py:PDF解析主脚本
- references/pdf-spec.pdf:PDF格式规范说明

必需字段:

字段 要求 说明
name 必需,最长64字符 技能唯一标识,建议小写字母+数字+连字符
description 必需 技能用途简述,超长会被自动截断

1.3 渐进式披露

Skills 采用渐进式披露设计,是技能机制的核心设计思想:

  1. 系统提示中仅注入技能摘要:技能名称(name)、描述(description)、技能路径(skillPath
  2. 模型判断需要使用某技能时,主动调用 read_skill(skill_name) 工具
  3. 加载该技能对应的完整 SKILL.md 内容
  4. 按需使用技能绑定的工具、脚本、参考资料等资源

优势:

  • 避免一次性注入大量技能内容导致提示词过长
  • 模型按需加载,精准使用能力
  • 技能可复用、可插拔、可扩展

2. 案例演示

2.1 下载 Skill

https://skills.sh/ 可以看到当前有 9 万多个 Skills ,可以在这里下载你想要的:

在这里插入图片描述
这里直接在 Spring AI Alibaba 框架 中下载一些:

在这里插入图片描述

包含 4 个技能:

技能英文 中文名称 技能描述
copywriting 商品文案写作 中文技能,根据商品信息生成营销文案,输出 10 字以内的简短文案
product-selection 选品分析 中文技能,根据市场趋势分析推荐商品品类,输出 10 字以内的选品建议
pdf-extractor PDF 提取器 从 PDF 文档中提取文本、表格和表单数据,支持元数据提取
grouped-tools-test 分组工具测试 测试技能,用于验证通过 groupedTools 注册的工具是否可用

2.2 注册中心

Spring AI Alibaba 提供两种标准 SkillRegistry 实现,用于加载技能资源:

  • FileSystemSkillRegistry:从本地文件系统加载技能
  • ClasspathSkillRegistry:从 Classpathresources) 加载技能

2.2.1 FileSystemSkillRegistry

适用于本地开发、外部技能目录,支持项目级/用户级目录覆盖。

通过 Builder 模式构建,支持以下配置项:

配置项 类型 默认值 用途
userSkillsDirectory String 或 Resource ~/saa/skills(用户主目录下的 skills 文件夹) 存放全局技能,对所有项目可用
projectSkillsDirectory String 或 Resource ./skills(当前工作目录下的 skills 文件夹) 存放项目特定技能,优先级高于用户技能
autoLoad boolean true 是否在初始化时自动加载技能
systemPromptTemplate SystemPromptTemplate 或 String 内置 DEFAULT_SYSTEM_PROMPT_TEMPLATE 自定义技能在系统提示中的展示方式,支持变量:{skills_registry}、{skills_list}、{skills_load_instructions}

使用示例:

// 使用默认配置
FileSystemSkillRegistry registry = FileSystemSkillRegistry.builder().build();

// 自定义用户技能目录
FileSystemSkillRegistry registry = FileSystemSkillRegistry.builder()
    .userSkillsDirectory("/custom/path/to/skills")
    .build();

// 完整配置
FileSystemSkillRegistry registry = FileSystemSkillRegistry.builder()
    .userSkillsDirectory("/global/skills")
    .projectSkillsDirectory("./project/skills")
    .autoLoad(true)
    .build();

// Use Spring Resource for directories
FileSystemSkillRegistry registry = FileSystemSkillRegistry.builder()
    .userSkillsDirectory(new FileSystemResource("/custom/user/skills"))
    .projectSkillsDirectory(new ClassPathResource("skills"))
    .build();

2.2.2 ClasspathSkillRegistry

适用于技能随项目打包(src/main/resources/skills),JAR 包内资源会自动复制到临时目录。

通过 Builder 模式构建,支持以下配置项:

配置项 类型 默认值 用途
classpathPath String skills(即 classpath:skills) 存放技能的类路径位置,支持文件系统(开发环境)和 JAR 包(生产环境)
basePath String /tmp 用于存储从类路径复制出来的技能资源(脚本、配置文件等),JAR 包内文件无法直接访问,会复制到此目录
autoLoad boolean true 是否在初始化时自动加载技能
systemPromptTemplate SystemPromptTemplate 或 String 内置 DEFAULT_SYSTEM_PROMPT_TEMPLATE 自定义技能在系统提示中的展示方式

使用示例:

// 使用默认配置(classpath:skills)
ClasspathSkillRegistry registry = ClasspathSkillRegistry.builder().build();

// 自定义类路径技能目录
ClasspathSkillRegistry registry = ClasspathSkillRegistry.builder()
    .classpathPath("custom/skills")
    .build();

// 完整配置
ClasspathSkillRegistry registry = ClasspathSkillRegistry.builder()
    .classpathPath("skills")
    .basePath("/tmp/skills")
    .autoLoad(true)
    .build();

2.2.3 配置注册中心

配置注册中心:

// 创建 FileSystemSkillRegistry 实例,用于从文件系统加载和管理技能
SkillRegistry registry = FileSystemSkillRegistry.builder()
    // 设置用户技能目录(全局技能,对所有项目可用)
    // System.getProperty("user.home") 获取当前用户主目录
    // Windows 示例:C:\Users\TD\saa\skills
    // Linux/Mac 示例:/home/td/saa/skills
    .userSkillsDirectory(System.getProperty("user.home") + "/saa/skills")
    
    // 设置项目技能目录(项目特有技能,优先级高于用户技能)
    // 如果用户技能和项目技能有同名,项目技能会覆盖用户技能
    // 当前项目路径:D:/java/ai/spring-ai-alibaba/skills
    .projectSkillsDirectory("D:/java/ai/spring-ai-alibaba/skills")
    
    // 启用自动加载(默认值为 true)
    // true = 在 build() 时立即扫描并加载两个目录下的所有技能
    // false = 不自动加载,需要手动调用 reload() 方法加载
    .autoLoad(true)
    .build();

默认的系统提示词模板:

/**
 * FileSystemSkillRegistry 的默认系统提示词模板。
 * 该模板定义了技能在系统提示词中的展示方式。
 * 支持以下变量:
 * - {skills_registry}:注册表类型名称
 * - {skills_list}:格式化后的可用技能列表
 * - {skills_load_instructions}:技能加载说明
 */
public static final String DEFAULT_SYSTEM_PROMPT_TEMPLATE = """

            ## 技能系统

            你可以使用一套技能库,它提供专业能力与领域知识支持。所有技能均存储在基于文件系统的技能注册表中。

            ### 可用技能

            {skills_list}

            ### 技能使用方式(渐进式披露)

            技能遵循**渐进式披露**模式——你已知晓技能的存在(名称+描述),但仅在需要时才读取完整指令:

            1. **判断技能适用场景**:检查用户任务是否匹配任一技能的描述
            2. **读取技能完整指令**:上方技能列表中会给出可用于 `read_skill` 的精确技能 ID
            3. **遵循技能执行流程**:SKILL.md 中包含分步工作流、最佳实践与示例
            4. **访问配套文件**:技能可能包含 Python 脚本、配置或参考文档,请使用绝对路径访问

            #### 如何读取完整技能指令

            当前使用的是基于文件系统的技能注册表,请按照下方加载规则执行:

            {skills_load_instructions}

            **重要说明:**

              - **对于 SKILL.md(技能说明文件)**:必须使用 `read_skill` 读取技能指令,禁止通过其他方式访问 SKILL.md
              - **对于技能使用的其他配套文件(脚本、参考文档等)**:可根据需要使用其他合适工具读取或访问,一律使用技能列表中的绝对路径

            #### 技能适用场景

              - 用户请求与技能领域匹配时(例如:“调研XX”→对应 web-research 技能)
              - 需要专业领域知识或结构化工作流时
              - 技能提供成熟方案可用于处理复杂任务时

            #### 技能自包含说明

              - 每个 SKILL.md 都会明确说明技能功能与使用方法
              - 上方技能列表展示了每个技能 SKILL.md 文件的完整路径

            #### 执行技能脚本

            技能可能包含 Python 脚本或其他可执行文件,请一律使用技能列表中的绝对路径运行。

            ### 流程示例

            用户:“你能调研一下量子计算的最新进展吗?”

            1. 查看上方可用技能 → 找到 `web-research` 技能及其 ID
            2. 使用列表中展示的 ID 读取该技能
            3. 遵循技能的调研流程(搜索→整理→归纳)
            4. 使用绝对路径调用相关辅助脚本

            记住:技能是提升你能力与输出一致性的工具。如有疑问,先检查是否存在对应任务的技能!
            """;

2.2.4 查询、管理技能

查看技能列表:

// 列出所有技能
List<SkillMetadata> allSkills = registry.listAll();

// 获取技能数量
int count = registry.size();

返回如下:

在这里插入图片描述

其中 SkillMetadata 包含的字段:

字段 说明
name 技能名称(如 inventory_management
description 技能描述
skillPath 技能目录的绝对路径
source 来源(userproject
allowedTools 允许使用的工具列表
fullContent SKILL.md 完整内容

查找技能:

// 按名称查找
Optional<SkillMetadata> skill = registry.get("inventory_management");

// 按路径查找
Optional<SkillMetadata> skillByPath = registry.getByPath("C:/Users/TD/saa/skills/inventory_management");

// 搜索(按名称、描述或路径)
List<SkillMetadata> results = registry.search("inventory");

// 检查是否存在
boolean exists = registry.contains("sales_analytics");

读取技能内容:

// 按名称读取 SKILL.md 完整内容
String content = registry.readSkillContent("inventory_management");

// 按路径读取
String contentByPath = registry.readSkillContentByPath("/path/to/skill");

查看注册表信息:

// 获取注册表类型(如 "FileSystem" 或 "Classpath")
String type = registry.getRegistryType();

// 获取技能加载说明
String instructions = registry.getSkillLoadInstructions();

// 获取系统提示模板
SystemPromptTemplate template = registry.getSystemPromptTemplate();

技能状态管理:

// 禁用技能
registry.disable("inventory_management");

// 按路径禁用
registry.disableByPath("/path/to/skill");

// 检查是否已禁用
boolean isDisabled = registry.isDisabled("inventory_management");

重新加载技能:

// 重新扫描目录并加载技能
registry.reload();

生产环境中,技能常配合 Python 脚本、Shell 命令 实现真实业务能力,以下为完整集成方案:

// 1. 加载 Classpath 技能
SkillRegistry registry = ClasspathSkillRegistry.builder()
        .classpathPath("skills")
        .build();

// 2. 技能 Hook
SkillsAgentHook skillsHook = SkillsAgentHook.builder()
        .skillRegistry(registry)
        .build();

// 3. Shell 工具 Hook(指定工作目录)
ShellToolAgentHook shellHook = ShellToolAgentHook.builder()
        .shellTool2(ShellTool2.builder(System.getProperty("user.dir")).build())
        .build();

// 4. 构建 Agent:技能 + Shell + Python
ReactAgent agent = ReactAgent.builder()
        .name("skills-integration-agent")
        .model(chatModel)
        .saver(new MemorySaver())
        .tools(PythonTool.createPythonToolCallback(PythonTool.DESCRIPTION))
        .hooks(List.of(skillsHook, shellHook))
        .enableLogging(true)
        .build();

// 5. 执行:模型先 read_skill,再调用 Python/Shell 处理文件
String pdfPath = "/skills/pdf-extractor/saa-roadmap.pdf";
agent.call("请从 " + pdfPath + " 中提取关键信息");

2.3 技能钩子

使用 SkillsAgentHookSkillRegistry 传递给 ReactAgent

SkillsAgentHook 提供的功能:

  • 自动注入 read_skill 工具 - LLM 可以用它读取技能详情
  • 自动注入 search_skills 工具 - 搜索可用技能
  • 自动注入 disable_skill 工具 - 禁用技能
  • 注册 SkillsInterceptor - 在系统提示中添加技能列表

必选配置:

配置项 类型 说明
skillRegistry SkillRegistry 技能注册表实例,必须提供。通常使用 FileSystemSkillRegistry.builder() 创建

可选配置:

配置项 类型 默认值 说明
autoReload boolean false 是否在每次 agent 调用前自动重新加载技能
groupedTools Map<String, List<ToolCallback>> 空 Map 技能名到工具列表的映射,将工具与技能绑定,实现按需暴露工具:仅在模型调用 read_skill 后,对应工具才会生效。

示例代码:

// 2. 创建 SkillsAgentHook
SkillsAgentHook hook = SkillsAgentHook.builder()
    .skillRegistry(registry)           // 必选:技能注册表
    .autoReload(true)                  // 可选:启用自动重载
    .build();

2.4 商品文案写作、选品分析

Agent 配置类:

@Configuration
class ClientConfig {

    @Bean("chatAgent")
    public ReactAgent chatAgent(ZhiPuAiChatModel zhiPuAiChatModel) throws GraphRunnerException {

        // 1. 创建 SkillRegistry
        SkillRegistry registry = FileSystemSkillRegistry.builder()
                .userSkillsDirectory(System.getProperty("user.home") + "/saa/skills")
                .projectSkillsDirectory("D:/java/ai/skills")
                .autoLoad(true)
                .build();


        // 2. 创建 Hook
        SkillsAgentHook hook = SkillsAgentHook.builder()
                .skillRegistry(registry)           // 必选:技能注册表
                .autoReload(true)                  // 可选:启用自动重载
                .build();

        // 3. 创建 agent
        return ReactAgent.builder()
                .name("chat_agent") // 指定名称
                .model(zhiPuAiChatModel) // 指定 ChatModel
                .hooks(hook) // 指定 Skill Hook
                .enableLogging(true)
                .build();
    }

}

商品文案写作输出结果:

@SpringBootTest
class ReactAgentTest {

    @Autowired
    private ReactAgent chatAgent;

    @Test
    void testCopywriting() throws Exception {
        Optional<OverAllState> result = chatAgent.invoke("请为这款咖啡杯写一个营销文案:容量 350ml,陶瓷材质,简约设计,适合办公室使用");
        printResult(result);
    }

    @Test
    void testProductSelection() throws Exception {
        Optional<OverAllState> result = chatAgent.invoke("现在市场上智能家居设备很火,年轻人喜欢便捷生活,请分析应该选什么品类");
        printResult(result);
    }

    private void printResult(Optional<OverAllState> result) {
        if (result.isPresent()) {
            System.out.println(result.get());
        }
    }
}

testCopywriting 输出结果:

{
  "OverAllState": {
    "data": {
      "_graph_execution_id_": "d877f9f0-f2bb-4d72-b148-b0a3e49055f3",
      "input": "请为这款咖啡杯写一个营销文案:容量 350ml,陶瓷材质,简约设计,适合办公室使用",
      "messages": [
        {
          "messageType": "USER",
          "metadata": {
            "messageType": "USER"
          },
          "media": [],
          "text": "请为这款咖啡杯写一个营销文案:容量 350ml,陶瓷材质,简约设计,适合办公室使用"
        },
        {
          "text": "\n我来为这款咖啡杯写一个营销文案。让我先查看文案写作技能的详细指导。\n",
          "toolCalls": [
            {
              "id": "call_-7703135548885431832",
              "name": "read_skill",
              "type": "function",
              "arguments": "{\"skill_name\":\"copywriting\"}"
            }
          ],
          "metadata": {
            "finishReason": "TOOL_CALLS",
            "id": "202604181702407bf1d5b8015a4ded",
            "role": "ASSISTANT",
            "messageType": [
              "org.springframework.ai.chat.messages.MessageType",
              "ASSISTANT"
            ]
          }
        },
        {
          "responses": [
            {
              "id": "call_-7703135548885431832",
              "name": "read_skill",
              "responseData": "\"# 商品文案写作\r\n\r\n你是一个商品文案写作专家。根据用户提供的商品信息,输出简短的营销文案。\r\n\r\n## 输出要求\r\n- 只输出10个字以内的文案\r\n- 格式:「文案:XXX」\""
            }
          ],
          "metadata": {
            "messageType": "TOOL"
          },
          "messageType": "TOOL",
          "text": ""
        },
        {
          "text": "\n根据您提供的咖啡杯信息,我来为您创作营销文案:\n\n**文案:职场好伴侣**",
          "toolCalls": [],
          "metadata": {
            "finishReason": "STOP",
            "id": "20260418170241c52c524468f041ea",
            "role": "ASSISTANT",
            "messageType": [
              "org.springframework.ai.chat.messages.MessageType",
              "ASSISTANT"
            ]
          }
        }
      ]
    }
  }
}

2.5 PDF 提取器

scripts/AI Agent 技能(Skill)标准工程中的核心可执行代码目录,用于存放执行时需要调用的确定性、可复用脚本,是技能的工具库

95% 场景只用 4 种语言:

  • Python
  • Shell / Bash
  • JavaScript (Node.js)
  • PowerShellWindows 专用)

提示pdf-extractor 中就包含了 Python 脚本,使用起来比只有 SKILL.md 的技能肯定需要添加执行脚本的能力。

pdf-extractorSKILL.md 内容:

---
名称:PDF提取器
描述:从PDF文档中提取文本、表格与表单数据,用于分析和处理。当用户要求提取、解析或分析PDF文件时使用。
---

# PDF提取器技能

你是一名专业的PDF提取专员。当用户要求从PDF文档中提取数据时,请遵循以下说明。

## 使用说明

1. **校验输入**
   - 确认已提供PDF文件路径
   - PDF文件默认路径为当前工作目录
   - 使用 `shell` 或 `read_file` 工具检查文件是否存在
   - 验证文件为有效PDF格式

2. **提取内容**
   - 通过 `shell` 工具执行提取脚本:
     ```bash
     python scripts/extract_pdf.py <pdf_file_path>
     ```
   - 脚本将以JSON格式输出提取到的数据

3. **处理结果**
   - 解析脚本输出的JSON数据
   - 将数据整理为易读格式
   - 处理各类编码问题(UTF-8、特殊字符等)

4. **呈现输出**
   - 对提取内容进行概括说明
   - 按用户要求的格式展示数据(JSON、Markdown、纯文本)
   - 标注出现的问题或使用限制

## 脚本位置

提取脚本路径:
`scripts/extract_pdf.py`

## 输出格式

脚本返回JSON结构:
```json
{
  "success": true,
  "filename": "report.pdf",
  "text": "完整文本内容……",
  "page_count": 10,
  "tables": [
    {
      "page": 1,
      "data": [["表头1", "表头2"], ["数值1", "数值2"]]
    }
  ],
  "metadata": {
    "title": "文档标题",
    "author": "作者姓名",
    "created": "2024-01-01"
  }
}

pdf-extractor 可以通过 Shell 执行 Python 脚本,本地除了需要 Python 环境外,还需要添加 ShellToolAgentHook 自动注入 shell 命令执行工具,并在 Agent 执行前后自动管理 Shell 会话的初始化和清理:

        // 3. 创建 ShellToolAgentHook (支持 shell 命令执行)
        ShellToolAgentHook shellHook = ShellToolAgentHook.builder()
                .build();

        // 4. 创建 agent,同时添加两个 Hook
        ReactAgent agent = ReactAgent.builder()
                .name("chat_agent")
                .model(zhiPuAiChatModel)
                .hooks(skillsHook, shellHook)  // 添加 Skills 和 Shell 两个 Hook
                .enableLogging(true)
                .build();

单元测试:

    @Test
    void testPdfExtractor() throws Exception {
        Optional<OverAllState> result = chatAgent.invoke("从 D:/java/ai/skills/pdf-extractor/saa-roadmap.pdf 中提取内容");
        printResult(result);
    }
// 1. 绑定工具到指定技能(key = 技能name)
Map<String, List<ToolCallback>> groupedTools = Map.of(
        "pdf-extractor",
        List.of(pdfParseTool, fileReadTool)
);

// 2. 注入分组工具
SkillsAgentHook hook = SkillsAgentHook.builder()
        .skillRegistry(registry)
        .groupedTools(groupedTools)
        .build();

3. 自定义 SkillRegistry 实现

只需实现 SkillRegistry 接口,即可扩展技能来源(DBNacosGit 等):

  • get() / listAll():获取技能信息
  • readSkillContent():读取 SKILL.md
  • reload():重载技能(可选)
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐