spring ai Alibaba(SAA)学习(二)

提示词

什么是提示词?

Prompt 是引导 AI 模型生成特定输出的输入格式,Prompt 的设计和措辞会显著影响模型的响应。

Prompt 最开始只是简单的字符串,随着时间的推移,prompt 逐渐开始包含特定的占位符,例如 AI 模型可以识别的 "USER:"、"SYSTEM:" 等。阿里云通义模型可通过将多个消息字符串分类为不同的角色,然后再由 AI 模型处理,为 prompt 引入了更多结构。每条消息都分配有特定的角色,这些角色对消息进行分类,明确 AI 模型提示的每个部分的上下文和目的。这种结构化方法增强了与 AI 沟通的细微差别和有效性,因为 prompt 的每个部分在交互中都扮演着独特且明确的角色。

提示词样例库

网址:Prompt Library | DeepSeek API Docshttps://api-docs.deepseek.com/zh-cn/prompt-library/

三种传入方式

在 Spring AI Alibaba中,call() 方法是调用大模型的核心入口,其参数中涉及的 Message、String、Prompt 是三种不同层级的输入封装形式,对应不同的使用场景和灵活性。下面从定义含义核心区别应用场景代码示例四个维度详细拆解:

SpringAI Alibaba 中的 call() 方法参数解析

在 Spring AI Alibaba(主要对接阿里云百炼 / 通义千问等大模型)中,call() 方法是与大模型交互的核心入口,而 messageStringprompt 是传入 call() 的三种核心参数形式,其本质是不同抽象层级的请求载体,适配不同的开发场景。下面从「定义含义」「核心区别」「应用场景」「代码示例」四个维度详细拆解:

一、先明确核心概念前提

Spring AI 对大模型交互做了标准化抽象,核心逻辑是:用户输入 → 封装为 Prompt → Prompt 拆分为 Message 列表 → 序列化为模型能识别的格式 → 调用模型 API``String 是最简化的输入,Message 是对话的最小单元,Prompt 是包含上下文 / 参数的完整请求体。

二、三种参数的详细解析

1. String 类型参数
(1)含义

最基础、最简化的输入形式,本质是「纯文本用户指令」,底层会被 Spring AI 自动封装为:SystemMessage(默认空) + UserMessage(传入的 String 内容) 的 Prompt,再调用模型。

(2)核心特点
  • 极简:无需手动封装,一行文本即可调用;
  • 无上下文:无法传递多轮对话历史、系统指令;
  • 无参数配置:无法设置温度、topP 等模型参数;
  • 适配单轮、无复杂需求的简单问答。
(3)应用场景
  • 快速测试:验证模型连通性、简单问答(如 "你好""1+1 等于几");
  • 无上下文的单次查询:如 "查询今天的天气""解释什么是 Spring AI";
  • 原型开发:快速搭建 demo,无需关注对话结构。
(4)代码示例
java 复制代码
@Autowired
private ChatClient chatClient;

// 直接传入 String 调用
public String simpleCall() {
    // 底层自动封装为 UserMessage,再封装为 Prompt
    String response = chatClient.call("解释一下 Spring AI 的核心功能");
    return response;
}
2. Message 类型参数(通常是 List<Message>)
(1)含义

Message 是 Spring AI 定义的「对话最小单元」,代表一次单方向的消息传递(用户→模型 / 模型→用户 / 系统→模型),核心实现类:

MessageType 枚举值 类型 作用 场景
user UserMessage 用户输入的指令 / 问题 核心交互输入
system SystemMessage 系统指令(定义模型角色 / 规则) 约束模型行为(如 "作为 Java 讲师")
assistant AssistantMessage 模型的历史回复 多轮对话上下文
tool ToolMessage 工具调用的返回结果 工具调用场景

传入 Message(或列表)时,底层会封装为包含这些 Message 的 Prompt,支持多轮对话上下文。

(2)核心特点
  • 支持上下文:可拼接多轮对话的 UserMessage + AssistantMessage;
  • 支持系统指令:通过 SystemMessage 定义模型角色;
  • 轻量灵活:比 Prompt 更底层,可手动控制对话序列;
  • 仍无法直接配置模型参数(需结合 PromptTemplate 或 PromptOptions)。
(3)应用场景
  • 多轮对话:如 "先问'什么是 Spring AI',再问'它和 OpenAI SDK 的区别'";
  • 定制模型角色:通过 SystemMessage 约束模型(如 "作为电商客服,语气友好,只回答商品相关问题");
  • 精准控制对话结构:需手动管理每一轮的消息顺序。
(4)代码示例
java 复制代码
@Autowired
private ChatClient chatClient;

public String multiRoundCall() {
    // 1. 系统指令:定义模型角色
    Message systemMessage = new SystemMessage("作为Java技术专家,用简洁的语言回答问题");
    // 2. 第一轮用户问题
    Message userMsg1 = new UserMessage("Spring AI 是什么?");
    // 3. 模型历史回复(模拟多轮)
    Message assistantMsg1 = new AssistantMessage("Spring AI 是Spring生态下的AI开发框架,简化大模型集成");
    // 4. 第二轮用户问题
    Message userMsg2 = new UserMessage("它支持阿里云通义千问吗?");

    // 拼接消息列表(上下文顺序:系统→第一轮用户→第一轮助手→第二轮用户)
    List<Message> messages = Arrays.asList(systemMessage, userMsg1, assistantMsg1, userMsg2);
    
    // 传入 Message 列表调用
    ChatResponse response = chatClient.call(messages);
    return response.getResult().getOutput().getContent();
}
3. Prompt 类型参数
(1)含义

Prompt 是 Spring AI 定义的「完整请求体」,是比 Message 更高层级的抽象,包含:

  • messages:核心对话列表(同上述 Message 列表);
  • options:模型参数配置(温度、topP、最大生成长度、超时时间等);
  • metadata:元数据(如请求 ID、自定义标签)。

简单说:Prompt = Message列表 + 模型参数 + 元数据

(2)核心特点
  • 完整可控:既包含对话上下文,又能配置模型行为参数;
  • 支持模板化:结合 PromptTemplate 实现动态参数替换(如 "查询 {城市} 的天气");
  • 适配生产级场景:满足复杂的参数配置、动态模板需求。
(3)应用场景
  • 生产级多轮对话:需配置模型参数(如温度 = 0.1 保证回答稳定);
  • 动态模板生成:如电商场景中 "生成 {商品名称} 的营销文案";
  • 定制化模型行为:如设置最大生成长度、禁止模型调用工具、配置超时时间;
  • 批量请求:结合 Prompt 模板批量生成不同参数的请求。
(4)代码示例
java 复制代码
@Autowired
private ChatClient chatClient;

public String promptCall() {
    // 1. 定义动态模板(参数化)
    PromptTemplate promptTemplate = new PromptTemplate("作为电商运营,为{product}生成一段不超过50字的营销文案,风格{style}");
    // 2. 填充模板参数
    Map<String, Object> params = new HashMap<>();
    params.put("product", "新款无线耳机");
    params.put("style", "活泼、年轻化");
    Prompt promptFromTemplate = promptTemplate.create(params);

    // 3. 自定义模型参数(温度、最大长度等)
    AlibabaChatOptions options = AlibabaChatOptions.builder()
            .temperature(0.8) // 创造性:0(严谨)~1(灵活)
            .topP(0.9)
            .maxTokens(100) // 最大生成长度
            .timeout(3000) // 超时时间
            .build();

    // 4. 构建完整 Prompt(模板消息 + 自定义参数)
    Prompt prompt = new Prompt(promptFromTemplate.getMessages(), options);

    // 5. 传入 Prompt 调用
    ChatResponse response = chatClient.call(prompt);
    return response.getResult().getOutput().getContent();
}

三、核心区别总结表

维度 String Message(List) Prompt
抽象层级 最低(纯文本) 中等(对话单元) 最高(完整请求)
上下文支持 ❌ 不支持 ✅ 支持多轮上下文 ✅ 支持(包含 Message)
模型参数配置 ❌ 无法配置(用默认) ❌ 无法配置 ✅ 可自定义(温度 / 长度等)
动态模板 ❌ 不支持 ❌ 需手动拼接 ✅ 结合 PromptTemplate 支持
核心场景 简单单轮问答、测试 多轮对话、角色定制 生产级复杂场景、模板化
灵活性 最低 中等 最高

四、选型建议

  1. 快速测试 / 简单问答 → 用 String
  2. 多轮对话 / 角色定制(无参数配置) → 用 Message 列表;
  3. 生产级场景(需参数配置 / 动态模板) → 用 Prompt
  4. 所有场景的底层最终都会封装为 Prompt 调用模型,区别仅在于手动封装还是自动封装。

Prompt四大角色

类型 value 值 作用 & 场景
SYSTEM "system" - 设定 AI 的边界、角色、定位- 指导 AI 的行为和响应方式- 决定 AI 如何理解、回复用户输入
USER "user" - 承载用户的原始提问 / 输入- 代表用户向 AI 发出的问题、命令或陈述
ASSISTANT "assistant" - 存储 AI 返回的响应信息(助手回复)- 用于多轮对话的上下文连贯交互- 实现 "记忆对话、积累回答" 的功能
TOOL "tool" - 桥接外部服务(如支付、数据查询)- 支持函数调用、调用第三方工具(后续章节会详细说明)

关键总结

这 4 种类型是 AI 对话的基础单元,典型的多轮对话流程会按以下顺序组合:SYSTEM(设定AI角色)→ USER(用户提问)→ ASSISTANT(AI回复)→ USER(下一轮提问)→ ASSISTANT(下一轮回复)TOOL则是扩展 AI 能力的模块,用于让 AI 调用外部工具完成更复杂的任务。

小试一手

新建Module
改pom
XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

新增配置文件

XML 复制代码
server.port=8005

server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA-05prompt

spring.ai.dashscope.api-key=${aliQwen-api}
启动类
java 复制代码
@SpringBootApplication
public class Saa05Prompt {
    public static void main(String[] args) {
        SpringApplication.run(Saa05Prompt.class,args);
    }
}
业务类
  • 配置类
java 复制代码
@SpringBootConfiguration
public class Saa05Config {
    // ===================== 模型常量定义 =====================
    /**
     * DeepSeek-v3模型标识(阿里云百炼平台的模型名称)
     * 常量定义便于统一维护,避免硬编码散落在代码中
     */
    private static final String MODEL_DEEPSEEK = "deepseek-v3";

    /**
     * Qwen-plus(通义千问增强版)模型标识(阿里云百炼平台的模型名称)
     */
    private static final String MODEL_QWEN = "qwen-plus";

    @Bean
    public DashScopeApi dashScopeApi() {
        // 从系统环境变量中读取阿里云百炼API密钥(生产环境推荐此方式,避免密钥提交到代码仓库)
        String apiKey = System.getenv("aliQwen-api");

        // 校验API密钥有效性,为空则抛出异常,提前阻断无效配置
        if (apiKey == null || apiKey.trim().isEmpty()) {
            throw new IllegalArgumentException("Environment variable 'aliQwen-api' is not set or empty.");
        }

        // 构建并返回DashScopeApi实例,所有模型共享此实例
        return DashScopeApi.builder()
                .apiKey(apiKey) // 设置API密钥,用于阿里云百炼接口鉴权
                .build();
    }

    @Bean(name = "deepseek") // 指定Bean名称,解决多ChatModel冲突问题
    public ChatModel deepSeek(DashScopeApi dashScopeApi) {
        // 构建DashScopeChatModel(Spring AI封装的百炼模型适配类)
        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi) // 注入公共API客户端
                // 设置该模型的默认配置
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(MODEL_DEEPSEEK) // 指定使用DeepSeek-v3模型
                        .build())
                .build();
    }

    @Bean(name = "qwen") // 指定Bean名称,解决多ChatModel冲突问题
    public ChatModel qwen(DashScopeApi dashScopeApi) {
        // 构建DashScopeChatModel(Spring AI封装的百炼模型适配类)
        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi) // 注入公共API客户端
                // 设置该模型的默认配置
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(MODEL_QWEN) // 指定使用Qwen-plus模型
                        .build())
                .build();
    }

    @Bean(name = "deepseekChatClient") // 指定Bean名称,业务层可通过@Resource(name = "deepseekClient")精准注入
    public ChatClient deepseekClient(@Qualifier("deepseek") ChatModel deepseek){
        // 基于DeepSeek模型的ChatModel构建ChatClient,并设置默认对话配置
        return ChatClient.builder(deepseek)
                // 配置该ChatClient的默认对话选项
                .defaultOptions(ChatOptions.builder()
                        .model(MODEL_DEEPSEEK) // 指定默认使用的模型标识(与底层模型保持一致)
                        .build())
                .build();
    }
    @Bean(name = "qwenChatClient") // 指定Bean名称,便于业务层精准注入使用
    public ChatClient qwenClient(@Qualifier("qwen") ChatModel qwen){
        // 基于Qwen模型的ChatModel构建ChatClient,并设置默认对话配置
        return ChatClient.builder(qwen)
                // 配置该ChatClient的默认对话选项,与底层模型标识对齐
                .defaultOptions(ChatOptions.builder()
                        .model(MODEL_QWEN) // 绑定默认模型为Qwen-plus,无需业务层重复指定
                        .build())
                .build();
    }
}
  • Controller层
    • 第一版(通过chatClient)
java 复制代码
@RestController
public class PromptTestController {
    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekChatClient;
    @Resource(name = "qwenChatClient")
    private ChatClient qwenChatClient;

    @GetMapping(value = "/prompt/chat")
    public Flux<String> chat(@RequestParam(name="question",defaultValue = "你谁谁") String msg){
        return deepseekChatClient.prompt().system("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告").user(msg).stream().content();
    }
}

问一个非法律的问题

再问一个关于法律的问题

  • 第二版(通过chatModel)
java 复制代码
    @GetMapping(value = "/prompt/chat2")
    public Flux<ChatResponse> chat2(@RequestParam(name="question",defaultValue = "你谁谁") String msg){
        SystemMessage systemMessage = new SystemMessage("你是一个相声大师,每一个相声有5个笑点");
        UserMessage userMessage = new UserMessage(msg);
        Prompt prompt = new Prompt(systemMessage, userMessage);
        return qwenChatModel.stream(prompt);
    }

上面这种返回形式,信息丰富了很多

  • 第三版

    java 复制代码
    @GetMapping("/prompt/chat3")
    public Flux<String> chat3(@RequestParam(name="question", defaultValue = "你谁谁") String question) {
        // 1. 系统消息(SystemMessage):用于设定模型的行为、角色或规则
        // 这里定义了一个系统提示,告诉模型它是一个"讲故事的助手",并且每个故事控制在600字以内,以HTML格式返回。
        SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手," +
                "每个故事控制在600字以内且以HTML格式返回");
    
        // 2. 用户消息(UserMessage):用户输入的问题或请求
        // 从请求参数中获取用户的问题,如果未传则默认为"你谁谁"
        UserMessage userMessage = new UserMessage(question);
    
        // 3. 构建 Prompt 对象:将系统消息和用户消息组合成一个完整的对话上下文
        // Prompt 是 Spring AI 中用于封装消息的结构,通常用于模型调用时传递上下文信息
        Prompt prompt = new Prompt(userMessage, systemMessage);
    
        // 4. 调用模型进行流式响应(stream)
        // deepseekChatModel 是一个实现了 Model 接口的对象,用于与 DeepSeek 或其他大模型进行交互
        // 使用 stream 方法实现流式输出,即逐步返回模型生成的内容
        return deepseekChatModel.stream(prompt)
                // 5. 处理模型返回结果
                // 假设模型返回的是一个列表,我们取第一个结果(get(0))
                // 然后获取该结果中的输出内容(output.getText())
                .map(response -> response.getResults().get(0).getOutput().getText());
    }
  • 第四版

java 复制代码
    @GetMapping("/prompt/chat4")
    public String chat4(@RequestParam(name="question", defaultValue = "你谁谁") String question)
    {
        // 1. 构建模型请求并执行同步调用:
        //    - prompt():创建Prompt构建器
        //    - user(question):添加用户消息(UserMessage)
        //    - call():同步调用模型(阻塞等待完整结果返回,区别于流式stream())
        //    - chatResponse():获取完整响应对象ChatResponse(包含结果、token消耗、状态等)
        //    - getResult():提取核心响应结果ChatResult(新版API单数形式,兼容2.0+)
        //    - getOutput():提取模型输出内容(类型为AssistantMessage,即助手/模型回复)
        AssistantMessage assistantMessage = deepseekChatClient.prompt()
                .user(question)
                .call()
                .chatResponse()
                .getResult()
                .getOutput();

        // 2. 提取文本并做空值兜底:避免模型返回空内容时抛出NullPointerException
        String responseText = assistantMessage != null ? assistantMessage.getText() : "";
        return responseText == null ? "" : responseText;
    }
  • 第五版
java 复制代码
    @GetMapping("/prompt/chat5")
    public String chat5(@RequestParam(name = "city",defaultValue = "北京") String city)
    {
        // ========== 第一步:调用深度求索大模型,获取天气回答 ==========
        // 1. prompt():创建Prompt构建器,用于构造模型请求
        // 2. user(...):添加用户消息,拼接城市参数生成天气查询问题
        // 3. call():同步调用模型(阻塞等待完整结果返回,非流式)
        // 4. chatResponse():获取模型完整响应对象ChatResponse(包含结果、元数据等)
        // 5. getResult():提取核心响应结果ChatResult(新版Spring AI 2.0+单数形式)
        // 6. getOutput():提取模型输出内容(AssistantMessage类型,即模型回复)
        // 7. getText():提取模型回复的纯文本内容(风险:可能返回null触发空指针)
        String answer = deepseekChatClient.prompt()
                .user(city + "未来3天天气情况如何?")
                .call()
                .chatResponse()
                .getResult()
                .getOutput()
                .getText();

        // ========== 第二步:手动构造ToolResponseMessage(工具响应消息) ==========
        // ToolResponseMessage:Spring AI中用于封装「工具调用返回结果」的消息类型
        // 核心作用是传递工具执行结果给模型,而非直接拼接返回给前端(当前用法不符合设计初衷)
        // 构造参数说明:
        // - ToolResponse(String id, String name, Object result):
        //   id:工具调用唯一标识;name:工具名称;result:工具返回的结果
        ToolResponseMessage toolResponseMessage =
                new ToolResponseMessage(
                        // 构造单个工具响应对象列表(ToolResponse)
                        List.of(new ToolResponseMessage.ToolResponse(
                                "1",          // 工具调用ID(自定义唯一标识)
                                "获得天气",    // 工具名称(描述工具功能)
                                city          // 工具返回的结果(此处仅传了城市名,无实际天气数据)
                        ))
                );

        // ========== 第三步:提取ToolResponseMessage的文本内容 ==========
        // getText():将ToolResponseMessage转换为字符串(默认是工具响应的JSON格式)
        // 风险:若toolResponseMessage为null,会触发空指针;当前仅传了城市名,无实际工具结果
        String toolResponse = toolResponseMessage.getText();

        // ========== 第四步:拼接模型回答和工具响应信息 ==========
        // 注:当前拼接逻辑无实际业务意义(工具响应仅为城市名,未关联真实天气数据)
        String result = answer + toolResponse;

        // 返回拼接后的最终结果
        return result;
    }

提示词Prompt Template

Prompt(提示词)的演化历程

Prompt(提示词)是人类与大语言模型(LLM)交互的核心载体,其演化历程本质是从 "简单指令" 到 "结构化、工程化、智能化交互范式" 的升级,伴随大模型能力迭代和应用场景深化逐步完善。结合 Spring AI 等框架的抽象设计,可将 Prompt 演化分为以下 5 个阶段,每个阶段对应不同的技术特征、应用场景和工具形态

一、阶段 1:原始指令阶段(2020 年前,GPT-3 初期)
二、阶段 2:角色与约束阶段(2020-2022,ChatGPT 诞生)
三、阶段 3:结构化拆分阶段(2022-2023,Spring AI/LLaMA Index 等框架兴起)
四、阶段 4:完整请求体阶段(2023-2024,Spring AI 2.0 / 阿里云百炼等企业级框架)
五、阶段 5:智能化 / 自动化阶段(2024 至今,Prompt 工程 + Agent 融合)

提示词模板(Prompt Template)是什么?

Prompt Template(提示词模板)是预定义的、可动态填充参数的提示词结构化模板,核心作用是将 "固定的提示词框架" 与 "动态的业务参数" 分离,解决纯手工拼接提示词的低效、易出错问题,是 Prompt 工程从 "纯文本编写" 走向 "工程化复用" 的核心工具。

简单类比:Prompt Template 就像 Word 里的 "邮件合并模板"------ 你先写好固定的框架(比如 "{姓名},你好!{日期} 的会议地点是 {地址}"),再动态填充不同的姓名、日期、地址,就能快速生成大量格式统一的内容;放到大模型交互中,就是先定义好提示词的固定逻辑(角色、约束、格式),再动态传入用户输入、业务参数等变量。

小试一手

新建Module
改pom
java 复制代码
server.port=8006

server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA-06PromptTemplate


# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}
主启动
java 复制代码
@SpringBootApplication
public class Saa06ApplicationMain {
    public static void main(String[] args) {
        SpringApplication.run(Saa06ApplicationMain.class,args);
    }
}
业务类
配置类
java 复制代码
@SpringBootConfiguration
public class Saa06Config {
    // ===================== 模型常量定义 =====================
    /**
     * DeepSeek-v3模型标识(阿里云百炼平台的模型名称)
     * 常量定义便于统一维护,避免硬编码散落在代码中
     */
    private static final String MODEL_DEEPSEEK = "deepseek-v3";

    /**
     * Qwen-plus(通义千问增强版)模型标识(阿里云百炼平台的模型名称)
     */
    private static final String MODEL_QWEN = "qwen-plus";

    // ===================== 核心公共Bean =====================
    /**
     * 构建阿里云百炼通用API客户端Bean(单例)
     * 作用:封装API密钥,提供统一的DashScopeApi实例,供所有模型复用
     * 注意:API密钥通过环境变量"aliQwen-api"获取,保证密钥安全性(避免硬编码)
     * @return DashScopeApi 阿里云百炼API客户端核心实例
     * @throws IllegalArgumentException 环境变量未配置或为空时抛出异常,快速暴露配置问题
     */
    @Bean
    public DashScopeApi dashScopeApi() {
        // 从系统环境变量中读取阿里云百炼API密钥(生产环境推荐此方式,避免密钥提交到代码仓库)
        String apiKey = System.getenv("aliQwen-api");

        // 校验API密钥有效性,为空则抛出异常,提前阻断无效配置
        if (apiKey == null || apiKey.trim().isEmpty()) {
            throw new IllegalArgumentException("Environment variable 'aliQwen-api' is not set or empty.");
        }

        // 构建并返回DashScopeApi实例,所有模型共享此实例
        return DashScopeApi.builder()
                .apiKey(apiKey) // 设置API密钥,用于阿里云百炼接口鉴权
                .build();
    }

    // ===================== 模型专属Bean =====================
    /**
     * 构建DeepSeek-v3模型的ChatModel Bean(命名为"deepseek")
     * 作用:提供DeepSeek-v3模型的对话能力,业务层可通过@Qualifier("deepseek")精准注入
     * @param dashScopeApi 依赖注入的公共DashScopeApi实例(复用鉴权配置)
     * @return ChatModel DeepSeek-v3模型的对话接口实例
     */
    @Bean(name = "deepseek") // 指定Bean名称,解决多ChatModel冲突问题
    public ChatModel deepSeek(DashScopeApi dashScopeApi) {
        // 构建DashScopeChatModel(Spring AI封装的百炼模型适配类)
        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi) // 注入公共API客户端
                // 设置该模型的默认配置
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(MODEL_DEEPSEEK) // 指定使用DeepSeek-v3模型
                        .build())
                .build();
    }

    /**
     * 构建Qwen-plus(通义千问增强版)模型的ChatModel Bean(命名为"qwen")
     * 作用:提供Qwen-plus模型的对话能力,业务层可通过@Qualifier("qwen")精准注入
     * @param dashScopeApi 依赖注入的公共DashScopeApi实例(复用鉴权配置)
     * @return ChatModel Qwen-plus模型的对话接口实例
     */
    @Bean(name = "qwen") // 指定Bean名称,解决多ChatModel冲突问题
    public ChatModel qwen(DashScopeApi dashScopeApi) {
        // 构建DashScopeChatModel(Spring AI封装的百炼模型适配类)
        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi) // 注入公共API客户端
                // 设置该模型的默认配置
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(MODEL_QWEN) // 指定使用Qwen-plus模型
                        .build())
                .build();
    }

    /**
     * 构建DeepSeek-v3模型对应的ChatClient Bean(命名为"deepseekClient")
     * ChatClient是Spring AI高层封装的对话客户端,比底层ChatModel更易用(链式调用、自动解析响应等)
     * @param deepseek 依赖注入指定名称的DeepSeek-v3模型ChatModel(通过@Qualifier精准匹配,避免多Bean冲突)
     * @return ChatClient 绑定DeepSeek-v3模型的高层对话客户端实例
     */
    @Bean(name = "deepseekChatClient") // 指定Bean名称,业务层可通过@Resource(name = "deepseekClient")精准注入
    public ChatClient deepseekClient(@Qualifier("deepseek") ChatModel deepseek){
        // 基于DeepSeek模型的ChatModel构建ChatClient,并设置默认对话配置
        return ChatClient.builder(deepseek)
                // 配置该ChatClient的默认对话选项
                .defaultOptions(ChatOptions.builder()
                        .model(MODEL_DEEPSEEK) // 指定默认使用的模型标识(与底层模型保持一致)
                        .build())
                .build();
    }

    /**
     * 构建Qwen-plus模型对应的ChatClient Bean(命名为"qwenClient")
     * 作用:为通义千问增强版提供高层、易用的对话能力,简化业务层调用逻辑
     * @param qwen 依赖注入指定名称的Qwen-plus模型ChatModel(@Qualifier避免多ChatModel Bean冲突)
     * @return ChatClient 绑定Qwen-plus模型的高层对话客户端实例
     */
    @Bean(name = "qwenChatClient") // 指定Bean名称,便于业务层精准注入使用
    public ChatClient qwenClient(@Qualifier("qwen") ChatModel qwen){
        // 基于Qwen模型的ChatModel构建ChatClient,并设置默认对话配置
        return ChatClient.builder(qwen)
                // 配置该ChatClient的默认对话选项,与底层模型标识对齐
                .defaultOptions(ChatOptions.builder()
                        .model(MODEL_QWEN) // 绑定默认模型为Qwen-plus,无需业务层重复指定
                        .build())
                .build();
    }
}
controller层
promptTemplate基本使用
java 复制代码
    @GetMapping("/prompttemplate/chat")
    public Flux<String> chat(String topic, String output_format, String wordCount) {
        // 构建提示词模板并填充参数
        PromptTemplate promptTemplate = new PromptTemplate(
                "讲一个关于{topic}的故事并以{output_format}格式输出,字数在{wordCount}左右");
        Prompt prompt = promptTemplate.create(Map.of(
                "topic", topic, "output_format", output_format, "wordCount", wordCount));

        // 流式调用大模型返回内容
        return deepseekChatClient.prompt(prompt).stream().content();
    }
promptTemplate读取模版文件实现模版功能
java 复制代码
    // 通过@Value注解加载类路径下的提示词模板文件
    // 模板文件路径:classpath:/prompttemplate/template
    @Value("classpath:/prompttemplate/template.txt")
    //这里的Resource要注意一下 引入org.springframework.core.io.Resource
    private org.springframework.core.io.Resource userTemplate;

    @GetMapping("/prompttemplate/chat2")
    public String chat2(String topic, String output_format)  {
        // 1. 基于加载的模板文件创建PromptTemplate实例
        //    该实例会解析模板文件中的占位符(如{topic}、{output_format})
        PromptTemplate promptTemplate = new PromptTemplate(userTemplate);

        // 2. 构建模板参数映射,将请求参数与模板占位符绑定
        //    Map.of() 创建不可变映射,key对应模板中的占位符名称,value对应请求传入的参数值
        Prompt prompt = promptTemplate.create(Map.of(
                "topic", topic,                // 绑定话题参数
                "output_format", output_format // 绑定输出格式参数
        ));

        // 3. 调用DeepSeek客户端发送提示词并获取响应
        //    prompt():设置提示词
        //    call():执行调用请求
        //    content():提取响应中的核心内容
        return deepseekChatClient.prompt(prompt).call().content();
    }
PromptTemplate多角色设定
java 复制代码
 @GetMapping("/prompttemplate/chat3")
    public String chat3(String job_name, String userTopic)
    {
        // 1. 创建系统提示词模板:限定AI身份为指定职业助手,仅回答该职业相关内容,且返回HTML格式结果
        // 模板中{job_name}为占位符,后续会替换为实际传入的职业名称
        SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是一位顶级{job_name}助手,只回答{job_name}相关的问题,其它问题无可奉告,以HTML格式的结果。");

        // 2. 填充系统提示词模板的占位符,生成系统消息对象
        // Map.of("job_name", job_name) 用于将占位符{job_name}替换为前端传入的实际职业名称
        Message sysMessage = systemPromptTemplate.createMessage(Map.of("job_name", job_name));

        // 3. 创建用户提示词模板:定义用户的核心请求(讲解指定主题)
        // 模板中{userTopic}为占位符,后续替换为用户实际想要讲解的主题
        PromptTemplate userProTemplate = new PromptTemplate("讲解一下{userTopic}");

        // 4. 填充用户提示词模板的占位符,生成用户消息对象
        // Map.of("userTopic", userTopic) 替换占位符为前端传入的实际主题
        Message userMessage = userProTemplate.createMessage(Map.of("userTopic", userTopic));

        // 5. 构建完整的Prompt对象:组合系统消息(AI身份限定)和用户消息(核心请求)
        // 消息列表的顺序通常有意义,系统消息在前可先设定AI的行为准则
        Prompt prompt = new Prompt(List.of(sysMessage, userMessage));

        // 6. 调用DeepSeek大模型客户端,发送Prompt请求并获取响应内容
        // prompt(prompt):构造请求,call():执行请求调用,content():提取响应的文本内容
        return deepseekChatClient.prompt(prompt).call().content();
    }
PromptTemplate人物设定
java 复制代码
@GetMapping("/prompttemplate/chat4")
    public String chat4(String question)
    {
        //1 系统消息
        SystemMessage systemMessage = new SystemMessage("你是一个医生助手,拒绝回答非医学的问题。");
        //2 用户消息
        UserMessage userMessage = new UserMessage(question);
        //3 系统消息+用户消息=完整提示词
        Prompt prompt = new Prompt(systemMessage, userMessage);
        //4 调用LLM
        String result = deepseekChatModel.call(prompt).getResult().getOutput().getText();
        System.out.println(result);
        return result;
    }

    @GetMapping("/prompttemplate/chat5")
    public Flux<String> chat5(String question)
    {
        return deepseekChatClient.prompt()
                .system("你是一个环境监测助手,拒绝回答非环境监测的问题。")
                .user(question)
                .stream()
                .content();
    }

格式化输出(Structured Output)

若需从 LLM 获取结构化输出,Structured Output 可将 ChatModel/ChatClient 方法的返回类型由 String 转换为指定类型。

LLM 生成结构化输出的能力对下游应用至关重要,这确保了输出值的可靠解析。开发者通常需要将 AI 模型的输出快速转换为 JSON、XML 或 Java 类等数据类型,以便传递给其他应用函数和方法。Spring AI 的结构化输出转换器可帮助实现 LLM 输出到结构化格式的转换。

record 类

Java 14 引入 Record(16 正式转正)的核心目标是解决 Java 中长期存在的「纯数据载体类」模板代码冗余、不可变性实现繁琐、数据语义表达不清晰 的问题,本质是为「仅承载数据、无复杂业务逻辑」的场景提供语言级的极简解决方案

Record 是 Java 语言对「纯数据载体类」场景的精准优化,核心解决:

  1. ✅ 纯数据类模板代码冗余的问题;
  2. ✅ 不可变性实现繁琐、易出错的问题;
  3. ✅ 数据类语义不清晰的问题;
  4. ✅ 依赖第三方工具实现极简数据类的问题。

关键补充:Record 的适用边界

Record 并非要替代所有类,它的核心适用场景是「只读、纯数据、无业务逻辑」的载体:

  • ✅ 接口请求 / 响应体(DTO/VO);
  • ✅ 临时数据容器(如 Stream 处理结果、日志上下文);
  • ✅ 元数据载体(如配置项、枚举扩展);
  • ❌ 持久化实体(Entity,需可变、无参构造);
  • ❌ 有复杂业务逻辑的类(如订单处理、计算逻辑)。

最终结论

Record 创造出来的核心价值是:用语言级特性极简地实现「不可变纯数据载体类」,消除模板代码,明确数据语义,提升开发效率和代码质量。它不是要替代所有类,而是精准解决「纯数据存储」这一特定场景的痛点,与传统 class、Lombok 形成互补。

小试一手

新建Module

SAA-07StructuredOutput

改pom
XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source> <!-- 你的JDK版本 -->
                    <target>17</target>
                    <!-- 核心:开启 -parameters 保留参数名 -->
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
创建配置文件
XML 复制代码
server.port=8007

server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA-07StructuredOutput


# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}
主启动文件
java 复制代码
@SpringBootApplication
public class Saa07ApplicationRun {
    public static void main(String[] args) {
        SpringApplication.run(Saa07ApplicationRun.class,args);
    }
}
业务类
  • 配置类
java 复制代码
@SpringBootConfiguration
public class Saa07Config {
    // ===================== 模型常量定义 =====================
    /**
     * DeepSeek-v3模型标识(阿里云百炼平台的模型名称)
     * 常量定义便于统一维护,避免硬编码散落在代码中
     */
    private static final String MODEL_DEEPSEEK = "deepseek-v3";

    /**
     * Qwen-plus(通义千问增强版)模型标识(阿里云百炼平台的模型名称)
     */
    private static final String MODEL_QWEN = "qwen-plus";

    // ===================== 核心公共Bean =====================
    /**
     * 构建阿里云百炼通用API客户端Bean(单例)
     * 作用:封装API密钥,提供统一的DashScopeApi实例,供所有模型复用
     * 注意:API密钥通过环境变量"aliQwen-api"获取,保证密钥安全性(避免硬编码)
     * @return DashScopeApi 阿里云百炼API客户端核心实例
     * @throws IllegalArgumentException 环境变量未配置或为空时抛出异常,快速暴露配置问题
     */
    @Bean
    public DashScopeApi dashScopeApi() {
        // 从系统环境变量中读取阿里云百炼API密钥(生产环境推荐此方式,避免密钥提交到代码仓库)
        String apiKey = System.getenv("aliQwen-api");

        // 校验API密钥有效性,为空则抛出异常,提前阻断无效配置
        if (apiKey == null || apiKey.trim().isEmpty()) {
            throw new IllegalArgumentException("Environment variable 'aliQwen-api' is not set or empty.");
        }

        // 构建并返回DashScopeApi实例,所有模型共享此实例
        return DashScopeApi.builder()
                .apiKey(apiKey) // 设置API密钥,用于阿里云百炼接口鉴权
                .build();
    }

    // ===================== 模型专属Bean =====================
    /**
     * 构建DeepSeek-v3模型的ChatModel Bean(命名为"deepseek")
     * 作用:提供DeepSeek-v3模型的对话能力,业务层可通过@Qualifier("deepseek")精准注入
     * @param dashScopeApi 依赖注入的公共DashScopeApi实例(复用鉴权配置)
     * @return ChatModel DeepSeek-v3模型的对话接口实例
     */
    @Bean(name = "deepseek") // 指定Bean名称,解决多ChatModel冲突问题
    public ChatModel deepSeek(DashScopeApi dashScopeApi) {
        // 构建DashScopeChatModel(Spring AI封装的百炼模型适配类)
        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi) // 注入公共API客户端
                // 设置该模型的默认配置
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(MODEL_DEEPSEEK) // 指定使用DeepSeek-v3模型
                        .build())
                .build();
    }

    /**
     * 构建Qwen-plus(通义千问增强版)模型的ChatModel Bean(命名为"qwen")
     * 作用:提供Qwen-plus模型的对话能力,业务层可通过@Qualifier("qwen")精准注入
     * @param dashScopeApi 依赖注入的公共DashScopeApi实例(复用鉴权配置)
     * @return ChatModel Qwen-plus模型的对话接口实例
     */
    @Bean(name = "qwen") // 指定Bean名称,解决多ChatModel冲突问题
    public ChatModel qwen(DashScopeApi dashScopeApi) {
        // 构建DashScopeChatModel(Spring AI封装的百炼模型适配类)
        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi) // 注入公共API客户端
                // 设置该模型的默认配置
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(MODEL_QWEN) // 指定使用Qwen-plus模型
                        .build())
                .build();
    }

    /**
     * 构建DeepSeek-v3模型对应的ChatClient Bean(命名为"deepseekClient")
     * ChatClient是Spring AI高层封装的对话客户端,比底层ChatModel更易用(链式调用、自动解析响应等)
     * @param deepseek 依赖注入指定名称的DeepSeek-v3模型ChatModel(通过@Qualifier精准匹配,避免多Bean冲突)
     * @return ChatClient 绑定DeepSeek-v3模型的高层对话客户端实例
     */
    @Bean(name = "deepseekChatClient") // 指定Bean名称,业务层可通过@Resource(name = "deepseekClient")精准注入
    public ChatClient deepseekClient(@Qualifier("deepseek") ChatModel deepseek){
        // 基于DeepSeek模型的ChatModel构建ChatClient,并设置默认对话配置
        return ChatClient.builder(deepseek)
                // 配置该ChatClient的默认对话选项
                .defaultOptions(ChatOptions.builder()
                        .model(MODEL_DEEPSEEK) // 指定默认使用的模型标识(与底层模型保持一致)
                        .build())
                .build();
    }

    /**
     * 构建Qwen-plus模型对应的ChatClient Bean(命名为"qwenClient")
     * 作用:为通义千问增强版提供高层、易用的对话能力,简化业务层调用逻辑
     * @param qwen 依赖注入指定名称的Qwen-plus模型ChatModel(@Qualifier避免多ChatModel Bean冲突)
     * @return ChatClient 绑定Qwen-plus模型的高层对话客户端实例
     */
    @Bean(name = "qwenChatClient") // 指定Bean名称,便于业务层精准注入使用
    public ChatClient qwenClient(@Qualifier("qwen") ChatModel qwen){
        // 基于Qwen模型的ChatModel构建ChatClient,并设置默认对话配置
        return ChatClient.builder(qwen)
                // 配置该ChatClient的默认对话选项,与底层模型标识对齐
                .defaultOptions(ChatOptions.builder()
                        .model(MODEL_QWEN) // 绑定默认模型为Qwen-plus,无需业务层重复指定
                        .build())
                .build();
    }
}
  • 创建记录类
java 复制代码
public record StudentRecord(String id, String sname, String major, String email) { }
  • 创建controller
java 复制代码
@GetMapping("/structuredoutput/chat")
public StudentRecord chat(
        // 显式声明请求参数(可选,增强可读性)
        @RequestParam String sname,
        @RequestParam String email
) {
    // 1. 调用通义千问客户端构建并发送提示词请求
    return qwenChatClient.prompt()
            // 2. 构造用户提示词(使用 Consumer 匿名内部类配置提示词参数)
            .user(new Consumer<ChatClient.PromptUserSpec>() {
                @Override
                public void accept(ChatClient.PromptUserSpec promptUserSpec) {
                    // 2.1 设置提示词模板:包含{sname}和{email}两个占位符
                    promptUserSpec.text("学号1001,我叫{sname},大学专业是计算机科学与技术,邮箱{email}")
                            // 2.2 替换占位符:将前端传入的sname赋值给模板中的{sname}
                            .param("sname", sname)
                            // 2.3 替换占位符:将前端传入的email赋值给模板中的{email}
                            .param("email", email);
                }
            })
            // 3. 执行大模型请求调用(发送提示词并获取响应)
            .call()
            // 4. 将大模型返回的响应结果自动映射为 StudentRecord 实例
            //    (底层依赖 JSON 序列化/反序列化,要求大模型返回与 StudentRecord 字段匹配的结构化数据)
            .entity(StudentRecord.class);
}
  • lambda形式

    java 复制代码
    @GetMapping("/structuredoutput/chat2")
        public StudentRecord chat2(@RequestParam(name = "sname") String sname,
                                   @RequestParam(name = "email") String email)
        {
            String stringTemplate = """
                    学号1002,我叫{sname},大学专业是软件工程,邮箱{email}
                    """;
    
            return qwenChatClient.prompt()
                    .user(promptUserSpec -> {
                        // 关键改进:不是仅拼接文本,而是「指令+数据」的组合
                        String userPrompt = """
                                请根据以下信息生成学生的结构化信息,仅返回JSON字符串:
                                1. 学号:1001
                                2. 姓名:{sname}
                                3. 专业:计算机科学与技术
                                4. 邮箱:{email}
                                要求:
                                - 仅返回JSON,无其他任何内容;
                                - 字段名严格为studentId、sname、major、email;
                                - 所有字段值为字符串类型。
                                """;
    
                        // 填充参数+设置提示词(完成用户输入的组装)
                        promptUserSpec.text(userPrompt)
                                .param("sname", sname)  // 替换姓名参数
                                .param("email", email); // 替换邮箱参数
                    })
                    .call()
                    .entity(StudentRecord.class);
        }

Chat Memory连续对话保存和持久化

对话记忆

"大模型的对话记忆"这一概念,根植于人工智能与自然语言处理领域,特别是针对具有深度学习能力的大型语言模型而言,它指的是模型在与用户进行交互式对话过程中,能够追踪、理解并利用先前对话上下文的能力。 此机制使得大模型不仅能够响应即时的输入请求,还能基于之前的交流内容能够在对话中记住先前的对话内容,并根据这些信息进行后续的响应。这种记忆机制使得模型能够在对话中持续跟踪和理解用户的意图和上下文,从而实现更自然和连贯的对话。

Spring AI Alibaba 的聊天记忆功能为 AI 聊天应用提供了维护对话上下文和历史记录的机制。由于大模型本身不具备数据存储能力,必须将历史对话信息完整地提供给模型才能实现连续对话。否则一旦服务重启,所有对话记录都会丢失。因此,实现数据持久化是必要的。

记录类型

  • 持久化媒介

内存存储、数据库存储、对象存、文件存储

  • 消息对话窗口,聊天记录上限

消息对话窗口上限(数量限制)、聊天记录上限(时间 / 存储限制)

小试一手

新建Module

改pom文件

XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--spring-ai-alibaba memory-redis-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
        </dependency>
        <!--jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source> <!-- 你的JDK版本 -->
                    <target>17</target>
                    <!-- 核心:开启 -parameters 保留参数名 -->
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>

写YML

XML 复制代码
server.port=8008

# 设置响应的字符编码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

spring.application.name=SAA-08Persistent

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}


# ==========redis config ===============
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.data.redis.connect-timeout=3
spring.data.redis.timeout=2

主启动

java 复制代码
@SpringBootApplication
public class Saa08PersistentApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(Saa08PersistentApplication.class,args);
    }
}
业务类
新建redis配置类
RedisChatMemoryRepository

RedisChatMemoryRepository 是 Spring AI 框架中基于 Redis 实现的对话内存(Chat Memory)存储仓库 ,核心作用是替代默认的 InMemoryChatMemoryRepository(本地内存存储),将 AI 对话的上下文(会话历史、上下文信息)持久化到 Redis 中,解决单机内存存储的局限性。

java 复制代码
/**
     * 从 application.properties 或 application.yml 中注入 Redis 主机地址。
     * 配置项:spring.data.redis.host
     */
    @Value("${spring.data.redis.host}")
    private String host;

    /**
     * 从 application.properties 或 application.yml 中注入 Redis 端口号。
     * 配置项:spring.data.redis.port
     */
    @Value("${spring.data.redis.port}")
    private int port;

    /**
     * 创建并返回一个 RedisChatMemoryRepository 实例。
     * 该方法使用 builder 模式配置 Redis 连接参数,并构建出可用于存储聊天记忆的 Redis 仓库。
     *
     * @return RedisChatMemoryRepository 实例
     */
    @Bean
    public RedisChatMemoryRepository redisChatMemoryRepository() {
        return RedisChatMemoryRepository.builder()
                .host(host)      // 设置 Redis 主机地址
                .port(port)      // 设置 Redis 端口号
                .build();        // 构建 RedisChatMemoryRepository 实例
    }
MessageWindowChatMemory 消息窗口聊天记忆
顾问(Advisors)

MessageChatMemoryAdvisor

配置类SaaLLMConfig
java 复制代码
@Configuration
public class SaaLLMConfig
{
    // 模型名称常量定义
    private final String DEEPSEEK_MODEL = "deepseek-v3";
    private final String QWEN_MODEL = "qwen-plus";

    @Bean(name = "deepseek")
    public ChatModel deepSeek()
    {
        return DashScopeChatModel.builder()
                        .dashScopeApi(DashScopeApi.builder()
                                    .apiKey(System.getenv("aliQwen-api"))
                                .build())
                .defaultOptions(
                        DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build()
                )
                .build();
    }

    @Bean(name = "qwen")
    public ChatModel qwen()
    {
        return DashScopeChatModel.builder().dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("aliQwen-api"))
                        .build())
                .defaultOptions(
                        DashScopeChatOptions.builder()
                                .withModel(QWEN_MODEL)
                                .build()
                )
                .build();
    }

    @Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen,
                                     RedisChatMemoryRepository redisChatMemoryRepository)
    {

        MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder()
                            .chatMemoryRepository(redisChatMemoryRepository)
                            .maxMessages(10)
                        .build();

        return ChatClient.builder(qwen)
                .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build())
                .build();
    }

    @Bean(name = "deepseekChatClient")
    public ChatClient deepseekChatClient(@Qualifier("deepseek") ChatModel deepSeek,RedisChatMemoryRepository redisChatMemoryRepository)
    {
        MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redisChatMemoryRepository)
                .maxMessages(10)
                .build();
        return ChatClient.builder(deepSeek)
                .defaultOptions(ChatOptions.builder()
                        .model(DEEPSEEK_MODEL)
                        .build())
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build())
                .build();
    }
}
Controller控制器
java 复制代码
/**
     * AI对话接口(基于用户ID关联会话上下文)
     * 接口路径:/chatmemory/chat
     * 请求方式:GET
     * @param msg 前端传入的用户对话消息内容(必填)
     * @param userId 用户唯一标识(必填),作为会话ID(CONVERSATION_ID)关联Chat Memory,保证上下文连续性
     * @return AI生成的对话回复内容
     */
    @GetMapping("/chatmemory/chat")
    public String chat(String msg, String userId)
    {
/* 以下为注释掉的通义千问(Qwen)模型调用示例,保留作为多模型扩展参考
        return qwenChatClient.prompt(msg).advisors(new Consumer<ChatClient.AdvisorSpec>()
        {
            @Override
            public void accept(ChatClient.AdvisorSpec advisorSpec)
            {
                // 为对话请求设置会话ID参数,关联用户的对话上下文
                advisorSpec.param(CONVERSATION_ID, cid);
            }
        }).call().content();
        */

        // 调用DeepSeek大模型进行对话生成
        return deepseekChatClient.prompt(msg) // 设置用户输入的对话消息
                // 配置对话顾问参数,将用户ID作为会话ID传入,关联Chat Memory上下文
                .advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID, userId))
                .call() // 执行对话请求,调用AI模型接口
                .content(); // 获取AI返回的纯文本回复内容
    }

测试效果:

文生图

阿里百炼文生图

基于文本描述创造全新图像-文本生成图像-大模型服务平台百炼-阿里云

通义万相-文生图

通过HTTP与SDK异步调用通义万相-文生图V2 API-大模型服务平台百炼-阿里云

小试一手

新建module

SAA-09Text2image

修改pom文件
XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

新建yml文件

XML 复制代码
server.port=8009

# 设置响应的字符编码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

spring.application.name=SAA-09Text2image

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}
启动类
java 复制代码
@SpringBootApplication
public class Saa09ApplicationRun {
    public static void main(String[] args) {
        SpringApplication.run(Saa09ApplicationRun.class,args);
    }
}
业务类
Controller文件
java 复制代码
@RestController
/**
 * 文本生成图片(Text to Image)接口控制器
 * 提供HTTP接口,接收文本描述参数,调用图片生成模型返回生成图片的URL
 */
public class Text2ImageController {

    /**
     * 图片生成模型的标识ID
     * 固定使用wanx2.1-t2i-turbo模型进行图片生成
     */
    public static final String IMAGE_MODEL = "wanx2.1-t2i-turbo";

    /**
     * 图片生成模型的核心服务类
     * 通过Spring依赖注入方式获取实例,用于调用模型生成图片
     */
    @Resource
    private ImageModel imageModel;

    /**
     * 文本生成图片的核心接口
     * @param prompt 图片生成的文本描述提示词,默认值为"小老虎"
     * @return 生成图片的网络访问URL
     */
    @GetMapping(value = "/t2i/image")
    public String image(@RequestParam(name = "prompt", defaultValue = "小老虎") String prompt) {
        // 1. 构建图片生成请求参数:文本提示词 + 模型配置
        ImagePrompt imagePrompt = new ImagePrompt(
                prompt,
                DashScopeImageOptions.builder()
                        .withModel(IMAGE_MODEL) // 指定使用的图片生成模型
                        .build()
        );
        // 2. 调用图片生成模型,获取返回结果
        // 3. 解析结果,提取生成图片的URL并返回
        return imageModel.call(imagePrompt)
                .getResult()       // 获取模型调用的核心结果体
                .getOutput()       // 获取结果中的输出数据体
                .getUrl();         // 获取输出数据中的图片URL
    }
}

文生音

阿里百炼文生音

CosyVoice实时语音合成API参考

SpeechSynthesizer

SpeechSynthesizer通过"import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;"方式引入,提供语音合成的关键接口。

DashScopeSpeechSynthesisOptions

SpeechSynthesisParam的链式方法配置模型、音色等参数

小试一手

新建module

SAA-10Text2voice

修改pom文件
XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
创建yml
XML 复制代码
server.port=8010

# 设置响应的字符编码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

spring.application.name=SAA-10Text2voice

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}
启动类
java 复制代码
@SpringBootApplication
public class Saa10ApplicationRun {
    public static void main(String[] args) {
        SpringApplication.run(Saa10ApplicationRun.class,args);
    }
}
Controller文件
音色列表配置

系统预置音色规格与参数列表-大模型服务平台百炼-阿里云

java 复制代码
/**
 * 文本转语音(Text to Voice)接口控制器
 * 提供HTTP接口接收文本内容,调用语音合成模型生成MP3音频文件,返回文件本地存储路径
 */
@RestController
public class Text2VoiceController
{
    /**
     * 语音合成模型核心服务类
     * 通过Spring依赖注入获取实例,用于调用阿里云CosyVoice语音合成模型
     */
    @Resource
    private SpeechSynthesisModel speechSynthesisModel;

    /**
     * 语音合成模型标识
     * 使用阿里云cosyvoice-v2模型(百炼语音合成模型V2版本)
     */
    public static final String BAILIAN_VOICE_MODEL = "cosyvoice-v2";

    /**
     * 语音合成音色配置
     * 音色值:longanhuan(龙应笑),音色列表参考阿里云官方文档:
     * https://help.aliyun.com/zh/model-studio/cosyvoice-java-sdk#722dd7ca66a6x
     */
    public static final String BAILIAN_VOICE_TIMBER = "longyingxiao";//龙应笑


    /**
     * 文本转语音核心接口
     * 接口地址:http://localhost:8010/t2v/voice
     * @param msg 待转换为语音的文本内容,默认值为"温馨提醒,支付宝到账100元请注意查收"
     * @return 生成的MP3音频文件本地存储路径(如:d:\xxxx-xxxx-xxxx-xxxx.mp3)
     */
    @GetMapping("/t2v/voice")
    public String voice(@RequestParam(name = "msg",defaultValue = "温馨提醒,支付宝到账100元请注意查收") String msg)
    {
        // 1. 生成唯一的MP3文件存储路径(D盘根目录,UUID避免文件名重复)
        String filePath = "d:\\" + UUID.randomUUID() + ".mp3";

        // 2. 构建语音合成参数配置
        // 包含指定使用的模型、音色等核心配置
        DashScopeSpeechSynthesisOptions options = DashScopeSpeechSynthesisOptions.builder()
                .model(BAILIAN_VOICE_MODEL)   // 指定语音合成模型
                .voice(BAILIAN_VOICE_TIMBER)  // 指定语音音色
                .build();

        // 3. 调用语音合成模型,传入文本提示词和配置参数,获取响应结果
        SpeechSynthesisResponse response = speechSynthesisModel.call(
                new SpeechSynthesisPrompt(msg, options)
        );

        // 4. 从响应结果中提取语音音频的字节缓冲区(二进制音频数据)
        ByteBuffer byteBuffer = response.getResult().getOutput().getAudio();

        // 5. 将二进制音频数据写入本地MP3文件
        // 使用try-with-resources自动关闭文件输出流,避免资源泄漏
        try (FileOutputStream fileOutputStream = new FileOutputStream(filePath))
        {
            // 将字节缓冲区数据转换为字节数组并写入文件
            fileOutputStream.write(byteBuffer.array());
        } catch (Exception e) {
            // 捕获文件写入异常,打印异常信息(可根据业务需求改为日志记录/异常抛出)
            System.out.println("音频文件生成失败:" + e.getMessage());
        }

        // 6. 返回生成的音频文件本地路径
        return filePath;
    }
}
测试

这里注意音色码和代码 里面要保持一致,不要复制错了

复制代码
public static final String BAILIAN_VOICE_MODEL = "cosyvoice-v2";

也要去到 cosyvoice-v2里面的音色

相关推荐
ZBritney4 小时前
JAVA中的异常二
java·开发语言
my_angle20164 小时前
c语言 个人学习计划打卡与番茄钟程序
学习
Elastic 中国社区官方博客4 小时前
在 Google MCP Toolbox for Databases 中引入 Elasticsearch 支持
大数据·人工智能·elasticsearch·搜索引擎·ai·语言模型·全文检索
汤姆yu4 小时前
基于springboot的运动服服饰销售购买商城系统
java·spring boot·后端
非著名架构师4 小时前
从预测到预调:疾风大模型如何驱动能源电力系统实现“气象自适应”调度?
大数据·人工智能·风光功率预测·高精度光伏功率预测模型·高精度气象数据·高精度天气预报数据·galeweather.cn
cici158744 小时前
含风电场的十机24时系统机组出力优化算法
人工智能·算法·机器学习
Yeats_Liao4 小时前
CANN Samples(十九):特色场景:机器人 AI 绘画 手写识别等
人工智能·目标跟踪·机器人
期待のcode4 小时前
Springboot数据层开发—Springboot整合JdbcTemplate和Mybatis
spring boot·后端·mybatis
柯南二号4 小时前
【后端】【Java】一文深入理解 Spring Boot RESTful 风格接口开发
java·spring boot·restful