Spring AI 核心架构、抽象模型与四大核心组件设计精髓

一、Spring AI 概述

Spring AI 是 Spring 官方推出的 AI 应用开发框架,旨在简化 Java 开发者构建 AI 驱动应用的过程。它遵循 Spring 一贯的设计哲学:约定优于配置面向接口编程依赖注入,为开发者提供了一套统一、可扩展的 API,屏蔽了不同 AI 模型提供商之间的差异。

Spring AI 的核心价值在于:

  • 提供统一的编程模型,支持多种 AI 模型提供商(OpenAI、Anthropic、Google、阿里云、字节跳动等)
  • 提供丰富的抽象层,将 AI 能力与 Spring 生态无缝集成
  • 支持结构化输出、函数调用、RAG(检索增强生成)等高级特性
  • 提供可观测性、重试、限流等企业级功能

二、Spring AI 核心架构详解

Spring AI 采用分层架构设计,从下到上分为模型层、核心抽象层、服务层和集成层。

2.1 模型层

模型层是 Spring AI 的最底层,负责与具体的 AI 模型提供商进行通信。它包含了各个模型提供商的客户端实现,将原生 API 转换为 Spring AI 的统一抽象。

主要组件:

  • 聊天模型客户端(ChatModel)
  • 嵌入模型客户端(EmbeddingModel)
  • 图像生成模型客户端(ImageModel)
  • 语音转文本模型客户端(SpeechModel)
  • 文本转语音模型客户端(TextToSpeechModel)

2.2 核心抽象层

核心抽象层是 Spring AI 的灵魂,定义了一套与具体模型提供商无关的接口和抽象类。这一层确保了开发者可以在不修改业务代码的情况下,轻松切换不同的 AI 模型提供商。

核心抽象:

  • Model:所有 AI 模型的根接口
  • ChatModel:聊天模型的抽象
  • EmbeddingModel:嵌入模型的抽象
  • Prompt:AI 输入的抽象
  • ChatResponse:AI 输出的抽象
  • OutputParser:输出解析器的抽象
  • FunctionCallback:函数调用的抽象

2.3 服务层

服务层基于核心抽象层,提供了更高层次的服务和功能。它封装了常见的 AI 应用场景,简化了开发者的使用。

主要服务:

  • ChatClient:统一的聊天客户端
  • VectorStore:向量存储服务
  • RetrievalAugmentor:检索增强生成服务
  • FunctionCallingService:函数调用服务

2.4 集成层

集成层负责将 Spring AI 与 Spring 生态和其他第三方框架进行集成。

主要集成:

  • Spring Boot 自动配置
  • Spring Web 集成
  • Spring Data 集成
  • Spring Security 集成
  • Spring Cloud 集成
  • Swagger/OpenAPI 集成

三、Spring AI 核心抽象模型深度解析

Spring AI 的核心设计理念是抽象与解耦。通过定义清晰的接口和抽象类,Spring AI 将 AI 能力的使用与具体实现分离开来,使得代码更加灵活、可扩展和可测试。

3.1 核心抽象模型概览

3.2 Model 接口

Model 是所有 AI 模型的根接口,定义了 AI 模型最基本的能力:接收一个 Prompt 并返回一个 ChatResponse

复制代码

package org.springframework.ai.model; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.model.ChatResponse; /** * 所有AI模型的根接口 * @author Spring AI Team */ public interface Model { /** * 调用AI模型 * @param prompt 输入提示 * @return 模型响应 */ ChatResponse call(Prompt prompt); }

Model 接口的设计非常简洁,只包含一个 call 方法。这种设计使得所有 AI 模型都具有统一的调用方式,极大地提高了代码的一致性和可维护性。

3.3 Prompt 抽象

Prompt 是 AI 输入的抽象,它包含了发送给 AI 模型的所有信息。一个 Prompt 由多个 Message 组成,每个 Message 代表一次对话中的一条消息。

复制代码

package org.springframework.ai.prompt; import org.springframework.ai.model.Message; import java.util.List; import java.util.Map; /** * AI输入提示的抽象 * @author Spring AI Team */ public class Prompt { private final List<Message> messages; private final Map<String, Object> options; public Prompt(List<Message> messages) { this(messages, Map.of()); } public Prompt(List<Message> messages, Map<String, Object> options) { this.messages = messages; this.options = options; } public List<Message> getMessages() { return messages; } public Map<String, Object> getOptions() { return options; } }

Message 接口定义了消息的基本属性:内容和角色。Spring AI 支持四种消息角色:

  • USER:用户消息
  • ASSISTANT:助手消息
  • SYSTEM:系统消息
  • FUNCTION:函数调用结果消息

3.4 ChatResponse 抽象

ChatResponse 是 AI 输出的抽象,它包含了 AI 模型返回的所有信息。一个 ChatResponse 由多个 Generation 组成,每个 Generation 代表模型生成的一个候选结果。

复制代码

package org.springframework.ai.model; import java.util.List; import java.util.Map; /** * AI聊天响应的抽象 * @author Spring AI Team */ public class ChatResponse { private final List<Generation> generations; private final Map<String, Object> metadata; public ChatResponse(List<Generation> generations) { this(generations, Map.of()); } public ChatResponse(List<Generation> generations, Map<String, Object> metadata) { this.generations = generations; this.metadata = metadata; } public List<Generation> getGenerations() { return generations; } public Map<String, Object> getMetadata() { return metadata; } public Generation getResult() { return generations.get(0); } }

Generation 类包含了生成的消息和相关的元数据,如完成原因、使用的令牌数等。

3.5 OutputParser 接口

OutputParser 是输出解析器的抽象,负责将 AI 模型返回的字符串转换为结构化的数据。这是 Spring AI 中非常重要的一个组件,它解决了 AI 模型输出格式不固定的问题。

复制代码

package org.springframework.ai.parser; /** * 输出解析器接口 * @param <T> 解析后的类型 * @author Spring AI Team */ public interface OutputParser<T> { /** * 解析AI模型的输出 * @param text AI模型返回的字符串 * @return 解析后的结构化数据 */ T parse(String text); /** * 获取格式说明,用于提示AI模型按照指定格式输出 * @return 格式说明字符串 */ String getFormat(); }

四、ChatClient:统一的 AI 交互入口

ChatClient 是 Spring AI 提供的统一聊天客户端,它封装了 ChatModel 的底层细节,提供了更加流畅和易用的 API。ChatClient 采用构建者模式设计,支持链式调用,极大地简化了 AI 交互的代码编写。

4.1 ChatClient 的设计理念

ChatClient 的设计理念是简单、灵活、可扩展。它提供了多种调用方式,满足不同场景的需求:

  • 同步调用:适用于简单的问答场景
  • 异步调用:适用于需要非阻塞处理的场景
  • 流式调用:适用于需要实时展示结果的场景

同时,ChatClient 支持丰富的配置选项,可以在调用时动态调整模型参数,如温度、最大令牌数等。

4.2 ChatClient 的核心 API

复制代码

package org.springframework.ai.chat.client; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Map; import java.util.function.Consumer; /** * 统一的聊天客户端接口 * @author Spring AI Team */ public interface ChatClient { /** * 创建一个新的ChatClient构建器 * @param chatModel 聊天模型 * @return ChatClient构建器 */ static Builder builder(ChatModel chatModel) { return new DefaultChatClientBuilder(chatModel); } /** * 创建一个新的请求规范 * @return 请求规范 */ RequestSpec prompt(); /** * ChatClient构建器 */ interface Builder { Builder defaultSystem(String text); Builder defaultSystem(String text, Map<String, Object> variables); Builder defaultOptions(Consumer<ChatOptions.Builder> optionsConsumer); ChatClient build(); } /** * 请求规范 */ interface RequestSpec { RequestSpec system(String text); RequestSpec system(String text, Map<String, Object> variables); RequestSpec user(String text); RequestSpec user(String text, Map<String, Object> variables); RequestSpec assistant(String text); RequestSpec options(Consumer<ChatOptions.Builder> optionsConsumer); ResponseSpec call(); StreamResponseSpec stream(); } /** * 同步响应规范 */ interface ResponseSpec { String content(); <T> T entity(Class<T> type); ChatResponse chatResponse(); } /** * 流式响应规范 */ interface StreamResponseSpec { Flux<String> content(); Flux<ChatResponse> chatResponse(); } }

4.3 ChatClient 的使用示例

首先,我们需要在 pom.xml 中添加必要的依赖:

复制代码

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <relativePath/> </parent> <groupId>com.jam.demo</groupId> <artifactId>spring-ai-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-ai-demo</name> <description>Spring AI Demo Project</description> <properties> <java.version>17</java.version> <spring-ai.version>1.0.0-M1</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-dashscope-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>2.0.52</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>33.1.0-jre</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

然后,在 application.yml 中配置阿里云通义千问的 API Key:

复制代码

spring: ai: dashscope: api-key: your-api-key-here chat: options: model: qwen-turbo temperature: 0.7 max-tokens: 1000 server: port: 8080 springdoc: swagger-ui: path: /swagger-ui.html api-docs: path: /v3/api-docs

接下来,创建一个配置类来配置 ChatClient

复制代码

package com.jam.demo.config; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.dashscope.chat.DashScopeChatModel; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Spring AI配置类 * @author ken */ @Configuration public class SpringAiConfig { /** * 配置ChatClient * @param dashScopeChatModel 通义千问聊天模型 * @return ChatClient实例 */ @Bean public ChatClient chatClient(DashScopeChatModel dashScopeChatModel) { return ChatClient.builder(dashScopeChatModel) .defaultSystem("你是一个专业的Java技术助手,回答问题要准确、简洁、专业。") .build(); } }

创建一个简单的控制器来演示 ChatClient 的使用:

复制代码

package com.jam.demo.controller; import com.jam.demo.dto.ChatRequest; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; /** * 聊天控制器 * @author ken */ @Slf4j @RestController @RequestMapping("/api/chat") @RequiredArgsConstructor @Tag(name = "聊天接口", description = "AI聊天相关接口") public class ChatController { private final ChatClient chatClient; /** * 同步聊天接口 * @param request 聊天请求 * @return AI响应 */ @PostMapping("/sync") @Operation(summary = "同步聊天", description = "发送消息并等待AI完整响应") public ResponseEntity<String> syncChat(@RequestBody ChatRequest request) { String response = chatClient.prompt() .user(request.getMessage()) .call() .content(); return ResponseEntity.ok(response); } /** * 流式聊天接口 * @param request 聊天请求 * @return AI流式响应 */ @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) @Operation(summary = "流式聊天", description = "发送消息并以SSE方式接收AI实时响应") public Flux<String> streamChat(@RequestBody ChatRequest request) { return chatClient.prompt() .user(request.getMessage()) .stream() .content(); } }

创建对应的 DTO 类:

复制代码

package com.jam.demo.dto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** * 聊天请求DTO * @author ken */ @Data @Schema(description = "聊天请求") public class ChatRequest { @Schema(description = "用户消息", example = "什么是Spring AI?") private String message; }

最后,创建 Spring Boot 启动类:

复制代码

package com.jam.demo; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Spring AI Demo启动类 * @author ken */ @SpringBootApplication @OpenAPIDefinition( info = @Info( title = "Spring AI Demo API", version = "1.0", description = "Spring AI演示项目API文档" ) ) public class SpringAiDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringAiDemoApplication.class, args); } }

4.4 ChatClient 的高级用法

ChatClient 支持很多高级特性,如动态调整模型参数、传递变量、多轮对话等。

动态调整模型参数
复制代码

String response = chatClient.prompt() .user("写一首关于春天的诗") .options(options -> options .temperature(0.9) .maxTokens(500)) .call() .content();

传递变量
复制代码

String response = chatClient.prompt() .user("请用{language}语言解释什么是{concept}", Map.of("language", "中文", "concept", "依赖注入")) .call() .content();

多轮对话
复制代码

// 第一轮对话 String response1 = chatClient.prompt() .user("什么是Spring Boot?") .call() .content(); // 第二轮对话(需要包含历史消息) String response2 = chatClient.prompt() .user("什么是Spring Boot?") .assistant(response1) .user("它和Spring Framework有什么区别?") .call() .content();

五、Prompt:AI 交互的语言桥梁

Prompt 是人类与 AI 模型沟通的语言,它直接决定了 AI 模型输出的质量。Spring AI 提供了强大的 Prompt 管理能力,支持模板化、变量替换、多消息组合等功能。

5.1 Prompt 的设计理念

Spring AI 的 Prompt 设计遵循以下原则:

  • 结构化:将 Prompt 分解为多个消息,每个消息有明确的角色
  • 可复用:支持模板化,将通用的 Prompt 逻辑提取为模板
  • 可扩展:支持自定义消息类型和模板引擎
  • 类型安全:提供类型安全的变量替换机制

5.2 Prompt 模板

Spring AI 内置了基于 Spring Expression Language (SpEL) 的模板引擎,支持变量替换、条件判断、循环等功能。

基本模板使用
复制代码

package com.jam.demo.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.prompt.PromptTemplate; import org.springframework.stereotype.Service; import java.util.Map; /** * Prompt服务 * @author ken */ @Slf4j @Service @RequiredArgsConstructor public class PromptService { private final ChatClient chatClient; /** * 使用模板生成解释 * @param concept 要解释的概念 * @param language 解释使用的语言 * @return 解释结果 */ public String explainConcept(String concept, String language) { PromptTemplate template = new PromptTemplate( "请用{language}语言详细解释什么是{concept}。" + "要求:1. 定义清晰准确;2. 举一个简单的例子;3. 说明它的主要用途。" ); String promptText = template.render(Map.of( "concept", concept, "language", language )); return chatClient.prompt() .user(promptText) .call() .content(); } }

条件判断
复制代码

PromptTemplate template = new PromptTemplate( "请写一篇关于{topic}的文章。" + "#if(${includeCode})" + "文章中需要包含一个简单的代码示例。" + "#end" ); String promptText = template.render(Map.of( "topic", "Java线程池", "includeCode", true ));

循环
复制代码

PromptTemplate template = new PromptTemplate( "请列出以下技术的主要特点:" + "#foreach($tech in $technologies)" + "- $tech" + "#end" ); String promptText = template.render(Map.of( "technologies", List.of("Spring Boot", "Spring Cloud", "Spring AI") ));

5.3 外部 Prompt 模板文件

对于复杂的 Prompt,建议将其保存为外部文件,便于管理和维护。Spring AI 支持从类路径加载 Prompt 模板文件。

创建 src/main/resources/prompts/explain-concept.st 文件:

复制代码

请用{language}语言详细解释什么是{concept}。 要求: 1. 定义清晰准确 2. 举一个简单的例子 3. 说明它的主要用途 4. 字数控制在300字以内

然后在代码中加载并使用:

复制代码

import org.springframework.core.io.ClassPathResource; import org.springframework.ai.prompt.PromptTemplate; public String explainConceptFromFile(String concept, String language) { PromptTemplate template = new PromptTemplate(new ClassPathResource("prompts/explain-concept.st")); String promptText = template.render(Map.of( "concept", concept, "language", language )); return chatClient.prompt() .user(promptText) .call() .content(); }

5.4 Prompt 工程最佳实践

  1. 明确角色:在系统提示中明确 AI 的角色和任务
  2. 清晰指令:给出清晰、具体的指令,避免模糊不清的表述
  3. 格式要求:明确指定输出格式,便于后续处理
  4. 示例引导:提供示例,让 AI 更好地理解你的需求
  5. 分步思考:对于复杂问题,引导 AI 分步思考
  6. 限制输出:限制输出的长度和内容,避免无关信息

六、Model:模型能力的抽象与适配

Model 是 Spring AI 对 AI 模型能力的抽象,它定义了 AI 模型应该具备的基本接口。Spring AI 提供了多种 Model 实现,支持不同类型的 AI 模型和不同的模型提供商。

6.1 Model 的设计理念

Spring AI 的 Model 设计遵循以下原则:

  • 统一接口:所有同类型的模型都实现相同的接口
  • 最小接口:接口只定义最基本的方法,保持简洁
  • 可扩展:通过选项机制支持模型的特定功能
  • 适配模式:使用适配器模式将原生 API 转换为统一接口

6.2 ChatModel 详解

ChatModel 是聊天模型的抽象,它定义了聊天模型应该具备的基本能力:同步调用和流式调用。

复制代码

package org.springframework.ai.chat.model; import org.springframework.ai.model.Model; import org.springframework.ai.prompt.Prompt; import reactor.core.publisher.Flux; /** * 聊天模型接口 * @author Spring AI Team */ public interface ChatModel extends Model { /** * 同步调用聊天模型 * @param prompt 输入提示 * @return 聊天响应 */ @Override ChatResponse call(Prompt prompt); /** * 流式调用聊天模型 * @param prompt 输入提示 * @return 流式聊天响应 */ Flux<ChatResponse> stream(Prompt prompt); }

6.3 模型选项

Spring AI 通过 ChatOptions 类来配置模型的参数。不同的模型提供商可能有不同的参数,但 Spring AI 定义了一组通用的参数,确保在不同模型之间的兼容性。

通用参数:

  • model:模型名称
  • temperature:温度,控制输出的随机性
  • maxTokens:最大生成令牌数
  • topP:核采样参数
  • frequencyPenalty:频率惩罚
  • presencePenalty:存在惩罚
复制代码

import org.springframework.ai.chat.model.ChatOptions; ChatOptions options = ChatOptions.builder() .model("qwen-turbo") .temperature(0.7) .maxTokens(1000) .topP(0.9) .build();

6.4 多模型支持

Spring AI 支持多种模型提供商,你可以在同一个应用中同时使用多个模型。

首先,添加不同模型提供商的依赖:

复制代码

<!-- 字节跳动豆包 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-doubao-spring-boot-starter</artifactId> </dependency> <!-- OpenAI --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency>

然后,在配置文件中配置多个模型:

复制代码

spring: ai: dashscope: api-key: your-dashscope-api-key chat: options: model: qwen-turbo doubao: api-key: your-doubao-api-key chat: options: model: doubao-pro openai: api-key: your-openai-api-key chat: options: model: gpt-3.5-turbo

最后,在代码中使用不同的模型:

复制代码

package com.jam.demo.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.dashscope.chat.DashScopeChatModel; import org.springframework.ai.doubao.chat.DoubaoChatModel; import org.springframework.ai.openai.chat.OpenAiChatModel; import org.springframework.stereotype.Service; /** * 多模型服务 * @author ken */ @Slf4j @Service @RequiredArgsConstructor public class MultiModelService { private final DashScopeChatModel dashScopeChatModel; private final DoubaoChatModel doubaoChatModel; private final OpenAiChatModel openAiChatModel; /** * 使用通义千问聊天 * @param message 用户消息 * @return AI响应 */ public String chatWithQwen(String message) { return ChatClient.create(dashScopeChatModel) .prompt() .user(message) .call() .content(); } /** * 使用豆包聊天 * @param message 用户消息 * @return AI响应 */ public String chatWithDoubao(String message) { return ChatClient.create(doubaoChatModel) .prompt() .user(message) .call() .content(); } /** * 使用GPT-3.5聊天 * @param message 用户消息 * @return AI响应 */ public String chatWithGpt35(String message) { return ChatClient.create(openAiChatModel) .prompt() .user(message) .call() .content(); } }

七、OutputParser:结构化输出的关键

OutputParser 是 Spring AI 中非常重要的一个组件,它负责将 AI 模型返回的非结构化字符串转换为结构化的数据。这使得开发者可以像调用普通方法一样调用 AI 模型,获取类型安全的返回值。

7.1 OutputParser 的设计理念

Spring AI 的 OutputParser 设计遵循以下原则:

  • 类型安全:提供类型安全的解析结果
  • 自动格式提示:自动生成格式说明,提示 AI 模型按照指定格式输出
  • 容错处理:提供容错机制,处理 AI 模型输出格式不规范的情况
  • 可扩展:支持自定义输出解析器

7.2 内置 OutputParser

Spring AI 提供了多种内置的 OutputParser,满足常见的需求:

  • BeanOutputParser:将输出解析为 Java Bean
  • ListOutputParser:将输出解析为列表
  • MapOutputParser:将输出解析为 Map
  • BooleanOutputParser:将输出解析为布尔值
  • IntegerOutputParser:将输出解析为整数
  • DoubleOutputParser:将输出解析为浮点数
BeanOutputParser 使用示例

首先,定义一个 Java Bean:

复制代码

package com.jam.demo.dto; import lombok.Data; /** * 书籍信息DTO * @author ken */ @Data public class Book { private String title; private String author; private String isbn; private double price; private String description; }

然后,使用 BeanOutputParser 解析 AI 输出:

复制代码

package com.jam.demo.service; import com.jam.demo.dto.Book; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.stereotype.Service; /** * 输出解析服务 * @author ken */ @Slf4j @Service @RequiredArgsConstructor public class OutputParserService { private final ChatClient chatClient; /** * 获取书籍信息 * @param bookName 书籍名称 * @return 书籍信息 */ public Book getBookInfo(String bookName) { BeanOutputParser<Book> parser = new BeanOutputParser<>(Book.class); String response = chatClient.prompt() .user("请提供关于《{bookName}》的详细信息。{format}", Map.of("bookName", bookName, "format", parser.getFormat())) .call() .content(); return parser.parse(response); } }

ListOutputParser 使用示例
复制代码

import org.springframework.ai.parser.ListOutputParser; /** * 获取技术栈列表 * @param domain 领域 * @return 技术栈列表 */ public List<String> getTechStack(String domain) { ListOutputParser parser = new ListOutputParser(); String response = chatClient.prompt() .user("请列出{domain}领域常用的10个技术栈。{format}", Map.of("domain", domain, "format", parser.getFormat())) .call() .content(); return parser.parse(response); }

7.3 ChatClient 集成 OutputParser

ChatClient 提供了 entity 方法,可以直接将 AI 输出解析为指定类型的对象,无需手动创建 OutputParser。

复制代码

/** * 使用ChatClient直接解析为Java Bean * @param bookName 书籍名称 * @return 书籍信息 */ public Book getBookInfoWithChatClient(String bookName) { return chatClient.prompt() .user("请提供关于《{bookName}》的详细信息。", Map.of("bookName", bookName)) .call() .entity(Book.class); }

7.4 自定义 OutputParser

如果内置的 OutputParser 不能满足你的需求,你可以自定义 OutputParser。

复制代码

package com.jam.demo.parser; import com.alibaba.fastjson2.JSON; import org.springframework.ai.parser.OutputParser; import org.springframework.util.StringUtils; import java.time.LocalDate; import java.time.format.DateTimeFormatter; /** * 日期输出解析器 * @author ken */ public class LocalDateOutputParser implements OutputParser<LocalDate> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Override public LocalDate parse(String text) { if (!StringUtils.hasText(text)) { return null; } // 尝试直接解析 try { return LocalDate.parse(text.trim(), FORMATTER); } catch (Exception e) { // 尝试从JSON中提取 try { String dateStr = JSON.parseObject(text).getString("date"); return LocalDate.parse(dateStr.trim(), FORMATTER); } catch (Exception ex) { throw new IllegalArgumentException("无法解析日期: " + text, ex); } } } @Override public String getFormat() { return "请按照yyyy-MM-dd格式输出日期。如果需要返回多个信息,请将日期放在date字段中,使用JSON格式输出。"; } }

使用自定义 OutputParser:

复制代码

/** * 获取节日日期 * @param festival 节日名称 * @return 节日日期 */ public LocalDate getFestivalDate(String festival) { LocalDateOutputParser parser = new LocalDateOutputParser(); String response = chatClient.prompt() .user("请告诉我{festival}是哪一天?{format}", Map.of("festival", festival, "format", parser.getFormat())) .call() .content(); return parser.parse(response); }

八、综合实战案例

下面我们将通过一个完整的案例,展示如何使用 Spring AI 构建一个智能问答系统,支持结构化输出和多轮对话。

8.1 需求分析

我们要构建一个智能图书问答系统,具备以下功能:

  1. 根据书名查询书籍基本信息
  2. 根据书名推荐相似书籍
  3. 根据作者查询其所有作品
  4. 多轮对话功能

8.2 数据库设计

创建书籍信息表:

复制代码

CREATE TABLE book ( id BIGINT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL COMMENT '书名', author VARCHAR(255) NOT NULL COMMENT '作者', isbn VARCHAR(20) UNIQUE COMMENT 'ISBN', price DECIMAL(10,2) COMMENT '价格', description TEXT COMMENT '简介', publish_date DATE COMMENT '出版日期', category VARCHAR(100) COMMENT '分类', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='书籍信息表';

插入测试数据:

复制代码

INSERT INTO book (title, author, isbn, price, description, publish_date, category) VALUES ('Java编程思想', 'Bruce Eckel', '9787111213826', 108.00, 'Java领域经典著作,全面介绍了Java语言的特性和编程思想。', '2007-06-01', '计算机/编程'), ('深入理解Java虚拟机', '周志明', '9787111641247', 129.00, '深入解析Java虚拟机的工作原理和实现细节。', '2019-12-01', '计算机/编程'), ('Spring实战', 'Craig Walls', '9787115547620', 89.00, '全面介绍Spring框架的核心概念和使用方法。', '2020-07-01', '计算机/编程'), ('Spring Boot实战', '丁雪丰', '9787115453686', 69.00, '详细讲解Spring Boot的使用和最佳实践。', '2016-05-01', '计算机/编程'), ('设计模式:可复用面向对象软件的基础', 'Erich Gamma', '9787111094043', 59.00, '设计模式领域的经典著作,介绍了23种设计模式。', '2000-09-01', '计算机/编程');

8.3 实体类和 Mapper

创建书籍实体类:

复制代码

package com.jam.demo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; /** * 书籍实体类 * @author ken */ @Data @TableName("book") public class Book { @TableId(type = IdType.AUTO) private Long id; private String title; private String author; private String isbn; private BigDecimal price; private String description; private LocalDate publishDate; private String category; private LocalDateTime createTime; private LocalDateTime updateTime; }

创建 Mapper 接口:

复制代码

package com.jam.demo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.jam.demo.entity.Book; import org.apache.ibatis.annotations.Mapper; /** * 书籍Mapper * @author ken */ @Mapper public interface BookMapper extends BaseMapper<Book> { }

8.4 服务层实现

创建智能图书服务:

复制代码

package com.jam.demo.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.jam.demo.dto.BookRecommendation; import com.jam.demo.entity.Book; import com.jam.demo.mapper.BookMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.List; import java.util.Map; /** * 智能图书服务 * @author ken */ @Slf4j @Service @RequiredArgsConstructor public class SmartBookService { private final ChatClient chatClient; private final BookMapper bookMapper; /** * 根据书名查询书籍信息 * @param title 书名 * @return 书籍信息 */ public Book getBookByTitle(String title) { if (!StringUtils.hasText(title)) { return null; } LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>(); wrapper.like(Book::getTitle, title); List<Book> books = bookMapper.selectList(wrapper); if (CollectionUtils.isEmpty(books)) { // 数据库中没有,使用AI生成 return chatClient.prompt() .user("请提供关于《{title}》的详细信息。", Map.of("title", title)) .call() .entity(Book.class); } return books.get(0); } /** * 推荐相似书籍 * @param title 书名 * @return 推荐书籍列表 */ public List<BookRecommendation> recommendSimilarBooks(String title) { Book book = getBookByTitle(title); if (book == null) { return List.of(); } return chatClient.prompt() .user("根据以下书籍信息,推荐5本相似的书籍:\n" + "书名:{title}\n" + "作者:{author}\n" + "分类:{category}\n" + "简介:{description}\n" + "请按照以下格式返回:\n" + "1. 书名 - 作者 - 推荐理由\n" + "2. 书名 - 作者 - 推荐理由\n" + "...", Map.of( "title", book.getTitle(), "author", book.getAuthor(), "category", book.getCategory(), "description", book.getDescription() )) .call() .entity(new org.springframework.core.ParameterizedTypeReference<List<BookRecommendation>>() {}); } /** * 根据作者查询作品 * @param author 作者 * @return 作品列表 */ public List<String> getBooksByAuthor(String author) { if (!StringUtils.hasText(author)) { return List.of(); } LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Book::getAuthor, author); List<Book> books = bookMapper.selectList(wrapper); if (CollectionUtils.isEmpty(books)) { // 数据库中没有,使用AI生成 return chatClient.prompt() .user("请列出{author}的主要作品。", Map.of("author", author)) .call() .entity(new org.springframework.core.ParameterizedTypeReference<List<String>>() {}); } return books.stream() .map(Book::getTitle) .toList(); } }

创建推荐书籍 DTO:

复制代码

package com.jam.demo.dto; import lombok.Data; /** * 书籍推荐DTO * @author ken */ @Data public class BookRecommendation { private String title; private String author; private String reason; }

8.5 控制器实现

复制代码

package com.jam.demo.controller; import com.jam.demo.dto.BookRecommendation; import com.jam.demo.entity.Book; import com.jam.demo.service.SmartBookService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * 智能图书控制器 * @author ken */ @Slf4j @RestController @RequestMapping("/api/book") @RequiredArgsConstructor @Tag(name = "智能图书接口", description = "智能图书问答相关接口") public class SmartBookController { private final SmartBookService smartBookService; /** * 根据书名查询书籍信息 * @param title 书名 * @return 书籍信息 */ @GetMapping("/info") @Operation(summary = "查询书籍信息", description = "根据书名查询书籍的详细信息") public ResponseEntity<Book> getBookInfo(@RequestParam String title) { Book book = smartBookService.getBookByTitle(title); return ResponseEntity.ok(book); } /** * 推荐相似书籍 * @param title 书名 * @return 推荐书籍列表 */ @GetMapping("/recommend") @Operation(summary = "推荐相似书籍", description = "根据书名推荐相似的书籍") public ResponseEntity<List<BookRecommendation>> recommendBooks(@RequestParam String title) { List<BookRecommendation> recommendations = smartBookService.recommendSimilarBooks(title); return ResponseEntity.ok(recommendations); }

相关推荐
波波0073 小时前
每日一题:什么是CQRS,在微服务中如何应用
微服务·架构
云烟成雨TD3 小时前
Spring AI Alibaba 1.x 系列【20】MessagesAgentHook 、MessagesModelHook 相关实现类
java·人工智能·spring
程序员小嬛3 小时前
中科院一区TOP:用于求解偏微分方程的物理信息神经网络前沿创新思路
人工智能·深度学习·神经网络·机器学习
霸道流氓气质3 小时前
SpringBoot中集成LangChain4j实现集成阿里百炼平台进行AI对话记忆功能和对话隔离功能
java·人工智能·spring boot·langchain4j
xiaotao1313 小时前
01-编程基础与数学基石:Matplotlib & Seaborn
人工智能·python·matplotlib
用户2018792831673 小时前
解密「并行派发特工」dispatching-parallel-agents:一个让AI工作效率×3的超级技能
人工智能
CHPCWWHSU3 小时前
智慧城市可视化:基于osgPotree 的都柏林大规模点云高程着色实践
人工智能·智能城市
JoyCong19983 小时前
ToDesk企业版助力伯锐锶:远程连接打破时空壁垒,国产高端电镜跑出“加速度”
大数据·人工智能·经验分享·物联网
Zldaisy3d3 小时前
联泰科技全链路鞋业智造解决方案出海印尼
大数据·人工智能