Spring AI 框架下接入 agent skill 手把手教程

Spring AI 框架下接入 agent skill 手把手教程
参考文档:Spring AI Agentic Patterns (Part 1): Agent Skills - Modular, Reusable Capabilities

引言

点进来的读者应该都了解了 agent skills 是什么,为什么会出现这种工程手段等等,此处不在多说,本篇博客聚焦于在 Spring-AI 下如何快速接入 Skills,并且探究背后实现的原理。
项目示例代码可以在 https://github.com/MimicHunterZ/PocketMind/tree/master/backend/src/main/java/com/doublez/pocketmindserver/demo 下查看,如果觉得项目不错,欢迎给我star~

环境准备

maven依赖

根据官方手册,skill 需要 Spring-AI 2.0.0-M2 版本以上,所以根据这个配置,项目demo的依赖如下:

<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><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><!--引入社区实现的 skills 工具--><dependency><groupId>org.springaicommunity</groupId><artifactId>spring-ai-agent-utils</artifactId><version>0.4.2</version></dependency></dependencies><dependencyManagement><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url></repository></repositories>
实测,Spring boot 3.5.10、jdk17、Spring AI 1.1.2 也可以跑通demo,不过不知道有没有更多的坑

yml配置

server:port:8080spring:application:name: pocketmind-server ai:chat:client:observations:log-prompt:truelog-completion:trueopenai:api-key: xxxx # 替换为你的 API Key base-url: xxxx # 替换为你的 Base URL 不需要 /v1 chat: options:model: deepseek-chat # 替换为你使用的模型名称 
示例demo采用 openai兼容的 api,如需兼容anthropic,那么根据对应文档进行切换即可

示例代码

skill.md

在根目录下添加对应的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

importorg.springaicommunity.agent.tools.FileSystemTools;importorg.springaicommunity.agent.tools.ShellTools;importorg.springaicommunity.agent.tools.SkillsTool;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.web.bind.annotation.*;importjava.util.Map;@RestController@RequestMapping("/demo")publicclassSkillController{privatefinalChatClient chatClient;publicSkillController(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")publicStringchat(@RequestBodyString 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")) 添加工具上下文,防止报错
    • .defaultToolContext(Map.of("foo", "bar")) 这个是为了框架报错,需要添加一个map传入作为ToolContext,否则无法正常build,为框架缺陷
  3. 通过链条进行构建llm的request
    • .user(message) 加载用户提示词
    • .call() 由框架内部发其请求
    • .content() 获取大模型返回的内容

源码分析

0. 设置目录:

publicclassSkillsTool{//...publicstaticclassBuilder{privateList<Skill> skills =newArrayList<>();privateString toolDescriptionTemplate = TOOL_DESCRIPTION_TEMPLATE;protectedBuilder(){}publicBuildertoolDescriptionTemplate(String template){this.toolDescriptionTemplate = template;returnthis;}publicBuilderaddSkillsResources(List<Resource> skillsRootPaths){for(Resource skillsRootPath : skillsRootPaths){this.addSkillsResource(skillsRootPath);}returnthis;}publicBuilderaddSkillsResource(Resource skillsRootPath){try{String path = skillsRootPath.getFile().toPath().toAbsolutePath().toString();this.addSkillsDirectory(path);}catch(IOException ex){thrownewRuntimeException("Failed to load skills from directory: "+ skillsRootPath, ex);}returnthis;}publicBuilderaddSkillsDirectory(String skillsRootDirectory){this.addSkillsDirectories(List.of(skillsRootDirectory));returnthis;}publicBuilderaddSkillsDirectories(List<String> skillsRootDirectories){for(String skillsRootDirectory : skillsRootDirectories){try{this.skills.addAll(skills(skillsRootDirectory));}catch(IOException ex){thrownewRuntimeException("Failed to load skills from directory: "+ skillsRootDirectory, ex);}}returnthis;}//...}//...}
  • addSkillsResourceaddSkillsDirectory 添加 skill 的路径,支持多个

toolDescriptionTemplate: 添加 skill 描述说明

在这里插入图片描述

1. 加载 skill 元数据

这是加载器的入口。它会去你指定的文件夹里找 SKILL.md 文件。
/** * Recursively finds all SKILL.md files in the given root directory and returns their * parsed contents. * @param rootDirectory the root directory to search for SKILL.md files * @return a list of SkillFile objects containing the path, front-matter, and content * of each SKILL.md file * @throws IOException if an I/O error occurs while reading the directory or files */privatestaticList<Skill>skills(String rootDirectory)throwsIOException{Path rootPath =Paths.get(rootDirectory);if(!Files.exists(rootPath)){thrownewIOException("Root directory does not exist: "+ rootDirectory);}if(!Files.isDirectory(rootPath)){thrownewIOException("Path is not a directory: "+ rootDirectory);}List<Skill> skillFiles =newArrayList<>();try(Stream<Path> paths =Files.walk(rootPath)){ paths.filter(Files::isRegularFile).filter(path -> path.getFileName().toString().equals("SKILL.md"))// 遍历目录.forEach(path ->{try{// 解析文件:分为 FrontMatter (元数据) 和 Content (正文)String markdown =Files.readString(path,StandardCharsets.UTF_8);MarkdownParser parser =newMarkdownParser(markdown); skillFiles.add(newSkill(path, parser.getFrontMatter(), parser.getContent()));}catch(IOException e){thrownewRuntimeException("Failed to read SKILL.md file: "+ path, e);}});}return skillFiles;}
  • FrontMatter (YAML头):包含技能的名字(如 name: pdf)和描述。这部分会被提取出来,告诉 AI “我有这个技能”。
  • Content (正文):这是具体的 Prompt 指令(比如“处理 PDF 的步骤是:1. 转换文本… 2. 提取摘要…”)。
  1. t添加 skill 技能
publicToolCallbackbuild(){Assert.notEmpty(this.skills,"At least one skill must be configured");String skillsXml =this.skills.stream().map(s -> s.toXml()).collect(Collectors.joining("\n"));returnFunctionToolCallback.builder("Skill",newSkillsFunction(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>

3. 调用skill

当 AI 决定调用 Skill("pdf") 时,实际上触发了这段逻辑:
publicstaticclassSkillsFunctionimplementsFunction<SkillsInput,String>{privateMap<String,Skill> skillsMap;publicSkillsFunction(Map<String,Skill> skillsMap){this.skillsMap = skillsMap;}@OverridepublicStringapply(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 下面的技能。

如果读者对于spring ai 框架下 ai 怎么进行多次工具调用循环好奇,可以查看Spring ai下的工具调用以及循环调用

Read more

Stable-Diffusion-v1-5-archive部署指南:GPU加速+7860端口一键启用

Stable-Diffusion-v1-5-archive部署指南:GPU加速+7860端口一键启用 想快速体验经典AI绘画的魅力吗?Stable Diffusion v1.5 Archive(简称SD1.5)作为文生图领域的“常青树”,以其稳定的表现和广泛的社区支持,依然是许多创作者和开发者的首选。今天,我们就来手把手教你,如何在几分钟内完成这个经典模型的部署,并立即通过浏览器开始你的AI绘画创作。 1. 环境准备与快速部署 部署过程非常简单,几乎不需要任何复杂的配置。你只需要一个支持GPU的云服务器实例,然后按照以下步骤操作即可。 1.1 系统要求与准备工作 在开始之前,请确保你的环境满足以下基本要求: * 操作系统:推荐使用Ubuntu 20.04或更高版本,其他Linux发行版也可。 * GPU支持:需要NVIDIA GPU,并已安装好相应的CUDA驱动(建议CUDA 11.8及以上)。 * 网络:能够正常访问互联网,用于下载模型文件。 * 存储空间:至少预留10GB的可用磁盘空间。 如果你使用的是云服务商提供的GPU实例,通常这些环境都已经预配置好了,

DAMODEL平台|Llama 3.1 开源模型快速部署:从零到上线

DAMODEL平台|Llama 3.1 开源模型快速部署:从零到上线

文章目录 * 一、Llama 3.1 系列的演进与发展历程 * 二、大型语言模型的力量:Llama 3.1 405B * 三、Llama 3.1 405B 部署教程 * 四、Llama 3.1在客户服务中的运用 一、Llama 3.1 系列的演进与发展历程 自开源LLM(大语言模型)兴起以来,Meta公司凭借其Llama系列逐步在全球AI领域占据重要地位。2024年7月23日,Meta发布了Llama 3.1系列,标志着该系列在技术上的一次重要飞跃。 Llama 3.1的发布不仅在算法优化和性能提升方面做出了突破,还在数据处理和模型架构上进行了革新。随着版本的不断迭代,Llama系列逐步从最初的研究原型发展为一个功能强大、易于扩展的工具,深刻影响了开源AI生态的进步。 本篇文章将详细回顾Llama 3.1系列的演进历程,探讨其在开源领域的重要贡献以及未来发展的潜力。 这一成就的背后,是超过15万亿的Tokens和超过1.

知网AIGC检测不通过?三步搞定降AI率

知网AIGC检测不通过?三步搞定降AI率

知网AIGC检测不通过?三步搞定降AI率 “我论文在知网AIGC检测里被判了52%的AI率,学校要求低于30%才能过,我该怎么办?” 最近几个月,这类求助在毕业生群里几乎天天都能看到。2026年的知网AIGC检测系统已经升级了好几轮,检测精度比去年高了不少,很多以前能蒙混过关的方法现在都不管用了。 但这不意味着没有办法。这篇文章,我把降知网AI率的方法浓缩成三个步骤,每一步都讲清楚具体该怎么操作。不绕弯子,直接上干货。 开始之前:了解知网AIGC检测的特点 要打败对手,先要了解对手。知网的AIGC检测与其他平台相比,有几个显著的特点: 检测颗粒度细:知网不仅给出全文的AI率,还会对每个段落甚至每个句子进行逐一判定。它的检测报告会用颜色标注每一段的AI概率——红色(高概率AI生成)、橙色(疑似AI生成)、绿色(人类写作)。 对学术文本更敏感:知网的训练数据包含大量学术论文,所以它对学术写作风格的AI特征识别得更准。那种一看就是AI写的"学术腔"文字,在知网面前特别容易露馅。 更新频率快:知网的检测模型会定期更新。上个月能过的文本,这个月不一定能过。所以不要依赖"据说有用

llamafactory微调qwen3-vl详细流程

llamafactory微调qwen3-vl详细流程

llamafactory微调qwen3-vl详细流程 目标:本文讲详细介绍多模态大模型使用llama-factory进行多模态模型微调(sft)的全部流程,以及微调后合并和工业落地部署方案。具体包括: 1. 环境安装部署 2. 数据集准备 3. 启动微调 4. 模型合并 5. 模型部署和请求方式(vllm部署) 示例模型: qwen2.5-vl-instruct qwen3-vl-instruct 环境安装 llama-factory环境准备 方式1 git直接下载 git clone --depth https://github.com/hiyouga/LLaMA-Factory.git 方式2 下载项目压缩包再解压 python环境安装 1. python虚拟环境创建 * conda create --name llama_env python=3.12 (默认已安装好anaconda或者minianaconda) * conda