java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot多AI模型共存与SSE流式输出

SpringBoot实现多AI模型共存与SSE流式输出的解决方案

作者:Just_Do_It°

在当下 AI 应用开发中,经常会遇到需要同时接入多个大模型的场景,比如用通义千问做中文对话、用 DeepSeek 处理复杂推理,本文将带你从零搭建一个 Spring Boot 项目,优雅地解决多模型共存 + SSE 流式输出两大核心需求,并给出两种主流接入方案,需要的朋友可以参考下

引言

在当下 AI 应用开发中,经常会遇到需要同时接入多个大模型的场景,比如用通义千问做中文对话、用 DeepSeek 处理复杂推理。Spring AI Alibaba 为我们屏蔽了不同厂商 API 的差异,但多模型并存也带来了 ChatModel Bean 冲突、流式输出适配等问题。本文将带你从零搭建一个 Spring Boot 项目,优雅地解决“多模型共存 + SSE 流式输出”两大核心需求,并给出两种主流接入方案。

一、核心知识点前置

在动手写代码之前,先梳理几个关键概念:

多模型共存
当容器中存在多个 ChatModel Bean 时,必须手动指定 Bean 名称,然后使用 @Resource(name="xxx") 按名称精准注入,放弃 Spring Boot 的自动装配。

SSE (Server-Sent Events)
一种基于 HTTP 的单向长连接协议,天然适合大模型“打字机”效果的流式返回。浏览器端直接监听接口,源源不断地接收文本片段,无需 WebSocket 的复杂握手。

两条实现路线

响应式环境要求
返回类型 Flux<String> 属于响应式流,控制器必须运行在 WebFlux 环境下(引入 spring-boot-starter-webflux),普通 Spring MVC 无法推送流数据。

二、整体方案设计

我们面对两种典型的集成模式,区别在于引入的 Starter 数量:

方案 A:只引入百炼一个 Starter
Maven 中仅添加 spring-ai-alibaba-starter-dashscope。所有模型(包括通义千问、DeepSeek 等)都通过阿里云百炼平台调用,只需在代码中通过 model 参数切换。适合企业统一管控、希望在阿里云上集中管理模型调用。

方案 B:同时引入百炼和 DeepSeek 两个 Starter
Maven 中同时添加 spring-ai-alibaba-starter-dashscope 和 spring-ai-deepseek-starter。通义千问走百炼,DeepSeek 直连 DeepSeek 官网,两套独立的配置和连接,适合需要发挥各厂商原生能力的场景。

本文会同时覆盖这两种方案,你可以根据实际情况选其一。

三、Maven 依赖配置

方案 A:仅引入百炼 Starter(一个 Starter)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>

方案 B:引入百炼 + DeepSeek 两个 Starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-deepseek-starter</artifactId>
</dependency>

四、application.yml 配置

方案 A(仅百炼)

spring:
  ai:
    dashscope:
      api-key: sk-xxxx
      chat:
        options:
          model: qwen-turbo   # 默认模型,实际调用时可覆盖

方案 B(双 key)

spring:
  ai:
    dashscope:
      api-key: sk-xxxx
      chat:
        options:
          model: qwen-turbo
    deepseek:
      api-key: sk-xxxx
      chat:
        options:
          model: deepseek-chat

五、配置类:手工打造多模型 Bean

这是本文最关键的一步。为避免自动装配的混乱,我们完全手动创建所有 Bean 并命名。

5.1 方案 A:一个 Starter 多模型 Bean(均走百炼)

@Configuration
public class DashScopeMultiModelConfig {
    @Bean
    public DashScopeApi dashScopeApi(DashScopeProperties properties) {
        return DashScopeApi.builder()
                .apiKey(properties.getApiKey())
                .build();
    }
    // Qwen 模型 Bean
    @Bean("qwen")
    public ChatModel qwenChatModel(DashScopeApi api) {
        DashScopeChatOptions options = DashScopeChatOptions.builder()
                .withModel("qwen-turbo")
                .build();
        return new DashScopeChatModel(api, options);
    }
    // 通过百炼调用的 DeepSeek 模型(需在百炼平台开通)
    @Bean("deepseek-via-bailian")
    public ChatModel deepSeekViaBailianModel(DashScopeApi api) {
        DashScopeChatOptions options = DashScopeChatOptions.builder()
                .withModel("deepseek-chat")
                .build();
        return new DashScopeChatModel(api, options);
    }
    @Bean("qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你是通义千问,简洁回答问题")
                .build();
    }
    @Bean("deepseekBailianChatClient")
    public ChatClient deepSeekBailianClient(@Qualifier("deepseek-via-bailian") ChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你是 DeepSeek 大模型")
                .build();
    }
}

5.2 方案 B:两个 Starter 独立 Bean(百炼 + DeepSeek 直连)

@Configuration
public class MultiLLMConfig {
    // ================= 通义千问(百炼) =================
    @Bean
    public DashScopeApi dashScopeApi(DashScopeProperties props) {
        return DashScopeApi.builder()
                .apiKey(props.getApiKey())
                .build();
    }
    @Bean("qwen")
    public ChatModel qwenChatModel(DashScopeApi api, DashScopeProperties props) {
        return new DashScopeChatModel(api, props.getChat().getOptions());
    }
    @Bean("qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你是通义千问,简洁回答问题")
                .build();
    }
    // ================= DeepSeek 直连 =================
    @Bean
    public DeepSeekApi deepSeekApi(DeepSeekProperties props) {
        return DeepSeekApi.builder()
                .apiKey(props.getApiKey())
                .build();
    }
    @Bean("deepseek")
    public ChatModel deepseekChatModel(DeepSeekApi api, DeepSeekProperties props) {
        return new DeepSeekChatModel(api, props.getChat().getOptions());
    }
    @Bean("deepseekChatClient")
    public ChatClient deepseekChatClient(@Qualifier("deepseek") ChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你是 DeepSeek 大模型")
                .build();
    }
}

六、Controller:SSE 流式输出实战

下面展示基于方案 B 的四种典型接口,方案 A 只需要把注入的 Bean 名替换为对应的百炼 Bean 即可。

@RestController
public class StreamController {
    @Resource(name = "deepseek")
    private ChatModel deepseekChatModel;
    @Resource(name = "qwen")
    private ChatModel qwenChatModel;
    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekChatClient;
    @Resource(name = "qwenChatClient")
    private ChatClient qwenChatClient;
    // 1. DeepSeek ChatModel 流式 SSE
    @GetMapping(value = "/stream/deepseek-model", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamDeepSeekModel(@RequestParam(defaultValue = "你是谁") String question) {
        return deepseekChatModel.stream(question);
    }
    // 2. Qwen ChatModel 流式 SSE
    @GetMapping(value = "/stream/qwen-model", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamQwenModel(@RequestParam(defaultValue = "你是谁") String question) {
        return qwenChatModel.stream(question);
    }
    // 3. DeepSeek ChatClient 流式 SSE
    @GetMapping(value = "/stream/deepseek-client", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamDeepSeekClient(@RequestParam(defaultValue = "你是谁") String question) {
        return deepseekChatClient.prompt(question)
                .stream()
                .content();
    }
    // 4. Qwen ChatClient 流式 SSE
    @GetMapping(value = "/stream/qwen-client", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamQwenClient(@RequestParam(defaultValue = "你是谁") String question) {
        return qwenChatClient.prompt(question)
                .stream()
                .content();
    }
}

关键点解析:

七、前端对接示例(原生 JS)

const eventSource = new EventSource('/stream/deepseek-client?question=你好');
eventSource.onmessage = (event) => {
    document.getElementById('output').innerText += event.data;
};
eventSource.onerror = (err) => {
    eventSource.close();
};

八、方案对比与选型建议

对比维度方案 A(仅百炼 Starter)方案 B(百炼 + DeepSeek 两个 Starter)
引入的依赖仅 spring-ai-alibaba-starter-dashscopespring-ai-alibaba-starter-dashscope + spring-ai-deepseek-starter
配置复杂度需手动指定每个模型的 model 名称各 Starter 独立配置 api-key 和参数
厂商特性支持依赖百炼封装,可能丢失细节可调用各厂商原生参数和特性
适用场景统一管控、企业级项目需要利用厂商差异化能力

如果项目只需要简单对话,方案 A 更简洁;若需深度定制 prompt 参数、访问 DeepSeek 特有功能,方案 B 更灵活。

九、总结

本文完整演示了 Spring AI Alibaba 在多模型共存环境下的整合思路,重点解决了 Bean 冲突和 SSE 流式输出的落地细节。核心要点回顾:

掌握了这套方法,无论是同时接入 2 个还是 10 个大模型,都能轻松驾驭。希望这篇文章能帮助你在 AI 应用开发中少走弯路,快速搭建出健壮的流式对话服务。

以上就是SpringBoot实现多AI模型共存与SSE流式输出的解决方案的详细内容,更多关于SpringBoot多AI模型共存与SSE流式输出的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文