1、简述
在 Spring AI 2.0 中,Skills 通过 SkillsTool 将本地 SKILL.md 文件注册为工具,让 Agent 按预设流程工作,而不是依赖临场发挥的 Prompt 。核心路径是:定义 Skill 文件 → 用 SkillsTool 加载 → 注册为 ToolCallback → 通过 ChatClient.tools() 让模型调用。
2、代码
设计三个实用的 Skills :查天气 、翻译 、写作。
添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ybw</groupId>
<artifactId>skills-ai-demo</artifactId>
<version>1.0.0</version>
<name>skills-ai-demo</name>
<description>skills-ai-demo</description>
<modules>
<module>quick-start</module>
</modules>
<packaging>pom</packaging>
<properties>
<java.version>21</java.version>
<spring-ai.version>2.0.0</spring-ai.version>
<mybatis-plus.version>3.5.15</mybatis-plus.version>
<fastjson2.version>2.0.62</fastjson2.version>
<hutool-all.version>5.8.36</hutool-all.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!--AI start-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-agent-utils</artifactId>
</dependency>
<!--AI end-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<!--工具-->
<!-- Source: https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
</dependencies>
<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>
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-agent-utils-bom</artifactId>
<version>0.10.0</version> <!-- 请检查并使用最新稳定版本 -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
天气查询 Skill
SKILL.md 文件
位置:src/main/resources/skills/weather-skill/SKILL.md
---
name: weather-skill
description: 查询任意城市的实时天气信息,支持摄氏度/华氏度切换。当用户询问天气、气温、降水、风速等信息时使用。
---
# Weather Query Skill
## 工作流程
1. **解析城市**:从用户输入中提取城市名称(支持中英文)。
2. **确定单位**:默认摄氏度(℃),用户明确说"华氏度"时才切换。
3. **调用工具**:执行 `getWeather` 工具获取数据。
4. **输出格式**:
- 当前温度、体感温度
- 天气状况(晴/多云/雨等)
- 湿度、风速
- 简短穿衣建议
## 边界
- 仅支持中国主要城市(北京、上海、广州等),小城市返回"暂不支持"。
- 信息来自公开 API,仅作参考,不保证 100% 准确。
Java 工具实现
package com.example.demo.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Component
public class WeatherTools {
// 模拟天气 API(实际可用高德、OpenWeatherMap 等)
private static final Map<String, Map<String, Object>> WEATHER_DB = Map.of(
"北京", Map.of("temp", 28, "condition", "晴", "humidity", 45, "wind", 12),
"上海", Map.of("temp", 32, "condition", "多云", "humidity", 70, "wind", 8),
"广州", Map.of("temp", 35, "condition", "雷阵雨", "humidity", 85, "wind", 15)
);
@Tool(description = "查询城市天气,返回 JSON 格式数据")
public String getWeather(String city) {
if (city == null || city.isBlank()) {
return "{\"error\": \"请提供城市名称\"}";
}
// 去除空格,支持 "北京 " -> "北京"
city = city.trim();
if (!WEATHER_DB.containsKey(city)) {
return String.format("{\"error\": \"暂不支持城市:%s\"}", city);
}
Map<String, Object> data = WEATHER_DB.get(city);
return String.format("""
{
"city": "%s",
"temperature": %d,
"condition": "%s",
"humidity": %d,
"wind_speed": %d
}""", city, data.get("temp"), data.get("condition"),
data.get("humidity"), data.get("wind"));
}
}
翻译 Skill
SKILL.md 文件
位置:src/main/resources/skills/translate-skill/SKILL.md
---
name: translate-skill
description: 多语言翻译工具,支持中英日韩法德互译。当用户要求翻译、translate、把某段文字译成某语言时使用。
---
# Translation Skill
## 工作流程
1. **识别方向**:从用户指令提取源语言和目标语言(默认源语言自动检测,目标语言为中文)。
2. **提取文本**:提取需要翻译的具体内容,忽略语气词。
3. **调用工具**:执行 `translate` 工具。
4. **输出**:
- 翻译结果
- 保留原格式(段落、标点)
- 必要时附上"直译"和"意译"两个版本
## 特殊规则
- 专业术语保留英文(如 API、AI、GPU)。
- 代码块、URL 不翻译,原样保留。
- 文化梗(网络用语)做适当本地化。
Java 工具实现
package com.example.demo.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class TranslateTools {
// 模拟翻译(实际可集成 DeepL、Google Translate API)
private static final Map<String, String> MOCK_TRANSLATIONS = Map.of(
"Hello world", "你好世界",
"How are you", "你好吗",
"I love programming", "我热爱编程"
);
@Tool(description = "翻译文本,支持中英互译")
public String translate(String sourceLang, String targetLang, String text) {
if (text == null || text.isBlank()) {
return "{\"error\": \"请提供待翻译文本\"}";
}
// 简单模拟
String result = MOCK_TRANSLATIONS.getOrDefault(text,
String.format("[模拟翻译] 从 %s 到 %s:%s", sourceLang, targetLang, text));
return String.format("""
{
"source_lang": "%s",
"target_lang": "%s",
"original": "%s",
"translation": "%s",
"note": "此为模拟翻译,实际请接入翻译 API"
}""", sourceLang, targetLang, text, result);
}
}
写作 Skill
SKILL.md 文件
位置:src/main/resources/skills/writing-skill/SKILL.md
---
name: writing-skill
description: 辅助写作工具,支持文章生成、润色、续写、大纲生成。当用户要求写文章、润色、生成大纲、续写时使用。
---
# Writing Assistance Skill
## 工作流程
1. **理解需求**:识别写作类型(议论文、记叙文、商业邮件、技术文档等)。
2. **收集要素**:
- 主题、字数要求、目标读者
- 风格偏好(正式/轻松/幽默/科技感)
3. **调用工具**:执行 `generateOutline` 和 `writeContent` 工具。
4. **输出结构**:
- 先给大纲(让用户确认)
- 再生成正文
- 最后附上修改建议
## 风格指南
- 技术文档:专业、结构化、带代码示例。
- 商业邮件:简洁、礼貌、有明确行动项。
- 创意写作:生动、有画面感、避免套话。
## 边界
- 不生成违反法律法规的内容。
- 如果主题涉及医疗、法律等专业领域,声明"建议咨询专业人士"。
Java 工具实现
package com.example.demo.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class WritingTools {
@Tool(description = "根据主题生成文章大纲,返回层级列表")
public String generateOutline(String topic, String writingType) {
if (topic == null || topic.isBlank()) {
return "{\"error\": \"请提供写作主题\"}";
}
// 模拟大纲生成
return String.format("""
{
"topic": "%s",
"type": "%s",
"outline": [
{"level": 1, "title": "引言:为什么 %s 很重要"},
{"level": 2, "title": "核心概念解析"},
{"level": 2, "title": "实际应用场景"},
{"level": 3, "title": "案例一:..."),
{"level": 3, "title": "案例二:..."),
{"level": 1, "title": "总结与展望"}
]
}""", topic, writingType, topic);
}
@Tool(description = "根据主题和风格生成完整文章")
public String writeContent(String topic, String style, int wordCount) {
if (topic == null || topic.isBlank()) {
return "{\"error\": \"请提供写作主题\"}";
}
// 模拟文章生成
String content = String.format("""
# %s
## 引言
在当今快速发展的时代,%s 已经成为不可忽视的重要议题。
本文将从多个维度深入探讨%s,并为读者提供有价值的见解。
## 主体内容
(此处为模拟生成内容,篇幅限制,实际接入 LLM 生成完整文章)
## 结论
%s 将继续深刻影响我们的生活,值得持续关注。
---
**风格**:%s | **字数**:%d 字(模拟)
""", topic, topic, topic, topic, style, wordCount);
return String.format("""
{
"topic": "%s",
"style": "%s",
"word_count": %d,
"content": "%s",
"note": "此为模拟内容,实际可接入 LLM 生成"
}""", topic, style, wordCount, content.replace("\n", "\\n"));
}
}
统一配置:注册所有 ChatClient 和 Skills
package com.ybw.config;
import com.ybw.tool.TranslateTools;
import com.ybw.tool.WeatherTools;
import com.ybw.tool.WritingTools;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置 ChatClient
*
* @author ybw
* @version V1.0
* @className ChatClientConfig
* @date 2026/6/29
**/
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder,
ToolCallback skillsTool,
WeatherTools weatherTools,
TranslateTools translateTools,
WritingTools writingTools) {
return chatClientBuilder
.defaultSystem("""
你是一个通用助手,可以处理以下任务:
1. 查天气 → 使用 Skill(command="weather-skill") + getWeather 工具
2. 翻译 → 使用 Skill(command="translate-skill") + translate 工具
3. 写作 → 使用 Skill(command="writing-skill") + generateOutline/writeContent 工具
根据用户意图自动选择合适的 Skill,先用 Skill 加载流程,再调用对应的 Java 工具执行。
用中文友好回复。
""")
.defaultTools(skillsTool, weatherTools, translateTools, writingTools)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
}
package com.ybw.config;
import org.springaicommunity.agent.tools.SkillsTool;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class SkillToolConfig {
@Bean
public ToolCallback skillsTool() {
return SkillsTool.builder()
.addSkillsResource(new ClassPathResource("skills"))
.build();
}
}
统一服务入口
package com.ybw.service;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class MultiSkillService {
private final ChatClient chatClient;
public String handle(String userInput) {
return chatClient.prompt()
.user(userInput)
.call()
.content();
}
}
测试用例
package com.ybw.service;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@Slf4j
public class MultiSkillServiceTest {
@Resource
private MultiSkillService service;
@Test
void testWeather() {
String result = service.handle("北京今天天气怎么样?");
System.out.println(result);
// 输出:调用 weather-skill → getWeather("北京") → 返回天气报告
}
@Test
void testTranslate() {
String result = service.handle("把 'Hello world' 翻译成中文");
System.out.println(result);
// 输出:调用 translate-skill → translate("en", "zh", "Hello world")
}
@Test
void testWriting() {
String result = service.handle("帮我写一篇关于 Java 技术栈的文章,500字左右");
System.out.println(result);
// 输出:调用 writing-skill → generateOutline → writeContent
}
}
核心要点总结
| Skill | 触发场景 | 关联 Tool | 特殊处理 |
|---|---|---|---|
| 天气 | "XX天气"、"温度" | getWeather(city) |
支持中英文城市名、单位切换 |
| 翻译 | "翻译"、"translate" | translate(src, tgt, text) |
保留专业术语、代码块、URL |
| 写作 | "写文章"、"润色"、"大纲" | generateOutline() + writeContent() |
先出大纲确认,再生成正文 |
每个 Skill 的 SKILL.md 定义了标准作业流程 ,而 Java @Tool 负责实际执行 ,模型作为调度中心 ,根据用户意图自动选择最合适的 Skill 组合。这种设计让 AI 行为可预测、可追溯、可维护。