Spring AI 接入与简单使用:从环境搭建到多轮对话(JDK 17 + Spring Boot 3.5)

前言

Spring AI 是 Spring 生态中用于对接大语言模型(LLM)的抽象层,可以统一调用 OpenAI、Azure OpenAI、以及各类 OpenAI 兼容 API(如 DeepSeek、国内大模型等)。通过少量配置和几行代码,就能实现同步调用流式输出,以及带上下文记忆的多轮对话,非常适合在现有 Spring Boot 项目里快速接入 AI 能力。本文基于 JDK 17Spring Boot 3.5Spring AI 1.1 记录从零接入到简单使用的完整过程,并总结对接时的注意项。

特别说明:本文除本段外,全部由AI生成。项目地址:https://gitee.com/husolar/fast-chat.git


一、环境准备

1.1 JDK 版本

  • 必须使用 JDK 17 及以上。本项目在 pom.xml 中指定 <java.version>17</java.version>
  • 若使用 JDK 8 等低版本,会出现类似「class file version 61.0, 应改为 52.0」的编译错误,因为 Spring Boot 3.x 与 Spring AI 1.x 均基于 Java 17 编译。

在终端验证:

java -version 

应看到 17 或更高版本。

1.2 项目基础

  • 使用 Spring Boot 3.5.11 作为 parent,Maven 项目即可。
  • 仅需 spring-boot-starter-web 即可同时支持同步接口和返回 Flux<String> 的流式接口,无需单独引入 spring-boot-starter-webflux(Spring Boot 已带 Reactor)。

二、依赖引入

在 pom.xml 中做三件事:指定 Spring AI 版本、引入 BOM、添加 Starter。

2.1 属性与 BOM(必选)

<properties> <java.version>17</java.version> <spring-ai.version>1.1.2</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> 

注意:Spring AI 各模块版本由 BOM 统一管理,不要在子依赖里再写版本号,避免与 Spring Boot 依赖冲突。

2.2 引入 OpenAI 兼容 API 的 Starter

对接 OpenAI 或任意 OpenAI 兼容 的接口(如 DeepSeek)时,使用:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> 

若需要多轮对话上下文记忆,再增加:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory</artifactId> </dependency> 

三、配置文件

在 application.yaml(或 application.properties)中配置 API Key、Base URL 和模型名。

3.1 示例(以 DeepSeek 为例)

spring: ai: openai: api-key: ${OPENAI_API_KEY} base-url: https://api.deepseek.com chat: options: model: deepseek-chat 
  • api-key:建议用环境变量 OPENAI_API_KEY,不要写死在配置或代码里。
  • base-url:对接国内或第三方时改为对应地址(如 DeepSeek、OpenAI 官方等)。
  • model:对应服务商提供的模型名。

3.2 可选:对话记忆窗口

若使用了 spring-ai-starter-model-chat-memory,可配置单会话保留的最近消息条数(默认 20):

app: chat: memory: max-messages: 20 

四、简单使用:同步与流式

Spring AI 自动配置 ChatClient.Builder,注入后 build() 得到 ChatClient,即可发起调用。

4.1 注入并构建 ChatClient

@RestController public class HelloController { private final ChatClient chatClient; public HelloController(ChatClient.Builder builder) { this.chatClient = builder.build(); } } 

4.2 同步调用

一行即可拿到完整回复文本:

@GetMapping("/hello") public String hello(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input) { return chatClient.prompt(input).call().content(); } 

4.3 流式调用

返回 Flux<String>,接口需声明 SSE 类型,便于浏览器按流式展示:

@GetMapping(value = "/hello/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> helloStream(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input) { return chatClient.prompt(input).stream().content(); } 

4.4 使用 Prompt 模板(可选)

通过 PromptTemplate 占位符注入角色、用户输入等:

PromptTemplate template = new PromptTemplate("你是一个{role},请根据以下内容生成回复:{input}"); Map<String, Object> params = Map.of("role", "幽默的助手", "input", input); return chatClient.prompt(template.render(params)).stream().content(); 

五、进阶:带上下文记忆的多轮对话

大模型本身是无状态的,不会记住上一轮对话。若要实现「你说了上一句,模型能结合历史回复」,需要在服务端维护会话历史,并在每次请求时把历史一并发给模型。Spring AI 提供了 ChatMemory 与 MessageChatMemoryAdvisor,按 conversationId 隔离会话即可。

5.1 引入依赖

上文已包含:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory</artifactId> </dependency> 

5.2 配置 ChatMemory 与带记忆的 ChatClient

自定义 ChatMemory(可配置窗口大小)和另一个 ChatClient,专门用于多轮对话:

@Configuration public class ChatMemoryConfig { @Bean public ChatMemory chatMemory(ChatMemoryRepository repository, @Value("${app.chat.memory.max-messages:20}") int maxMessages) { return MessageWindowChatMemory.builder() .chatMemoryRepository(repository) .maxMessages(maxMessages) .build(); } @Bean public ChatClient chatClientWithMemory(ChatClient.Builder builder, ChatMemory chatMemory) { return builder .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); } } 
  • MessageWindowChatMemory:只保留最近 N 条消息,避免上下文过长。
  • MessageChatMemoryAdvisor:在每次请求前注入历史消息,请求后再把本轮 user/assistant 写入 memory。

5.3 提供带 conversationId 的接口

每次请求带上 conversationId,同一会话使用同一 ID,即可共享历史:

@RestController public class ChatController { private final ChatClient chatClientWithMemory; private final ChatMemory chatMemory; public ChatController(ChatClient chatClientWithMemory, ChatMemory chatMemory) { this.chatClientWithMemory = chatClientWithMemory; this.chatMemory = chatMemory; } @GetMapping("/chat") public String chat(@RequestParam("conversationId") String conversationId, @RequestParam("input") String input) { return chatClientWithMemory.prompt() .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .user(input) .call() .content(); } @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> chatStream(@RequestParam("conversationId") String conversationId, @RequestParam("input") String input) { return chatClientWithMemory.prompt() .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .user(input) .stream() .content(); } @DeleteMapping("/chat/{conversationId}") public void clearHistory(@PathVariable("conversationId") String conversationId) { chatMemory.clear(conversationId); } } 
  • 同步GET /chat?conversationId=xxx&input=yyy
  • 流式GET /chat/stream?conversationId=xxx&input=yyy
  • 清空该会话历史DELETE /chat/{conversationId}

前端或客户端需自己生成并维护 conversationId(如 UUID),同一会话内保持不变即可。


六、对接过程中的注意项

  1. JDK 版本
    必须 JDK 17+,否则会报 class file version 相关错误。
  2. BOM 与版本
    Spring AI 依赖统一由 spring-ai-bom 管理,子依赖不要再写 <version>,以免与 Spring Boot 冲突。
  3. API Key 与 base-url
    • API Key 建议用环境变量(如 OPENAI_API_KEY)注入,不要写死在配置或代码中。
    • 对接国内或第三方时,只需修改 base-url(如 DeepSeek:https://api.deepseek.com),模型名改为对方提供的名称即可。
  4. 流式与 SSE
    流式接口返回的是 SSE(Server-Sent Events),前端需按 data: 行解析并拼接内容再展示;若直接把原始流当纯文本显示,会看到满屏的 data: xxx 而不是连贯句子。
  5. Reactor 依赖
    仅使用 spring-boot-starter-web 即可支持返回 Flux<String> 的流式接口,无需额外引入 webflux;Spring Boot 已传递 Reactor 依赖。
  6. 多 Bean 注入
    若同时存在「无记忆」和「带记忆」的 ChatClient,注入时需用 @Qualifier("chatClientWithMemory") 或按 bean 名称注入,避免注入错误实例。

七、总结

本文以 JDK 17Spring Boot 3.5.11Spring AI 1.1.2 为基础,介绍了从依赖引入、配置 API Key 与 base-url,到同步调用流式调用以及带上下文记忆的多轮对话的完整接入过程。核心步骤可以概括为:在 pom.xml 中通过 BOM 引入 spring-ai-starter-model-openai(及可选的 chat-memory Starter),在配置文件中设置 api-keybase-url 和 model,在代码中注入 ChatClient.Builder 或自定义的带记忆 ChatClient,即可用 prompt(...).call().content() 或 prompt(...).stream().content() 完成调用。多轮对话时,使用 MessageChatMemoryAdvisor 配合 ChatMemory,并按 conversationId 隔离会话即可。

对接时请务必注意 JDK 17+BOM 统一管理版本API Key 使用环境变量以及流式接口在前端的 SSE 解析,可少踩很多坑。若你使用的是其他 OpenAI 兼容服务,只需更换 base-url 和 model 名称,代码无需改动。

Read more

Java后端开发神器:飞算JavaAI让我从菜鸟变高手

Java后端开发神器:飞算JavaAI让我从菜鸟变高手

目录 前言 一、飞算JavaAI的核心理念 二、核心功能深度剖析 2.1 智能分析读懂你的"老项目" 2.2 自定义AI规则 2.3 引导式开发与模块化生成 三、用飞算JavaAI实战演练 3.1 飞算JavaAI的安装和登录 3.2 分析现有项目 3.3 测试为项目增加新功能 四、其他亮点功能一览 五、谁会使用飞算JavaAI?它将如何改变开发生态? 全文总结  🎬 攻城狮7号:个人主页 🔥 个人专栏:《AI前沿技术要闻》 ⛺️ 君子慎独!  🌈 大家好,欢迎来访我的博客! ⛳️ 此篇文章主要介绍 AI编程工具飞算JavaAI 📚 本期文章收录在《AI前沿技术要闻》,大家有兴趣可以自行查看! ⛺️ 欢迎各位 ✔️ 点赞 👍 收藏

By Ne0inhk
【Kafka高级篇】避开Kafka原生重试坑,Java业务端自建DLQ体系,让消息不丢失、不积压

【Kafka高级篇】避开Kafka原生重试坑,Java业务端自建DLQ体系,让消息不丢失、不积压

🍃 予枫:个人主页 📚 个人专栏: 《Java 从入门到起飞》《读研码农的干货日常》 💻 Debug 这个世界,Return 更好的自己! 引言 做Java消息中间件开发的同学,大概率都踩过Kafka重试的坑——相较于RabbitMQ丰富的原生重试机制,Kafka的重试支持显得十分简陋,一旦消息消费失败,要么反复重试导致系统雪崩,要么直接丢弃造成数据丢失。今天就手把手教大家,在Java业务端通过自建“重试Topic”和“死信Topic”,打造一套闭环的消息异常容错体系,彻底解决Kafka消息消费的兜底难题。 文章目录 * 引言 * 一、KAFKA原生重试机制的痛点剖析 * 二、Java业务端异常兜底核心方案:自建重试Topic+死信Topic * 核心流程拆解(图文结合理解,建议收藏) * 三、方案落地实现(Java代码实战,直接复制可用) * 3.1 环境准备(依赖配置) * 3.2 核心实体设计(封装消息,记录重试次数) * 3.

By Ne0inhk
如何解决IDEA/Datagrip无法连接数据库的问题:解决方法为添加参数-Djava.net.preferIPv4Stack=true

如何解决IDEA/Datagrip无法连接数据库的问题:解决方法为添加参数-Djava.net.preferIPv4Stack=true

如何解决IDEA/Datagrip无法连接数据库的问题:解决方法为添加参数-Djava.net.preferIPv4Stack=true 引言 在开发过程中,我们常常使用集成开发环境(IDE)如 IntelliJ IDEA 或 JetBrains DataGrip 来与数据库进行交互。然而,有时可能会遇到无法连接数据库的情况,尤其是当使用新版的 IDEA 或 DataGrip 时。这种问题通常是由于网络配置或者 IDE 与数据库之间的兼容性问题引起的。 一种常见的解决办法是添加 JVM 参数 -Djava.net.preferIPv4Stack=true,以优先使用 IPv4 协议栈。这种方式能够有效解决因 IPv6 配置问题导致的数据库连接失败问题。本文将详细介绍如何通过修改 IDEA 或 DataGrip 的启动参数来解决这个问题。 文章目录 * 如何解决IDEA/Datagrip无法连接数据库的问题:解决方法为添加参数-Djava.net.

By Ne0inhk