Spring AI Alibaba入门-生态集成
- 一、版本说明
- 二、SpringAi拓展
-
- [1. ChatClient](#1. ChatClient)
-
- 1.1简单示例
- [1.2多个 Chat Model](#1.2多个 Chat Model)
- [1.3ChatClient 响应](#1.3ChatClient 响应)
-
- [1.3.1返回完整 ChatResponse(获取 Token 消耗、元信息)](#1.3.1返回完整 ChatResponse(获取 Token 消耗、元信息))
- [1.3.2 entity() 将 AI 输出直接映射为 Java 实体对象](#1.3.2 entity() 将 AI 输出直接映射为 Java 实体对象)
-
- [1.3.2.1简单对象,使用 Record(Java 16+ 推荐)](#1.3.2.1简单对象,使用 Record(Java 16+ 推荐))
- [1.3.2.2 泛型集合场景:ParameterizedTypeReference](#1.3.2.2 泛型集合场景:ParameterizedTypeReference)
- [1.3.3 开启模型原生结构化输出 Native Structured Output](#1.3.3 开启模型原生结构化输出 Native Structured Output)
- [1.3.4 stream () 流式响应(SSE 打字机效果)](#1.3.4 stream () 流式响应(SSE 打字机效果))
-
- [1.3.4.1 流式获取文本 Flux<String>](#1.3.4.1 流式获取文本 Flux
) - [1.3.4.2 流式返回完整 Flux<ChatResponse>](#1.3.4.2 流式返回完整 Flux
) - [1.3.4.3 流式输出如何转 Java 实体对象](#1.3.4.3 流式输出如何转 Java 实体对象)
- [1.3.4.1 流式获取文本 Flux<String>](#1.3.4.1 流式获取文本 Flux
- [1.4 Prompt 模板](#1.4 Prompt 模板)
-
- [1.4.1 基础模板用法](#1.4.1 基础模板用法)
- [1.4.2 修改模板占位符分隔符](#1.4.2 修改模板占位符分隔符)
- [1.4.3 全局设置模板渲染器示例](#1.4.3 全局设置模板渲染器示例)
- [1.5 Advisor拦截器](#1.5 Advisor拦截器)
-
- [1.5.1 什么是 Advisor](#1.5.1 什么是 Advisor)
- [1.5.2 AdvisorSpec 核心 API](#1.5.2 AdvisorSpec 核心 API)
- [1.5.3 示例:聊天记忆 + RAG 检索组合](#1.5.3 示例:聊天记忆 + RAG 检索组合)
- [1.5.4 调试日志:SimpleLoggerAdvisor](#1.5.4 调试日志:SimpleLoggerAdvisor)
- [1.5.5ChatMemory 对话记忆](#1.5.5ChatMemory 对话记忆)
- [2. Chat Models](#2. Chat Models)
-
- [2.1 DashScope](#2.1 DashScope)
-
- [2.1.1 Maven 依赖](#2.1.1 Maven 依赖)
- [2.1.2 基础配置 application.yml](#2.1.2 基础配置 application.yml)
- [2.1.3 简单 Controller 示例](#2.1.3 简单 Controller 示例)
- [2.1.4 运行时覆盖参数 DashScopeChatOptions](#2.1.4 运行时覆盖参数 DashScopeChatOptions)
- [2.1.5 多模态调用(图文音视频)](#2.1.5 多模态调用(图文音视频))
-
- [2.1.5.1 图片理解 qwen‑vl‑plus /qwen‑vl‑max](#2.1.5.1 图片理解 qwen‑vl‑plus /qwen‑vl‑max)
- [2.1.5.2 音频、视频](#2.1.5.2 音频、视频)
- [2.1.6 Qwen3 推理模型,获取思考过程](#2.1.6 Qwen3 推理模型,获取思考过程)
- [2.1.7 手动创建 DashScopeChatModel(非自动装配)](#2.1.7 手动创建 DashScopeChatModel(非自动装配))
- [2.1.8 自定义 ApiKey(密钥动态获取、密钥轮换)](#2.1.8 自定义 ApiKey(密钥动态获取、密钥轮换))
- [2.2 DeepSeek](#2.2 DeepSeek)
-
- [2.2.1Maven 依赖(自动装配 Starter)](#2.2.1Maven 依赖(自动装配 Starter))
- [2.2.2 application.yml 配置](#2.2.2 application.yml 配置)
- [2.2.3 基础 Controller 示例](#2.2.3 基础 Controller 示例)
- [2.2.4 运行时动态参数覆盖 DeepSeekChatOptions](#2.2.4 运行时动态参数覆盖 DeepSeekChatOptions)
- [2.2.5 deepseek‑reasoner 推理模型,获取思维链 CoT](#2.2.5 deepseek‑reasoner 推理模型,获取思维链 CoT)
- [2.2.6 Prefix Completion 前缀补全(代码续写)](#2.2.6 Prefix Completion 前缀补全(代码续写))
- [2.2.7 手动构建 DeepSeekChatModel(关闭自动装配)](#2.2.7 手动构建 DeepSeekChatModel(关闭自动装配))
- [2.3 OpenAI](#2.3 OpenAI)
-
- [2.3.1 Maven 依赖(自动装配 Starter)](#2.3.1 Maven 依赖(自动装配 Starter))
- [2.3.2 application.yml 配置](#2.3.2 application.yml 配置)
- [2.3.3基础 Controller 示例](#2.3.3基础 Controller 示例)
- [2.3.4 运行时动态参数 OpenAiChatOptions](#2.3.4 运行时动态参数 OpenAiChatOptions)
- [2.3.5 结构化输出 Structured‑Outputs](#2.3.5 结构化输出 Structured‑Outputs)
- 2.3.6多模态(图文、音频)
-
- [2.3.6.1 图片理解 gpt‑4o](#2.3.6.1 图片理解 gpt‑4o)
- [2.3.6.2音频输入输出 gpt‑4o‑audio‑preview](#2.3.6.2音频输入输出 gpt‑4o‑audio‑preview)
- [2.3.7 OpenAI 兼容服务:extraBody(vLLM / Ollama / DeepSeek)](#2.3.7 OpenAI 兼容服务:extraBody(vLLM / Ollama / DeepSeek))
- [2.3.8 推理内容 reasoningContent 说明](#2.3.8 推理内容 reasoningContent 说明)
- [2.3.9 手动构建 OpenAiChatModel(关闭自动装配,动态密钥)](#2.3.9 手动构建 OpenAiChatModel(关闭自动装配,动态密钥))
- [2.4 OpenAi 兼容模型](#2.4 OpenAi 兼容模型)
-
- [2.4.1 Maven 依赖](#2.4.1 Maven 依赖)
- [2.4.2 application.yml 通用模板](#2.4.2 application.yml 通用模板)
- [2.4.3 DashScope(阿里百炼 兼容 OpenAI 模式)](#2.4.3 DashScope(阿里百炼 兼容 OpenAI 模式))
- [2.4.4 DeepSeek](#2.4.4 DeepSeek)
- [2.4.5 vLLM / Ollama(本地部署兼容服务)](#2.4.5 vLLM / Ollama(本地部署兼容服务))
- [2.4.6 代码使用](#2.4.6 代码使用)
- [2.4.7 和 Spring‑AI‑Alibaba DashScope 原生 Starter 区别](#2.4.7 和 Spring‑AI‑Alibaba DashScope 原生 Starter 区别)
- [3. RAG](#3. RAG)
-
- [3.1 QuestionAnswerAdvisor 快速 RAG(简单业务首选)](#3.1 QuestionAnswerAdvisor 快速 RAG(简单业务首选))
-
- 3.1.1基础用法
-
- 3.1.1.1运行时动态元数据过滤
- [3.1.1.2自定义 RAG 提示模板](#3.1.1.2自定义 RAG 提示模板)
- [3.1.2 RetrievalAugmentationAdvisor 模块化高级 RAG](#3.1.2 RetrievalAugmentationAdvisor 模块化高级 RAG)
-
- [3.1.2.1 Naive RAG(朴素 RAG)](#3.1.2.1 Naive RAG(朴素 RAG))
- [3.1.2.2 Advanced RAG:增加查询改写](#3.1.2.2 Advanced RAG:增加查询改写)
- [3.2 Pre‑Retrieval 查询预处理模块(QueryTransformer)](#3.2 Pre‑Retrieval 查询预处理模块(QueryTransformer))
-
- [3.2.1. CompressionQueryTransformer 对话压缩](#3.2.1. CompressionQueryTransformer 对话压缩)
- [3.2.2 RewriteQueryTransformer 查询改写](#3.2.2 RewriteQueryTransformer 查询改写)
- [3.2.3. TranslationQueryTransformer 查询翻译](#3.2.3. TranslationQueryTransformer 查询翻译)
- [3.2.4. MultiQueryExpander 查询扩展](#3.2.4. MultiQueryExpander 查询扩展)
- [3.3 Retrieval 检索模块](#3.3 Retrieval 检索模块)
- [3.4 Post‑Retrieval 检索后处理](#3.4 Post‑Retrieval 检索后处理)
- [3.5 Generation:ContextualQueryAugmenter](#3.5 Generation:ContextualQueryAugmenter)
Spring AI Alibaba 简称 SAA,它是面向 Java 开发人员的智能体 AI 框架。Spring AI Alibaba 基于 DAG 图的核心概念构建,可以轻松实现单智能体、多智能体和复杂工作流编排。

| 名称 | 地址 |
|---|---|
| 官网地址 | https://java2ai.com/ |
| GitHub 地址 | https://github.com/alibaba/spring-ai-alibaba |
注意! Spring AI Alibaba 深度集成 Spring AI 生态,它是一个专为多智能体系统和工作流编排设计的项目。因此,Spring AI Alibaba 中的 ReactAgent 实际上运行在 Graph Runtime 之上,其设计目标主要是完成工作流和多智能体编排功能。
Spring AI Alibaba 项目从架构上包含如下三层:
- Agent Framework,是一个以 ReactAgent 设计理念为核心的 Agent 开发框架,使开发者能够构建具备自动上下文工程和人机交互等核心能力的 Agent。
- Graph,Graph 是一个低级别的工作流和多代理协调框架,能够帮助开发者实现复杂的应用程序编排,它具备丰富的预置节点和简化的图状态定义,Graph 是 Agent Framework 的底层运行时基座。
- Augmented LLM,以 Spring AI 框架底层原子抽象为基础,为构建大型语言模型(LLM)应用提供基础抽象,例如模型(Model)、工具(Tool)、多模态组件(MCP)、消息(Message)、向量存储(Vector Store)等。
对Spring Ai的增强。
整体架构

一、版本说明
版本兼容表
请注意 Spring AI Alibaba 1.1.2.0 对应的是 Spring AI 的 1.1.2 版本,暂未支持 Spring AI 2.0 版本。
| SAA 版本 | Spring AI | Spring AI Extensions | Spring Boot | 说明 |
|---|---|---|---|---|
| 1.1.2.0(当前推荐) | 1.1.2 | 1.1.2.1 或 1.1.2.0 | 3.5.x | 支持 Agent Skills,提供 Supervisor、Routing 等 Multi-Agent 能力。 |
| 1.1.0.0 | 1.1.0 | 1.1.0.0 | 3.4.x | 1.1.0 首个正式版 |
| 1.1.0.0-RC2 | 1.1.0-RC2 | 1.1.0.0-RC2 | 3.4.x | 1.1.0 候选版,请使用 1.1.0.0 或 1.1.2.0 版本 |
| 1.1.0.0-RC1 | 1.1.0-RC1 | 1.1.0.0-RC1 | 3.4.x | 1.1.0 候选版,请使用 1.1.0.0 或 1.1.2.0 版本 |
| 1.0.x | 1.0.0 | --- | 3.4.x | 1.0 系列 |
依赖管理(推荐使用 BOM)
新项目建议通过 BOM 统一版本,避免与 Spring AI、Spring Boot 冲突:
bash
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>1.1.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-extensions-bom</artifactId>
<version>1.1.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
</dependencies>
组件与生态版本
以下组件随 Spring AI Alibaba 主仓库发布,版本与 BOM 中的 SAA 版本一致(如 1.1.2.0)。
| 组件 / 模块 | artifactId | 说明 | 版本约定 |
|---|---|---|---|
| BOM | spring-ai-alibaba-bom | 依赖管理,统一 SAA 各模块版本 | 同 SAA 主版本 |
| Agent Framework | spring-ai-alibaba-agent-framework | ReactAgent、多智能体编排、Hooks、Skills 等 | 同 BOM |
| Graph Core | spring-ai-alibaba-graph-core | 图工作流运行时、持久化、流式、MCP 节点等 | 同 BOM |
| Studio | spring-ai-alibaba-studio | 嵌入式 Agent 调试与可视化 UI | 同 BOM |
| Sandbox | spring-ai-alibaba-sandbox | Agent 沙箱运行时 | 同 BOM |
| Admin | spring-ai-alibaba-admin | 一站式 Agent 平台(可视开发、可观测、MCP 管理) | 随主仓库发布 |
| Starter A2A Nacos | spring-ai-alibaba-starter-a2a-nacos | 基于 Nacos 的 A2A 通信 | 同 BOM |
| Starter Config Nacos | spring-ai-alibaba-starter-config-nacos | 基于 Nacos 的动态配置与模型热更新 | 同 BOM |
| Starter Graph Observation | spring-ai-alibaba-starter-graph-observation | Graph 可观测性(Micrometer/OpenTelemetry) | 同 BOM |
| Starter Builtin Nodes | spring-ai-alibaba-starter-builtin-nodes | 预置图节点(LlmNode、AgentNode 等) | 同 BOM |
二、SpringAi拓展
使用 spring-ai-alibaba-extensions-bom 来统一管理版本
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-extensions-bom</artifactId>
<version>1.1.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
</dependencies>
1. ChatClient
ChatClient 使用 ChatClient.Builder 对象创建。可以为任何 ChatModel Spring Boot 自动配置获取自动配置的 ChatClient.Builder 实例,或以编程方式创建一个。
1.1简单示例
使用 Spring AI 自动注入的 ChatClient 做一个简单示例
在配置文件中配置如下信息:
yml
spring:
application:
name: 01spring-ai-extend
ai:
dashscope:
api-key: sk-1e36e15xxx 你的百炼平台key
代码实现。我这里使用了 Lombok 的 @RequiredArgsConstructor 注解自动生成构造器注入 ChatClient。
java
@RestController
@RequiredArgsConstructor
public class ChatClientTestController {
// final 字段,@RequiredArgsConstructor 自动生成构造器注入 ChatClient
private final ChatClient chatClient;
/**
* 普通对话调用
* @param prompt 用户输入提示词,默认值:你好
* @return AI返回文本内容
*/
@GetMapping("/generation")
public String generation(@RequestParam(defaultValue = "你好") String prompt) {
// call() 为阻塞同步调用,.content() 直接获取响应文本;null 时返回空字符串避免 NPE
return chatClient.prompt(prompt)
.call()
.content() == null ? "" : chatClient.prompt(prompt).call().content();
}
}
结果:

1.2多个 Chat Model
在实际业务系统开发中,很少会只使用单一的大模型。
我们经常会遇到这些业务场景:
- 任务分级:复杂逻辑推理、文档分析使用能力强的大模型;简单问答、内容摘要调用轻量低成本模型,节约 Token 成本。
- 故障降级 / 回退:主模型服务限流、不可用时,自动切换备用模型保障业务可用。
- A/B 测试:同一业务场景,对比不同模型输出效果,用于模型选型评估。
- 用户可选模型:页面提供下拉框,由终端用户自主选择想要使用的大模型。
- 专用模型组合:代码生成专用模型、创意文案生成模型、多模态识图模型各司其职。
Spring AI 默认自动装配只提供单个 ChatClient.Builder ,只能使用一套模型配置。
如果业务需要同时维护多个模型、多个不同配置的 ChatClient,就需要手动接管 Bean 的创建,关闭自动配置。
1、关闭 ChatClient 自动装配
当我们需要手动管理多个 ChatClient,首先要关闭 Spring AI 的默认 ChatClient.Builder 自动配置。
application.yml
spring:
ai:
chat:
client:
enabled: false
设置
spring.ai.chat.client.enabled=false,Spring 将不再自动创建默认的ChatClient.BuilderBean,全部 ChatClient 实例由我们代码手动构建。
2、场景 1:同一个底层 Model,创建多个不同配置的 ChatClient
场景:使用同一个大模型(例如通义千问),但是不同业务使用不同系统提示词、不同温度参数。底层复用同一个
ChatModelBean,构建多个差异化的ChatClient。
ChatModel 对象仍然由 Spring AI starter 自动注入(例如 DashScope 的 DashScopeChatModel)。
我们基于同一个 ChatModel,构建多个业务专属的 ChatClient。
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MultiChatClientConfig {
/**
* 同一个ChatModel,构建不同配置的ChatClient
* @param dashScopeChatModel starter自动装配的DashScope ChatModel
*/
@Bean
public ChatClient normalBizChatClient(ChatModel dashScopeChatModel) {
return ChatClient.builder(dashScopeChatModel)
// 默认系统提示词:通用业务助手
.defaultSystem("你是业务助手,回答简洁客观")
.build();
}
@Bean
public ChatClient creativeBizChatClient(ChatModel dashScopeChatModel) {
return ChatClient.builder(dashScopeChatModel)
// 默认系统提示词:创意文案助手
.defaultSystem("你是创意文案专家,输出富有想象力,语言生动")
.build();
}
}
Controller 中使用,配合 Lombok @RequiredArgsConstructor
使用 @Qualifier 指定需要注入哪一个 ChatClient Bean:
java
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class MultiChatController {
@Qualifier("normalBizChatClient")
private final ChatClient normalBizChatClient;
@Qualifier("creativeBizChatClient")
private final ChatClient creativeBizChatClient;
@GetMapping("/chat/normal")
public String normalChat(@RequestParam String prompt) {
return normalBizChatClient.prompt(prompt).call().content();
}
@GetMapping("/chat/creative")
public String creativeChat(@RequestParam String prompt) {
return creativeBizChatClient.prompt(prompt).call().content();
}
}
关键点:底层共用同一个
ChatModel(网络连接池、鉴权信息复用),仅仅是 ChatClient 层面的系统 Prompt、参数不一样。适合同一个模型多种业务角色。
请求结果:


3、场景 2:多种不同 Model 类型,多模型共存(DashScope / OpenAI 同时存在)
场景:项目中同时接入通义千问 DashScope、OpenAI,两套完全独立的 ChatModel,分别创建各自的
ChatClientBean。
在 pom 中添加 OpenAI 的依赖
xml
<!-- 这里是dependencyManagement Spring AI的BOM -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 此处为OpenAI的依赖文件 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
添加 MultiModelConfig 配置
java
import org.springframework.ai.chat.client.ChatClient;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.openai.chat.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MultiModelConfig {
/**
* DashScope(通义千问)ChatClient
*/
@Bean("dashScopeChatClient")
public ChatClient dashScopeChatClient(DashScopeChatModel dashScopeChatModel) {
return ChatClient.create(dashScopeChatModel);
}
/**
* OpenAI ChatClient
*/
@Bean("openAiChatClient")
public ChatClient openAiChatClient(OpenAiChatModel openAiChatModel) {
return ChatClient.create(openAiChatModel);
}
}
@Qualifier 注入指定 Bean
@RestController
@RequiredArgsConstructor
public class MultiModelController {
@Qualifier("dashScopeChatClient")
private final ChatClient dashScopeChatClient;
@Qualifier("openAiChatClient")
private final ChatClient openAiChatClient;
@GetMapping("/chat/dashscope")
public String dashScopeChat(@RequestParam String prompt) {
return dashScopeChatClient.prompt(prompt).call().content();
}
@GetMapping("/chat/openai")
public String openAiChat(@RequestParam String prompt) {
return openAiChatClient.prompt(prompt).call().content();
}
}
1.3ChatClient 响应
ChatClient 提供了 4 种响应处理模式:
- 返回完整
ChatResponse对象(携带 Token、生成元数据) entity()直接映射为 Java 实体 / Record(结构化输出)- 开启模型原生结构化输出能力
stream()流式响应,返回Flux,实现前端打字机效果
1.3.1返回完整 ChatResponse(获取 Token 消耗、元信息)
.content() 只拿到 AI 回答文本,丢弃全部计费与生成元数据。
调用 .chatResponse() 返回 ChatResponse,里面包含:Token 消耗、多组生成结果 Generations、模型返回原始元信息。
重要:大模型服务商按 Token 计费,统计 Token 消耗、做计费日志、监控都依赖这个对象。
java
@GetMapping("/test1")
public String test1(){
ChatResponse chatResponse = chatClient.prompt()
.user("讲一个笑话")
.call()
.chatResponse();
// 获取AI回答文本
assert chatResponse != null;
String content = chatResponse.getResult().getOutput().getText();
// Token 使用统计
var usage = chatResponse.getMetadata().getUsage();
//提示词Token
int promptTokens = usage.getPromptTokens();
//生成Token
int completionTokens = usage.getCompletionTokens();
// 总Token
int totalTokens = usage.getTotalTokens();
System.out.println("提示词Token: " + promptTokens);
System.out.println("生成Token: " + completionTokens);
System.out.println("总Token: " + totalTokens);
return content;
}
ChatResponse 内部结构:
Generations:集合,一次请求模型可以返回多条候选结果;Metadata:模型返回元数据,Token 消耗、请求 ID 等信息。
业务场景:记录日志、统计调用成本、监控模型接口返回情况。
1.3.2 entity() 将 AI 输出直接映射为 Java 实体对象
日常开发,我们不想自己手写 JSON 解析,可以直接使用 .entity(),Spring AI 自动把大模型输出的 JSON 字符串反序列化为 Java 对象。
1.3.2.1简单对象,使用 Record(Java 16+ 推荐)
定义 Record 数据载体:
java
// 演员与参演电影列表
record ActorFilms(String actor, List<String> movies) {}
调用 entity(Class<T>) 自动转换:
java
record ActorFilms(String actor, List<String> movies) {}
ActorFilms actorFilms = chatClient.prompt()
.user("随机生成一位中国演员的电影作品列表")
.call()
.entity(ActorFilms.class);
System.out.println(actorFilms.actor());
System.out.println(actorFilms.movies());
运行结果

1.3.2.2 泛型集合场景:ParameterizedTypeReference
当需要返回集合对象 (List<ActorFilms>),直接传 List.class 会泛型擦除,需要使用 ParameterizedTypeReference 保留泛型信息。
java
record ActorFilms(String actor, List<String> movies) {}
List<ActorFilms> actorFilmsList = chatClient.prompt()
.user("生成 张译 和 张学友 的电影作品")
.call()
.entity(new ParameterizedTypeReference<List<ActorFilms>>() {});
actorFilmsList.forEach(System.out::println);
结果:
⚠️注意:
- 需要大模型输出合法 JSON;Spring AI 底层会给模型追加格式提示;
- 如果模型输出 Markdown 代码块
json ...,框架会自动剥离标记提取 JSON。
1.3.3 开启模型原生结构化输出 Native Structured Output
现在主流大模型支持原生结构化输出:模型不再输出自由文本,强制输出 JSON,减少 JSON 解析失败、格式错乱问题。
Spring‑AI 通过 AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT 开启该能力。
单次调用开启
java
ActorFilms actorFilms = chatClient.prompt()
// 开启本次请求原生结构化输出
.advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
.user("随机生成一位演员的电影作品列表")
.call()
.entity(ActorFilms.class);
全局默认开启(ChatClient.Builder)
构建 ChatClient 时全局设置,所有 prompt 默认启用原生结构化输出:
@Bean
public ChatClient chatClient(ChatClient.Builder builder){
return builder
.defaultAdvisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
.build();
}
注意兼容坑:
部分模型(例如 OpenAI)原生结构化输出不支持顶层数组 ,只能返回 JSON 对象。
这种场景不要开启原生模式,回退 Spring AI 默认的提示词驱动结构化转换。
1.3.4 stream () 流式响应(SSE 打字机效果)
.call() 是同步阻塞,等待模型全部生成完毕再返回。
.stream() 返回 Flux,模型每生成一小块 token 就推送出来,适合前端打字机实时展示。
1.3.4.1 流式获取文本 Flux
Flux<String> contentFlux = chatClient.prompt()
.user("讲一个小故事")
.stream()
.content();
Controller SSE 接口示例(配合 WebFlux)
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String prompt){
return chatClient.prompt()
.user(prompt)
.stream()
.content();
}
1.3.4.2 流式返回完整 Flux<ChatResponse>
Flux<ChatResponse> fluxChatResponse = chatClient.prompt()
.user("讲一个小故事")
.stream()
.chatResponse();
1.3.4.3 流式输出如何转 Java 实体对象
当前版本 Spring‑AI stream() 没有直接提供
.entity()。解决方案:先把流全部收集为完整字符串,再使用
BeanOutputConverter做结构化转换。
java
record ActorFilms(String actor, List<String> movies) {}
// 定义转换器,指定目标泛型类型
BeanOutputConverter<List<ActorFilms>> converter =
new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorFilms>>() {});
Flux<String> flux = chatClient.prompt()
.user(u -> u.text("""
随机生成2位中国演员的电影作品。
{format}
""")
// 将格式描述模板变量填充到prompt
.param("format", converter.getFormat()))
.stream()
.content();
// 将流式分片全部收集,拼接完整字符串
String fullContent = flux.collectList()
.block()
.stream()
.collect(Collectors.joining());
// 将完整字符串转换为实体集合
List<ActorFilms> resultList = converter.convert(fullContent);
resultList.forEach(System.out::println);
运行结果

关键点:需要把转换器的格式提示
converter.getFormat()填充进用户 prompt,告诉 AI 输出什么 JSON 结构。
1.4 Prompt 模板
ChatClient 支持 Prompt 模板语法,可以在提示词中定义占位符,运行时动态填充变量,不用手动字符串拼接。底层依靠 PromptTemplate + TemplateRenderer 完成变量渲染,默认使用 StringTemplate 引擎,占位符语法 {变量名}。
1.4.1 基础模板用法
通过 user(u -> u.text("模板文本").param("key", 值)) 设置模板与参数。
java
// 模板:查询某作曲家配乐的5部电影
String answer = chatClient.prompt()
.user(u -> u
.text("列出{composer}配乐的5部电影名称")
.param("composer", "久石让"))
.call()
.content();
渲染后传给大模型的实际提示词:
列出久石让配乐的5部电影名称
底层默认实现:
StTemplateRenderer(StringTemplate)。如果不需要模板解析,可以切换为
NoOpTemplateRenderer直接使用原始文本。
1.4.2 修改模板占位符分隔符
当 Prompt 里面包含 JSON 内容时,{} 和 JSON 符号冲突,会造成模板解析异常。
可以自定义开始、结束标记,例如改为 <变量>。
java
String answer = chatClient.prompt()
.user(u -> u
.text("列出<composer>配乐的5部电影名称")
.param("composer", "久石让"))
// 修改模板分隔符为 < >
.templateRenderer(StTemplateRenderer.builder()
.startDelimiterToken('<')
.endDelimiterToken('>')
.build())
.call()
.content();
重要注意点
.templateRenderer()设置只对当前这条 prompt 生效;- ChatClient 构建时配置的
templateRenderer,只对链式调用内的 user /system 文本生效; - 不会影响 RAG 的
QuestionAnswerAdvisor内部模板,Advisor 有自己独立的模板自定义配置; - 支持实现
TemplateRenderer接口,接入自研或其它模板引擎。
1.4.3 全局设置模板渲染器示例
构建 ChatClient 时全局指定,所有 prompt 默认使用该渲染器:
java
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.templateRenderer(StTemplateRenderer.builder()
.startDelimiterToken('<')
.endDelimiterToken('>')
.build())
.build();
}
1.5 Advisor拦截器
1.5.1 什么是 Advisor
Advisor 是 Spring‑AI 中强大的拦截增强组件,可以拦截、修改、加工 AI 请求与响应 。
常见增强场景:
- 追加对话历史记忆
- RAG 检索,把知识库上下文注入 Prompt
- 请求 / 响应日志打印调试
- 参数处理、输出过滤、安全校验
核心:Advisor 是按链顺序执行,执行顺序非常关键,前一个 Advisor 的输出,作为后一个 Advisor 的输入。
1.5.2 AdvisorSpec 核心 API
在prompt()之后通过advisors()添加拦截器,接口定义:
interface AdvisorSpec {
// 设置单个参数
AdvisorSpec param(String k, Object v);
// 批量参数
AdvisorSpec params(Map<String, Object> p);
// 添加一个/多个Advisor
AdvisorSpec advisors(Advisor... advisors);
AdvisorSpec advisors(List<Advisor> advisors);
}
1.5.3 示例:聊天记忆 + RAG 检索组合
MessageChatMemoryAdvisor:把对话历史追加到 prompt,实现多轮记忆。
QuestionAnswerAdvisor:RAG,从向量库检索相关文档作为上下文。
⚠️顺序:先加载聊天历史,再执行知识库检索。检索会结合历史理解用户问题。
ChatResponse response = chatClient.prompt()
// 先加载对话记忆,再做RAG检索
.advisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.user("介绍一下Spring‑AI的Advisor")
.call()
.chatResponse();
1.5.4 调试日志:SimpleLoggerAdvisor
内置日志 Advisor,可以打印完整请求和响应内容,开发调试非常方便。
建议放在 Advisor 链最后执行。
基础使用
ChatResponse response = chatClient.prompt()
.advisors(new SimpleLoggerAdvisor())
.user("讲个笑话")
.call()
.chatResponse();
yaml 开启 debug 日志:
logging:
level:
org.springframework.ai.chat.client.advisor: DEBUG
自定义日志输出格式
可以自定义打印哪些字段,避免打印敏感业务数据:
SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> "【自定义请求】用户提问:" + request.prompt().getUserMessage(),
response -> "【自定义响应】AI回答:" + response.getResult(),
0
);
chatClient.prompt()
.advisors(customLogger)
.user("你好")
.call()
.content();
生产环境注意:不要打印密钥、用户隐私等敏感信息。
1.5.5ChatMemory 对话记忆
大模型接口本身是无状态的,多轮对话需要手动携带历史消息。
ChatMemory 负责存储会话消息,内置实现:MessageWindowChatMemory。
MessageWindowChatMemory:窗口式记忆,默认最多保存 20 条消息;超过数量自动淘汰旧消息,系统消息会保留。- 存储仓库:
-
InMemoryChatMemoryRepository:内存存储,重启丢失,测试用 -
JdbcChatMemoryRepository:数据库持久化 -
还支持 Cassandra、Neo4j 等存储
// 创建窗口聊天记忆
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(15) // 最大保存15条消息
.build();// 注入Advisor使用
MessageChatMemoryAdvisor memoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory).build();
-
重要踩坑说明
-
Advisor 顺序决定执行逻辑,顺序错会造成 RAG 检索结果不准、记忆失效。
-
Spring Boot3.4 存在 bug,使用图像模型等能力务必配置:
spring:
http:
client:
factory: jdk -
流式
stream()依赖 webflux 响应式栈;普通同步调用依赖 servlet web 栈。混合使用时两个 starter 都要引入。 -
Tool 调用为阻塞命令式,会造成部分 Micrometer 链路观测不完整。
-
内置 Advisor:普通同步调用内部阻塞;流式调用内部非阻塞;Scheduler 调度器可以通过 Builder 自定义。
2. Chat Models
Chat Models 对比
此表格对比了 Spring AI 支持的各种 Chat Models,详细说明了它们的功能:
- Multimodality: 模型可以处理的输入类型(例如,text、image、audio、video)。
- Tools/Function Calling: 模型是否支持 function calling 或 tool use。
- Streaming: 模型是否提供 streaming 响应。
- Retry: 是否支持 retry 机制。
- Observability: 用于监控和调试的功能。
- Built-in JSON: 原生支持 JSON 输出。
- Local deployment: 模型是否可以在本地运行。
- OpenAI API Compatibility: 模型是否与 OpenAI 的 API 兼容。
| Provider | Multimodality | Tools/Functions | Streaming | Retry | Observability | Built-in JSON | Local | OpenAI API Compatible |
|---|---|---|---|---|---|---|---|---|
| DashScope | text, pdf, image | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
| Qwen | text, pdf, image | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | x |
| Anthropic Claude | text, pdf, image | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
| Azure OpenAI | text, image | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| DeepSeek (OpenAI-proxy) | text | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Google GenAI | text, pdf, image, audio, video | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ |
| Google VertexAI Gemini | text, pdf, image, audio, video | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Groq (OpenAI-proxy) | text, image | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
| HuggingFace | text | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Mistral AI | text, image, audio | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| MiniMax | text | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
| Moonshot AI | text | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | |
| NVIDIA (OpenAI-proxy) | text, image | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
| OCI GenAI/Cohere | text | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
| Ollama | text, image | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| OpenAI SDK (Official) | In: text, image, audio Out: text, audio | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| OpenAI | In: text, image, audio Out: text, audio | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Perplexity (OpenAI-proxy) | text | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
| QianFan | text | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
| ZhiPu AI | text, image, docs | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
| Amazon Bedrock Converse | text, image, video, docs (pdf, html, md, docx ...) | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
2.1 DashScope
阿里云 DashScope,提供通义系列大模型 API(文本、多模态图文音视频),支持调用、微调、模型定制。
使用前需要前往 DashScope 控制台 生成 api‑key。
2.1.1 Maven 依赖
引入 starter 实现自动装配,同时项目引入 Spring‑AI‑Alibaba BOM 做版本管理。
xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring‑ai‑alibaba‑starter‑dashscope</artifactId>
</dependency>
2.1.2 基础配置 application.yml
yml
spring:
ai:
# 全局dashscope配置
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 从环境变量读取,不要硬编码密钥
base-url: https://dashscope.aliyuncs.com
# 可选工作空间id
# work-space-id: xxx
chat:
options:
model: qwen‑plus # 默认模型 qwen‑plus / qwen‑turbo / qwen‑max / qwen3
temperature: 0.7 # 随机性 0~2,越高越发散
top-p: 0.8
repetition‑penalty: 1.1
enable‑search: false # 是否开启联网搜索
# enable‑thinking: true # Qwen3开启思考过程
# 开启dashscope chat模型,关闭设置为 none
model:
chat: dashscope
优先级:
spring.ai.dashscope.chat.*会覆盖全局spring.ai.dashscope.*,适合多账号、多模型场景。
2.1.3 简单 Controller 示例
注入标准ChatModel即可,兼容 Spring AI 标准 API。
java
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;
@RestController
public class DashScopeChatController {
private final ChatModel chatModel;
public DashScopeChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
/** 同步调用 */
@GetMapping("/ai/generate")
public Map<String,String> generate(@RequestParam(defaultValue = "讲个小故事") String message) {
String res = chatModel.call(new Prompt(new UserMessage(message))).getResult().getOutput().getContent();
return Map.of("answer", res);
}
/** 流式SSE返回 */
@GetMapping(value = "/ai/generateStream", produces = org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam(defaultValue = "讲个小故事") String message) {
return chatModel.stream(new Prompt(new UserMessage(message)))
.map(resp -> resp.getResult().getOutput().getContent());
}
}
2.1.4 运行时覆盖参数 DashScopeChatOptions
单次请求动态修改模型、temperature 等参数,不修改全局 yml 配置。
java
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
// 单次请求强制使用 qwen‑max,温度0.4
DashScopeChatOptions options = DashScopeChatOptions.builder()
.model("qwen‑max")
.temperature(0.4)
.topP(0.7)
.maxTokens(1000)
.build();
ChatResponse response = chatModel.call(new Prompt("介绍Spring AI Alibaba", options));
2.1.5 多模态调用(图文音视频)
2.1.5.1 图片理解 qwen‑vl‑plus /qwen‑vl‑max
支持本地资源、网络图片 URL。
java
UserMessage userMsg = new UserMessage(
"描述这张图片里面有什么",
List.of(new Media(MimeTypeUtils.IMAGE_PNG, URI.create("https://xxx/test.png")))
);
Prompt prompt = new Prompt(userMsg, DashScopeChatOptions.builder().model("qwen‑vl‑plus").build());
ChatResponse resp = chatModel.call(prompt);
2.1.5.2 音频、视频
音频 / 视频需要在消息 metadata 指定消息格式:
java
UserMessage userMsg = new UserMessage(
"分析这段音频内容",
List.of(new Media(MimeTypeUtils.parseMimeType("audio/mp3"), audioResource))
);
userMsg.getMetadata().put(DashScopeApiConstants.MESSAGE_FORMAT, MessageFormat.AUDIO);
视频同理,MessageFormat.VIDEO。
2.1.6 Qwen3 推理模型,获取思考过程
开启enableThinking(true),推理内容存放在AssistantMessage的 metadata 的reasoningContent。
java
DashScopeChatOptions options = DashScopeChatOptions.builder()
.model("qwen3")
.enableThinking(true)
.thinkingBudget(1000)
.build();
ChatResponse resp = chatModel.call(new Prompt("9.11 和 9.8哪个数字更大", options));
AssistantMessage assistantMsg = resp.getResult().getOutput();
// 获取模型内部思考过程
String reasoning = assistantMsg.getMetadata().get("reasoningContent");
// 获取最终回答
String answer = assistantMsg.getContent();
流式场景下,推理片段会分块返回,需要业务代码手动拼接。
2.1.7 手动创建 DashScopeChatModel(非自动装配)
适合动态密钥、多账号、自定义密钥管理场景。
java
DashScopeApi dashScopeApi = DashScopeApi.builder()
.apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
.build();
DashScopeChatOptions chatOptions = DashScopeChatOptions.builder()
.model("qwen‑plus")
.temperature(0.5)
.build();
DashScopeChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.defaultOptions(chatOptions)
.build();
2.1.8 自定义 ApiKey(密钥动态获取、密钥轮换)
实现ApiKey接口,可以从配置中心、密钥管理服务拉取密钥。
java
ApiKey customApiKey = () -> {
// 自定义逻辑,从密钥服务读取
return "sk‑xxx";
};
DashScopeApi api = DashScopeApi.builder().apiKey(customApiKey).build();
2.2 DeepSeek
DeepSeek 提供 deepseek‑chat 通用对话、deepseek‑reasoner 推理模型。
需要前往 DeepSeek 开放平台 获取 api‑key。
2.2.1Maven 依赖(自动装配 Starter)
需要项目引入 Spring AI BOM 管理版本。
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring‑ai‑starter‑model‑deepseek</artifactId>
</dependency>
2.2.2 application.yml 配置
yml
spring:
ai:
deepseek:
api-key: ${DEEPSEEK_API_KEY} # 从环境变量读取密钥,禁止硬编码
base-url: https://api.deepseek.com
chat:
enabled: true
options:
model: deepseek‑chat # 可选 deepseek‑chat / deepseek‑reasoner
temperature: 0.7
topP: 1.0
maxTokens: 2000
# 全局重试配置
ai:
retry:
max‑attempts: 3
backoff:
initial‑interval: 2s
multiplier: 2
max‑interval: 1min
优先级:
spring.ai.deepseek.chat.*> 全局spring.ai.deepseek.*,支持不同会话使用不同账号密钥。
2.2.3 基础 Controller 示例
注入 DeepSeekChatModel,支持同步、流式返回。
java
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;
@RestController
public class DeepSeekChatController {
private final DeepSeekChatModel chatModel;
public DeepSeekChatController(DeepSeekChatModel chatModel) {
this.chatModel = chatModel;
}
/** 同步调用 */
@GetMapping("/ai/deepseek/generate")
public Map<String,String> generate(@RequestParam(defaultValue = "讲一个小故事") String message) {
ChatResponse resp = chatModel.call(new Prompt(new UserMessage(message)));
return Map.of("answer", resp.getResult().getOutput().getText());
}
/** SSE流式输出 */
@GetMapping(value = "/ai/deepseek/stream", produces = org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam(defaultValue = "讲一个小故事") String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return chatModel.stream(prompt)
.map(r -> r.getResult().getOutput().getText());
}
}
2.2.4 运行时动态参数覆盖 DeepSeekChatOptions
单次请求覆盖配置,不修改 yml 全局配置。
java
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
.withModel(DeepSeekApi.ChatModel.DEEPSEEK_CHAT.getValue())
.withTemperature(0.4f)
.withMaxTokens(1500)
.build();
ChatResponse response = chatModel.call(new Prompt("写快速排序算法", options));
2.2.5 deepseek‑reasoner 推理模型,获取思维链 CoT
推理内容存放在 DeepSeekAssistantMessage 对象中 getReasoningContent()。
java
import org.springframework.ai.deepseek.message.DeepSeekAssistantMessage;
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
.withModel(DeepSeekApi.ChatModel.DEEPSEEK_REASONER.getValue())
.build();
Prompt prompt = new Prompt("9.11 和 9.8哪个数字更大?", options);
ChatResponse resp = chatModel.call(prompt);
DeepSeekAssistantMessage assistantMsg = (DeepSeekAssistantMessage) resp.getResult().getOutput();
// 模型思考过程
String reasoningCoT = assistantMsg.getReasoningContent();
// 最终输出答案
String answer = assistantMsg.getText();
⚠️多轮对话重要坑点:
输入消息不能携带 reasoning_content ,只把最终
text加入消息上下文,否则 API 返回 400。
// 多轮追加消息,只存入answer,不要存入CoT思考内容
messages.add(AssistantMessage.builder().content(answer).build());
2.2.6 Prefix Completion 前缀补全(代码续写)
可以指定 assistant 输出前缀,强制模型按指定格式输出,例如直接输出 python 代码。
java
import org.springframework.ai.deepseek.message.DeepSeekAssistantMessage;
UserMessage userMsg = new UserMessage("写快速排序");
// 指定助手输出前缀
DeepSeekAssistantMessage prefixMsg = DeepSeekAssistantMessage.prefixAssistantMessage("```python\n");
Prompt prompt = new Prompt(List.of(userMsg, prefixMsg),
DeepSeekChatOptions.builder().stopSequences(List.of("```")).build());
ChatResponse resp = chatModel.call(prompt);
String code = resp.getResult().getOutput().getText();
2.2.7 手动构建 DeepSeekChatModel(关闭自动装配)
适合动态密钥、多实例场景
java
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
DeepSeekApi deepSeekApi = DeepSeekApi.builder()
.apiKey(System.getenv("DEEPSEEK_API_KEY"))
.build();
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
.withModel(DeepSeekApi.ChatModel.DEEPSEEK_CHAT.getValue())
.withTemperature(0.5f)
.build();
DeepSeekChatModel chatModel = DeepSeekChatModel.builder()
.deepSeekApi(deepSeekApi)
.defaultOptions(options)
.build();
2.3 OpenAI
适配官方 OpenAI,同时兼容 vLLM、Ollama、DeepSeek‑R1 等 OpenAI‑Compatible 兼容接口
前往 OpenAI 平台 获取 api‑key。
注意:o1/o3/o4‑mini 推理模型不会返回文本形式思考过程 ,仅返回
reasoning_tokens计数;DeepSeek‑R1、vLLM 推理解析器才会返回reasoningContent元数据。
2.3.1 Maven 依赖(自动装配 Starter)
项目需要引入 Spring‑AI BOM 做版本管控
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring‑ai‑starter‑model‑openai</artifactId>
</dependency>
2.3.2 application.yml 配置
yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY} # 环境变量读取密钥,禁止硬编码
base-url: https://api.openai.com
# organization-id: xxx
# project-id: xxx
chat:
options:
model: gpt‑4o‑mini
temperature: 0.7
topP: 1.0
# 普通对话模型用 maxTokens
# maxTokens: 2000
# o1/o3推理模型必须使用 maxCompletionTokens,与maxTokens互斥
# maxCompletionTokens: 4000
优先级:
spring.ai.openai.chat.*> 全局spring.ai.openai.*,支持不同会话不同密钥 / 端点。
spring.ai.model.chat=openai/none控制 chat 模型开关。
2.3.3基础 Controller 示例
注入OpenAiChatModel,同步 + SSE 流式输出
java
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
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;
import reactor.core.publisher.Flux;
import java.util.Map;
@RestController
public class OpenAiChatController {
private final OpenAiChatModel chatModel;
public OpenAiChatController(OpenAiChatModel chatModel) {
this.chatModel = chatModel;
}
/** 同步调用 */
@GetMapping("/ai/openai/generate")
public Map<String,String> generate(@RequestParam(defaultValue = "讲个小故事") String message) {
ChatResponse resp = chatModel.call(new Prompt(new UserMessage(message)));
return Map.of("answer", resp.getResult().getOutput().getText());
}
/** SSE流式 */
@GetMapping(value = "/ai/openai/stream", produces = org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam(defaultValue = "讲个小故事") String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return chatModel.stream(prompt)
.map(r -> r.getResult().getOutput().getText());
}
}
2.3.4 运行时动态参数 OpenAiChatOptions
单次请求覆盖配置,maxTokens 与 maxCompletionTokens 互斥
java
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
// 普通GPT‑4o
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_O.getValue())
.temperature(0.4)
.maxTokens(1500)
.build();
// o1推理模型,使用 maxCompletionTokens
OpenAiChatOptions o1Options = OpenAiChatOptions.builder()
.model("o1‑preview")
.maxCompletionTokens(2000)
.build();
ChatResponse response = chatModel.call(new Prompt("简单解释量子计算", options));
2.3.5 结构化输出 Structured‑Outputs
支持 JSON_OBJECT 和 JSON_SCHEMA(强约束),可以结合BeanOutputConverter从 Java Record 自动生成 schema。
java
String jsonSchema = """
{
"type":"object",
"properties":{
"final_answer":{"type":"string"}
},
"required":["final_answer"],
"additionalProperties":false
}
""";
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_O_MINI.getValue())
.responseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, jsonSchema))
.build();
Prompt prompt = new Prompt("解方程式 8x+7=-23", options);
ChatResponse resp = chatModel.call(prompt);
2.3.6多模态(图文、音频)
2.3.6.1 图片理解 gpt‑4o
支持网络 URL、本地资源
UserMessage userMsg = new UserMessage("描述这张图片",
List.of(new Media(MimeTypeUtils.IMAGE_PNG, URI.create("https://xxx/demo.png"))));
Prompt prompt = new Prompt(userMsg,
OpenAiChatOptions.builder().model(OpenAiApi.ChatModel.GPT_4_O.getValue()).build());
ChatResponse resp = chatModel.call(prompt);
2.3.6.2音频输入输出 gpt‑4o‑audio‑preview
// 音频输入
UserMessage audioMsg = new UserMessage("总结这段录音",
List.of(new Media(MimeTypeUtils.parseMimeType("audio/mp3"), audioResource)));
// 模型输出音频
OpenAiChatOptions audioOptions = OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_O_AUDIO_PREVIEW.getValue())
.outputModalities(List.of("text","audio"))
.outputAudio(new AudioParameters(Voice.ALLOY, AudioResponseFormat.WAV))
.build();
2.3.7 OpenAI 兼容服务:extraBody(vLLM / Ollama / DeepSeek)
extraBody 向兼容服务传递扩展参数(top_k、repetition_penalty、min_p 等,官方 OpenAI 会忽略)
java
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("llama3.2")
.extraBody(Map.of(
"top_k",40,
"repeat_penalty",1.1
))
.build();
yml 配置方式:
yml
spring:
ai:
openai:
base-url: http://127.0.0.1:8000/v1
chat:
options:
model: meta‑llama/Llama‑3‑8B‑Instruct
extra-body:
top_k: 50
repetition_penalty: 1.1
2.3.8 推理内容 reasoningContent 说明
⚠️重要区分
-
官方 OpenAI o1/o3 系列 :Chat Completions 接口拿不到文本思考过程 ,只统计
reasoning_tokens;要用 Responses API(Spring‑AI 暂未封装)。 -
DeepSeek‑R1、vLLM 开启推理解析器 :思考文本放入
AssistantMessage.getMetadata().get("reasoningContent");多轮对话不要把 reasoningContent 塞回 messages,只把最终 content 入上下文。ChatResponse resp = chatModel.call(prompt);
AssistantMessage assistantMsg = resp.getResult().getOutput();
String reasoning = assistantMsg.getMetadata().get("reasoningContent");
String answer = assistantMsg.getContent();
流式场景需要手动拼接推理片段:
Flux<ChatResponse> flux = chatModel.stream(prompt);
StringBuilder reasoningSb = new StringBuilder();
StringBuilder answerSb = new StringBuilder();
flux.subscribe(chunk->{
AssistantMessage msg = chunk.getResult().getOutput();
String r = msg.getMetadata().get("reasoningContent");
if(r!=null) reasoningSb.append(r);
if(msg.getContent()!=null) answerSb.append(msg.getContent());
});
2.3.9 手动构建 OpenAiChatModel(关闭自动装配,动态密钥)
支持自定义ApiKey实现,对接配置中心、密钥轮换
OpenAiApi openAiApi = OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_O_MINI.getValue())
.temperature(0.5f)
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(openAiApi)
.defaultOptions(options)
.build();
自定义密钥:
ApiKey customApiKey = ()->{
// 从配置中心/密钥管理服务读取
return "sk‑xxx";
};
OpenAiApi api = OpenAiApi.builder().apiKey(customApiKey).build();
2.4 OpenAi 兼容模型
核心原理:只要服务端实现 OpenAI Chat Completions v1 协议 ,就复用
spring‑ai‑starter‑model‑openai,替换base‑url / api‑key / model即可,DashScope 百炼兼容模式、DeepSeek、vLLM、Ollama、OneAPI 都属于这一类。
2.4.1 Maven 依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
注意:Spring‑AI BOM 需要统一管理版本,不要手动写 version;
Spring‑AI‑Alibaba 内部也是复用原生 openai starter 做兼容模式接入。
2.4.2 application.yml 通用模板
yml
spring:
application:
name: spring-ai-alibaba-openai-compatible-demo
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: ${OPENAI_BASE_URL}
chat:
options:
model: ${MODEL_NAME}
temperature: 0.7
max-tokens: 2000
2.4.3 DashScope(阿里百炼 兼容 OpenAI 模式)
环境变量
export OPENAI_API_KEY=sk-xxx从百炼控制台获取
export OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
export MODEL_NAME=qwen-max
⚠️重要:dashscope 兼容模式后缀必须带
/v1,很多人漏写导致 404。
yml 片段
yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
chat:
options:
model: qwen-max
2.4.4 DeepSeek
环境变量
export OPENAI_API_KEY=sk‑xxx
export OPENAI_BASE_URL=https://api.deepseek.com/v1
export MODEL_NAME=deepseek-chat
yml 片段
yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.deepseek.com/v1
chat:
options:
model: deepseek-chat
2.4.5 vLLM / Ollama(本地部署兼容服务)
spring:
ai:
openai:
api-key: dummy # 本地vllm/ollama不需要密钥,随便填占位
base-url: http://127.0.0.1:8000/v1
chat:
options:
model: deepseek‑ai/DeepSeek‑R1‑Distill‑Qwen‑7B
extra-body: # vLLM扩展参数放在extra‑body
repetition_penalty: 1.1
top_k: 50
2.4.6 代码使用
兼容模式下直接注入标准
ChatModel,不需要引入各厂商 SDK,一套代码切换多家模型。
java
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/chat")
public class CompatibleChatController {
private final ChatModel chatModel;
// 自动注入OpenAiChatModel,向上转型为ChatModel
public CompatibleChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/simple")
public String simpleChat(String prompt) {
return chatModel.call(new Prompt(prompt))
.getResult()
.getOutput()
.getContent();
}
}
流式 SSE 示例
java
@GetMapping(value = "/stream", produces = "text/event-stream")
public Flux<String> streamChat(String prompt) {
return chatModel.stream(new Prompt(prompt))
.map(resp -> resp.getResult().getOutput().getText());
}
- DashScope 兼容模式限制
部分通义特有能力(如图片理解、特定参数),兼容模式不一定全部支持;高级能力优先使用spring‑ai‑alibaba‑dashscope原生 starter。
2.4.7 和 Spring‑AI‑Alibaba DashScope 原生 Starter 区别
表格
| 方式 | 优点 | 缺点 |
|---|---|---|
| OpenAI 兼容模式 | 一套代码跑所有兼容服务;切换模型只改配置 | 厂商部分特有参数 / 能力不支持;少部分字段映射丢失 |
| DashScope 原生 starter | 完整支持通义全部能力、多模态、工具调用、输入输出审核 | 只能跑阿里云百炼,换模型要改代码和依赖 |
简单对话场景优先兼容模式;深度使用通义全部特性用原生 dashscope starter。
3. RAG
Spring‑AI 提供两套 RAG 实现:
- QuestionAnswerAdvisor:简单开箱即用 RAG,快速上手;
- RetrievalAugmentationAdvisor:模块化高级 RAG,可自由组装查询改写、多路检索、重排、压缩等组件。
依赖说明
简单 RAG(QuestionAnswerAdvisor)
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
模块化高级 RAG(RetrievalAugmentationAdvisor)
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-rag</artifactId>
</dependency>
底层依赖
VectorStore向量库(Milvus、PGVector、Chroma 等),向量库完成文档入库之后,Advisor 只负责检索 + 上下文拼接 + 调用大模型。
注入SimpleVectorStore - 一个简单的向量存储实现,仅适用于测试目的。
bash
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel){
return SimpleVectorStore.builder(embeddingModel).build();
}
3.1 QuestionAnswerAdvisor 快速 RAG(简单业务首选)
3.1.1基础用法
java
// vectorStore:已经完成文档写入的向量实例
var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.similarityThreshold(0.8d) // 相似度阈值
.topK(6) // 返回topN文档
.build())
.build();
ChatResponse response = ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(qaAdvisor)
.user("你的用户问题")
.call()
.chatResponse();
3.1.1.1运行时动态元数据过滤
通过 Advisor 参数FILTER_EXPRESSION实现每次请求不同过滤条件,表达式为跨向量库通用类 SQL 语法。
java
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore).build())
.build();
String resp = chatClient.prompt()
.user("你的问题")
// 运行时动态过滤,例如只取元数据type=Spring的文档
.advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
.call()
.content();
3.1.1.2自定义 RAG 提示模板
模板必须包含两个占位符:
<query>:用户原始问题<question_answer_context>:检索出来的参考文档上下文
java
PromptTemplate customTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
.template("""
<query>
参考上下文如下:
---------------------
<question_answer_context>
---------------------
严格依据上面上下文回答问题,不要使用外部知识。
如果上下文中不存在答案,请直接回复"未找到相关信息"。
""")
.build();
QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.promptTemplate(customTemplate)
.build();
注意:废弃方法
userTextAdvise()不再使用,统一使用promptTemplate()。
3.1.2 RetrievalAugmentationAdvisor 模块化高级 RAG
遵循 Modular‑RAG 论文架构,分为五大模块:
Pre‑Retrieval(查询预处理) → Retrieval(文档检索) → Post‑Retrieval(文档后处理/重排) → Join文档合并 → Generation生成
3.1.2.1 Naive RAG(朴素 RAG)
java
Advisor naiveRagAdvisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.50)
.build())
// allowEmptyContext=true:检索不到文档,也允许大模型回答;false则禁止回答
.queryAugmenter(ContextualQueryAugmenter.builder()
.allowEmptyContext(true)
.build())
.build();
String answer = chatClient.prompt()
.advisors(naiveRagAdvisor)
.user("用户问题")
.call()
.content();
动态过滤(请求级别)
String answer = chatClient.prompt()
.advisors(naiveRagAdvisor)
.advisors(a -> a.param(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "type == 'Spring'"))
.user("用户问题")
.call()
.content();
3.1.2.2 Advanced RAG:增加查询改写
RewriteQueryTransformer:让大模型重写用户原始 query,优化检索效果,建议温度设置为 0,保证确定性。
java
Advisor advancedRagAdvisor = RetrievalAugmentationAdvisor.builder()
.queryTransformers(RewriteQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder.mutate().defaultOptions(ChatOptions.builder().temperature(0.0).build()))
.build())
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.50)
.build())
.build();
3.2 Pre‑Retrieval 查询预处理模块(QueryTransformer)
作用:在调用向量库检索之前改造用户查询,提升召回质量。
3.2.1. CompressionQueryTransformer 对话压缩
适合多轮会话,把历史对话 + 当前问题压缩成独立查询,消除上下文依赖。
java
// 原始query带对话历史
Query query = Query.builder()
.text("它的第二大城市是什么?")
.history(
new UserMessage("丹麦首都是哪里?"),
new AssistantMessage("哥本哈根是丹麦首都。")
).build();
QueryTransformer transformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder)
.build();
Query newQuery = transformer.transform(query);
3.2.2 RewriteQueryTransformer 查询改写
模糊、冗长问题,让 LLM 生成更适合向量检索的 query。
3.2.3. TranslationQueryTransformer 查询翻译
当 Embedding 模型只支持特定语言,把用户 query 翻译成向量库文档的语言。
java
QueryTransformer transformer = TranslationQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder)
.targetLanguage("english")
.build();
3.2.4. MultiQueryExpander 查询扩展
把 1 个问题扩展为 N 个不同角度的子查询,多路检索,提高召回,可选择是否保留原始 query。
java
MultiQueryExpander expander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3)
.includeOriginal(true)
.build();
List<Query> expandQueries = expander.expand(new Query("如何运行SpringBoot应用"));
3.3 Retrieval 检索模块
VectorStoreDocumentRetriever
支持:相似度阈值、topK、静态 / 动态过滤表达式
java
DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.73)
.topK(5)
// 静态过滤
.filterExpression(new FilterExpressionBuilder().eq("genre", "fairytale").build())
.build();
List<Document> docs = retriever.retrieve(new Query("故事的主角是谁?"));
租户动态过滤(Supplier 形式)
适合多租户场景,从上下文实时获取租户 ID
java
DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.filterExpression(() -> new FilterExpressionBuilder()
.eq("tenant", TenantContextHolder.getTenantIdentifier())
.build())
.build();
请求级别过滤
java
Query query = Query.builder()
.text("Who is Anacletus?")
.context(Map.of(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "location == 'Whispering Woods'"))
.build();
List<Document> docs = retriever.retrieve(query);
ConcatenationDocumentJoiner
多路查询检索得到多组文档,做合并、去重。
3.4 Post‑Retrieval 检索后处理
接口:DocumentPostProcessor
可自定义实现:
- Rerank 重排(Cross‑Encoder)
- 文档内容压缩
- 过滤低质量、冗余文档
3.5 Generation:ContextualQueryAugmenter
负责把检索出来的文档上下文与用户 query 拼接,送入大模型。
allowEmptyContext=false:检索不到文档,禁止模型回答;allowEmptyContext=true:检索不到,允许大模型凭自有知识回答。
✅ 选型建议
表格
| 组件 | 适用场景 |
|---|---|
| QuestionAnswerAdvisor | 内部知识库、简单 RAG,快速开发,绝大多数业务 |
| RetrievalAugmentationAdvisor | 需要查询改写、多查询扩展、重排、多数据源,复杂高级 RAG |