002 Spring AI Alibaba框架整合百炼大模型平台 — 聊天、文生图、语音、向量模型整合

Spring AI Alibaba框架整合百炼大模型平台

文章目录

版本springboot 3.5.x, jdk17, springaiAlibaba1.1.2.2

1.Chat聊天模型

yml配置

yml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}      
      # 聊天模型
      chat:
        enabled: true
        options:
          # 指定使用的模型型号(如 qwen-turbo、qwen-plus、qwen-max 等)
          model: qwen-plus-2025-07-28
          temperature: 0.7  #温度参数(0~2),控制生成文本的随机性 越低输出越确定,越高越随机(默认 0.85)
          top-p: 0.9 # 核采样(0~1),控制生成多样性(默认 0.8)
          max-tokens: 2000 # 最大生成 Token 数
          enable-search: false  # 是否开启联网搜索(仅部分模型支持)
          # 停止词,生成到这些词时提前终止
          stop: []
          stream: true #是否流式返回
          # --- 高级参数 ---
          repetition-penalty: 1.1    # 重复惩罚
          seed: 12345                # 随机种子 (保证结果一致)
          tools: []     # 工具调用配置 (Function Calling)

配置类

java 复制代码
/**
 * ChatClient 配置类
 *
 * ChatClient 是 Spring AI 推荐的现代调用方式,支持链式编程和插件机制。
 * 相比直接使用 ChatModel,ChatClient 提供了更丰富的功能:
 * - 链式调用 API
 * - 默认系统提示词
 * - Advisor 插件机制(日志、记忆、RAG 等)
 */
@Configuration
public class ChatClientConfig {

    /**
     * 创建 ChatClient Bean
     *
     * @param chatModel 自动注入的 ChatModel(由 DashScope Starter 提供)
     * @return 配置好的 ChatClient 实例
     */
    @Bean
    public ChatClient chatClient(DashScopeChatModel chatModel) {
        return ChatClient.builder(chatModel)
                // 设置默认系统提示词,定义了 AI 的基础人设和行为规范
                .defaultSystem("你是一个专业、友好的AI助手,请用中文回答问题。")
                // 添加日志插件,自动打印每次请求和响应的详细信息,方便调试
                // SimpleLoggerAdvisor 是 Spring AI 内置的日志 Advisor
                .defaultAdvisors(
                        new SimpleLoggerAdvisor()
                        //自定义插件
                        //,new MyLoggerAdvisor()
                )
                // 设置默认选项(可选)
                // 【设置默认参数】
                .defaultOptions(ChatOptions.builder()
                        .temperature(0.7) // 温度:控制回答的随机性。0=确定,1=创造性高
                        .topP(0.9)        // 核采样:控制词汇选择的多样性
                        .build())
                .build();
    }



    /**
     * 创建 ChatClient.Builder Bean,供需要动态构建 ChatClient 的场景使用
     * (例如多轮对话中需要为每个会话创建独立的 ChatClient)
     */
    @Bean
    public ChatClient.Builder chatClientBuilder(DashScopeChatModel chatModel) {
        return ChatClient.builder(chatModel);
    }
    
}
1.获取回答
java 复制代码
    @Test
    public void chatTest() {
        String content = "讲一个笑话";
        //String response2 = chatClient.prompt(content).call().content();
        String response = chatClient
                .prompt()            // 1. 开始构建提示词请求
                .user(content)       // 2. 设置用户输入的问题
                .call()              // 3. 同步调用 AI 接口
                .content();          // 4. 获取 AI 返回的文本内容
        System.out.println("AI助手:" + response);
    }
2.获取完整响应信息(含token用量)
java 复制代码
    /**
     * 2. 获取完整响应信息(包含 Token 用量等元数据)
     * 有时候你需要知道这次请求花了多少 Token,这时候可以获取完整的 ChatResponse 对象。
     */
    @PostMapping("/detail")
    public Map<String, Object> detailChat(@RequestBody ChatRequest request) {
        // chatResponse() 返回的是 ChatResponse 对象,包含 Token 用量、模型名称等元数据
        ChatResponse response = chatClient.prompt()
                .user(request.getQuestion())
                .call()
                .chatResponse();

        // 从 ChatResponse 中提取文本内容
        String content = response.getResult().getOutput().getText();

        return Map.of(
                "question", request.getQuestion(),
                "answer", content,
                // metadata 中包含 token 用量等信息
                "metadata", response.getMetadata()
        );
    }

测试结果

json 复制代码
{
  "question": "讲一个笑话",
  "answer": "当然可以!来一个轻松又带点小哲理的冷笑话 😄:\n\n> 为什么数学老师离婚了?  \n> 因为他发现------  \n> **"人生不是非黑即白,但他的世界里,只有0和1。"**  \n> (前妻说:"你连'我生气了'都要我写成布尔表达式?!")\n\n😂  \n(附赠解释:0 和 1 是二进制基础,也常被程序员/理工人"过度理性化"地套用到生活里......结果------bug 出现在感情模块,且无法 debug 🐞)\n\n想听更多类型(谐音梗、动物版、程序员专属、或者适合讲给孩子听的)?我随时待命~",
  "metadata": {
    "id": "ba246ae9-b5fa-9ac3-8de7-f93faf1ec7f1",
    "model": "",
    "rateLimit": {
      "tokensRemaining": 0,
      "requestsLimit": 0,
      "requestsReset": "PT0S",
      "tokensReset": "PT0S",
      "tokensLimit": 0,
      "requestsRemaining": 0
    },
    "usage": {
      "promptTokens": 30,
      "completionTokens": 160,
      "totalTokens": 190
    },
    "promptMetadata": [
    ],
    "empty": true
  }
}
3.流式响应
java 复制代码
/**
 * 3. 流式输出(SSE)
 * 像 ChatGPT 网页版那样,一个字一个字地显示回答,体验更好。
 * 使用 Server-Sent Events (SSE) 协议,响应类型为 text/event-stream。
 * 大白话解释:就像打字机一样,边想边打字,不用等全部想完才能看到内容。
 * {"question": "给我讲一个200字的童话故事"}
 */
@PostMapping(value = "/stream", produces = "text/event-stream")
public Flux<String> streamChat(@RequestBody ChatRequest request) {
    return chatClient.prompt()
            .user(request.getQuestion())
            .stream()           // 开启流式模式
            .content();         // 获取 token 流
}

/**
 * 4. 流式输出(带元数据)
 * 如果你既想要打字机效果,又想拿到每个片段的元数据,
 * 可以用 chatResponse() 替代 content()。
 */
@PostMapping(value = "/stream/detail", produces = "text/event-stream")
public Flux<ChatResponse> streamDetailChat(@RequestBody ChatRequest request) {
    return chatClient.prompt()
            .user(request.getQuestion())
            .stream()
            .chatResponse();    // 每个 chunk 都包含元数据
}

2.Image文生图模型

yml配置

yml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
      # ====================== 图像生成(文生图) ======================
      image:
        enabled: true
        options:
          model: wanx-v1
          n: 1  #数量
          # 图片尺寸:['1024*1024', '720*1280', '1280*720', '768*1152']
          width: 1280
          height: 720
          style: <3d cartoon>    # 风格

调用测试

java 复制代码
/**
 * 文生图image
 */
@RestController
@RequestMapping("/api/image")
public class ImageChatController {


    @Autowired
    private ImageModel imageModel;


    /**
     * imageModel对象会自动使用默认yml配置
     * 或使用DashScopeImageModel也可以
     */
    @GetMapping("/generate-image")
    public String generateImage(@RequestParam String prompt) {
        ImagePrompt imagePrompt = new ImagePrompt(prompt);
        ImageResponse response = imageModel.call(imagePrompt);
        String base64Image = response.getResult().getOutput().getB64Json(); // 或获取Base64编码
        // 假设我们只取第一张图片的URL
        return response.getResult().getOutput().getUrl(); // 返回图片URL,前端可以通过这个URL显示图片
    }

    /**
     * 自定义覆盖默认配置
     */
    @GetMapping("/generate-image2")
    public String generateImage2(@RequestParam String prompt) {
        //自定义配置
        DashScopeImageOptions options = DashScopeImageOptions
                .builder()
                .model("qwen-image-edit-plus")
                .build();
        ImagePrompt imagePrompt = new ImagePrompt(prompt,options);
        ImageResponse response = imageModel.call(imagePrompt);
        String base64Image = response.getResult().getOutput().getB64Json(); // 或获取Base64编码
        // 假设我们只取第一张图片的URL
        return response.getResult().getOutput().getUrl(); // 返回图片URL,前端可以通过这个URL显示图片
    }

}

3.Audio语音模型

yml配置的voice音色属性会被默认值覆盖,需要手动指定,可能是bug,优先自定义设置

yml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
     # ==================== 音频配置 ====================
      audio:
        speech:
          options:
            # cosyvoice-v3-flash 是阿里云的通义千问系列模型,采用异步合成模式。
            # 当你发送合成请求后,模型会立即返回一个包含音频文件URL的响应,而不是等待音频合成完成再返回数据。
            # qwen3-tts-flash(同步) 因此,你需要在收到响应后,根据URL去下载音频文件。
            model: cosyvoice-v3-flash    # 语音合成模型 qwen3-tts-flash(同步)  cosyvoice-v3-flash
            # 音色 Marcus等,cosyvoice-v3-flash需要传id而不是通用名
            # audio.speech.options.voice 配置项无法绑定,会丢失,永远用默认值覆盖!大坑
            voice: cosyvoice-v3-flash-xxxxxxxxxxxxxxxx
            format: mp3    # 音频格式: wav/mp3
            speed: 1.0    # 语速 (0.5~2.0)
            volume: 80      #音量
java 复制代码
/**
 * 阿里云百炼 TTS 语音合成 完整测试控制器
 */
@RestController
@RequestMapping("/ai/tts")
public class TtsCompleteController {

    /**
     * 注入 Spring AI 标准语音合成接口
     * 底层实现类 = DashScopeAudioSpeechModel(阿里云百炼)
     */
    @Autowired
    private TextToSpeechModel textToSpeechModel;



    /**
     * 语音合成模型返回音频url
     * @param text 要合成语音的文字
     * @return url
     */
    @GetMapping("/basicUrl")
    public String basicUrl(@RequestParam(defaultValue = "欢迎使用阿里云百炼语音合成") String text) {
        //会和yml的全局配置进行合并,优先这里的
        DashScopeAudioSpeechOptions options = DashScopeAudioSpeechOptions.builder()
                .model("qwen3-tts-flash") // 语音模型
                .voice("Cherry")         // 动态音色
                .build();

        // 1. 构建语音合成请求(使用 yml 里的默认配置:模型、音色、语速)
        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text,options);
        // 2. 调用百炼接口,执行语音合成,返回响应结果
        DashScopeTTSApiSpec.DashScopeAudioTTSResponse response =
                (DashScopeTTSApiSpec.DashScopeAudioTTSResponse) textToSpeechModel.call(prompt);
        // 获取url
        return response.getOutput().audio().url();
    }




    /**
     * 实时语音合成模型直接返回音频流(不支持同步调用)
     * @param text  文本
     * @return 自定义语音
     */
    @GetMapping("/ttsStream")
    public ResponseEntity<Resource> ttsStream(@RequestParam(defaultValue = "欢迎使用阿里云百炼语音合成") String text) {

        //会和yml的全局配置进行合并,优先这里的
        DashScopeAudioSpeechOptions options = DashScopeAudioSpeechOptions.builder()
                //cosyvoice-v3-flash 实时模型必须传音色id,不能传通用名
                .voice("longyan_v3")
                .build();

        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text,options);


        Flux<byte[]> audioFlux = textToSpeechModel.stream(prompt)
                .map(res -> res.getResult().getOutput());

        // 把流式音频合并成完整 MP3
        byte[] fullAudio = audioFlux.collectList()
                .map(chunks -> {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    chunks.forEach(baos::writeBytes);
                    return baos.toByteArray();
                }).block();

        // 返回给浏览器播放
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=tts.mp3")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(new ByteArrayResource(fullAudio));
    }


    /**
     * 流式语音合成(长文本专用,边合成边播放)
     * @param text 长文本
     * @return 流式音频分片(Flux)
     */
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<byte[]> stream(@RequestParam(defaultValue = "欢迎使用阿里云百炼语音合成") String text) {

        // 构建请求,yml里面的音色配置不会生效,会采用默认
        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text);

        // 一边合成一边返回音频片段,不用等全部完成
        return textToSpeechModel.stream(prompt)
                .map(response ->
                        response.getResult().getOutput());
    }

    /**
     * 多音色拼接接口
     * 模型自动适配,全部使用 stream() 方式获取音频
     */
    @GetMapping("/multi-voice")
    public ResponseEntity<Resource> multiVoice() {

        // ====================== 第一段语音:音色1 ======================
        DashScopeAudioSpeechOptions options1 = DashScopeAudioSpeechOptions.builder()
                .model("qwen3-tts-flash") // 语音模型
                .voice("Cherry")         // 动态音色
                .build();

        byte[] part1 = getAudioBytes("我是智能语音一号。", options1);

        // ====================== 第二段语音:音色2 ======================
        DashScopeAudioSpeechOptions options2 = DashScopeAudioSpeechOptions.builder()
                .model("qwen3-tts-flash") // 语音模型
                .voice("Li")         // 动态音色
                .build();

        byte[] part2 = getAudioBytes("我是智能语音二号。", options2);

        // ====================== 拼接两段音频 ======================
        byte[] allAudio = new byte[part1.length + part2.length];
        System.arraycopy(part1, 0, allAudio, 0, part1.length);
        System.arraycopy(part2, 0, allAudio, part1.length, part2.length);

        // ====================== 返回给浏览器播放 ======================
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=multi-voice.mp3")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(new ByteArrayResource(allAudio));
    }

    /**
     * 【通用工具方法】
     * 从 TTS 流式接口中获取完整音频字节
     */
    private byte[] getAudioBytes(String text, DashScopeAudioSpeechOptions options) {
        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text, options);

     
        Flux<byte[]> audioFlux = textToSpeechModel.stream(prompt)
                .map(response -> response.getResult().getOutput());

        // 收集所有音频分片 → 合并成完整音频
        return audioFlux.collectList()
                .map(chunks -> {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    chunks.forEach(baos::writeBytes);
                    return baos.toByteArray();
                }).block();
    }

}

4.Embedding向量/文本嵌入模型

文本(句子 / 段落)转换成一段数字向量,让计算机能理解语义、做相似度匹配。常用于RAG 知识库检索,语义搜索,文档去重,推荐系统

yml配置

spring-ai-alibaba底层会默认自动配置,即使注释也会自动创建,且 enabled属性不生效

yml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
      # ==================== 文本嵌入模型配置 ====================
      embedding:
        # 是否启用嵌入功能(默认 true)alibaba的该配置不生效,依旧会启用
        enabled: true
        options:
          # 嵌入模型型号
          model: text-embedding-v1
          dimensions: 1536    # 向量维度(固定1536,不用改)

注册向量库

java 复制代码
    /**
     * 注册 内存向量库 Bean,重启会清空,生产环境用redis等
     * 自动注入 EmbeddingModel(百炼文本嵌入模型)
     */
    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel) {
        return SimpleVectorStore.builder(embeddingModel).build();
    }

使用

java 复制代码
/**
 * 阿里云百炼 文本嵌入(Embedding) 测试控制器
 * 功能:文本转向量、批量转向量、RAG语义检索测试
 */
@RestController
@RequestMapping("/ai/embedding")
public class EmbeddingController {

    // 注入 百炼文本嵌入模型(自动根据yml配置初始化)
    @Autowired
    private EmbeddingModel embeddingModel;

    // 注入向量库(用于RAG检索)
    @Autowired
    private VectorStore vectorStore;

    // ====================== 1. 单文本 生成向量 ======================
    @GetMapping("/single")
    public List<Double> singleEmbedding(@RequestParam(value = "text", defaultValue = "我爱Spring AI") 
                                        String text) {
        // 1. 调用百炼接口,将文本转为 1536 维 float[] 向量
        float[] floatVector = embeddingModel.embed(text);

        // 2. 把 float[] 转成 List<Double> 方便前端使用
        List<Double> result = new ArrayList<>();
        for (float f : floatVector) {
            result.add((double) f);
        }

        return result;
    }

    // ====================== 2. 批量文本 生成向量 ======================
    @GetMapping("/batch")
    public List<List<Double>> batchEmbedding() {
        // 1. 构造批量测试文本
        List<String> texts = List.of(
                "机器学习是人工智能的一个分支",
                "深度学习是机器学习的子集",
                "Spring AI 简化AI开发流程"
        );

        // 2. 批量生成向量:返回 List<float[]>
        List<float[]> floatVectors = embeddingModel.embed(texts);

        // 3. 转成前端友好的 List<List<Double>> 格式
        List<List<Double>> result = new ArrayList<>();
        for (float[] fv : floatVectors) {
            List<Double> doubles = new ArrayList<>();
            for (float f : fv) {
                doubles.add((double) f);
            }
            result.add(doubles);
        }

        return result;
    }

    // ====================== 3. RAG 知识库:向量化存入向量库 ======================
    @GetMapping("/addDocs")
    public String addDocuments() {
        // 构造知识库文档
        List<Document> documents = List.of(
                new Document("Spring AI Alibaba 可以对接阿里云百炼大模型"),
                new Document("文本嵌入可以把文字变成向量用于语义搜索"),
                new Document("RAG = 检索增强生成,让AI回答更精准")
        );

        // 向量化并保存到向量库
        vectorStore.add(documents);

        return "文档向量化入库成功!共" + documents.size() + "条";
    }

    // ====================== 4. RAG 语义检索(最核心功能) ======================
    @GetMapping("/search")
    public List<Document> search(@RequestParam(value = "query", defaultValue = "什么是RAG?") String query) {
        // 1. 用户输入问题
        // 2. 框架自动:问题转向量 → 向量库相似度检索 → 返回最相关的文档
        return vectorStore.similaritySearch(query);
    }
}
相关推荐
南湖北漠2 小时前
记录生活中的那些小事(佚名)
网络·人工智能·计算机网络·其他·安全·生活
随风,奔跑2 小时前
Spring Security
java·后端·spring
清水白石0082 小时前
《解锁 Python 潜能:从核心语法到 AI 服务层架构的工业级进阶与实战》
人工智能·python·架构
shuair2 小时前
openclaw对接飞书
ai·飞书·openclaw
大连好光景2 小时前
学会评估模型的拟合状态和泛化能力
人工智能·机器学习
老兵发新帖2 小时前
Hermes:openclaw的最佳替代之基于源码部署的飞书配置
人工智能·飞书
weixin_513449962 小时前
walk_these_ways项目学习记录第七篇(通过行为多样性 (MoB) 实现地形泛化)--核心环境下
人工智能·python·学习
智在碧得2 小时前
碧服智能体进化:AI赋能意图识别能力,“一问”更智能
大数据·人工智能·机器学习
人工智能AI技术2 小时前
Visual Studio Code 1.114 更新:AI 聊天体验全面优化
人工智能