SpringBoot使用SpringAi完成简单智能助手

一、主流大模型开发框架对比
Java 领域实现大模型(LLM)开发的两大核心框架 为 Spring AI 与 LangChain4J,二者定位、适配场景、技术栈差异明确,以下是完整详细说明。
| 框架 | 最低 JDK 版本 | 核心定位 | 核心优势 | 设计理念 |
|---|---|---|---|---|
| Spring AI | 17 | Spring 生态原生 AI 工程框架 | 深度适配 SpringBoot、自动装配、开发成本低 | 沿用 Spring 设计原则:可移植、模块化、面向 POJO |
| LangChain4J | 8 | Java 版通用 LLM 应用开发框架 | 兼容性强、支持低版本 JDK、生态插件丰富 | 跨语言 LangChain 理念,通用 LLM 集成 |
1.1 Spring AI
1. 定位与来源
Spring AI 是 Spring 官方 推出的 AI 应用开发框架,直接继承 Spring 生态设计哲学,目标是把可移植性、模块化、POJO 开发带入 AI 领域。
2. 核心能力(来自官方文档)
- 支持主流 AI 厂商:OpenAI、Anthropic、Microsoft、Google、Ollama 等
- 统一模型 API:对话、向量嵌入、文生图、语音转写、文本朗读、内容审核
- 结构化输出:直接将模型返回映射为 POJO
- 向量库全支持:Chroma、Milvus、PGVector、Redis、Weaviate 等
- 函数调用 / 工具调用:模型可主动调用本地接口
- 内置 RAG、对话记忆、ETL 文档处理、可观测性、模型评估
- Spring Boot 自动配置:starter 一键引入,无需手动配置
3. 核心优势
- SpringBoot 深度绑定:自动配置、依赖注入、 Starter 机制,开发体验一致
- 学习成本极低:Spring 开发者零门槛上手
- 企业级友好:适配微服务、监控、日志、安全等 Spring 体系
- 代码简洁:提供流式 API(ChatClient),类似 WebClient 风格
4. 适用场景
- 新项目、微服务架构
- 团队熟悉 SpringBoot
- JDK 版本 ≥17
- 追求开发效率、统一规范
1.2 LangChain4J
1. 定位与来源
LangChain4J 是 LangChain 的 Java 移植版,定位是通用 LLM(Large Language Mode 大语言模型 ) 应用框架,不绑定任何上层框架。
2. 核心优势
- 极致兼容性:最低支持 JDK 8,老旧项目可直接接入 AI
- 不挑技术栈:可在 SpringBoot、SSM、JavaEE、甚至纯 Java 项目中使用
- 生态插件极丰富:覆盖模型、向量库、工具、记忆、解析等全链路
- 跨语言一致:与 Python LangChain 用法接近,便于团队切换
3. 适用场景
- 老系统升级、JDK <17
- 非 Spring 技术栈
- 需要最大兼容性、最小侵入
- 熟悉 LangChain 生态的团队
1.3 差异总结与选择
差异总结
- JDK 版本
- Spring AI:必须 JDK17+
- LangChain4J:JDK8+ 即可
- 侵入性
- Spring AI:强绑定 SpringBoot
- LangChain4J:无侵入,可独立使用
- 开发体验
- Spring AI:Spring 风格,极简代码
- LangChain4J:更灵活,但配置相对繁琐
- 生态
- Spring AI:依托 Spring 庞大企业生态
- LangChain4J:依托全球 LLM 开源社区
如何选择
-
选 Spring AI:新项目、SpringBoot 技术栈、JDK17+、追求高效开发
-
选 LangChain4J:老旧项目、JDK8、非 Spring 栈、需要最大兼容

二、快速开始
2.1 环境准备
- 软件环境检查
JDK版本 17+
IDEA配置
Maven匹配IDEA的版本
- 配置大模型
- 大模型APIKEY 这里使用硅基流动(国内可直接用,无需魔法)
首先需要使用Ai的对应key
介绍一个网站,叫做硅基流动 https://www.siliconflow.cn/,可以从这获取,当然后面也会介绍其他的模型获取网站ps:点击链接会默认填写推荐码,账户会自动免费充值2元的额度,供大家使用

无论什么大模型网站我们都需要获取两个内容
模型请求地址

API key

2.2 项目搭建
- 新建SpringBoot项目

- 选择SpringBoot版本(注意3.5.9以上)

- 引入依赖
首先,在项目pom.xml中添加spring-ai的版本信息:
xml
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version> <!-- Spring AI 版本 -->
</properties>
然后,添加spring-ai的依赖管理项:
xml
<!-- 管理Spring AI 所有依赖版本,避免版本冲突 -->
<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-openai 的依赖:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>${spring-ai.version}</version>
</dependency>
为了方便后续开发,我们再手动引入一个Lombok依赖:
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
注意: 千万不要用start.spring.io提供的lombok,有bug!!
完整依赖(复制注意修改项目信息)
xml
<?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.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ai</groupId>
<artifactId>SpringAi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringAi</name>
<description>SpringAi</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-SNAPSHOT</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>
<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-openai</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.3 代码书写
- 配置文件书写
yml
spring:
# Spring AI 核心配置
ai:
openai:
api-key: sk-mnfsdfsdfsdfsd33333333qpfx # 硅基流动/OpenAI的API-Key(我瞎写的改成你自己的)
base-url: https://api.siliconflow.cn # 大模型接口地址(硅基流动/OpenAI官方地址)
chat:
options:
model: deepseek-ai/DeepSeek-V3 # 模型名称(硅基流动/OpenAI模型名,你选择模型的名字)
temperature: 0.7 # 随机性(0-1,值越大回答越灵活)
completions-path: /v1/chat/completions # 对话接口路径(上面截图API地址)
- 书写代码
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ai")
public class AiChatController {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
OpenAiChatModel openAiChatModel;
// 访问:http://localhost:8080/ai/chat
@GetMapping("/chat")
public String ai(@RequestParam(required = false,defaultValue = "介绍一下自己") String question) {
return openAiChatModel.call(question);
}
}
2.4 测试
请求地址:http://localhost:8080/ai/chat

三、Spring AI 核心API介绍
Spring AI 围绕ChatClient构建统一对话入口, 这些是 Spring AI 最核心、最常用、必须掌握的 8 个 API,所有 AI 项目都离不开它们。 核心相关 API 及作用如下:
| 核心 API | 所属包 | 核心作用 |
|---|---|---|
| ChatClient | org.springframework.ai.chat.client | 大模型对话统一入口,封装同步 / 流式调用 |
| ChatClient.Builder | org.springframework.ai.chat.client | 构建 ChatClient 实例,配置系统提示、增强器 |
| OpenAiChatModel | org.springframework.ai.openai | OpenAI 兼容的模型实现,由 Spring 自动装配 |
| ChatMemory | org.springframework.ai.chat.memory | 对话记忆接口,存储多轮对话上下文 |
| MessageWindowChatMemory | org.springframework.ai.chat.memory | ChatMemory 的实现类,基于消息窗口保留上下文 |
| MessageChatMemoryAdvisor | org.springframework.ai.chat.client.advisor | 会话记忆增强器,实现多轮对话上下文关联 |
| SimpleLoggerAdvisor | org.springframework.ai.chat.client.advisor | 日志增强器,打印 AI 交互全流程日志 |
| Flux | reactor.core.publisher | 流式返回结果类型,实现前端实时接收数据 |
3.1 ChatClient
所属包: org.springframework.ai.chat.client
核心作用:封装同步 / 流式调用
一句话理解:Spring AI 的总遥控器,所有和 AI 聊天、提问、交互,都用它。
- 统一所有 AI 模型(OpenAI、Ollama、硅基流动、通义千问...)
- 提供最简洁的调用方式:
prompt().call().content() - 支持同步、流式、多轮对话、日志、记忆等所有能力
3.2 ChatClient.Builder
所属包: org.springframework.ai.chat.client
核心作用:可配置系统提示、增强器、模型参数
一句话理解:ChatClient 的包工头
- 给 AI 设置身份 / 角色(系统提示词)
- 加入对话记忆、日志、拦截器
- 配置温度、模型名、最大 token
3.3 OpenAiChatModel
所属包: org.springframework.ai.openai
核心作用:OpenAI 兼容的模型底层实现,Spring 自动装配
一句话理解:真正发送 HTTP 请求去调用 AI 的底层工人
- 对接 OpenAI、硅基流动、DeepSeek、阿里云、腾讯云等所有兼容 OpenAI 格式的平台
- 处理请求、响应、token、异常
- 开发者几乎不用直接写它,Spring 自动注入
作用总结 :你不用管它,它默默干活。配置好 api-key + base-url ,它自动工作。
3.4ChatMemory
所属包: org.springframework.ai.chat.memory
核心作用: 对话记忆接口,定义存储多轮对话上下文的规范
一句话理解:AI 的记忆规则说明书
- 定义 "怎么存对话""怎么取对话""怎么删对话"
- 是一个接口,不能直接 new
- 具体实现靠它的子类
3.5 MessageWindowChatMemory
所属包: org.springframework.ai.chat.memory
核心作用: ChatMemory 的实现类,基于消息窗口保留最近 N 轮对话
一句话理解AI 的短期记忆
- 默认保留最近 10 条消息
- 防止对话过长、token 爆炸
- 最常用、最稳定的记忆实现
3.6 MessageChatMemoryAdvisor
所属包: org.springframework.ai.chat.client.advisor
核心作用 :对话记忆增强器,把记忆自动注入对话上下文
一句话理解:把记忆交给 AI 的快递员
- 它是一个 Advisor(增强器)
- 自动把历史对话塞给 AI
- 让 AI 知道 "你们之前聊了什么"
3.7 SimpleLoggerAdvisor
所属包: org.springframework.ai.chat.client.advisor
核心作用: 日志增强器,打印 AI 完整请求 / 响应日志
一句话理解:AI 对话监控器,把所有聊天内容打印到控制台
- 打印:发给 AI 什么、AI 返回什么、耗时、token 数
- 开发调试必备
- 不影响业务逻辑
3.8 Flux <String>
所属包: reactor.core.publisher
核心作用: 流式返回类型,实现前端实时逐字接收 AI 回复*
一句话理解:像打字机一样,一个字一个字返回给前端。
- 用于流式对话(SSE / 实时推送)
- 不用等 AI 全部回答完才返回
- 是 WebFlux 类型,SpringBoot 自带支持
四、Spring AI 简单使用
4.1 AiChatConfig配置类编写
为什么要写 AiChatConfig 配置类?
-
统一管理、全局复用
-
依赖清晰、可读性强
-
统一配置全局生效
-
低耦合、符合 Spring 设计原则
-
便于测试、替换与扩展
-
企业级规范、生产可用
代码书写实现
配置类书写完成目标:
- 防止OpenAiChatModel在多个Controller重复注入
- 使ChatClient可以直接通过注入的形式在多个Controller中使用
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AiChatConfig {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
OpenAiChatModel openAiChatModel;
/**
* 配置 ChatClient - OpenAI兼容的聊天客户端
* @return ChatClient 实例
*/
@Bean("open-ai")
public ChatClient openAIChatClient() {
return ChatClient.builder(openAiChatModel)
.build();
}
}
快速开始Controller修改
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ai")
public class AiChatController {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
ChatClient chatClient;
@GetMapping("/chat")
public String ai(@RequestParam(required = false,defaultValue = "介绍一下自己") String question) {
return chatClient.prompt(question).call().content();
}
}
4.2 流式结果
同步调用是指等待大模型完整返回结果后,再将数据返回给前端,适用于短文本问答场景,
核心API 为
String ChatClient.prompt().call().content()。4.1 修改后就是使用同步调用
流式调用是指大模型逐字 / 逐段返回结果,前端实时接收并展示,解决同步调用 "等待时间长、用户体验差" 的问题,核心基于WebFlux实现核心 API 为
Flux<String> ChatClient.prompt().stream().content()。
编写流式对话接口
在ChatController中添加流式调用接口chat2:
java
/**
* 流式对话接口
* @param prompt 用户提问的提示词,默认值为"介绍一下你自己"
* @return Flux<String> 流式结果,前端实时接收
*/
@RequestMapping(value = "/chat2", produces = "text/html;charset=UTF-8")
public Flux<String> chat2(@RequestParam(defaultValue = "介绍一下你自己") String prompt) {
// 流式调用核心API链
return chatClient
.prompt(prompt) // 传入用户提示词
.stream() // 流式调用:非阻塞,逐段返回结果
.content(); // 提取流式的纯文本内容
}
重启测试,再次访问,会一点点生成数据展示在页面上。

4.3 系统(角色)设置
可以发现,当我们询问AI你是谁的时候,它回答自己是DeepSeek-R1,这是大模型底层的设定。如果我们希望AI按照新的设定工作,就需要给它设置System背景信息。
在SpringAI中,设置System信息非常方便,不需要在每次发送时封装到Message,而是创建ChatClient时指定即可。用于定义 AI 的角色、回答风格、业务规则,无需在每次调用时重复传递,由 ChatClient 自动附加到 Prompt 中,不同的项目可能需要设置不同的System信息,所以一般会定义对应的类保存。
创建SystemManage系统设置管理类
java
/**
* AI服务System模板管理
* 职责:统一管理所有AI服务使用的System模板
*/
public class SystemManage {
//提示词Demo
public static final String DEMO_SYSTEM= """
你是一个专业的AI智能助手,你的名字叫小艾同学,请以友好、乐于助人和愉快的方式解答各种问题。
重要:当用户询问具体信息时,不要编造不存在的数据。
""";
}
修改AiChatConfig类
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AiChatConfig {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
OpenAiChatModel openAiChatModel;
/**
* 配置 ChatClient - OpenAI兼容的聊天客户端
* @return ChatClient 实例
*/
@Bean("open-ai")
public ChatClient openAIChatClient() {
return ChatClient.builder(openAiChatModel)
// 配置系统提示:定义AI的角色、回答风格
.defaultSystem(SystemManage.DEMO_SYSTEM)
.build();
}
}
重启测试,再次访问,会按照定义的内容生成。

4.4 多轮对话上下文
多轮对话的核心是上下文关联,即 AI 能识别上一轮的提问和回答,例如:
用户:12 个苹果分给 2 个人,每人分几个?
AI:6 个;
用户:分给 3 个人呢?
AI:4 个(需识别 "12 个苹果" 的上下文)。
核心原理
- MessageChatMemoryAdvisor会在每次调用大模型时,自动从 ChatMemory 中读取历史对话消息,并附加到当前 Prompt 中;
- 大模型基于历史消息 + 当前提问生成回答,实现上下文关联;
- 回答生成后,MessageChatMemoryAdvisor会自动将当前用户消息和 AI 回答存入 ChatMemory,更新上下文。
Spring AI 通过ChatMemory + MessageChatMemoryAdvisor实现该能力,无需额外编写业务代码,只需在配置类中完成相关 Bean 配置。
Advisor 核心概念
Advisor 是 Spring AI 基于AOP 切面机制实现的功能增强接口,用于在大模型对话的请求前、响应后进行无侵入式处理,核心执行流程:
java
用户请求 → AdvisedRequest(增强前请求)→ Advisor增强(如添加上下文、记录日志)→ Prompt(最终请求)→ 大模型 → ChatResponse(模型响应)→ Advisor增强(如存储上下文)→ AdvisedResponse(增强后响应)→ 返回给用户
Advisor API
| Advisor 类名 | 核心作用 | 配置方式 |
|---|---|---|
| MessageChatMemoryAdvisor | 实现多轮对话上下文关联,自动读写 ChatMemory | MessageChatMemoryAdvisor.builder(chatMemory).build() |
修改AiChatConfig类
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AiChatConfig {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
OpenAiChatModel openAiChatModel;
/**
* 配置 ChatMemory - 内存存储的会话记忆
*
* @return ChatMemory 内存存储的会话记忆实例
*/
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(30) // 窗口最大消息数目,保留最近30条消息
.build();
}
/**
* 配置 ChatClient - OpenAI兼容的聊天客户端
* @return ChatClient 实例
*/
@Bean("open-ai")
public ChatClient openAIChatClient() {
return ChatClient.builder(openAiChatModel)
// 配置系统提示:定义AI的角色、回答风格
.defaultSystem(SystemManage.DEMO_SYSTEM)
// 配置增强器:会话记忆(多轮对话)
.defaultAdvisors(
//会话记忆增强器,实现多轮对话上下文关联
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 会话记忆增强器
)
.build();
}
}
测试多轮对话
- 第一次访问:http://localhost:8080/ai/chat?prompt=12个苹果分给2个人,每人分几个?
返回:6 个; - 第二次访问:http://localhost:8080/ai/chat?prompt=分给3个人呢?
返回:4 个(成功识别上下文 "12 个苹果")。

4.5 日志集成
默认情况下,应用于AI的交互时不记录日志的,我们无法得知SpringAI组织的提示词到底长什么样,有没有问题。这样不方便我们调试。
SpringAI基于AOP机制实现与大模型对话过程的增强、拦截、修改等功能。所有的增强通知都需要实现Advisor接口。

Advisor API
| Advisor 类名 | 核心作用 | 配置方式 |
|---|---|---|
| SimpleLoggerAdvisor | 记录 AI 交互全流程日志(Prompt、响应、Token 等) | new SimpleLoggerAdvisor() |
修改AiChatConfig类
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AiChatConfig {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
OpenAiChatModel openAiChatModel;
/**
* 配置 ChatMemory - 内存存储的会话记忆
*
* @return ChatMemory 内存存储的会话记忆实例
*/
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(30) // 窗口最大消息数目,保留最近30条消息
.build();
}
/**
* 配置 ChatClient - OpenAI兼容的聊天客户端
* @return ChatClient 实例
*/
@Bean("open-ai")
public ChatClient openAIChatClient() {
return ChatClient.builder(openAiChatModel)
// 配置系统提示:定义AI的角色、回答风格
.defaultSystem(SystemManage.DEMO_SYSTEM)
// 配置增强器:会话记忆(多轮对话)+ 日志记录(调试)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 会话记忆增强器
new SimpleLoggerAdvisor() // 日志增强器
)
.build();
}
}
修改日志级别
在application.yaml中添加日志配置,更新日志级别:
位置要顶格写,不要写在 spring 标签的下面
yml
logging:
level:
org.springframework.ai: debug # AI对话的日志级别
com.ruangong.springai: debug # 本项目的日志级别
重启项目,再次聊天就能看到AI对话的日志信息了~

4.6 工具调用
工具调用是 Spring AI 核心扩展能力之一,允许大模型根据用户提问自动判断并调用自定义工具(如天气查询、数据库操作、计算器等),而非仅依赖模型自身知识回答问题,核心解决 "大模型知识时效性差、无法执行实时 / 计算类操作" 的问题。
工具调用流程

工具调用常用注解
| 核心组件 | 所属包 | 注解位置 | 作用 |
|---|---|---|---|
| @Tool | org.springframework.ai.annotation | 方法 | 标记方法为可被 AI 调用的工具 |
| @ToolParam | org.springframework.ai.annotation | 参数 | 标记被 AI 调用的工具方法参数 |
定义可被 AI 调用的工具类
java
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
/**
* 这里两个示例工具方法就直接返回字符串,实际项目中可以注入mapper实现数据库查询
* 工具1:数学计算工具
* 工具2:天气查询工具
*/
@Component
public class MyTool {
@Tool(
name = "calculator_tool",
description = "执行数学计算,参数:expression(数学表达式,如 10+20*3)",
returnDirect = true
)
public String calculator_tool(@ToolParam(description = "执行数学计算,如10+20*3等") String expression) {
System.out.println("------------------------------------------数学计算-----------------------------------------");
// 简单计算(实际可使用安全的表达式解析库,如 JEP)
return "自己拿计算器算去";
}
@Tool(
name = "weather_tool",
description = "查询指定城市的实时天气信息,入参:city(城市名称,如北京、上海)",
returnDirect = true
)
public String weather_tool(@ToolParam(description = "搜索关键词,如'北京'、'上海'等") String city) {
System.out.println("------------------------------------------天气查询-----------------------------------------");
String weatherInfo = switch (city) {
case "北京" -> "晴,气温 18~28℃,西南风 2级";
case "上海" -> "多云,气温 22~30℃,东南风 3级";
case "广州" -> "小雨,气温 25~32℃,东北风 1级";
default -> "暂未查询到「" + city + "」的天气信息,请确认城市名称";
};
return weatherInfo;
}
}
修改AiChatConfig类
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AiChatConfig {
@Resource
//自动装配OpenAiChatModel:由spring-ai-starter-model-openai自动配置
OpenAiChatModel openAiChatModel;
@Resource
//注入工具函数所在工具类
MyTool myTool;
/**
* 配置 ChatMemory - 内存存储的会话记忆
*
* @return ChatMemory 内存存储的会话记忆实例
*/
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(30) // 窗口最大消息数目,保留最近30条消息
.build();
}
/**
* 配置 ChatClient - OpenAI兼容的聊天客户端
* @return ChatClient 实例
*/
@Bean("open-ai")
public ChatClient openAIChatClient() {
return ChatClient.builder(openAiChatModel)
// 配置系统提示:定义AI的角色、回答风格
.defaultSystem(SystemManage.DEMO_SYSTEM)
// 配置增强器:会话记忆(多轮对话)
.defaultAdvisors(
//会话记忆增强器,实现多轮对话上下文关联
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 会话记忆增强器
)
// 配置自定义调用工具
.defaultTools(myTool)
.build();
}
}
这样就可以实现工具调用了,由大模型自动识别调用,当然为了提高工具调用的命中率可以修改系统设置
java
//提示词Demo
public static final String TEST_SYSTEM= """
你是一个专业的AI智能助手,你的名字叫小艾同学,请以友好、乐于助人和愉快的方式解答各种问题。
## 可用工具函数:
当用户询问具体信息时,你应该**优先使用工具函数**查询数据库中的实际数据,而不是凭空回答:
1. **calculator_tool(expression)** - 执行数学计算
- 用途:当用户询问'帮我算'、'帮我计算',如帮我算10+20*3等
- 示例:calculator_tool("10+20*3")
2. **weather_tool(city)** - 查询指定城市的实时天气信息
- 用途:当用户询问'今天北京天气怎么样'、'广州天气'等
- 示例:weather_tool("北京")
**重要**:当用户询问具体信息时,必须先调用相应工具查询,然后基于查询结果进行回答。不要编造不存在的数据。
""";
4.7 前端集成
在浏览器通过地址访问,非常麻烦,也不够优雅。如果能有一个优美的前端页面就好了。
别着急,我提前给大家准备了一个前端页面。已经进行了资源绑定

前端启动流程
- 删除node_modules,pakage-lock.json文件
- 执行 npm install --registry=https://registry.npmmirror.com
- 执行 npm run dev 就可以启动了
解决CORS问题
前后端在不同域名,存在跨域问题,因此我们需要在服务端解决cors问题。在 config 包中添加一个类:
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("Content-Disposition");
}
}
重启服务,如果你的服务端接口正确,那么应该就可以聊天了。
注意: 前端访问服务端的默认路径是:http://localhost:8080
聊天对话的接口是:POST /ai/chat 如果想实现流式对话将后台请求路径修改
请确保你的服务端接口也是这样。

恭喜您,你的第一个AI对话机器人就简单完成了。
4.8 会话隔离
在实际项目中,不同用户会在不同会话进行提问,在上文的多轮对话上下文中,使用MessageChatMemoryAdvisor仅能实现上下文,无法实现会话隔离。
因为所有的请求都使用默认的记忆,所以要实现会话级别的记忆那么久需要为每一个会话都创建一个专属于会话的"记忆",例如前台案例请求携带的chatId,就是会话的标识
修改ChatController 对应方法
java
/**
* 流式对话接口
* @param prompt 用户提问的提示词,默认值为"介绍一下你自己"
* @return Flux<String> 流式结果,前端实时接收
*/
@RequestMapping(value = "/chat2", produces = "text/html;charset=UTF-8")
public Flux<String> chat2(@RequestParam(defaultValue = "介绍一下你自己") String prompt,String chatId) {
// 流式调用核心API链
return chatClient
.prompt(prompt) // 传入用户提示词
//为当前请求设置对话记忆(使用默认的会话id标识与前台生成的chatId)
.advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, chatId))
.stream() // 流式调用:非阻塞,逐段返回结果
.content(); // 提取流式的纯文本内容
}
4.9 前端界面功能简单实现
多会话切换历史查询
页面在进行会话切换时,会将会话的Id发送给后台,获取信息,所以后台需要保存对话信息
创建AiChatMessage类,保存对话信息
java
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* AI聊天消息实体类
* @author system
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
//AI聊天消息实体类
public class AiChatMessage {
//消息ID
private Long id;
//会话ID
private String sessionId;
//角色:user-用户,assistant-AI助手
private String role;
//消息内容")
private String content;
//创建时间
private LocalDateTime createTime;
}
数据实际应该存储到数据库,本文使用自定义数据类模拟
创建MessageData类,模拟存储对话数据
java
import java.util.ArrayList;
//保存对话信息工具类,模拟数据库存储
public class MessageData {
public static ArrayList<AiChatMessage> messages=new ArrayList<>();
public static void addMessage(AiChatMessage message){
messages.add(message);
}
}
修改ChatController类内容
java
/**
* 流式对话接口
* @param prompt 用户提问的提示词,默认值为"介绍一下你自己"
* @return Flux<String> 流式结果,前端实时接收
*/
@RequestMapping(value = "/chat2", produces = "text/html;charset=UTF-8")
public Flux<String> chat2(@RequestParam(defaultValue = "介绍一下你自己") String prompt,String chatId) {
// 流式调用核心API链 获取结果
Flux<String> content = chatClient
.prompt(prompt)
.advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, chatId))
.stream() // 流式调用
.content();
//将用户信息存储
MessageData.addMessage(AiChatMessage.builder()
.role(MessageType.USER.getValue())
.content(prompt)
.sessionId(chatId)
.build());
StringBuilder fullResponse = new StringBuilder();
return content.doOnNext(fullResponse::append)
//返回结束时执行
.doOnComplete(() -> {
//获取会话结果存储
MessageData.addMessage(AiChatMessage.builder()
.role(MessageType.ASSISTANT.getValue())
.content(fullResponse.toString())
.sessionId(chatId)
.build());
});
}
@GetMapping("/history/chat/{chatId}")
public List<AiChatMessage> history(@PathVariable String chatId){
//获取指定会话的历史信息并返回
return MessageData.messages.stream().filter(msg->msg.getSessionId().equals(chatId)).toList();
}
获取当前网页会话下的多个聊天记录
因为只是一个案例,所以使用session存储会话id,实际开发中创建会话可以存储到数据库中,然后通过数据库查询返回
修改ChatController类内容
java
/**
* 流式对话接口
* @param prompt 用户提问的提示词,默认值为"介绍一下你自己"
* @return Flux<String> 流式结果,前端实时接收
*/
@RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8")
public Flux<String> chat2(@RequestParam(defaultValue = "介绍一下你自己") String prompt,String chatId,HttpSession session) {
//获取当前会话中的chatId列表
HashSet<String> sessionIds= ( HashSet<String>) session.getAttribute("sessionIds");
//因为是set集合所以每次请求直接添加即可
sessionIds.add(chatId);
//将最新的chatId列表存入sessin
session.setAttribute("sessionIds",sessionIds);
// 流式调用核心API链 获取结果
Flux<String> content = chatClient
.prompt(prompt)
.advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, chatId))
.stream() // 流式调用
.content();
//将用户信息存储
MessageData.addMessage(AiChatMessage.builder()
.role(MessageType.USER.getValue())
.content(prompt)
.sessionId(chatId)
.build());
StringBuilder fullResponse = new StringBuilder();
return content.doOnNext(fullResponse::append)
//返回结束时执行
.doOnComplete(() -> {
//获取会话结果存储
MessageData.addMessage(AiChatMessage.builder()
.role(MessageType.ASSISTANT.getValue())
.content(fullResponse.toString())
.sessionId(chatId)
.build());
});
}
@GetMapping("/history/chat/{chatId}")
public List<AiChatMessage> history(@PathVariable String chatId){
return MessageData.messages.stream().filter(msg->msg.getSessionId().equals(chatId)).toList();
}
/**
* 获取历史会话列表方法
* @param session 当前会话对象 自动注入
* @return HashSet<String> 存储ChatId的集合
*/
@GetMapping("/history/chat")
public HashSet<String> history1(HttpSession session){
System.out.println("----------------------------------------------");
HashSet<String> sessionIds= (HashSet<String>) session.getAttribute("sessionIds");
if(sessionIds==null){
sessionIds=new HashSet<String>();
//加几个模拟数据 要不需要手动点击
sessionIds.add("1773648154863");
sessionIds.add("1773648154864");
sessionIds.add("1773648154865");
}
return sessionIds;
}