跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaAIjava

Spring AI Agent Skills 接入指南与原理剖析

Spring AI Agent Skills 允许开发者通过定义 SKILL.md 文件为 AI 模型扩展模块化能力。本文演示了如何在 Spring Boot 项目中集成该功能,包括 Maven 依赖配置、Skill 文件规范及 ChatClient 工具注册流程。同时深入解析了底层加载元数据、构建 ToolCallback 及函数调用的实现机制,帮助开发者理解技能分发的核心逻辑。

片刻发布于 2026/4/8更新于 2026/10/25101 浏览
Spring AI Agent Skills 接入指南与原理剖析

Spring AI Agent Skills 接入指南与原理剖析

引言

Agent Skills 是 Spring AI 中一种将模块化、可复用能力注入 Agent 的机制。本文聚焦于如何在 Spring AI 项目中快速接入 Skills,并深入探究其背后的实现原理。

环境准备

Maven 依赖

根据官方文档,Skill 功能需要 Spring AI 2.0.0-M2 及以上版本。以下是项目所需的依赖配置:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.2</version>
    <relativePath/>
</parent>
<properties>
    <java.version>21</java.version>
    <spring-ai.version>2.0.0-M2</spring-ai.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <>org.springframework.ai
        spring-ai-starter-model-openai
    
    
    
        org.springaicommunity
        spring-ai-agent-utils
        0.4.2
    


    
        
            org.springframework.ai
            spring-ai-bom
            ${spring-ai.version}
            pom
            import
        
    


    
        spring-milestones
        Spring Milestones
        https://repo.spring.io/milestone
    

groupId
</groupId>
<artifactId>
</artifactId>
</dependency>
<!-- 引入社区实现的 skills 工具 -->
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
<type>
</type>
<scope>
</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>
</id>
<name>
</name>
<url>
</url>
</repository>
</repositories>

实测环境下,Spring Boot 3.5.10、JDK 17 配合 Spring AI 1.1.2 也能跑通 Demo,具体兼容性视实际场景而定。

配置文件

server:
  port: 8080
spring:
  application:
    name: pocketmind-server
  ai:
    chat:
      client:
        observations:
          log-prompt: true
          log-completion: true
        openai:
          api-key: xxxx # 替换为你的 API Key
          base-url: xxxx # 替换为你的 Base URL,不需要 /v1
      options:
        model: deepseek-chat # 替换为你使用的模型名称

示例 Demo 采用 OpenAI 兼容 API,如需兼容 Anthropic,请参照对应文档切换配置。

示例代码

Skill 定义文件

在根目录下创建对应的 skill 目录,结构如下:

my-skill/
├── SKILL.md       # Required: instructions + metadata
├── scripts/       # Optional: executable code
├── references/    # Optional: documentation
└── assets/        # Optional: templates, resources

SKILL.md 文件中至少应包含元信息和详细的说明文档,格式参考如下:

---
name: code-reviewer
description: Reviews Java code for best practices, security issues, and Spring Framework conventions. Use when user asks to review, analyze, or audit code
---
# Code Reviewer

## Instructions
When reviewing code:
1. Check **for** security vulnerabilities (SQL injection, XSS, etc.)
2. Verify Spring Boot best practices (proper use of @Service, @Repository, etc.)
3. Look **for** potential null pointer exceptions
4. Suggest improvements **for** readability and maintainability
5. Provide specific line-by-line feedback with code examples

![技能目录结构示意]

Controller 实现

import org.springaicommunity.agent.tools.FileSystemTools;
import org.springaicommunity.agent.tools.ShellTools;
import org.springaicommunity.agent.tools.SkillsTool;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;
import java.util.Map;

@RestController
@RequestMapping("/demo")
public class SkillController {
    private final ChatClient chatClient;

    public SkillController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder
                .defaultToolCallbacks(SkillsTool.builder()
                        .addSkillsDirectory(".claude/skills") // 也可以使用下面这个
                        // .addSkillsResource(resourceLoader.getResource("classpath:.claude/skills"))
                        .build())
                .defaultTools(FileSystemTools.builder().build())
                .defaultTools(ShellTools.builder().build())
                .defaultToolContext(Map.of("foo", "bar"))
                .build();
    }

    /**
     * 测试 skill 流程
     * @param message 用户的输入
     * @return
     */
    @PostMapping("/skill")
    public String chat(@RequestBody String message) {
        return chatClient.prompt().user(message).call().content();
    }
}

此时运行程序,访问对应端口即可查看返回内容。

代码逻辑解析

  1. 声明与注入:首先声明一个 ChatClient 并通过 DI 进行注入。
  2. 构建策略:通过 chatClientBuilder 进行 Builder 模式构建。
    • .defaultToolCallbacks(...):给 ChatClient 注册一个'已经组装好'的工具包(包含代码逻辑 + JSON Schema 描述),此处即为注册 Skill 功能。
    • .defaultTools():注册系统工具名称,用于动态发现和使用 Skill。
    • .defaultToolContext(Map.of("foo", "bar")):添加工具上下文。这是为了防止框架报错,需要传入一个 Map 作为 ToolContext,否则无法正常 build,属于框架当前的限制点。
  3. 请求构建:通过链式调用构建 LLM 的请求。
    • .user(message):加载用户提示词。
    • .call():由框架内部发送请求。
    • .content():获取大模型返回的内容。

源码分析

目录配置

SkillsTool 类提供了灵活的配置入口,支持添加多个 Skill 路径:

public static class Builder {
    private List<Skill> skills = new ArrayList<>();
    private String toolDescriptionTemplate = TOOL_DESCRIPTION_TEMPLATE;
    
    protected Builder() {}
    
    public Builder toolDescriptionTemplate(String template) {
        this.toolDescriptionTemplate = template;
        return this;
    }
    
    public Builder addSkillsResources(List<Resource> skillsRootPaths) {
        for (Resource skillsRootPath : skillsRootPaths) {
            this.addSkillsResource(skillsRootPath);
        }
        return this;
    }
    
    public Builder addSkillsResource(Resource skillsRootPath) {
        try {
            String path = skillsRootPath.getFile().toPath().toAbsolutePath().toString();
            this.addSkillsDirectory(path);
        } catch (IOException ex) {
            throw new RuntimeException("Failed to load skills from directory: " + skillsRootPath, ex);
        }
        return this;
    }
    
    public Builder addSkillsDirectory(String skillsRootDirectory) {
        this.addSkillsDirectories(List.of(skillsRootDirectory));
        return this;
    }
    
    public Builder addSkillsDirectories(List<String> skillsRootDirectories) {
        for (String skillsRootDirectory : skillsRootDirectories) {
            try {
                this.skills.addAll(skills(skillsRootDirectory));
            } catch (IOException ex) {
                throw new RuntimeException("Failed to load skills from directory: " + skillsRootDirectory, ex);
            }
        }
        return this;
    }
    // ... 省略其他方法
}

其中 toolDescriptionTemplate 用于定义 Skill 的描述模板。

加载 Skill 元数据

这是加载器的核心入口。它会递归扫描指定文件夹下的 SKILL.md 文件:

private static List<Skill> skills(String rootDirectory) throws IOException {
    Path rootPath = Paths.get(rootDirectory);
    if (!Files.exists(rootPath)) {
        throw new IOException("Root directory does not exist: " + rootDirectory);
    }
    if (!Files.isDirectory(rootPath)) {
        throw new IOException("Path is not a directory: " + rootDirectory);
    }
    List<Skill> skillFiles = new ArrayList<>();
    try (Stream<Path> paths = Files.walk(rootPath)) {
        paths.filter(Files::isRegularFile)
             .filter(path -> path.getFileName().toString().equals("SKILL.md"))
             .forEach(path -> {
                 try {
                     String markdown = Files.readString(path, StandardCharsets.UTF_8);
                     MarkdownParser parser = new MarkdownParser(markdown);
                     skillFiles.add(new Skill(path, parser.getFrontMatter(), parser.getContent()));
                 } catch (IOException e) {
                     throw new RuntimeException("Failed to read SKILL.md file: " + path, e);
                 }
             });
    }
    return skillFiles;
}

这里主要提取两部分内容:

  • FrontMatter (YAML 头):包含技能的名字(如 name: pdf)和描述。这部分会被提取出来,告诉 AI '我有这个技能'。
  • Content (正文):具体的 Prompt 指令(比如'处理 PDF 的步骤是:1. 转换文本… 2. 提取摘要…')。

添加 Skill 技能

此步骤会将扫描到的技能列表编织进工具的描述里:

public ToolCallback build() {
    Assert.notEmpty(this.skills, "At least one skill must be configured");
    String skillsXml = this.skills.stream()
            .map(s -> s.toXml())
            .collect(Collectors.joining("\n"));
    return FunctionToolCallback.builder("Skill", new SkillsFunction(toSkillsMap(this.skills)))
            .description(this.toolDescriptionTemplate.formatted(skillsXml))
            .inputType(SkillsInput.class)
            .build();
}

当 AI 看到这个工具时,它的 Prompt 里会出现你定义过的 Skill 列表,例如:

  • <skill><name>pdf</name><description>Extract text from PDF</description></skill>
  • <skill><name>git</name><description>Git version control</description></skill>

调用 Skill

当 AI 决定调用 Skill("pdf") 时,实际上触发了这段逻辑:

public static class SkillsFunction implements Function<SkillsInput, String> {
    private Map<String, Skill> skillsMap;
    
    public SkillsFunction(Map<String, Skill> skillsMap) {
        this.skillsMap = skillsMap;
    }
    
    @Override
    public String apply(SkillsInput input) {
        Skill skill = this.skillsMap.get(input.command());
        if (skill != null) {
            var skillBaseDirectory = skill.path().getParent().toString();
            return "Base directory for this skill: %s\n\n%s".formatted(skillBaseDirectory, skill.content());
        }
        return "Skill not found: " + input.command();
    }
}

此时返回的是'路径'和'正文内容',于是 AI 读到返回的文字后,会发现这是一份'Code Review 的操作指南'。

至此,Skill 的机制已经完整实现,AI 只需要根据返回的 SKILL.md 就可以调用对应的说明或者 reference/scripts 下面的技能。

目录

  1. Spring AI Agent Skills 接入指南与原理剖析
  2. 引言
  3. 环境准备
  4. Maven 依赖
  5. 配置文件
  6. 示例代码
  7. Skill 定义文件
  8. Code Reviewer
  9. Instructions
  10. Controller 实现
  11. 代码逻辑解析
  12. 源码分析
  13. 目录配置
  14. 加载 Skill 元数据
  15. 添加 Skill 技能
  16. 调用 Skill

更多推荐文章

查看全部
  • K-means 聚类算法原理与实现详解
  • MySQL DQL 全面解析
  • 基于 Rokid 灵珠平台搭建旅游 AR 智能体实战指南
  • Webots 导入 STL 模型及尺寸颜色调整指南
  • Android 快递物流信息布局实现详解
  • SonarQube 跳过指定代码检查方法
  • Diffusion Transformer (DiT) 详解:从 U-Net 到 ViT,应用于视频生成与机器人动作预测
  • OpenAI Whisper 本地高精度语音转文字部署指南
  • Python 调用高德地图 MCP 服务查询天气实战
  • 高性能C++服务多线程资源调度优化实战
  • 现代 C++ 高性能实践:右值引用、并发编程与协程解析
  • Windows 10 系统 Python 3.10 详细安装与环境配置指南
  • 鸿蒙金融理财全栈项目:生态合作、用户运营与数据变现
  • Python 异步爬虫实战:Playwright 与 aiohttp 爬取科技媒体文章
  • VSCode Copilot 认证失败排查与修复指南
  • Python 毕业生就业追踪系统 Vue3 架构与实现
  • Linux 核心 IO 模型深析:CMake 构建与 Poll 多路转接实现
  • FPGA 入门:基于 LED 的 2 选 1 多路选择器设计
  • 大模型幻觉深度治理:技术体系、工程实践与未来演进
  • Linux 命名管道与共享内存基础

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online