基于 Spring AI 构建智能餐厅推荐系统:多模型集成的实践指南
本文将教您如何使用 Spring AI 项目构建基于不同聊天模型的应用程序。Spring AI 聊天模型是一个简单易用的接口,允许我们与这些模型进行交互。我们的 Spring Boot 示例应用程序将在 OpenAI、Mistral AI 和 Ollama 提供的三种流行聊天模型之间切换,并展示如何使用 Spring AI 框架实现多轮对话、结构化输出等核心功能。
源代码
如果您想亲自尝试,可以随时查看我的源代码。为此,您必须克隆我的示例 GitHub 仓库。然后,您只需按照我的说明进行操作即可。
项目目标
使用 Spring AI 构建一个智能餐厅推荐系统,实现以下核心功能:
- ✅ 智能推荐:根据用户偏好推荐合适的餐厅
- ✅ 多轮对话:支持上下文对话,提供连贯的用户体验
- ✅ 结构化输出:返回标准化的 JSON 数据格式
- ✅ 多模型支持:支持多个 AI 模型提供商的无缝切换
- ✅ 中文支持:完美支持中文参数和响应
- ✅ 生产就绪:包含完整的配置管理和错误处理
环境搭建
技术栈
- Java 21:使用最新的 LTS 版本
- Spring Boot 3.5.6:提供企业级应用框架
- Spring AI 1.1.0-M2:AI 集成框架
- Maven:依赖管理和构建工具
- Lombok:简化 Java 代码
依赖配置
Spring AI 项目仍在积极开发中,当前使用里程碑版本 1.1.0-M2
。
<repositories>
<repository>
<id>central</id>
<name>Central</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
然后,我们应该包含指定版本的 Spring AI 项目的 Maven BOM。
<properties>
<java.version>21</java.version>
<spring-ai.version>1.1.0-M2</spring-ai.version>
</properties>
<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>
由于我们的示例应用暴露了一些 REST 端点,我们应该包含 Spring Boot Web Starter。我们可以包含 Spring Boot Test Starter 来创建一些 JUnit 测试。Spring AI 模块包含在 Maven profiles
部分中。每个聊天模型提供商都有三个不同的配置文件。默认情况下,我们的应用使用 OpenAI,因此它激活了 open-ai
配置文件,该文件包含 spring-ai-starter-model-openai
库。我们应该激活 mistral-ai
配置文件来切换到 Mistral AI。第三个选项是 ollama-ai
配置文件,包含 spring-ai-starter-model-ollama
依赖。这将使在不同聊天模型 AI 提供商之间切换变得轻而易举——我们只需要在 Maven 运行命令中设置配置文件参数。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>
<profile>
<id>open-ai</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>mistral-ai</id>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-mistral-ai</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>ollama-ai</id>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
项目配置
环境配置
在开始开发之前,我们需要配置 AI 模型提供商的 API 密钥。
配置 OpenAI
我们必须在 OpenAI 平台门户上拥有一个账户。登录后,访问 API 密钥 页面来生成 API 令牌。
export OPENAI_API_KEY=<YOUR_TOKEN_VALUE>
配置 Mistral AI
在 Mistral AI 平台门户上创建账户,访问 API 密钥 页面生成令牌。
export MISTRAL_API_KEY=<YOUR_TOKEN_VALUE>
运行和配置 Ollama
Ollama 允许在本地运行大型语言模型。从 下载页面 下载并安装 Ollama,然后运行模型:
ollama run llama3.2
配置兼容 OpenAI 的模型
- Groq AI
- 模型: Llama 3.1 70B Versatile
- 特点: 极快的推理速度,高性能
- API: https://api.groq.com/openai/v1
- 获取 API Key: https://console.groq.com/keys
- Docker Model Runner (DMR)
- 模型: Llama 3.2
- 特点: 本地运行,Docker 容器化部署
- API: http://localhost:11434/v1
- 安装: https://docs.docker.com/ai/model-runner/
- OpenRouter AI
- 模型: Llama 3.1 8B Instruct (Free)
- 特点: 多种开源模型选择,成本较低
- API: https://openrouter.ai/api/v1
- 获取 API Key: https://openrouter.ai/keys
- DeepSeek AI
- 模型: DeepSeek Chat
- 特点: 强大的中文理解能力
- API: https://api.deepseek.com/v1
- 获取 API Key: https://platform.deepseek.com/api_keys
- Qwen AI
- 模型: Qwen Plus
- 特点: 阿里巴巴开发的中文大模型
- API: https://dashscope.aliyuncs.com/compatible-mode/v1
- 获取 API Key: https://dashscope.console.aliyun.com/apiKey
Spring Boot 配置
1. 通用配置 (application.yml)
spring.ai.openai.api-key: ${OPENAI_API_KEY}
spring.ai.openai.chat.options.model: gpt-5
spring.ai.openai.chat.options.temperature: 1
spring.ai.mistralai.api-key: ${MISTRAL_API_KEY}
spring.ai.ollama.chat.options.model: llama3.2
2. OpenAI 兼容模型配置
Groq AI 配置 (application-groq.yml)
spring.ai.openai.api-key: ${GROQ_API_KEY}
spring.ai.openai.base-url: https://api.groq.com
spring.ai.openai.chat.options.model: llama-3.1-70b-versatile
DeepSeek AI 配置 (application-deepseek.yml)
spring.ai.openai.api-key: ${DEEPSEEK_API_KEY}
spring.ai.openai.base-url: https://api.deepseek.com
spring.ai.openai.chat.options.model: deepseek-chat
OpenRouter AI 配置 (application-openrouter.yml)
spring.ai.openai.api-key: ${OPENROUTER_API_KEY}
spring.ai.openai.base-url: https://openrouter.ai
spring.ai.openai.chat.completions-path: /api/v1/chat/completions
spring.ai.openai.chat.options.model: qwen/qwen3-coder:free
Qwen AI 配置 (application-qwen.yml)
spring.ai.openai.api-key: ${QWEN_API_KEY}
spring.ai.openai.base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.openai.chat.options.model: qwen-plus
Docker Model Runner 配置 (application-dmr.yml)
spring.ai.openai.base-url: http://localhost:12434/engines/
spring.ai.openai.api-key: dummy-key
spring.ai.openai.chat.options.model: llama3.2
3. 环境变量设置
# 选择其中一个模型设置对应的环境变量
export OPENAI_API_KEY=your-openai-api-key
export MISTRAL_API_KEY=your-mistral-api-key
export GROQ_API_KEY=your-groq-api-key
export DEEPSEEK_API_KEY=your-deepseek-api-key
export OPENROUTER_API_KEY=your-openrouter-api-key
export QWEN_API_KEY=your-qwen-api-key
核心功能实现
1. 数据模型设计
首先定义我们的核心数据模型,使用 Lombok 简化代码:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Restaurant {
private Long id;
private String name;
private String cuisine;
private String location;
private Double rating;
private String description;
private String priceRange;
private String[] features;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dish {
private Long id;
private String name;
private String description;
private String cuisine;
private Double price;
private String category;
private List<String> ingredients;
private String dietaryInfo;
private Integer calories;
private String preparationTime;
private String difficulty;
}
2. REST API 设计
设计 RESTful API 接口,支持中文参数和 JSON 数据交换:
@PostMapping("/dishes/generate")
public ResponseEntity<List<Dish>> generateDishes(@RequestBody Map<String, Object> request) {
String cuisine = (String) request.get("cuisine");
Integer countObj = (Integer) request.get("count");
int count = countObj != null ? countObj : 5;
// 限制菜品数量在合理范围内
int validCount = Math.max(1, Math.min(count, 10));
PromptTemplate template = new PromptTemplate("""
为{cuisine}菜系生成{count}道特色菜品,包含以下信息:
- 菜品名称
- 详细描述
- 主要食材
- 价格范围
- 菜品分类(开胃菜/主菜/甜点等)
- 饮食信息(素食/无麸质/低卡等)
- 卡路里
- 制作时间
- 难度等级
请返回JSON格式的菜品数据,不要包含解释性文字。
""");
Prompt prompt = template.create(Map.of(
"cuisine", cuisine,
"count", validCount
));
List<Dish> dishes = chatClient.prompt(prompt)
.call()
.entity(new ParameterizedTypeReference<List<Dish>>() {
});
return ResponseEntity.ok(dishes);
}
@RestController
类注入自动配置的 ChatClient.Builder
来创建 ChatClient
实例。RestaurantRecommendationController
实现了从 POST /api/restaurants/recommend
端点返回餐厅列表的方法。主要目标是生成一个包含 5 个对象的列表,这些对象具有在 Restaurant
类中定义的字段。
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/restaurants")
public class RestaurantRecommendationController {
private final ChatClient chatClient;
@PostMapping("/recommend")
public ResponseEntity<List<Restaurant>> recommendRestaurants(@RequestBody RecommendationRequest request) {
PromptTemplate template = new PromptTemplate("""
根据以下用户偏好推荐5家合适的餐厅:
位置: {location}
菜系: {cuisine}
价格范围: {priceRange}
饮食限制: {dietaryRestrictions}
场合: {occasion}
人数: {groupSize}
用餐时间: {timeOfDay}
其他偏好: {preferences}
请返回餐厅列表,包含餐厅名称、菜系、位置、评分、描述、价格范围和特色。
不要包含任何解释性文字,只返回JSON格式的餐厅数据。
""");
Prompt prompt = template.create(Map.of(
"location", request.getLocation() != null ? request.getLocation() : "北京市",
"cuisine", request.getCuisine() != null ? request.getCuisine() : "不限",
"priceRange", request.getPriceRange() != null ? request.getPriceRange() : "不限",
"dietaryRestrictions", request.getDietaryRestrictions() != null ?
String.join(", ", request.getDietaryRestrictions()) : "无",
"occasion", request.getOccasion() != null ? request.getOccasion() : "日常用餐",
"groupSize", request.getGroupSize() != null ? request.getGroupSize().toString() : "1-2人",
"timeOfDay", request.getTimeOfDay() != null ? request.getTimeOfDay() : "午餐",
"preferences", request.getPreferences() != null ?
String.join(", ", request.getPreferences()) : "无"
));
List<Restaurant> restaurants = chatClient.prompt(prompt)
.call()
.entity(new ParameterizedTypeReference<List<Restaurant>>() {});
return ResponseEntity.ok(restaurants);
}
}
结果并不令人惊讶——系统会返回符合用户偏好的餐厅列表。然而,第二次调用同一个端点时,列表会略有不同。根据我们的提示,聊天客户端应该"返回当前餐厅列表"。所以,我期望得到与之前相同的列表。在这种情况下,问题是聊天客户端不记得之前的对话。
运行应用
# 使用 OpenAI GPT-4o(推荐)
mvn spring-boot:run
# 使用 OpenAI(默认配置)
mvn spring-boot:run
# 使用 Mistral AI
mvn spring-boot:run -Pmistral-ai
# 使用 Ollama
mvn spring-boot:run -Pollama-ai
# 使用 Groq AI
mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=groq"
# 使用 DeepSeek AI
mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=deepseek"
# 使用 Qwen AI
mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=qwen"
# 使用 OpenRouter AI
mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=openrouter"
# 使用 DMR AI
mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=dmr"
测试 API
# 推荐餐厅
curl -X POST http://localhost:8080/api/restaurants/recommend \
-H "Content-Type: application/json" \
-d '{
"location": "北京市",
"cuisine": "中餐",
"priceRange": "中等价位",
"dietaryRestrictions": ["素食"],
"occasion": "商务聚餐",
"groupSize": 4,
"timeOfDay": "晚餐"
}'
# 生成菜品
curl -X POST "http://localhost:8080/api/restaurants/dishes/generate" \
-H "Content-Type: application/json" \
-d '{"cuisine": "中餐", "count": 3}'
# 获取建议
curl -X POST "http://localhost:8080/api/restaurants/advice" \
-H "Content-Type: application/json" \
-d '{"query": "适合情侣约会的餐厅"}'
# 多语言聊天
curl -X POST "http://localhost:8080/api/restaurants/chat" \
-H "Content-Type: application/json" \
-d '{"message": "推荐一家好吃的川菜馆", "language": "zh"}'
核心技术特性
1. 多轮对话支持
多轮对话是智能应用的核心特性,Spring AI 提供了开箱即用的解决方案:
@Configuration
public class ChatConfig {
/**
* 配置聊天记忆
*/
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.build();
}
/**
* 配置 ChatClient
* 包含记忆功能和日志记录
*/
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory) {
return chatClientBuilder
.defaultSystem("你是一个专业的餐厅推荐助手。请用中文回答,提供准确、有用的餐厅和菜品推荐。")
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
new SimpleLoggerAdvisor())
.build();
}
}
@SpringBootApplication
public class RestaurantApplication {
public static void main(String[] args) {
SpringApplication.run(RestaurantApplication.class, args);
}
}
现在,让我们进行最终测试。应用重启后,我们可以调用生成餐厅列表的端点。然后,我们将调用 GET /api/restaurants/{id}/details
端点来仅显示具有指定 ID 的单个餐厅。最后,我们可以重复调用 POST /api/restaurants/recommend
端点来验证它是否返回相同的列表。
多轮对话测试
让我们测试多轮对话功能:
# 第一轮对话:询问川菜馆推荐
curl -X POST http://localhost:8080/api/restaurants/chat \
-H "Content-Type: application/json" \
-d '{"message": "我想在北京找一家川菜馆", "language": "zh"}'
# 第二轮对话:询问价格(AI 能记住之前的推荐)
curl -X POST http://localhost:8080/api/restaurants/chat \
-H "Content-Type: application/json" \
-d '{"message": "刚才推荐的餐厅中,哪家最便宜?", "language": "zh"}'
测试结果:AI 能够记住之前的对话内容,提供连贯的回复,准确回答关于之前推荐餐厅的问题。
多轮对话实现原理
- 自动记忆管理:
MessageChatMemoryAdvisor
自动管理对话历史 - 内存存储:使用
MessageWindowChatMemory
+InMemoryChatMemoryRepository
- 透明集成:通过
ChatClient
的defaultAdvisors
配置 - 上下文保持:每次对话都会包含完整的对话历史
2. 结构化输出支持
Spring AI 提供了多种结构化输出方式,满足不同的开发需求:
1. 直接 .entity() 方法(最简单)
@PostMapping("/restaurant/direct-entity")
public ResponseEntity<Restaurant> getRestaurantDirectEntity(@RequestBody Map<String, Object> request) {
String cuisine = (String) request.get("cuisine");
PromptTemplate template = new PromptTemplate("""
推荐1家{cuisine}餐厅,返回JSON格式的餐厅信息。
包含:name, cuisine, location, rating, description, priceRange, features
""");
Prompt prompt = template.create(Map.of("cuisine", cuisine));
// 直接使用 .entity() 方法
Restaurant restaurant = chatClient.prompt(prompt)
.call()
.entity(Restaurant.class);
return ResponseEntity.ok(restaurant);
}
2. ParameterizedTypeReference(当前项目使用)
@PostMapping("/restaurants/type-ref")
public ResponseEntity<List<Restaurant>> getRestaurantsWithTypeRef(@RequestBody Map<String, Object> request) {
String cuisine = (String) request.get("cuisine");
PromptTemplate template = new PromptTemplate("""
推荐3家{cuisine}餐厅,返回JSON格式的餐厅列表。
每个餐厅包含:name, cuisine, location, rating, description, priceRange, features
""");
Prompt prompt = template.create(Map.of("cuisine", cuisine));
// 使用 ParameterizedTypeReference
List<Restaurant> restaurants = chatClient.prompt(prompt)
.call()
.entity(new ParameterizedTypeReference<List<Restaurant>>() {});
return ResponseEntity.ok(restaurants);
}
3. BeanOutputConverter
@PostMapping("/restaurant/bean-converter")
public ResponseEntity<Restaurant> getRestaurantWithBeanConverter(@RequestBody Map<String, Object> request) {
String cuisine = (String) request.get("cuisine");
PromptTemplate template = new PromptTemplate("""
推荐1家{cuisine}餐厅,返回JSON格式的餐厅信息。
包含:name, cuisine, location, rating, description, priceRange, features
""");
Prompt prompt = template.create(Map.of("cuisine", cuisine));
// 使用 BeanOutputConverter
BeanOutputConverter<Restaurant> converter = new BeanOutputConverter<>(Restaurant.class);
String response = chatClient.prompt(prompt)
.call()
.content();
Restaurant restaurant = converter.convert(response);
return ResponseEntity.ok(restaurant);
}
4. MapOutputConverter
@PostMapping("/restaurant/map-converter")
public ResponseEntity<Map<String, Object>> getRestaurantWithMapConverter(@RequestBody Map<String, Object> request) {
String cuisine = (String) request.get("cuisine");
PromptTemplate template = new PromptTemplate("""
推荐1家{cuisine}餐厅,返回JSON格式的餐厅信息。
包含:name, cuisine, location, rating, description, priceRange, features
""");
Prompt prompt = template.create(Map.of("cuisine", cuisine));
// 使用 MapOutputConverter
MapOutputConverter converter = new MapOutputConverter();
String response = chatClient.prompt(prompt)
.call()
.content();
@SuppressWarnings("unchecked")
Map<String, Object> restaurant = (Map<String, Object>) converter.convert(response);
return ResponseEntity.ok(restaurant);
}
5. ListOutputConverter
@PostMapping("/restaurants/list-converter")
public ResponseEntity<List<Map<String, String>>> getRestaurantsWithListConverter(@RequestBody Map<String, Object> request) {
String cuisine = (String) request.get("cuisine");
PromptTemplate template = new PromptTemplate("""
推荐3家{cuisine}餐厅,返回JSON格式的餐厅列表。
每个餐厅包含:name, cuisine, location, rating, description, priceRange, features
""");
Prompt prompt = template.create(Map.of("cuisine", cuisine));
// 使用 ListOutputConverter
ListOutputConverter converter = new ListOutputConverter();
String response = chatClient.prompt(prompt)
.call()
.content();
@SuppressWarnings("unchecked")
List<String> restaurantStrings = (List<String>) converter.convert(response);
// 将字符串列表转换为 Map 列表(这里简化处理)
List<Map<String, String>> restaurants = restaurantStrings.stream()
.map(str -> Map.of("name", str, "cuisine", cuisine))
.toList();
return ResponseEntity.ok(restaurants);
}
结构化输出方式对比
输出方式 | 代码简洁度 | 类型安全 | 灵活性 | 推荐场景 |
---|---|---|---|---|
.entity() | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 简单对象 |
ParameterizedTypeReference | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 复杂泛型 |
BeanOutputConverter | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 自定义转换 |
MapOutputConverter | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | 动态结构 |
ListOutputConverter | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | 简单列表 |
结构化输出测试
# 测试直接 .entity() 方法
curl -X POST http://localhost:8080/api/structured/restaurant/direct-entity \
-H "Content-Type: application/json" \
-d '{"cuisine": "川菜"}'
# 测试 ParameterizedTypeReference
curl -X POST http://localhost:8080/api/structured/restaurants/type-ref \
-H "Content-Type: application/json" \
-d '{"cuisine": "川菜"}'
# 测试 BeanOutputConverter
curl -X POST http://localhost:8080/api/structured/restaurant/bean-converter \
-H "Content-Type: application/json" \
-d '{"cuisine": "川菜"}'
测试结果:所有结构化输出方式都能正确工作,返回符合预期格式的数据。
功能扩展
基于对当前项目的分析,以下是本项目未涉及但 Spring AI 框架支持的强大功能:
🚀 核心功能扩展
1. 流式响应 (Streaming)
// 当前项目:同步调用
List<Restaurant> restaurants = chatClient.prompt()
.user("推荐餐厅")
.call()
.entity(new ParameterizedTypeReference<List<Restaurant>>() {});
// 未实现:流式响应
Flux<String> stream = chatClient.prompt()
.user("推荐餐厅")
.stream()
.content();
2. 函数调用 (Function Calling)
// 未实现:工具调用
@Bean
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> {
// 调用天气 API
return weatherService.getWeather(request.getLocation());
};
}
// 在 ChatClient 中使用
chatClient.prompt()
.user("北京今天天气如何?")
.functions("weatherFunction")
.call();
3. 向量数据库集成 (RAG)
// 未实现:检索增强生成
@Bean
public VectorStore vectorStore() {
return new PineconeVectorStore(pineconeApiKey, "restaurant-index");
}
// RAG 查询
chatClient.prompt()
.user("推荐适合商务聚餐的餐厅")
.advisors(RetrievalAugmentationAdvisor.builder(vectorStore).build())
.call();
🎨 多模态支持
4. 图像识别和处理
// 未实现:图像输入
@PostMapping("/analyze-image")
public ResponseEntity<String> analyzeImage(@RequestParam MultipartFile image) {
return chatClient.prompt()
.user("分析这张餐厅图片")
.media(image)
.call()
.content();
}
5. 语音转文字和文字转语音
// 未实现:语音处理
@PostMapping("/voice-chat")
public ResponseEntity<byte[]> voiceChat(@RequestParam MultipartFile audio) {
// 语音转文字
String text = speechToTextService.transcribe(audio);
// AI 处理
String response = chatClient.prompt()
.user(text)
.call()
.content();
// 文字转语音
return textToSpeechService.synthesize(response);
}
⚡ 性能优化功能
6. 异步处理
// 未实现:异步调用
@Async
public CompletableFuture<List<Restaurant>> recommendRestaurantsAsync(RecommendationRequest request) {
return chatClient.prompt()
.user("推荐餐厅")
.call()
.entity(new ParameterizedTypeReference<List<Restaurant>>() {})
.toFuture();
}
7. 缓存机制
// 未实现:响应缓存
@Cacheable("restaurant-recommendations")
public List<Restaurant> getCachedRecommendations(RecommendationRequest request) {
return chatClient.prompt()
.user("推荐餐厅")
.call()
.entity(new ParameterizedTypeReference<List<Restaurant>>() {});
}
8. 批量处理
// 未实现:批量处理
public List<List<Restaurant>> batchRecommend(List<RecommendationRequest> requests) {
return chatClient.batch()
.prompt("推荐餐厅")
.call()
.entity(new ParameterizedTypeReference<List<Restaurant>>() {});
}
🔧 高级管理功能
9. 模型路由和负载均衡
// 未实现:智能路由
@Bean
public ModelRouter modelRouter() {
return ModelRouter.builder()
.addRoute("restaurant", "gpt-4o")
.addRoute("dish", "claude-3")
.addRoute("chat", "llama-3")
.build();
}
10. 提示词模板管理
// 未实现:复杂模板
@Bean
public PromptTemplate restaurantTemplate() {
return new PromptTemplate("""
你是一个专业的餐厅推荐助手。
用户偏好:{preferences}
位置:{location}
预算:{budget}
{% if occasion == "商务聚餐" %}
请推荐环境优雅、服务专业的餐厅。
{% elif occasion == "朋友聚会" %}
请推荐氛围轻松、菜品丰富的餐厅。
{% endif %}
""");
}
🏠 企业级功能
11. 多租户支持
// 未实现:多租户隔离
@Bean
public TenantAwareChatClient tenantAwareChatClient() {
return TenantAwareChatClient.builder()
.tenantResolver(tenantResolver)
.modelProvider(tenantModelProvider)
.build();
}
12. 安全性和内容过滤
// 未实现:内容安全
@Bean
public ContentFilterAdvisor contentFilterAdvisor() {
return ContentFilterAdvisor.builder()
.contentFilter(contentFilter)
.build();
}
13. 监控和指标
// 未实现:性能监控
@Bean
public MetricsAdvisor metricsAdvisor() {
return MetricsAdvisor.builder()
.meterRegistry(meterRegistry)
.build();
}
💰 成本优化功能
14. 成本控制
// 未实现:成本控制
@Bean
public CostOptimizationAdvisor costOptimizationAdvisor() {
return CostOptimizationAdvisor.builder()
.maxCostPerRequest(0.01)
.fallbackModel("gpt-3.5-turbo")
.build();
}
15. 模型热更新
// 未实现:动态切换
@PostMapping("/switch-model")
public ResponseEntity<String> switchModel(@RequestParam String modelName) {
modelManager.switchModel(modelName);
return ResponseEntity.ok("模型已切换");
}
总结
Spring AI 作为 Spring 生态系统的新成员,为 Java 开发者提供了构建智能化应用的强大工具。随着 AI 技术的不断发展,我们可以期待:
- 更多模型支持:集成更多 AI 模型提供商
- 功能增强:更丰富的 AI 功能特性
- 开发工具:更完善的开发工具和调试支持
- 企业级功能:更强大的企业级特性和监控支持
参考资源
本文基于 Spring AI 1.1.0-M2 版本编写,随着框架的不断发展,部分 API 可能会有变化。建议参考官方文档获取最新信息。