Spring AI
本文最后更新于 2025年10月26日
持续更新中
1.Spring AI概述
Spring AI是一款Spring官方推出的一款Java调用大模型的工具,用于开发基于Java语言的大模型应用。作为Spring家族的产品,Spring AI充分利用了Spring Boot的一些特性,大大的简化了开发。
本文以DeepSeek大模型为例,介绍Spring AI在开发大模型应用中的用法。
类似的框架还有LangChain4j,截至成文日期,Spring AI对比LangChain4j有以下区别:
| Spring AI | LangChain4j | |
|---|---|---|
| Chat | 支持 | 支持 |
| Function | 支持 | 支持 |
| RAG | 支持 | 支持 |
| 对话模型 | 15+ | 15+ |
| 向量模型 | 10+ | 15+ |
| 向量数据库 | 15+ | 20+ |
| 多模态模型(图像,音频) | 5+ | 1 |
| JDK | 17 | 8 |
2.ChatClient聊天机器人
通过Spring AI基于DeepSeek大模型,创建一个智能聊天机器人,并进行对话。
1.基于jdk-21创建spring-boot项目,引入spring-boot依赖3.5.7,spring-ai依赖1.0.3
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-ai</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</version>
</parent>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
</dependencies>
</project>2.application.yml配置中进行配置,并填写DeepSeek的API_KEY,我是从DeepSeek官方(https://platform.deepseek.com/)购买获得,充值后,可以从https://platform.deepseek.com/api_keys页面获得API_KEY
⚠ 为防止误提交代码到公开仓库,spring文档建议将API_KEY写进本机环境变量,yml中设置为
api-key: ${DEEPSEEK_API_KEY}
更多配置项,可见官方文档:https://docs.spring.io/spring-ai/reference/api/chat/deepseek-chat.html
spring:
ai:
deepseek:
base-url: https://api.deepseek.com
api-key: sk-02**********************d86663.编写一个配置类,声明一个对话客户端,并且注入配置好的DeepSeek模型,通过defaultSystem()来指定大模型的默认角色和任务背景
package org.example;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModelConfig {
@Bean
public ChatClient chatClient(DeepSeekChatModel model) {
return ChatClient.builder(model)
.defaultSystem("你是聪明的智能助手,名字叫小羊")
.build();
}
}4.编写一个测试类,和创建的智能助手“小羊”打个招呼,通过user()指定用户输入的指令,此处也可以设置system()系统指定,如果不设置会使用defaultSystem
package org.example.test;
import jakarta.annotation.Resource;
import org.example.Main;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = Main.class)
public class ModelTest {
@Resource
private ChatClient chatClient;
@Test
public void testHello() {
String content = chatClient.prompt()
.user("hi,你是谁?")
.call()
.content();
System.out.println(content);
}
}得到聊天机器人的回复了,正常打印输出,一个基于DeepSeek大模型的简单的聊天机器人就实现了
通过call()是阻塞的调用,如果在http请求中使用会出现无限等待的情况,如果要实现不断输出的效果,需要web环境下使用stream()流式调用返回Flux,并设置返回格式为text/html;charset=utf-8,否则输出的中文是乱码
package org.example.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("ai")
public class ChatController {
@Resource
private ChatClient chatClient;
@GetMapping(value = "chat-stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String msg) {
return chatClient.prompt()
.user(msg)
.stream()
.content();
}
}通过使用stream()流式调用返回Flux,可以得到以下效果的输出

3.Advisors拦截增强
Spring AI通过Advisor(https://docs.spring.io/spring-ai/reference/api/advisors.html)接口提供了会话的增强功能,可以利用其开发更加高级的会话功能

Advisor接口主要用到以下实现类:
org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor简单的日志打印功能org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor可以实现会话记忆org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor与RAG知识库功能有关
在使用QuestionAnswerAdvisor时,需要额外添加依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>3.1 记录日志
可以在创建ChatClient的时候就指定默认的Advisor为SimpleLoggerAdvisor实现输出日志功能
@Bean
public ChatClient chatClient(DeepSeekChatModel model) {
return ChatClient.builder(model)
.defaultAdvisors(new SimpleLoggerAdvisor())
.defaultSystem("你是聪明的智能助手,名字叫小羊")
.build();
}SimpleLoggerAdvisor日志级别默认为DEBUG,如果要使用SimpleLoggerAdvisor打印日志到控制台,需要修改yml配置文件中的日志级别:
logging:
level:
org.springframework.ai: debug3.2 会话记忆
大模型不具备记忆能力,要想让大模型记住之前的聊天内容,唯一的办法是把之前的聊天内容和新的提示词一并发送给大模型,此时就需要用到MessageChatMemoryAdvisor
使用MessageChatMemoryAdvisor,需要先定义一个ChatMemory接口的实现,来自定义管理会话数据的逻辑(添加,获取,删除),比如可以自己选择维护会话数据到mysql,redis,或者Map中
org.springframework.ai.chat.memory.ChatMemory
public interface ChatMemory {
String DEFAULT_CONVERSATION_ID = "default";
String CONVERSATION_ID = "chat_memory_conversation_id";
default void add(String conversationId, Message message) {
Assert.hasText(conversationId, "conversationId cannot be null or empty");
Assert.notNull(message, "message cannot be null");
this.add(conversationId, List.of(message));
}
void add(String conversationId, List<Message> messages);
List<Message> get(String conversationId);
void clear(String conversationId);
}Spring AI为我们默认实现了一个实现类InMemoryChatMemoryRepository,可将会话保存到本地内存中用于测试,如果我们没有自定义ChatMemory实现类注入,默认的InMemoryChatMemoryRepository将会注入
此处,为了测试功能,就以默认的InMemoryChatMemoryRepository为例
@Bean
public ChatClient chatClient(DeepSeekChatModel model, ChatMemory chatMemory) {
return ChatClient.builder(model)
.defaultAdvisors(
SimpleLoggerAdvisor.builder().build(),
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.defaultSystem("你是聪明的智能助手,名字叫小羊")
.build();
}Controller的代码需要用户发起聊天时,调用接口传入会话的ID:chatId,并通过.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, chatId))传递给chatClient
@GetMapping(value = "chat-stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String msg, String chatId) {
return chatClient.prompt()
.user(msg)
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, chatId))
.stream()
.content();
}然后测试,先指定会话ID为001,先后两次分别提问“40除以2等于几”和“那除以5呢”,会发现第二次提问没有带上40也得到了正确答案8,再将ID改为002继续问“那乘以3呢”,大模型随即忘记了数字40,失去了记忆,这说明大模型此时通过MessageChatMemoryAdvisor增强,已经有了记忆,并且能够根据不同的会话进行区分!



这样,MessageChatMemoryAdvisor大模型会话记忆就实现了,不过当前使用的InMemoryChatMemoryRepository将会话保存在内存,进程结束即销毁,如果正式的项目需要换成其他的实现来真正的持久化,而且会话的ID应该后台生成并和当前登录用户绑定,而不是由前端随便的传进去。
"如果文章对您有帮助,可以请作者喝杯咖啡吗?"
微信支付
支付宝
