Spring AI Alibaba入门-生态集成

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.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.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.Builder Bean,全部 ChatClient 实例由我们代码手动构建。

2、场景 1:同一个底层 Model,创建多个不同配置的 ChatClient

场景:使用同一个大模型(例如通义千问),但是不同业务使用不同系统提示词、不同温度参数。底层复用同一个ChatModel Bean,构建多个差异化的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,分别创建各自的 ChatClient Bean。

在 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 种响应处理模式:

  1. 返回完整 ChatResponse 对象(携带 Token、生成元数据)
  2. entity() 直接映射为 Java 实体 / Record(结构化输出)
  3. 开启模型原生结构化输出能力
  4. 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();

重要注意点

  1. .templateRenderer() 设置只对当前这条 prompt 生效;
  2. ChatClient 构建时配置的 templateRenderer,只对链式调用内的 user /system 文本生效;
  3. 不会影响 RAG 的 QuestionAnswerAdvisor 内部模板,Advisor 有自己独立的模板自定义配置;
  4. 支持实现 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();

重要踩坑说明

  1. Advisor 顺序决定执行逻辑,顺序错会造成 RAG 检索结果不准、记忆失效。

  2. Spring Boot3.4 存在 bug,使用图像模型等能力务必配置:

    spring:
    http:
    client:
    factory: jdk

  3. 流式stream()依赖 webflux 响应式栈;普通同步调用依赖 servlet web 栈。混合使用时两个 starter 都要引入。

  4. Tool 调用为阻塞命令式,会造成部分 Micrometer 链路观测不完整。

  5. 内置 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 说明

⚠️重要区分

  1. 官方 OpenAI o1/o3 系列 :Chat Completions 接口拿不到文本思考过程 ,只统计reasoning_tokens;要用 Responses API(Spring‑AI 暂未封装)。

  2. 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());
}
  1. DashScope 兼容模式限制
    部分通义特有能力(如图片理解、特定参数),兼容模式不一定全部支持;高级能力优先使用 spring‑ai‑alibaba‑dashscope 原生 starter。
2.4.7 和 Spring‑AI‑Alibaba DashScope 原生 Starter 区别

表格

方式 优点 缺点
OpenAI 兼容模式 一套代码跑所有兼容服务;切换模型只改配置 厂商部分特有参数 / 能力不支持;少部分字段映射丢失
DashScope 原生 starter 完整支持通义全部能力、多模态、工具调用、输入输出审核 只能跑阿里云百炼,换模型要改代码和依赖

简单对话场景优先兼容模式;深度使用通义全部特性用原生 dashscope starter。

3. RAG

Spring‑AI 提供两套 RAG 实现:

  1. QuestionAnswerAdvisor:简单开箱即用 RAG,快速上手;
  2. 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
相关推荐
智感子14 分钟前
测控链路:从传感器到上位机
人工智能·嵌入式硬件·fpga开发
人工智能技术咨询.4 小时前
具身智能中的世界模型训练
人工智能
LaughingZhu4 小时前
Product Hunt 每日热榜 | 2026-10-06
人工智能·深度学习·神经网络·搜索引擎·百度
AOI小白新手上路4 小时前
AOI 缺陷检测复现实操指南:Anomalib + MVTec AD(glass)与 YOLOv8 + NEU-DET 两条路线
人工智能·深度学习·yolo
henrylin99994 小时前
RD-AGENT 第一讲 · AI 因子工厂是怎么运转的
人工智能
高洁014 小时前
具身智能中的世界模型训练
人工智能·python·深度学习·机器学习·transformer
无线通信科研笔记4 小时前
IEEE TVT 2026 论文精读与完整复现|相位误差如何重塑近场 RIS 的幅相响应
论文阅读·人工智能·python·算法·论文笔记
字节渡客4 小时前
Redis键明明过期了,业务还在读到旧数据
数据库·redis·spring
Devlive 开源社区4 小时前
AuthX 正式更名 GrantForge:我们重新做了一遍权限管理系统
大数据·人工智能·架构
朝朝辞暮i5 小时前
VLA 系统学习第 1 课:VLA 到底在干什么?
人工智能·python·计算机视觉·vla