文章目录
- Spring AI 学习笔记
- 前言
- 基本介绍
- 快速入门
- 接口介绍
- ChatModel 接口介绍
- ChatClient 接口介绍
- 其他 API 介绍
- 消息类型
- 提示词模板
- 字符串模板
- 文件模板
- 消息模板
- 输出方式
- 流式输出 vs 非流式输出
- 流式输出
- 非流式输出
- 结构化输出 vs 非结构化数据
- 结构化输出
- 非结构化输出
- 模型记忆
- 基本介绍
- 实战演练
- 记忆管理
- 工具函数
- 基本介绍
- 工具函数的定义
- 工具函数的注册
- 工具函数的调用
- Spring AI 整合 MCP
- Spring AI 整合 RAG
- Spring AI Alibaba 学习笔记
- Spring AI Alibaba 基本介绍
- Spring AI Alibaba 快速入门
- Spring AI Alibaba 整合 MCP
- Spring AI Alibaba 整合 RAG
- Graph工作流
- MCP Gateway
- NL2SQL
- 多智能体协同
随着大语言模型(LLM)技术的爆发式发展,AI 能力集成已成为 Java 开发者的核心需求之一。但面对五花八门的大模型厂商(OpenAI、DeepSeek、文心一言、通义千问等)、差异化的原生 API、复杂的交互逻辑(对话记忆、工具调用、RAG 检索),开发者往往陷入 “重复造轮子” 的困境 —— 为每个模型编写适配代码,为每个场景处理繁琐的上下文管理,最终导致系统耦合度高、扩展性差。
Spring AI 的出现,正是为了解决这一痛点。作为 Spring 生态专为 AI 工程打造的框架,它延续了 Spring 家族 “约定优于配置”“模块化”“可插拔” 的核心设计理念,让 Java 开发者能够用熟悉的方式(POJO、依赖注入、配置化)快速集成各类 AI 能力,无需关注底层厂商 API 的差异,真正实现 “一次编码,多模型适配”。
本笔记聚焦 Spring AI 核心能力与实战落地,从基础概念、快速入门,到核心接口、消息机制、提示词工程,再到流式输出、结构化返回、工具调用等进阶场景,全方位拆解 Spring AI 的使用方式。无论你是想快速接入大模型实现智能对话,还是构建包含 RAG 检索增强、向量数据库的企业级 AI 应用,都能从这份笔记中找到清晰的实践路径。
希望通过这份学习笔记,帮助更多 Java 开发者低门槛迈入 AI 工程领域,让 Spring AI 成为你集成 AI 能力的 “瑞士军刀”,高效、标准化地构建稳定且可扩展的 AI 应用。
- Spring AI 是什么?
Spring 官方介绍:Spring AI is an application framework for AI engineering. Its goal is to apply to the AI domain Spring ecosystem design principles such as portability and modular design and promote using POJOs as the building blocks of an application to the AI domain.At its core, Spring AI addresses the fundamental challenge of AI integration: Connecting your enterprise Data and APIs with the AI Models.
翻译过来就是:Spring AI 是一个用于 AI 工程的应用框架。它的目标是将 Spring 生态系统设计原则(如可移植性和模块化设计)应用于 AI 领域,并促进将 pojo 作为 AI 领域应用程序的构建块。Spring AI 的核心是解决 AI 集成的基本挑战:将企业数据和 api 与 AI 模型连接起来。
直白点说就是:Spring AI 是 Spring 家族中专门对于 AI 领域的一个整合框架(就好比 SpringCloud 是专门针对微服务领域各类技术栈的一个整合),它主要的作用是让 Java 开发者能用写普通 Spring 项目的习惯、风格(简单、统一、可替换、易扩展)、思维,轻松接入各种大模型(ChatGPT、文心一言、通义千问、Claude 等),不用写复杂的原生 API 代码,实现低门槛、快速、标准化接入 AI 功能。
- Spring AI 英文文档
- Spring AI 中文文档
- Spring AI 有什么用?
- 统一大模型调用接口:不管接入 DeepSeek、ChatGPT、文心一言、通义千问、Claude,代码写法完全一样,换模型只改配置
- 支持 AI 大模型各项能力:对话聊天、图像生成、语音转文字/文字转语音
- RAG 检索增强生成:让大模型读取你的文档 / 知识库再回答,不胡说八道
- 支持向量数据库:对接 Milvus、Redis、ES 等,用于 RAG 知识库检索
- 支持函数调用:让 AI 主动调用你项目里的 Java 方法(比如查订单、查天气)
- 提示词模板管理:统一管理提示词,方便维护、支持动态传参
- 为数据工程提供 ETL(数据抽取、转换、加载)框架
- Spring AI 版本介绍
Spring AI 主要有三种版本
-
SNAPSHOT:指的是快照版,会在此版本上持续更新。 -
PRE:指的是预览版,主要提供给开发测试人员找 bug,不到修改完善的。 -
GA:指的是 General Availability,意为正式发布的版本,推荐使用(主要是稳定)。
- Spring AI 的应用场景
- LangChain4j 和 Spring AI 的比较
前置要求:
- JDK 17+
- SpringBoot3.2+
- 选择你的 LLM 提供商并获取 API-KEY
这里我以 Spring AI 接入 DeepSeek 为例,所以我们还需要去申请一个 DeepSeek 的 API-KEY,然后需要购买对应的 Token,购买 Token 请前往:DeepSeek 开放平台
- Step1:创建 Maven 工程
- Step2:引入依赖
17
17
UTF-8
1.0.0-M5
org.springframework.boot
spring-boot-starter-parent
3.3.8
org.springframework.ai
spring-ai-openai-spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.ai
spring-ai-bom
${spring-ai.version}
pom
import
- Step3:编写配置文件
spring:
ai:
openai: # 这里依然用 openai 的配置前缀,因为使用的是 openai starter api-key: # 建议从环境变量读取 base-url: https://api.deepseek.com # 修改为 DeepSeek 的地址 chat: options: model: deepseek-chat # 指定模型名称,也可以是 deepseek-reasoner temperature: 0.7 # 设置温度参数 0.7 控制回复的随机性(值越大越随机)
- Step4:测试
@SpringBootTest
public class OpenAiChatModelTest {
@Autowired private OpenAiChatModel openAiChatModel; @Test public void test() { String response = this.openAiChatModel.call("DeepSeek"); System.out.println("response : "+response); }
}

在 Spring AI 中,
ChatModel和ChatClient是与大语言模型(LLM)交互的两个核心抽象接口。理解它们的区别和适用场景对于构建高质量的 AI 应用至关重要。简单来说:
ChatModel是底层驱动,提供原始能力;ChatClient是高层封装,提供开发体验和功能增强。
低阶 API (ChatModel)
高阶 API (ChatClient)
ChatModel, StreamingChatModel
ChatClient
自动管理 (通过 ChatMemoryAdvisor)
内置 ObservationConvention,轻松集成 Micrometer/Prometheus 追踪
内置 RetryAdvisor 等异常处理策略
ChatModel 接口介绍
ChatModel是 Spring AI 中最基础的接口,直接对应于底层的 LLM API(如 OpenAI 的 Chat Completion API)。
- 定位:它是“裸机”操作层。它负责将请求发送给模型并接收原始响应。
- 主要方法:
-
call(Prompt prompt): 同步调用,返回ChatResponse。 -
stream(Prompt prompt): 流式调用,返回Flux。
- 输入/输出:
- 输入是标准的
Prompt对象(包含Message列表和PromptOptions)。 - 输出是原始的
ChatResponse对象,包含Generation、元数据、Token 使用量等。你需要手动解析内容。
- 特点:
- 无状态:它不管理对话历史,每次调用你都需要传入完整的上下文。
- 无额外功能:默认不支持自动重试、记忆管理、输出转换(POJO)、工具调用注册等高级功能。如果需要这些,你需要自己编写代码包裹它。
- 灵活性:适合需要完全控制请求/响应细节的场景,或者作为构建更高级组件的基础。
- 创建方式:
- 方式一:手动创建。通常通过
ChatModel.builder创建,并可以全局配置默认的Advisors,然后通过@Bean注解注入 IOC 容器
- 方式二:自动注入。通过配置 yml 配置,Spring 自动创建 ChatModel Bean 并注入 IOC 容器
ChatClient 接口介绍
ChatClient是 Spring AI 推荐的主要交互方式(特别是在 Spring AI 1.0 M4+ 版本之后)。它是一个流畅的(Fluent)、声明式的 API,旨在简化开发并内置了企业级功能。
- 定位:它是“自动驾驶”层。它在
ChatModel之上提供了一层丰富的抽象。 - 核心优势:
- API 调用便捷:链式调用,代码可读性极高。
- 内置功能丰富 :通过
Advisor机制轻松集成复杂功能,无需手动编码:
- Memory (记忆):自动管理对话历史 (
MessageChatMemoryAdvisor)。 - Retry (重试):自动处理临时故障 (
RetryAdvisor)。 - Tool Calling (工具调用):自动注册和解析函数调用。
- Observation (观测):自动集成 Micrometer Tracing。
- 结构化输出便捷:可以直接要求返回 Java 对象 (
entity(MyPojo.class)),内部自动处理 Prompt 工程和解析。 - 耦合度低:你可以轻松切换底层模型实现,而业务逻辑代码几乎不需要改动。
- 创建方式:
- 方式一:手动创建。通常通过
ChatClient.builder(chatModel)创建,并可以全局配置默认的Advisors,然后通过@Bean注解注入 IOC 容器 - 方式二:自动注入。通过配置 yml 配置,Spring 自动创建 ChatClient Bean 并注入 IOC 容器
其他 API 介绍
Spring AI 中除了
ChatModel和ChatClient这对核心组合外,还提供了针对图像、音频、向量处理以及 RAG 的专用 API
ChatModel
ChatClient
ImageModel
ImageClient
AudioModel、SpeechModel
AudioClient
EmbeddingModel
VectorStore
ToolSpecification
@Tool 注解
接下来,让我们用下面几个简单示例来让你快速了解上面这些 API 吧
1)图像生成 (文生图):根据文字描述生成一张“赛博朋克风格的猫”
@Service public class ImageService { // 注入高阶 ImageClient (推荐) private final ImageClient imageClient; // 注入低阶 ImageModel (用于精细控制) private final ImageModel imageModel; public ImageService(ImageClient imageClient, ImageModel imageModel) { this.imageClient = imageClient; this.imageModel = imageModel; } // 方式一:使用 ImageClient (简洁) public String generateImageSimple(String prompt) // 方式二:使用 ImageModel (精细控制) public String generateImageAdvanced(String prompt)
}
2)音频处理 (语音转文字/文字转语音):将一段录音转为文字,或将一段文字转为语音文件
@Service public class AudioService { // 语音转文字 (ASR) - 使用 AudioModel private final AudioModel audioModel; // 文字转语音 (TTS) - 使用 SpeechModel private final SpeechModel speechModel; public AudioService(AudioModel audioModel, SpeechModel speechModel) { this.audioModel = audioModel; this.speechModel = speechModel; } // 1. 语音转文字 (Whisper 等模型) public String speechToText(byte[] audioData) // 2. 文字转语音 (TTS) public byte[] textToSpeech(String text)
}
3)向量化 (RAG 核心):将“你好,世界”这句话变成计算机能理解的数字列表(向量)
@Service public class EmbeddingService { private final EmbeddingModel embeddingModel; public EmbeddingService(EmbeddingModel embeddingModel) { this.embeddingModel = embeddingModel; } public void showEmbedding()
}
4)数据存储 (向量数据库):把向量存进数据库,并搜索相似内容
@Service public class VectorStoreService { // Spring AI 会自动注入配置好的 VectorStore 实现 (如 Chroma, Milvus, Redis) private final VectorStore vectorStore; public VectorStoreService(VectorStore vectorStore) { this.vectorStore = vectorStore; } // 1. 存入数据 public void storeData() { List
documents = List.of( new Document("Spring AI 简化了 AI 模型集成"), new Document("Java 是一门面向对象的语言"), new Document("Python 在 AI 领域很流行") ); // add 方法会自动处理向量化并存库 vectorStore.add(documents); } // 2. 相似性搜索 public void searchData()
}
5)工具调用:让 AI 能够调用本地方法获取当前时间
// 1. 定义工具类 (使用 @Tool 注解 - 高阶用法) @Component public class DateTimeTools @Tool(description = "计算两个数字的和") public int add(int a, int b) { return a + b; }
}
// 2. 在业务中注入并使用 (使用 ChatClient) @Service public class ChatService {
private final ChatClient chatClient; private final DateTimeTools dateTimeTools; public ChatService(ChatClient.Builder builder, DateTimeTools dateTimeTools) { this.chatClient = builder.build(); this.dateTimeTools = dateTimeTools; } public String askWithTools(String userMessage) { // 通过 .tools() 方法注册工具,AI 会自动判断是否调用 return chatClient.prompt(userMessage) .tools(dateTimeTools) .call() .content(); }
}

- 理解消息角色概念
在 Spring AI(以及其底层的通用大模型交互协议)中,消息角色(Message Role) 是一个核心概念,它决定了 对话的上下文逻辑 和 模型的行为模式。
简单来说,大模型(LLM)本身是无状态的,它不知道“你是谁”或“刚才聊了什么”。它完全依赖你发送给它的消息列表(Conversation History) 来理解当前语境。而 角色(Role) 就是给每一条消息打上的标签,告诉模型:这句话是谁说的?是系统指令、用户提问,还是模型之前的回答?
- 消息的分类
- 代码示例:为了展示这四种核心消息类型在实际代码中是如何创建和流转的,这里模拟了一个“智能助手调用计算器”的完整闭环流程
/ * 演示4: 消息的完整流转流程 * 模拟"智能助手计算器"的完整闭环,展示消息如何组装和流转 */ @Test public void testCompleteFlow() } } printSeparator(); // Step 5: 构建多轮对话上下文 System.out.println("[Step 5] 继续对话,展示消息列表的组装"); String followUpResponse = chatClient.prompt() .messages( systemMessage, userMessage, response.getResult().getOutput() ) .user("谢谢! 那 1000 减去这些数字等于多少?") .call() .content(); System.out.println("AI回复: " + followUpResponse); printSeparator(); System.out.println("========================================"); System.out.println(" 消息流转完成 - SystemMessage、UserMessage、"); System.out.println(" AssistantMessage 三种消息类型已演示"); System.out.println("========================================"); }
在之前的文章中,我们已经学习了如何使用 Spring AI 构建基础的聊天服务。但在实际开发中,我们的提示词(Prompt)往往不是静态的,而是包含了各种动态参数(如用户名、查询条件、上下文)。
很多初学者容易陷入“字符串拼接”的泥潭,导致代码难以维护。今天,我们将聚焦于 Spring AI 中的核心组件——
PromptTemplate,看看它是如何将提示词从“零散的字符串”升级为“可维护的工程化组件”的。
- 什么是 PromptTemplate?
如果把大模型比作一个厨师,Prompt就是菜单,而PromptTemplate则是生成菜单的模具。
它允许我们定义带有占位符的模板,在运行时动态填入参数。它的核心价值在于:
- 标准化:提供统一的模板语法(默认基于 StringTemplate 引擎),避免开发者各自为战。
- 解耦:将提示词的结构(模板)与业务数据(参数)分离,修改文案无需重新编译代码。
- 复用性:一次定义,多处调用。
字符串模板
这是最直接的方式,适合简单的动态提示词。我们使用
{变量名}作为占位符
@RestController
@RequestMapping(“/ai”) public class PromptTemplateController {
private final ChatClient chatClient; // 注入 ChatClient public PromptTemplateController(ChatClient.Builder builder) { this.chatClient = builder.build(); } @GetMapping("/poem") public String generatePoem(@RequestParam String topic) { // 1. 定义模板 String template = "请写一首关于 {topic} 的五言绝句,风格要豪迈。"; // 2. 创建 PromptTemplate PromptTemplate promptTemplate = new PromptTemplate(template); // 3. 准备参数 Map
params = Map.of("topic", topic); // 4. 生成 Prompt 对象 Prompt prompt = promptTemplate.create(params); // 5. 调用大模型 return chatClient.prompt(prompt).call().content(); }
}
文件模板
当提示词变得非常长(例如包含复杂的系统指令、Few-Shot 示例)时,写在 Java 代码里会非常痛苦。Spring AI 支持从类路径加载
.st(StringTemplate) 文件
- Step1:创建模板文件
在src/main/resources/templates目录下创建system-role.st:
你是一个资深的 {role} 专家。 你的任务是分析用户输入的内容,并按照以下格式输出:
- 核心观点
- 潜在风险
- 建议方案
用户输入:{userInput}
- Step2:Java 代码调用
@GetMapping(“/analyze”) public String analyze(@RequestParam String input) { // 1. 加载文件资源 Resource resource = new ClassPathResource("templates/system-role.st"); // 2. 创建模板 PromptTemplate promptTemplate = new PromptTemplate(resource); // 3. 填充参数 Map
params = Map.of( "role", "金融风控", "userInput", input ); Prompt prompt = promptTemplate.create(params); return chatClient.prompt(prompt).call().content();
}
消息模板
在实际的对话系统中,我们需要区分系统提示词和用户提示词。Spring AI 提供了
SystemPromptTemplate和Message接口来处理这种复杂场景
下面的例子展示了如何同时控制“人设”和“任务”:
@GetMapping(“/chat”) public AssistantMessage chat(@RequestParam String topic, @RequestParam String tone) { // 用户消息模板 String userText = "请给我讲一个关于 {topic} 的故事。"; PromptTemplate userTemplate = new PromptTemplate(userText); Message userMessage = userTemplate.createMessage(Map.of("topic", topic)); // 系统消息模板(设定人设) String systemText = "你是一个讲故事的高手,请用 {tone} 的语气来讲述。"; SystemPromptTemplate systemTemplate = new SystemPromptTemplate(systemText); Message systemMessage = systemTemplate.createMessage(Map.of("tone", tone)); // 组装消息列表 Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); return chatClient.prompt(prompt).call().chatResponse().getResult().getOutput();
}
底层原理小贴士:PromptTemplate 底层默认使用了 StringTemplate 引擎。这意味着它不仅支持简单的变量替换,还支持条件判断(\(if\))和循环(\(for\))。例如,你可以根据是否有订单ID,动态决定是否在提示词中展示订单信息。
在 Spring AI 中,输出方式主要可以从两个维度来划分:一是数据流的模式(流式 vs 非流式),二是返回数据的结构(非结构化文本 vs 结构化对象)
PS:通常这两个维度是混合的,排列组合共 4 种输出方式(比如:流式结构化、非流式结构化、流式非结构化、非流式非结构化)
流式输出 vs 非流式输出
流式输出
流式输出基于 Server-Sent Events 协议(或 WebSocket)。一旦大模型生成了第一个文本块(Chunk),就会立即推送到客户端,随后源源不断地发送后续片段,直到生成结束
- 优势:
- 极低延迟:用户几乎在提问后立即能看到反馈,显著提升感知速度。
- 交互性强:模拟人类对话的节奏,用户可以边看边思考,甚至在生成过程中进行打断(停止生成)。
- 节省资源:客户端可以逐块渲染,无需等待大数据量加载完成。
- 劣势:
- 连接维护:需要保持长连接,对网络稳定性有一定要求。
- 统计困难:由于数据是分片返回的,完整的 Token 用量统计通常在最后一个数据包中才返回。
- 适用场景:
- AI 聊天机器人/助手:这是目前最主流的场景,流式输出能带来“丝滑”的对话体验,避免用户长时间等待。
- 长文本生成:如写故事、写报告、代码生成。如果一次性返回,用户可能需要等几十秒;流式输出则能即时反馈。
- 实时翻译/字幕:需要极低的延迟来配合语音或视频流。
- 代码示例:
// 伪代码示例 (基于 Spring AI / Reactor) chatModel.stream(“你好,请介绍下自己”) .doOnNext(chunk -> ) .doOnComplete(() -> { // 流结束时的回调 System.out.println("
— 生成完毕 —“);
}) .subscribe();
非流式输出
这是最传统的请求处理方式。客户端发送请求后,服务端(或大模型)会进行完整的计算和生成,直到所有内容准备完毕,才通过 HTTP 响应一次性返回给客户端
- 优势:
- 实现简单:标准的请求-响应模式,无需处理复杂的连接状态。
- 数据完整:适合需要一次性获取完整结构化数据(如 JSON)的场景,便于后续逻辑处理。
- 并发控制:不存在流式传输中的并发竞争问题。
- 劣势:
- “白屏”时间长:在模型生成较长文本时,用户面对空白屏幕的时间会很久,容易产生焦虑或误以为系统卡死。
- 内存占用:服务端和客户端通常需要缓冲完整的数据。
- 适用场景:
- 后台数据处理:如批量分析文档、生成财务报表。后台任务通常不需要实时展示过程,只需要最终结果。
- 结构化数据提取:如果你让 AI 提取 JSON 格式的数据,非流式更安全,因为流式可能会在 JSON 中间截断,导致解析失败。
- 简单的问答:对于极短的回答(如“今天天气不错”),流式的优势不明显,非流式反而更节省连接资源。
- 代码示例:
String response = chatModel.call("你好,请介绍下自己");
// 等待… 3秒后 System.out.println(response); // 一次性打印完整内容
结构化输出 vs 非结构化数据
结构化输出
结构化输出是指通过特定技术手段(如 OpenAI 的 Structured Outputs、LangChain 的 Pydantic 定义、Spring AI 的 BeanOutputConverter),强制大模型输出符合预定义格式的数据。
- 优势:
- 系统集成友好:可直接存入数据库或作为 API 响应,无需二次解析。
- 数据可靠性高:通过预定义 Schema 确保了字段完整性和类型安全。
- 提升开发效率:减少了大量用于解析和清洗文本的代码。
- 劣势:
- 信息可能丢失:强制格式化可能会牺牲部分上下文的细微之处。
- 依赖模型能力:需要模型本身具备良好的指令遵循能力。
- 需要额外定义:必须预先设计好数据结构(Java Bean/JSON Schema)。
- 适用场景:
- 数据提取:从合同中提取金额、日期和签约方。
- 信息分类:将用户反馈分类为“Bug报告”、“功能请求”等。
- 生成业务对象:根据描述生成一个完整的小说大纲、产品规格或数据库记录。
- 实现方式:
Map
List
.entity() 方法
非结构化输出
非结构化数据是大模型最自然的产出形式(像写文章一样)
- 优势:
- 信息丰富:保留了完整的上下文、情感和细微差别。
- 灵活性强:模型可以自由发挥,适合创意和探索性任务。
- 开发简单:直接获取文本,无需定义格式。
- 劣势:
- 难以程序化处理:需要复杂的 NLP 技术(如情感分析、实体识别)来提取价值。
- 存储与查询复杂:不适合传统的关系型数据库。
- 不确定性高:每次输出的格式和措辞可能不同,导致下游系统不稳定。
- 适用场景:
- 创意写作:写诗、写故事、生成营销文案。
- 开放式问答:客服聊天机器人、知识问答。
- 内容总结:为一篇长文章生成摘要。
- 代码示例:
String message = “介绍一下Java”; // 最原始的输出,不作任何处理 String response = chatClient.prompt() .user(message) .call() .content(); System.out.println("response : " + response);
基本介绍
Spring AI 的记忆体系设计得非常清晰,主要围绕三个核心概念展开:
- ChatMemory(记忆接口)
这是记忆的“内存操作入口”,负责管理单个会话的消息。你可以把它想象成一个笔记本,专门用来记录你和某个人的对话。它的核心职责是:
-
add(Message… messages):向当前会话追加新的对话消息。 -
getMessages():获取当前会话的所有历史消息。 -
clear():清空当前会话的所有消息。
- ChatMemoryRepository(持久化仓库)
这是记忆的“存储层”,负责将对话历史持久化保存。你可以把它想象成一个文件柜,用来存放所有的对话笔记本。通过实现这个接口,我们可以将记忆存储到任何地方:
- 内存(InMemory):使用
ConcurrentHashMap存储,简单快速,但重启应用后数据会丢失,适合开发和测试。 - 数据库(JDBC):将对话历史存入 MySQL、PostgreSQL 等关系型数据库,适合生产环境,数据持久可靠。
- Redis:将对话历史存入 Redis,非常适合分布式、需要水平扩展的应用,多个服务实例可以共享同一份对话状态。
- ChatMemoryAdvisor(记忆顾问)
这是整个记忆机制的“调度官”和“自动化引擎”。它基于 Spring 的 AOP 思想,像一个拦截器一样,无缝地包装在ChatClient的调用链中。它的工作流程是:
- 请求前: 拦截用户的请求,根据会话 ID 从
ChatMemoryRepository中加载历史消息。 - 注入上下文: 将加载的历史消息和当前用户的新问题一起组装成完整的 Prompt,发送给大模型。
- 响应后: 拦截大模型的回复,将用户的提问和 AI 的回复作为新的一轮对话,追加存储到
ChatMemoryRepository中。
通过 Advisor,开发者完全不需要手动管理记忆的加载和保存,实现了记忆功能与业务逻辑的解耦。
实战演练
- Step1:配置 ChatClient 和 Advisor
在你的配置类中,创建一个ChatClientBean,并将MessageChatMemoryAdvisor作为默认顾问注入
@Configuration public class AiConfig { @Bean public ChatClient chatClient(ChatClient.Builder builder, ChatMemoryRepository chatMemoryRepository) { // 创建一个基于消息窗口的记忆,最多保留最近10条消息 ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build(); return builder // 将记忆顾问设置为默认行为 .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)) .build(); }
}
- Step2:在业务代码中使用
现在,你的ChatClient已经具备了记忆能力。在调用时,只需传入一个唯一的conversationId,Spring AI 就会自动处理后续的所有记忆逻辑。
@Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient chatClient) { this.chatClient = chatClient; } public String chat(String userId, String userMessage) { // 使用 userId 作为会话ID,实现用户级别的记忆隔离 return chatClient.prompt() .user(userMessage) .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, userId)) .call() .content(); }
}
记忆管理
随着对话的进行,历史记录会越来越长。如果无限制地追加,不仅会消耗大量的 Token,增加成本,还可能导致超出模型的上下文窗口限制。因此,我们需要一些策略来管理记忆
1. 滑动窗口(MessageWindowChatMemory)
这是最常用的策略。它只保留最近 N 条消息,就像一扇窗户,随着新消息的进入,最旧的消息会被自动移出窗外。这保证了上下文的时效性,同时将 Token 消耗控制在可接受的范围内。
// 只保留最近 20 条消息 ChatMemory memory = MessageWindowChatMemory.builder() .maxMessages(20) .build();
2. 摘要记忆(Summary Chat Memory)
这是一种更高级的策略。当对话历史超过一定长度时,系统会调用一次 LLM,将早期的对话内容总结成一段精简的摘要。这样既能保留关键信息,又能大幅压缩上下文长度。
3. 向量记忆(VectorStoreChatMemoryAdvisor)
对于更复杂的场景,你可以使用向量存储来实现语义检索。它不是根据时间,而是根据与当前问题的语义相似度来检索相关的历史对话。这相当于给你的 AI 装上了一个“长期记忆库”,可以回想起很久以前聊过的、但与当前话题相关的内容。
基本介绍
- 工具函数是什么?
简单来说,工具函数就是一种机制,允许大模型在生成回答的过程中,主动请求执行一段代码(通常是 Java 方法),获取结果后,再结合结果生成最终的回答。
- 为什么需要工具函数?
如果你用过 ChatGPT 或类似的聊天机器人,你可能会发现一个有趣的现象:如果你问它“现在北京几点了?”,它要么会给你一个过时的答案,要么会诚实地告诉你它不知道。
为什么?因为大语言模型(LLM)本质上是一个“被关在盒子里的博学天才”。它读过互联网上几乎所有的书,能写诗、能写代码、能分析哲学,但它无法触达外部世界。它没有眼睛看实时新闻,没有手去操作数据库,也没有嘴巴去调用 API。
Spring AI 的工具函数(Tool Calling / Function Calling),就是为了解决这个问题而生的。它就像是给这个“盒子里的天才”装上了“手和脚”,让它不仅能“思考”,还能“行动”。
- 工具函数是如何工作的?
Spring AI 将这一复杂的过程封装得非常优雅,但在深入代码之前,我们需要理解底层的四步舞:
- 注册(Registration): 你告诉 Spring AI 有哪些工具可用(比如
WeatherService)。Spring AI 会将这些方法的元数据(名称、描述、参数结构)转换成模型能听懂的 JSON Schema。 - 决策(Decision): 用户提问。模型分析意图,发现需要外部信息,于是返回一个特殊的“工具调用请求”,而不是直接回答。
- 执行(Execution): Spring AI 拦截这个请求,在本地找到对应的 Java 方法并执行它。
- 反馈(Feedback): Spring AI 将执行结果返回给模型。模型结合这个结果,生成最终的自然语言回答
工具函数的定义
定义工具函数,就是把你普通的 Java 方法“包装”一下,让大模型能够理解它的用途和用法。Spring AI 提供了多种定义方式,其中最常用、最直观的是使用
@Tool注解。
方式一:使用 @Tool 注解(常用、推荐)
你只需要在一个 Spring 管理的 Bean 的方法上加上 @Tool 注解,并附上清晰的描述即可
注解参数介绍:
description(描述):这是最关键的部分。大模型完全依赖这段描述来判断何时应该调用这个工具。描述越精确,模型调用的准确率就越高。-
@ToolParam(参数描述):用于详细说明方法的参数,帮助模型准确地提取和传递参数值。
import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; @Service public class WeatherService
@Tool(description = "根据订单ID查询订单的当前状态") public String getOrderStatus( @ToolParam(description = "订单的唯一标识符") String orderId) { // 模拟查询数据库 return "订单 " + orderId + " 的状态是:已发货"; }
}
方式二:FunctionToolCallback
它允许你将一个 java.util.function.Function 或 Lambda 表达式注册为工具,适合一些动态或无状态的场景
工具函数的注册
定义好工具后,你需要将它“介绍”给
ChatClient,这样ChatClient在对话时才知道有哪些工具可用。这个过程称为“注册”。注册之后,调用就变得非常简单,Spring AI 会自动处理所有复杂的流程
通常,我们会在配置类中创建一个 ChatClient Bean,并将定义好的工具注册进去。Spring AI 提供了 ToolCallbackProvider 来简化这个过程
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AiConfig {
private final WeatherService weatherService; // 通过构造函数注入 WeatherService public AiConfig(WeatherService weatherService) { this.weatherService = weatherService; } @Bean public ChatClient chatClient(ChatClient.Builder builder) { // 使用 MethodToolCallbackProvider 扫描 weatherService 中所有带 @Tool 注解的方法 ToolCallbackProvider tools = MethodToolCallbackProvider.builder() .toolObjects(weatherService) .build(); // 将工具提供者注册到 ChatClient 中 return builder .defaultToolCallbacks(tools) .build(); }
}
工具函数的调用
注册完成后,你就可以在业务代码中像使用普通聊天客户端一样使用它。当用户的问题需要调用工具时,Spring AI 会自动拦截、执行并整合结果
@Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient chatClient) { this.chatClient = chatClient; } public String chat(String message) { // 调用非常简单,无需关心工具的执行细节 return chatClient.prompt() .user(message) .call() .content(); }
}
当你向 chat 方法发送请求“帮我查一下北京的天气”时,整个调用链路如下:
- 用户提问:
ChatClient收到“帮我查一下北京的天气”。 - 模型决策:大模型分析后,发现需要调用
getWeather工具,并返回一个调用指令}。 - 框架执行:Spring AI 拦截到这个指令,找到
WeatherService.getWeather方法并执行它。 - 结果返回:
getWeather方法返回“北京今天天气晴朗…”。 - 最终回答:Spring AI 将工具的执行结果返回给大模型,大模型再生成最终的自然语言回复:“北京今天天气晴朗,气温 25℃,微风。”
整个过程对开发者来说是完全透明的,你只需要关注工具的定义和业务逻辑的实现。
持续更新中,敬请期待(●’◡’●)
PS:后续会单独出两篇文章,一篇讲 MCP,一篇讲 RAG,关于 Spring AI 、Spring AI Alibaba、LangChain4j 整合 MCP 和 RAG 相关内容都会放到这两篇文章的实战部分
持续更新中,敬请期待(●’◡’●)
PS:后续会单独出两篇文章,一篇讲 MCP,一篇讲 RAG,关于 Spring AI 、Spring AI Alibaba、LangChain4j 整合 MCP 和 RAG 相关内容都会放到这两篇文章的实战部分
- Spring AI Ailbaba 是什么
Spring AI Alibaba 简称 SAA,它是阿里巴巴基于 Spring AI 开源项目扩展的 AI 应用开发框架。它的核心定位是:让 Java 开发者能像开发普通 Spring Boot 应用一样,轻松构建基于大模型(尤其是阿里云通义系列)的生成式 AI 应用。
简单来说,如果说 Spring AI 是“通用底座”,那么 Spring AI Alibaba 就是针对阿里云生态和中国企业级需求进行的深度增强版(这个类似于 SpringCloud 和 SpringCloudAlibaba 的关系)
- Spring AI Alibaba 在线文档
- Spring AI Alibaba Github 仓库
- Spring AI 和 Spring AI Alibaba 有什么区别
Spring AI Alibaba 是基于 Spring AI 的一个扩展和增强,在 Spring AI 原有能力之上,对国内大模型(尤其是通义千问)还有中文环境下有一个更好的适配。
相较于 Spring AI ,Spring AI Alibaba 解决了 Java 开发者在使用 LangChain (Python) 或原生 HTTP 调用大模型时的繁琐问题,提供了声明式、注解驱动的开发体验,特别增强了多智能体 (Multi-Agent)、工作流编排 (Workflow) 和 RAG (检索增强生成) 等企业刚需功能。
特性Spring AI (官方社区版)Spring AI Alibaba (阿里增强版)维护方Spring 社区 (VMware/ Broadcom)阿里巴巴 (阿里云)模型支持通用支持 (OpenAI, Azure, Ollama 等)深度优化 阿里云通义系列,同时兼容 OpenAI 协议Agent 编排基础支持高级支持 (基于 Graph 的多智能体工作流)RAG 能力基础向量存储接口企业级 RAG (集成 DashVector, 文档解析,重排序)适用场景通用 AI 应用,多模型切换阿里云用户, 复杂 Agent 编排,企业级 RAG依赖关系基础库依赖 Spring AI (它是 Spring AI 的超集/扩展)
- Spring AI Alibaba 相关概念
- DashScope :它是阿里云推出的模型推理服务平台,中文叫灵积模型服务,它负责托管、运行和分发各种 AI 模型。你不需要自己买显卡、部署模型环境,只需要通过 HTTP 请求(API)发送数据给 DashScope,它就会返回结果。
- 阿里云百炼:这是阿里云最近推出的大模型应用开发平台,百炼平台底层调用的其实就是 DashScope 的 API,DashScope 更偏向于底层 API 服务(适合开发者直接写代码调用),百炼 更偏向于可视化开发(可以在网页上拖拽编排 Agent、管理知识库、微调模型),但它生成的应用最终也是通过 DashScope 的接口运行的
- 通义千问:这是阿里云自研的具体大语言模型,它是你在 DashScope 平台上可以调用的众多模型中的一个(最核心的一个),当你调用 API 时,你需要指定参数
model: “qwen-max”或model: “qwen-plus”,这就是在告诉 DashScope 平台:“请调用通义千问这个模型来处理我的请求
- ReAct Agent:是大模型(LLM)领域中最经典、最强大的智能体(Agent)设计模式之一。
它的名字来源于两个核心能力的结合:
- Re = Reasoning(推理/思考):模型先“想”清楚下一步该做什么,分析当前情况。
- Act = Action(行动/执行):模型根据思考的结果,去调用外部工具(如搜索、计算器、数据库)或执行操作。
简单来说,ReAct 就是让 AI 学会“边想边做”,而不是“瞎猜答案”。
前置要求:
- JDK 17+
- Maven 3.8+
- SpringBoot3.2+
- 选择你的 LLM 提供商并获取 API-KEY
这里我们以 qwen3.5-plus 为例,相关 API-KEY 申请请前往:大模型服务平台百炼控制台
PS:相较于之前 Spring AI 的快速入门,Spring AI Alibaba 官方文档中,给我们提供的快速入门 Demo 是教我们构建一个 ReactAgent ,而不是像传统 Spring AI 教程那样先教你怎么调用 ChatClient 进行简单的对话,这主要反映了 Spring AI Alibaba 的设计哲学,我们接入模型不仅仅是“聊天”,而是“做事”。
- Step1:创建 Maven 工程
- Step2:引入依赖
1.1.2.0
org.springframework.boot
spring-boot-starter-parent
3.3.8
com.alibaba.cloud.ai
spring-ai-alibaba-agent-framework
com.alibaba.cloud.ai
spring-ai-alibaba-starter-dashscope
org.springframework.boot
spring-boot-starter-test
test
com.alibaba.cloud.ai
spring-ai-alibaba-bom
${spring-ai-alibaba.version}
pom
import
- Step3:编写配置文件
server:
port: 8080 spring: ai:
openai: # 填写你的大模型 API Key api-key: # 填写你的大模型 API URL base-url: https://api.deepseek.com chat: options: # 模型名称 model: deepseek-chat # 设置温度参数 0.7 控制回复的随机性(值越大越随机) temperature: 0.7
- Step4:构建大模型对象
@Configuration
public class SpringAiAlibabaConfig {
/ * 通过构造器注入 ChatClient.Builder * Spring AI Alibaba 会自动注册 Builder Bean */ @Bean public ChatClient dashScopeChatClient(ChatClient.Builder chatClientBuilder) { return chatClientBuilder // 设置系统提示语 .defaultSystem("你是一个天气预报员") // 配置记忆模式,默认是 20 轮,交互超过20轮会自动将前面的对话记忆丢失(经过测试发现这个配置失效) .defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().maxMessages(20).build()).build()) // 配置入职提示(生产环境建议关掉) .defaultAdvisors(new SimpleLoggerAdvisor()) .build(); }
}
- Step5:测试
@Autowired private ChatClient chatClient; @Test public void test() { String message = "广州今天天气如何"; String userId = "1"; String response = chatClient .prompt(message) // 添加对话id,确保不同用户共用一个 chatClient 能够实现数据隔离 .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, userId)) .call() .content(); System.out.println(response); } @Test public void testMemoryWindowLimit() { String message = "十个苹果吃掉一个还剩几个"; String userId1 = "1"; String userId2 = "2"; // 用户1发起对话 String response = chatClient .prompt(message) // 添加对话id,确保不同用户共用一个 chatClient 能够实现数据隔离 .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, userId1)) .call() .content(); System.out.println(response); // 9 System.out.println("================================================"); // 用户1发起对话 message = "再吃掉一个还剩几个"; response = chatClient .prompt(message) // 添加对话id,确保不同用户共用一个 chatClient 能够实现数据隔离 .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, userId1)) .call() .content(); System.out.println(response); // 8 System.out.println("================================================"); // 用户2发起对话 message = "十个苹果吃掉一个还剩几个"; response = chatClient .prompt(message) // 添加对话id,确保不同用户共用一个 chatClient 能够实现数据隔离 .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, userId2)) .call() .content(); System.out.println(response); // 9 System.out.println("================================================"); // 用户1发起对话 message = "再吃掉一个还剩几个"; response = chatClient .prompt(message) // 添加对话id,确保不同用户共用一个 chatClient 能够实现数据隔离 .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, userId1)) .call() .content(); System.out.println(response); // 7 }

关注博主,后续将持续更新(●ˇ∀ˇ●)……
PS:后续会单独出两篇文章,一篇讲 MCP,一篇讲 RAG,关于 Spring AI 、Spring AI Alibaba、LangChain4j 整合 MCP 和 RAG 相关内容都会放到这两篇文章的实战部分
关注博主,后续将持续更新(●ˇ∀ˇ●)……
PS:后续会单独出两篇文章,一篇讲 MCP,一篇讲 RAG,关于 Spring AI 、Spring AI Alibaba、LangChain4j 整合 MCP 和 RAG 相关内容都会放到这两篇文章的实战部分
关注博主,后续将持续更新(●ˇ∀ˇ●)……
关注博主,后续将持续更新(●ˇ∀ˇ●)……
关注博主,后续将持续更新(●ˇ∀ˇ●)……
关注博主,后续将持续更新(●ˇ∀ˇ●)……
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/280403.html