Spring AI的核心组件以及源码解析。
Spring AI 是一个旨在简化 Java 应用集成大语言模型(LLM)的框架,其设计核心在于抽象化和模块化。它通过定义清晰的接口和提供丰富的实现,使开发者能够以统一的方式与不同的 AI 模型和服务进行交互,极大地提升了开发效率和代码的可维护性。
其核心架构与组件设计遵循分层思想,主要包括:
- 抽象层 (Abstraction Layer):定义了与 AI 模型交互的核心接口,是业务代码直接依赖的部分。
- 实现层 (Implementation Layer):包含了对接各种具体 AI 服务(如 OpenAI, DeepSeek)的代码。
- 应用层 (Application Layer):由开发者编写的业务逻辑,通过调用抽象层的接口来使用 AI 能力。
一、 核心组件与源码解析
以下是 Spring AI 的核心组件及其在源码中的体现:
1. ChatClient 接口:统一的对话入口
这是 Spring AI 中最核心的接口之一,定义了与大语言模型进行对话的基本行为。它有两个主要实现:
- 同步调用:
ChatClient接口,定义了call()方法,用于发送请求并同步等待响应。 - 流式调用:
StreamingChatClient接口,定义了stream()方法,返回一个Flux<ChatResponse>,用于处理流式响应(如 ChatGPT 的逐字输出效果)。
源码解析:
- 接口定义:
ChatClient接口继承自ModelClient<Prompt, ChatResponse>,明确了其处理Prompt请求并返回ChatResponse响应的职责。 - 默认方法:它提供了一个便捷的
call(String message)默认方法,可以快速地将一个字符串消息包装成Prompt并发起调用。
2. Prompt 与 Message:请求的构建
Prompt 类是发送给 AI 模型的请求对象,它封装了对话的核心内容。一个 Prompt 包含一个或多个 Message 对象和一个 ChatOptions 对象。
源码解析:
- Message 层次结构:
Message是一个接口,代表对话中的一条消息。框架提供了多种实现,如UserMessage(用户输入)、AssistantMessage(助手回复)、SystemMessage(系统指令)等,覆盖了对话所需的全部角色。 - ChatOptions:这个类封装了影响模型生成行为的各种参数,如
temperature(创造力)、maxTokens(最大输出长度)等。
3. ChatResponse 与 Generation:响应的封装
ChatResponse 类是 AI 模型返回的响应对象,它包含了模型生成的内容和相关元数据。
源码解析:
- Generation 对象:
ChatResponse的核心是Generation对象,它代表了模型生成的一条具体结果。Generation包含一个AiMessage(通常是AssistantMessage)和一个ChatGenerationMetadata对象。 - 元数据 (Metadata):
ChatGenerationMetadata是一个关键的元数据容器,它存储了模型响应的附加信息,如finishReason(停止原因)。这个字段对于判断模型是否需要调用工具(tool_calls)至关重要。
4. 模型实现类:对接具体 AI 服务
这是框架的实现层,为不同的 AI 服务提供商提供了具体的 ChatModel 实现。
源码解析 (以 OpenAI 为例):
- 实现类:
OpenAiChatModel类是ChatModel接口的一个具体实现,专门用于与 OpenAI 的 API 进行交互。 - 核心方法:它实现了
call()和stream()方法。在call()方法中,它会将传入的Prompt对象转换为 OpenAI API 所需的ChatCompletionRequest请求体,然后通过RestClient发送 HTTP 请求,并将响应转换为ChatResponse对象。 - 辅助组件:该类内部使用了
RetryTemplate(用于失败重试)、ToolCallingManager(用于管理工具调用)等组件,体现了框架的健壮性和扩展性。
5. EmbeddingModel 与 VectorStore:向量处理与存储
除了对话,Spring AI 也支持处理文本嵌入(Embeddings),这是实现语义搜索、RAG(检索增强生成)等高级功能的基础。
源码解析:
- EmbeddingModel:这是一个接口,定义了生成文本嵌入向量的方法。框架为 OpenAI、DeepSeek 等提供了具体实现,如
OpenAiEmbeddingModel。 - VectorStore:这是一个抽象类,定义了向量存储和检索的基本操作,如
add()(添加文档)、similaritySearch()(相似性搜索)。框架提供了基于 Milvus、Chroma、pgvector 等多种数据库的实现。VectorStore会使用EmbeddingModel来为文档生成嵌入向量并存入数据库。
6. 自动配置 (Auto Configuration):开箱即用的魔法
为了让开发者能够“开箱即用”,Spring AI 大量使用了 Spring Boot 的自动配置机制。
源码解析 (以 pgvector 为例):
- 条件装配:在
ChromaVectorStoreAutoConfiguration这样的配置类中,使用了@ConditionalOnClass、@ConditionalOnProperty等注解。这意味着只有当 classpath 中存在特定的类(如EmbeddingModel、RestClient)并且配置文件中启用了特定属性时,相关的VectorStoreBean 才会被创建。 - 属性绑定:使用
@EnableConfigurationProperties注解将配置文件(如application.properties)中的属性自动绑定到配置类中,使得配置非常灵活和集中。
二、 核心工作流程总结
从源码层面看,一次典型的 AI 调用流程如下:
- 请求构建:开发者通过
ChatClient的call()或stream()方法,传入一个Prompt对象。 - 模型选择:Spring Boot 的自动配置机制根据应用配置,实例化了具体的
ChatModel实现(如OpenAiChatModel),并将其注入到ChatClient中。 - 请求执行:
ChatClient调用ChatModel的call()或stream()方法。具体的模型实现类会将Prompt转换为对应服务商的 API 请求格式,并通过 HTTP 客户端发送。 - 响应处理:模型实现类接收到 HTTP 响应后,会将其解析并封装成统一的
ChatResponse对象返回给ChatClient。 - 结果返回:
ChatClient将ChatResponse返回给开发者。开发者可以从ChatResponse中提取出Generation,进而获取模型生成的文本内容和元数据。
三、Spring AI 中工具调用(Tool Calling)和 RAG(检索增强生成)的实现原理
Spring AI 中工具调用(Tool Calling)和 RAG(检索增强生成)的实现,都体现了其设计精巧、模块化的核心思想。它们并非单一组件,而是多个模块协同工作的结果。
1. 工具调用(Tool Calling)的实现原理
工具调用的实现核心是 “代理模式” 与 “递归调用” 的结合,其流程如下:
1) 定义工具 (Tool)
开发者首先需要将外部功能(如查询天气、计算)封装成 Tool 对象。这通常通过实现 FunctionCallback 接口或使用其构建器来完成,需要提供函数名、描述、参数定义和实际的执行逻辑。
2) 发起调用与模型响应
当通过 ChatClient 发起调用时,框架会自动将注册的工具信息(名称、参数、描述)以特定格式(如 JSON Schema)注入到发送给大模型的 Prompt 中。模型在理解用户意图后,如果需要使用工具,会返回一个特殊的响应,其中包含 finish_reason(如 tool_calls)以及要调用的工具名称和参数。
3) 核心处理循环
这是框架自动处理工具调用的关键,发生在 OpenAiChatModel 等具体实现类的 internalCall 方法中,流程如下:
- 判断响应:框架检查模型返回的
Generation对象,判断其是否为工具调用(通过finishReason和是否存在toolCalls)。 - 执行工具:如果是工具调用,框架会遍历所有
toolCalls,根据名称找到对应的FunctionCallback实现,并通过反射机制执行其业务逻辑,获取工具执行结果。 - 构建新 Prompt:框架将原始对话历史、上一轮的 AI 回复(包含工具调用指令)以及本轮工具执行的结果,组合成一个新的
Prompt对象。 - 递归调用:框架使用这个新的
Prompt递归地调用internalCall方法,将工具执行的结果“告知”给大模型。模型会根据这些新信息,决定是继续调用其他工具,还是生成最终的用户可读回复。
4) 控制开关:proxyToolCalls
这是一个重要的配置选项,它决定了上述自动处理循环是否生效。
proxyToolCalls = false(默认):框架会自动执行上述完整的工具调用、结果处理和递归调用流程。开发者最终得到的是模型基于工具结果生成的最终答案。proxyToolCalls = true:框架会“代理”模式,即不自动处理工具调用。它会将模型返回的原始ChatResponse(其中包含ToolCall信息)直接返回给开发者。开发者需要自己解析ToolCall,执行工具逻辑,并构建新的 Prompt 进行下一次调用。这提供了更高的灵活性,适用于需要自定义工具调用流程的复杂场景。
2. RAG(检索增强生成)的实现原理
RAG 的实现是一个典型的 ETL(抽取、转换、加载)流程,结合了向量存储和检索技术,主要分为数据准备和查询增强两个阶段。
1) 数据准备阶段(构建知识库)
- 文档加载 (Extract):使用自定义的
DocumentLoader从各种数据源(如文件系统、数据库、网页)加载原始文本数据,转换为Document对象。Document对象不仅包含文本内容,还可以携带元数据(metadata),如来源 URL、作者、章节标题等。 - 文档切分 (Transform):由于大模型有上下文长度限制,需要将长文档切分成较小的、语义连贯的块(chunks)。Spring AI 提供了如
TokenTextSplitter等工具来实现智能切分。 - 向量化与存储 (Load):这是 RAG 的核心。框架使用
EmbeddingModel(如OpenAiEmbeddingModel)将每个文档块转换为一个高维向量(embedding)。这个向量能够捕捉文本的语义信息。然后,这些向量连同其对应的原始文本和元数据,被存储到VectorStore中。Spring AI 支持多种向量数据库(如 pgvector, Milvus, Chroma),通过统一的接口屏蔽了底层差异。
2) 查询增强阶段(回答问题)
- 用户查询:用户向
ChatClient提出一个问题。 - 检索相关文档:在将用户问题发送给大模型之前,框架会先使用
EmbeddingModel将问题转换为向量。然后,在VectorStore中执行相似度搜索(similarity search),找出与问题向量最接近的几个文档块。 - 构建增强 Prompt:框架将检索到的相关文档块的文本内容,以及一个预设的系统指令(system prompt),与用户的问题组合在一起,构建一个新的、信息更丰富的 Prompt。这个系统指令通常会告诉模型:“请基于以下参考资料回答用户问题。如果资料不足以回答问题,请明确告知用户。”
- 生成最终答案:这个增强后的 Prompt 被发送给大模型。模型在生成回复时,不仅基于其内部知识,还会重点参考和整合检索到的参考资料,从而生成更准确、更相关、更可信的答案。
总而言之,Spring AI 通过提供 FunctionCallback、EmbeddingModel、VectorStore 等核心抽象和实现,将复杂的工具调用和 RAG 流程进行了高度封装和自动化。开发者只需关注业务逻辑的实现,而无需过多关心底层的网络请求、数据转换和流程控制,极大地提升了开发效率和应用的健壮性。
四、使用的设计模式
在分析 Spring AI 的核心组件后,可以清晰地看到它巧妙地运用了多种经典的设计模式,这些模式是其实现高内聚、低耦合和高度可扩展架构的关键。除了之前提到的抽象工厂模式,主要还应用了以下几种:
1. 策略模式 (Strategy Pattern)
这是 Spring AI 中应用最广泛的设计模式之一,核心思想是定义一系列算法,并将每个算法封装起来,使它们可以相互替换。
- 应用场景:AI 模型(
ChatModel,EmbeddingModel)和向量存储(VectorStore)的实现。 - 模式解析:
- 接口定义策略:
ChatModel接口本身就是一个策略接口,它声明了call()和stream()等方法。 - 具体策略实现:
OpenAiChatModel,DeepSeekChatModel等都是实现了ChatModel接口的具体策略类。每个类都封装了与特定 AI 服务商 API 交互的具体算法。 - 上下文使用策略:
ChatClient作为上下文,持有一个ChatModel的引用。在运行时,根据配置(如通过 Spring 的依赖注入),ChatClient会使用具体的OpenAiChatModel或DeepSeekChatModel等策略来执行实际的 AI 调用。
- 接口定义策略:
- 优点:高度解耦。业务代码(
ChatClient)只依赖于抽象的ChatModel接口,无需关心具体是哪家 AI 服务的实现。这使得新增一个 AI 服务商(如阿里云通义千问)只需新增一个策略类,而无需修改现有业务逻辑,完美符合开闭原则。
2. 模板方法模式 (Template Method Pattern)
该模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变算法结构即可重定义算法的某些特定步骤。
- 应用场景:各种
ChatModel和EmbeddingModel的实现基类。 - 模式解析:
- 抽象类定义骨架:框架内部可能提供一个抽象基类,如
AbstractChatModel,它实现了ChatModel接口。在这个基类中,它可能定义了call()方法的通用流程:如记录请求日志 -> 执行前置处理 -> 发送 HTTP 请求 -> 处理响应 -> 记录响应日志 -> 返回结果。 - 抽象方法延迟实现:其中,“发送 HTTP 请求”和“处理响应”这两个核心步骤在基类中被声明为抽象方法(例如
doCall(Prompt prompt))。 - 子类实现特定步骤:具体的模型类(如
OpenAiChatModel)继承自AbstractChatModel,并只需要实现doCall()方法,编写与 OpenAI API 交互的特定代码即可。它无需关心日志记录、重试等通用逻辑。
- 抽象类定义骨架:框架内部可能提供一个抽象基类,如
- 优点:代码复用和流程统一。避免了在每个具体的模型实现类中都重复编写日志、重试、异常处理等横切关注点的代码,保证了核心调用流程的一致性。
3. 适配器模式 (Adapter Pattern)
该模式将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以协同工作。
- 应用场景:将第三方 AI 服务的 API 响应适配到 Spring AI 的统一模型。
- 模式解析:
- 目标接口:Spring AI 定义了自己的响应模型,如
ChatResponse和Generation。 - 被适配对象:第三方服务(如 OpenAI)返回的是其自身的 JSON 响应结构,例如 OpenAI 的
ChatCompletionResponse。 - 适配器实现:在
OpenAiChatModel类中,当接收到ChatCompletionResponse对象后,会有一个转换过程(例如,调用一个ResponseMapper或直接在代码中进行属性映射),将其“适配”成 Spring AI 的ChatResponse对象。
- 目标接口:Spring AI 定义了自己的响应模型,如
- 优点:兼容性。使得 Spring AI 的核心抽象层可以与任何第三方服务集成,无论其内部 API 设计如何,只需提供一个适配器即可,进一步增强了框架的扩展性。
4. 观察者模式 (Observer Pattern)
该模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,会通知所有观察者,使它们能够自动更新。
- 应用场景:AI 调用过程中的事件监听,例如回调函数(Callbacks)和监听器(Listeners)。
- 模式解析:
- 主题(Subject):框架内部可能有一个事件发布器,用于在 AI 调用的关键节点(如请求开始、响应结束、发生错误)发布事件。
- 观察者(Observer):开发者可以注册自己的监听器(Listener)或回调函数(Callback),这些监听器/回调函数实现了特定的接口(如
ApplicationListener<AiCallStartedEvent>)。 - 通知与更新:当 AI 调用开始时,框架发布
AiCallStartedEvent事件,所有监听该事件的观察者就会收到通知,并执行自定义逻辑(如记录日志、统计耗时)。
- 优点:可扩展性和关注点分离。允许开发者在不侵入核心调用逻辑的情况下,对 AI 调用过程进行监控、日志记录、性能分析等操作。
5. 代理模式 (Proxy Pattern)
该模式为其他对象提供一种代理以控制对这个对象的访问。
- 应用场景:
ChatClient接口的设计。 - 模式解析:
- 真实主题(Real Subject):真正执行 AI 调用的是具体的
ChatModel实现类(如OpenAiChatModel)。 - 代理(Proxy):
ChatClient接口及其实现类扮演了代理的角色。业务代码不直接调用ChatModel,而是通过ChatClient这个代理层进行访问。 - 控制访问:
ChatClient代理层可以在调用ChatModel之前和之后添加额外的逻辑,例如参数校验、结果缓存、统一异常处理等。
- 真实主题(Real Subject):真正执行 AI 调用的是具体的
- 优点:增强和控制。在不改变
ChatModel核心逻辑的前提下,通过代理层可以方便地为 AI 调用添加横切功能。
总结
Spring AI 通过组合运用这些设计模式,构建了一个优雅且强大的框架:
- 策略模式:实现了算法与使用的分离,是支持多模型的核心。
- 模板方法模式:封装了不变的流程,将变化的部分延迟到子类,提高了代码复用率。
- 适配器模式:解决了与第三方服务集成时的接口不匹配问题。
- 观察者模式:提供了非侵入式的扩展点,方便进行监控和日志记录。
- 代理模式:为真实对象提供了访问控制和功能增强的入口。
这些设计模式共同作用,使得 Spring AI 具备了高度的灵活性、可扩展性和可维护性,让 Java 开发者能够轻松地将 AI 能力集成到自己的应用中。
五、实战案例
下面是一个完整的 Spring Boot 应用示例,它演示了如何使用 Spring AI 的 ChatClient 和工具调用(Tool Calling)功能,让 AI 模型在回答问题时自动查询天气。
这个示例将带你完成以下核心步骤:
- 项目搭建与依赖配置:创建一个 Spring Boot 项目并引入必要的依赖。
- 工具定义:将一个天气查询功能封装成 AI 模型可以调用的工具。
- 业务逻辑编写:通过
ChatClient发起对话,并观察 AI 模型如何自动决定是否调用工具。 - 运行与验证:启动应用并测试工具调用的效果。
1. 项目搭建与依赖配置 (Maven)
首先,创建一个新的 Spring Boot 项目。在你的 pom.xml 文件中,确保引入了以下关键依赖。请注意,由于 Spring AI 仍处于里程碑(Milestone)阶段,你需要额外配置 Spring 的里程碑仓库。
<?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.5.0</version> <!-- 使用与 Spring AI 兼容的 Boot 版本 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-tool-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai-tool-demo</name>
<description>Demo project for Spring AI Tool Calling</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M4</spring-ai.version> <!-- 使用最新的 Spring AI 版本 -->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入 Spring AI 的 OpenAI 模块,用于调用 OpenAI 或兼容 API -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- 引入 Spring AI 的工具模块,包含 FunctionCallback 等核心类 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tool-support</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 配置 Spring Milestone 仓库,因为 Spring AI 在此发布 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
2. 定义工具 (Tool)
接下来,我们需要将一个具体的功能(例如查询天气)定义为 AI 模型可以理解和调用的工具。这通过实现 FunctionCallback 接口或使用其构建器来完成。
// 文件: src/main/java/com/example/springaitool/WeatherService.java
package com.example.springaitool;
import org.springframework.ai.tool.FunctionCallback;
import org.springframework.ai.tool.Tool;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 天气服务工具类,实现了 FunctionCallback 接口,可以被 AI 模型调用。
*/
@Component
public class WeatherService implements FunctionCallback {
// 模拟的天气数据
private static final Map<String, String> WEATHER_DATA = Map.of(
"北京", "晴,25℃,空气质量优",
"上海", "多云,28℃,空气质量良",
"广州", "雷阵雨,30℃,空气质量中"
);
@Override
public String call(String toolCallMessage) {
// 1. 解析工具调用的参数。实际项目中这里可能是 JSON 字符串,需要解析。
// 为简化示例,我们假设传入的是纯城市名。
String city = toolCallMessage.trim();
// 2. 执行工具逻辑,查询天气
String weatherInfo = WEATHER_DATA.getOrDefault(city, "未找到该城市的天气信息");
// 3. 返回工具执行结果
return weatherInfo;
}
@Override
public Tool getDefinition() {
// 定义工具的元信息,包括名称、描述和参数
return Tool.builder()
.name("get_weather")
.description("查询指定城市的天气情况")
.parameters(Map.of(
"type", "object",
"properties", Map.of(
"city", Map.of(
"type", "string",
"description", "要查询天气的城市名称"
)
),
"required", new String[]{"city"}
))
.build();
}
}
3. 编写业务逻辑 (Controller)
现在,我们创建一个 Web 控制器,通过 ChatClient 发起对话,并将我们定义的工具注册进去。
// 文件: src/main/java/com/example/springaitool/AiController.java
package com.example.springaitool;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.prompt.UserPromptTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class AiController {
private final ChatClient chatClient;
private final WeatherService weatherService;
// 通过构造函数注入 ChatClient 和 WeatherService
public AiController(ChatClient.Builder builder, WeatherService weatherService) {
// 构建 ChatClient,并注册我们的天气工具
this.chatClient = builder
.defaultTools(weatherService) // 将工具注册到客户端
.build();
this.weatherService = weatherService;
}
/**
* 同步调用示例
*/
@GetMapping("/chat")
public String chat(@RequestParam(value = "message", defaultValue = "你好") String message) {
// 创建一个用户消息的 Prompt
Prompt prompt = new Prompt(message);
// 发起同步调用,并获取响应内容
return chatClient.call(prompt).getResult().getOutput().getContent();
}
/**
* 流式调用示例 (Server-Sent Events)
*/
@GetMapping(value = "/chat/stream", produces = "text/event-stream")
public Flux<String> chatStream(@RequestParam(value = "message", defaultValue = "你好") String message) {
Prompt prompt = new Prompt(message);
// 发起流式调用,返回一个 Flux<String>,可以逐块处理响应
return chatClient.stream(prompt).map(ChatResponse::getContent);
}
}
4. 配置与运行
在 application.properties 或 application.yml 文件中,配置你的 AI 模型服务信息。这里以 OpenAI 为例:
# application.properties
spring.ai.openai.api-key=sk-你的-openai-api-key
spring.ai.openai.base-url=https://api.openai.com/v1
spring.ai.openai.chat.options.model=gpt-4o-mini
运行应用:
- 启动你的 Spring Boot 应用。
- 打开浏览器或使用
curl、Postman 等工具,访问http://localhost:8080/chat?message=北京今天天气怎么样?。
观察结果:
你会看到 AI 模型并没有直接回答问题,而是返回了一个工具调用的指令,其中指定了要调用 get_weather 工具,并传入了参数 {"city": "北京"}。这正是我们之前定义的工具。框架会自动捕获这个指令,执行 WeatherService.call() 方法,并将结果返回给模型,最终由模型生成一个自然语言的回答。
这个完整的示例清晰地展示了 Spring AI 是如何通过 ChatClient 和 FunctionCallback 等核心组件,实现强大且易于使用的工具调用功能的。
六、RAG 的实战示例
下面是一个使用 Spring Boot 和 pgvector 构建本地知识库问答系统的完整 RAG 实战示例。
这个示例将带你完成以下核心步骤:
- 环境准备与项目搭建:配置 PostgreSQL 和 pgvector 扩展,并创建 Spring Boot 项目。
- 数据准备与向量化:编写代码加载本地文档(如 PDF、TXT),进行切分并存储到向量数据库。
- RAG 问答接口开发:通过
ChatClient实现一个接口,在生成回答前自动检索知识库并增强 Prompt。 - 运行与验证:启动应用并测试基于本地知识的问答效果。
1. 环境准备与项目搭建
前提条件:确保你已安装并运行了 PostgreSQL 数据库。
步骤一:在 PostgreSQL 中启用 pgvector 扩展
- 连接到你的 PostgreSQL 数据库(例如
mydb)。 - 执行以下 SQL 命令来创建 pgvector 扩展:
CREATE EXTENSION IF NOT EXISTS vector; - 验证扩展是否创建成功:
SELECT * FROM pg_extension WHERE extname = 'vector';
步骤二:创建 Spring Boot 项目并配置依赖
在你的 pom.xml 文件中,确保引入了以下关键依赖:
<!-- 引入 Spring AI 的核心 starter,包含 ChatClient 等基础组件 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter</artifactId>
</dependency>
<!-- 引入 Spring AI 的 OpenAI starter,用于调用 OpenAI 的 Embedding 和 Chat 模型 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- 引入 Spring AI 的 Data JPA Vector Store starter,用于将向量数据存储到 PostgreSQL -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-data-jpa</artifactId>
</dependency>
<!-- 引入 Spring Boot 的 Data JPA 和 PostgreSQL 驱动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- 引入 Spring Boot 的 Web starter,用于创建 REST API -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
步骤三:配置 application.properties
# application.properties
# 配置数据源,连接到你的 PostgreSQL 数据库
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=your_username
spring.datasource.password=your_password
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# 配置 OpenAI 的 API Key
spring.ai.openai.api-key=sk-你的-openai-api-key
# 配置默认的 Chat Model (可选,如果不设置则使用 OpenAI 的默认模型)
spring.ai.openai.chat.options.model=gpt-4o-mini
# 配置默认的 Embedding Model (可选,如果不设置则使用 OpenAI 的默认 embedding 模型)
spring.ai.openai.embedding.options.model=text-embedding-3-small
2. 数据准备与向量化 (构建知识库)
步骤一:准备文档
将你想要纳入知识库的文档(例如 knowledge.txt)放在 src/main/resources/documents/ 目录下。
步骤二:编写文档加载与向量化代码
// 文件: src/main/java/com/example/springairag/VectorizationService.java
package com.example.springairag;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
@Service
public class VectorizationService {
private final VectorStore vectorStore;
@Autowired
public VectorizationService(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
/**
* 从 classpath 加载文本文件,切分文档,并将 Document 对象存储到 VectorStore 中
*/
public void loadAndStoreDocuments() throws IOException {
// 1. 加载文档
ClassPathResource resource = new ClassPathResource("documents/knowledge.txt");
TextReader textReader = new TextReader(resource);
Document document = textReader.read();
// 2. 切分文档
TokenTextSplitter textSplitter = new TokenTextSplitter();
List<Document> documentChunks = textSplitter.apply(document);
// 3. 将文档块存储到向量数据库
vectorStore.add(documentChunks);
}
}
步骤三:创建初始化 Bean,在应用启动时自动构建知识库
// 文件: src/main/java/com/example/springairag/AppInitializer.java
package com.example.springairag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class AppInitializer implements CommandLineRunner {
private final VectorizationService vectorizationService;
@Autowired
public AppInitializer(VectorizationService vectorizationService) {
this.vectorizationService = vectorizationService;
}
@Override
public void run(String... args) throws Exception {
vectorizationService.loadAndStoreDocuments();
System.out.println("知识库构建完成!");
}
}
3. RAG 问答接口开发
// 文件: src/main/java/com/example/springairag/AiController.java
package com.example.springairag;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
public class AiController {
private final ChatClient chatClient;
private final VectorStore vectorStore;
public AiController(ChatClient chatClient, VectorStore vectorStore) {
this.chatClient = chatClient;
this.vectorStore = vectorStore;
}
/**
* RAG 问答接口
*/
@GetMapping("/ask")
public String ask(@RequestParam String question) {
// 1. 从向量数据库中检索与问题最相关的文档块
List<Document> relevantDocuments = vectorStore.similaritySearch(question);
// 2. 构建增强后的 Prompt
// 将检索到的文档内容拼接起来,作为上下文
String context = relevantDocuments.stream()
.map(Document::getContent)
.reduce("", (a, b) -> a + "\n\n" + b);
// 构建最终的 Prompt,告诉模型基于给定的上下文回答问题
String prompt = String.format(
"你是一个问答机器人。请根据以下参考资料回答用户的问题。如果参考资料不足以回答问题,请明确告知用户。\n\n" +
"参考资料:\n%s\n\n" +
"用户问题:%s",
context, question);
// 3. 调用大模型生成最终答案
return chatClient.call(prompt).getResult().getOutput().getContent();
}
}
4. 运行与验证
步骤一:启动应用
运行你的 Spring Boot 应用。在启动过程中,AppInitializer 会自动执行,加载并切分 knowledge.txt 文件,然后使用 OpenAI 的 Embedding 模型将其向量化并存储到 PostgreSQL 的 pgvector 扩展中。
步骤二:测试接口
启动成功后,打开浏览器或使用 curl、Postman 等工具,访问 http://localhost:8080/ask?question=你的问题。
观察结果:
当你提出一个问题时,/ask 接口会首先在向量数据库中检索与问题最相关的知识片段,然后将这些片段作为上下文,连同问题本身一起发送给大模型。大模型会基于这个增强的上下文生成最终的回答,从而实现对本地知识的准确问答。
这个完整的示例展示了如何利用 Spring AI 的模块化设计,快速构建一个功能强大的 RAG 应用。从文档加载、切分、向量化存储到最终的检索和生成,每一步都清晰且易于扩展。
希望这两个实战示例能帮助你更好地理解和应用 Spring AI!
七、部署和性能调优指南
在完成了功能开发后,部署和性能调优是确保 Spring AI 应用在生产环境中稳定、高效运行的关键一步。一个好的生产级应用,不仅在于功能实现,更在于其稳定性、可观测性和成本效益。
下面为您整理了一份 Spring AI 生产部署与性能调优指南,涵盖了您提到的模型缓存、连接池配置等核心方面。
1. 核心组件性能调优
连接池与超时配置 (以 OpenAI 为例)
AI 模型服务通常是远程调用,网络延迟和连接管理至关重要。Spring AI 基于 Project Reactor 构建,底层使用 Reactor Netty 的 HttpClient,因此可以通过标准的 Spring Boot 配置来优化连接池和超时。
# application.yml
# 配置底层 HttpClient 的连接池
spring:
ai:
openai:
# 连接超时(毫秒),默认 5000
connect-timeout: 5000
# 读取超时(毫秒),默认 30000
read-timeout: 30000
# 写入超时(毫秒),默认 30000
write-timeout: 30000
# 配置 Reactor Netty 的连接池参数
reactor:
netty:
http:
client:
# 连接池最大连接数,默认 200
max-connections: 200
# 每个远程主机的最大连接数,默认 200
max-connections-per-host: 200
# 连接池中连接的最大存活时间(毫秒),-1 表示无限制
max-life-time: -1
# 连接池中连接的最大空闲时间(毫秒),-1 表示无限制
max-idle-time: 60000
调优建议:
- 高并发场景:如果您的应用并发量很高,可以适当增加
max-connections和max-connections-per-host。 - 网络不稳定环境:可以适当增加超时时间 (
read-timeout,write-timeout),防止因短暂网络波动导致请求失败。 - 资源受限环境:如果服务器资源有限,可以适当调低连接池大小,防止过多连接耗尽系统资源。
模型响应缓存 (Caching)
对于重复性问题或基于固定知识库的查询,缓存可以显著降低 API 调用成本并提高响应速度。
方案一:本地缓存 (如 Caffeine)
适用于单机部署,缓存命中率高的场景。
-
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency> -
配置与使用:
@Configuration @EnableCaching public class CacheConfig { // 配置 Caffeine 缓存 } @Service public class AiService { // 使用 @Cacheable 注解缓存方法结果 @Cacheable(value = "aiResponses", key = "#prompt") public String getCachedResponse(String prompt) { // 调用 ChatClient 的代码 } }
方案二:分布式缓存 (如 Redis)
适用于集群部署,需要共享缓存的场景。
-
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> -
配置与使用:
// 配置 RedisCacheManager (略) @Service public class AiService { @Cacheable(value = "aiResponses", key = "#prompt") public String getCachedResponse(String prompt) { // 调用 ChatClient 的代码 } }
调优建议:
- 缓存粒度:可以基于完整的 Prompt、用户 ID + Prompt 或者业务上下文来设计缓存 Key。
- 缓存失效:合理设置缓存的 TTL (Time To Live),特别是当您的知识库或业务数据频繁更新时。
高级功能:Prompt Caching
值得注意的是,Spring AI 从 2025.1.0-M1 版本开始,已支持 Anthropic Claude 和 AWS Bedrock 的 Prompt Caching 功能。该功能允许模型缓存部分 Prompt 的计算结果,对于重复使用的系统提示词 (System Prompt) 或长文档分析等场景,可以显著降低成本和延迟。这需要在代码中进行特定配置以启用。
2. 生产环境稳定性与可观测性
限流、熔断与重试 (Resilience4j)
AI 服务的 API 可能会因过载或故障而不可用。为了防止级联故障,必须实施服务保护机制。
-
添加依赖:
<dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> </dependency> -
配置与使用:
# application.yml resilience4j: circuitbreaker: instances: aiService: # 熔断器配置 failureRateThreshold: 50 waitDurationInOpenState: 5000 ringBufferSizeInHalfOpenState: 3 ringBufferSizeInClosedState: 10 ratelimiter: instances: aiService: # 限流器配置 limitForPeriod: 100 limitRefreshPeriod: 1s retry: instances: aiService: # 重试配置 maxAttempts: 3 waitDuration: 100ms@Service public class AiService { @CircuitBreaker(name = "aiService") @RateLimiter(name = "aiService") @Retry(name = "aiService") public String safeCall(String prompt) { // 调用 ChatClient 的代码 } }
调优建议:
- 熔断:当 AI 服务错误率过高时,快速失败并返回友好提示,避免线程池耗尽。
- 限流:保护您的应用不被突发流量打垮,也防止因用户滥用导致 API 费用激增。
- 重试:对于网络抖动等瞬时故障,进行有限次数的重试可以有效提高成功率。
监控与告警 (Prometheus + Grafana)
为了让系统“可观测”,需要建立完善的监控体系。
- 集成 Micrometer:Spring Boot Actuator 和 Micrometer 是事实上的标准,可以轻松收集应用的性能指标(如方法调用耗时、调用次数、错误率等)。
- 对接 Prometheus:将 Micrometer 收集的指标暴露给 Prometheus 进行存储和查询。
- 配置 Grafana 面板:使用 Grafana 可视化 Prometheus 中的数据,创建包含以下关键指标的仪表盘:
- 模型性能:平均/最大响应延迟、请求吞吐量 (RPS)、API 调用错误率。
- 资源使用:JVM 内存、CPU 利用率。
- 业务指标:Token 消耗量(成本)、缓存命中率、RAG 检索召回率。
- 设置告警规则:在 Prometheus 中设置告警规则,例如当熔断器打开、限流触发、错误率超过阈值时,通过邮件、企业微信或钉钉通知到负责人。
3. 部署与资源配置
JVM 参数调优
AI 应用通常是计算密集型和内存密集型的。建议为应用分配足够的堆内存,并选择合适的垃圾回收器。
- 堆内存:根据应用规模和并发量,通常建议至少
-Xmx4g。对于处理大文档或高并发的场景,可能需要-Xmx8g或更高。 - GC 选择:对于大内存应用,建议使用 G1 GC (
-XX:+UseG1GC),它在大堆内存下能提供更好的停顿时间控制。
资源配置建议
| 场景 | CPU 核心 | 内存 | 存储 | GPU |
|---|---|---|---|---|
| 开发/测试 | 4 | 16GB | SSD | 无 |
| 中小规模生产 | 8-16 | 32-64GB | NVMe SSD | 可选,用于本地模型 |
| 大规模生产 | 32+ | 128GB+ | 分布式存储 | 推荐,用于加速本地模型 |
希望这份指南能帮助您将 Spring AI 应用成功部署到生产环境。如果需要针对特定云服务商(如阿里云、AWS)的部署细节,或者更深入的 JVM 调优方案,以后可以继续探讨。
更多推荐


所有评论(0)