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_OBJECTJSON_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
相关推荐
YOLO数据集集合15 分钟前
尼帕果实目标检测数据集 | 尼帕果实检测 农业视觉 热带作物 目标检测 YOLO格式9013期
人工智能·yolo·目标检测·机器学习·计算机视觉·农业果实
码视野21 分钟前
基于 Spring Boot + Vue3 的【高校化学实验室安全准入考试与危化品配伍排查系统】设计与实现(含PRD/三端高保真源码/大屏)
前端·人工智能·spring boot·后端·安全·vue3
大模型真好玩21 分钟前
大模型训练全流程实战指南实战篇(十四)——网络安全大模型数据获取
人工智能·ollama·deepseek
重生之我是Java开发战士22 分钟前
【Java EE】认识Linux与项目部署
java·linux·java-ee
liliangcsdn23 分钟前
RSI相对强弱指数因子背后逻辑的探索
人工智能
LXMXHJ25 分钟前
springboot项目测试
java·spring boot·后端·测试
weixin_4896900226 分钟前
训练素材数据转变过程
人工智能·python
东莞市云毅网络有限公司28 分钟前
基于GEO的品牌信源矩阵构建:从内容生产到AI采信的全链路设计
人工智能·机器学习·矩阵