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

C++ 模板进阶:特化、萃取与可变参数模板

C++ 模板进阶:特化、萃取与可变参数模板

C++ 模板进阶:特化、萃取与可变参数模板 💡 学习目标:掌握模板进阶技术的核心用法,理解模板特化的深层应用、类型萃取的实现原理,以及可变参数模板的灵活使用,提升泛型编程的实战能力。 💡 学习重点:模板特化的进阶场景、类型萃取工具的设计与应用、可变参数模板的展开技巧、折叠表达式的使用方法。 一、模板特化进阶:处理复杂类型场景 💡 模板特化不只是针对单一类型的定制,还能处理指针、引用、数组等复杂类型,实现更精细的类型适配逻辑。 1.1 指针类型的模板特化 通用模板默认处理普通类型,我们可以为指针类型单独编写特化版本,实现指针专属的逻辑。 #include<iostream>#include<string>usingnamespace std;// 通用模板:处理普通类型template<typenameT>classTypeProcessor{public:staticvoidprocess(T data){ cout

By Ne0inhk

C++ 设计模式概述及常用模式

C++ 设计模式概述 本文介绍了C++中23种设计模式的分类及实现示例,主要分为三大类: 创建型模式(5个):单例模式(常用)、工厂方法模式(常用)、抽象工厂模式(常用)、建造者模式和原型模式。这些模式专注于对象的创建机制。 结构型模式(7个):适配器模式(常用)、桥接模式、组合模式和装饰器模式(常用)等。这些模式处理类和对象的组合方式。 行为型模式:未完整列出,但包含观察者模式等(未展示完整代码)。 文章通过简洁的C++代码示例展示了常用设计模式的实现方法,如单例模式通过私有构造函数和静态方法确保唯一实例,工厂方法模式通过抽象工厂类创建产品等。这些模式为解决特定设计问题提供了可重用的解决方案。 C++ 设计模式概述及常用模式 设计模式可分为三大类:创建型、结构型、行为型。以下是23个设计模式的分类及代码示例: 一、创建型模式(5个) 1. 单例模式(Singleton)⭐ 常用 classSingleton{private:static

By Ne0inhk
C++测试与调试:确保代码质量与稳定性

C++测试与调试:确保代码质量与稳定性

C++测试与调试:确保代码质量与稳定性 一、学习目标与重点 本章将深入探讨C++测试与调试的核心知识,帮助你确保代码的质量与稳定性。通过学习,你将能够: 1. 理解测试与调试的基本概念,掌握测试方法和工具 2. 学会使用单元测试框架,如Google Test和Catch2 3. 理解集成测试的重要性,确保系统的功能正确性 4. 学会使用调试工具,如GDB和Visual Studio调试器 5. 培养测试与调试思维,设计高质量的代码 二、测试的基本概念 2.1 测试的分类 测试可以分为以下几类: * 单元测试:测试单个函数或类的功能 * 集成测试:测试多个模块的集成功能 * 系统测试:测试整个系统的功能 * 验收测试:测试系统是否满足用户需求 * 性能测试:测试系统的性能指标 2.2 测试原则 测试应该遵循以下原则: * 测试应该尽可能早地进行 * 测试应该覆盖所有可能的场景 * 测试应该是自动化的

By Ne0inhk

Java + Vue 毕业设计选题效率提升指南:从脚手架到自动化部署的全链路优化

毕业设计季又到了,对于计算机专业的同学来说,用 Java 做后端,Vue 做前端,是一个非常经典且实用的技术栈组合。但很多同学在真正动手时,常常被各种“琐事”绊住,比如环境配半天、前后端接口对不上、部署时手忙脚乱,导致宝贵的开发时间被大量浪费。今天,我就结合自己带学弟学妹做毕设的经验,聊聊如何通过一套标准化的流程和工具,把 Java + Vue 毕设的开发效率提上去,让你把精力真正花在业务逻辑和创新点上。 1. 毕业设计效率痛点:我们到底在哪儿“卡”住了? 在开始技术选型之前,我们先得搞清楚,做 Java + Vue 毕设时,哪些环节最容易“掉链子”。根据我的观察,主要有这么几个: 1. 环境配置地狱:这是第一个拦路虎。A 同学的 MySQL 是 8.0,B 同学是

By Ne0inhk