1182 字
6 分钟
小小折腾一下Spring AI
2025-10-11
无标签

Spring AI自从推出就受到了许多的关注,被认为给Spring甚至Java又一次续命的框架,最近花了一点时间研究了一下,成功将Deepseek的API接入了Spring Boot。在此记录一下步骤以供参考。

写在前面#

Spring AI虽然已经发布了1.0正式版,但是仍然算不上十分稳定,可能后续版本还会有很多Breaking change,因此在应用于生产环境中时应当慎重。

本文全部参考Spring AI官方文档编写。因为AI实在错的太多了

引入Spring AI依赖#

假设你已经会搭建基本的Spring Boot开发环境了,若不会请移步Spring Initializr/Cloud Native App Initializer

下面配置使用的是Maven,如果用的是Gradle请参考Getting Started :: Spring AI Reference

Spring AI使用BOM来统一管理依赖版本,因此在依赖管理下首先引入BOM:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

如果你需要使用SNAPSHOT版本,需要额外配置仓库:

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

然后我这里要使用的是Deepseek的API和Spring提供的对话记忆功能,因此引入以下依赖:

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
<!--对话内容持久化到数据库-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>

配置文件#

主要就是填个Deepseek的密钥,如果你之前没有配过数据库的话也得设置一下。(其他存储方式参考Chat Memory :: Spring AI Reference

spring.ai.deepseek.api-key=你自己的密钥
# 指定总是进行自动建表
spring.ai.chat.memory.repository.jdbc.initialize-schema=always
# 如有需要,自定义建表SQL
spring.ai.chat.memory.repository.jdbc.schema=classpath:org/springframework/ai/chat/memory/repository/jdbc/schema-@@platform@@.sql

这里有一个坑,1.0.0版本的Spring AI并没有针对我使用的MySQL的自动建表语句,因此会报错,但是翻阅了Github源码发现在5月份的一次提交中已经支持了,因此推测是还没有被打到包里,所以这边我暂时先把版本改成1.1.0-SNAPSHOT来解决这个问题,你也可以手动指定建表文件进行建表,这个SQL文件长这样:

CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY (
`conversation_id` VARCHAR(36) NOT NULL,
`content` TEXT NOT NULL,
`type` ENUM('USER', 'ASSISTANT', 'SYSTEM', 'TOOL') NOT NULL,
`timestamp` TIMESTAMP NOT NULL,
INDEX `SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_TIMESTAMP_IDX` (`conversation_id`, `timestamp`)
);

编写接口#

这边的示例是调用Deepseek的API并将对话记录保存到数据库当中,因此需要使用:

JdbcChatMemoryRepository:Jdbc实现的聊天记录存数据库 ChatMemory:负责保存聊天记录 DeepSeekChatModel:最底层的对于API的封装 ChatClient:对ChatModel的封装,用于简化调用,同时将对话记录保存

这些Bean虽然有的可以自动注入,但是有的必须在构造函数当中注入,因此这里统一在构造函数注入以免在构造函数中使用相关对象时其还未被Spring初始化。

我这里只是一个演示因此我直接在Controller层写了,如果你要在Service层写也可以。

示例代码:

JdbcChatMemoryRepository chatMemoryRepository;
private final DeepSeekChatModel deepSeekChatModel;
private final ChatClient client;
private ChatMemory chatMemory;
// 构造函数
public ChatController(DeepSeekChatModel deepSeekChatModel, JdbcChatMemoryRepository jdbcChatMemoryRepository) {
this.chatMemoryRepository = jdbcChatMemoryRepository;
this.deepSeekChatModel = deepSeekChatModel;
this.chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository) //设置记忆仓库
.maxMessages(50) //设置当前对话记录最多保存多少条
.build();
this.client = ChatClient.builder(deepSeekChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) //设置聊天记忆
.build();
}

到这里就可以进行调用了。在调用时,你需要提供一个id来对应对话和聊天记录,这里的Result类是我自己的返回格式封装,需要根据业务需求改一下。

@PostMapping("/chat")
public Result chat(@RequestBody MessageVO messageVO) {
var response = client.prompt()
.user(messageVO.getMessage()) //填入用户发来的信息
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, messageVO.getId() //这里填会话ID))
.call();
return Result.ok(response.content());
}

虽然ChatMemory有根据ID查找聊天记录的方法,但是他的Message封装似乎并不支持序列化,不便于返回前端,但是由于我们知道这个表的结构,因此我这边使用再写一个Mapper专门查聊天记录的方式解决这个问题。

image-20250721153344936

/**
* 封装了聊天记录的实体类
*/
@Data
public class Turn {
private String type;
private String content;
}
public interface MessageMapper {
@Select("select content, type from spring_ai_chat_memory where conversation_id = #{id}")
List<Turn> getChatHistory(String id);
}

碎碎念#

Spring AI自推出起就被视为继微服务后又一次为Java和Spring续命的框架,但是自己搭下来感觉只是把你调用AI需要实现的相关代码提前实现好了,确实很方便,毕竟不少人自己搓出来的代码水平肯定不如这个,langchain4j也没有Spring AI这么优雅,而且能快速地为已有的Spring项目接入AI。但是感觉“续命”这个说法还是有些夸大了。

小小折腾一下Spring AI
https://next.ivmiku.com/posts/14/
作者
iVMiku
发布于
2025-10-11
许可协议
CC BY-NC-SA 4.0