基于 Spring AI 的多平台多模型动态切换实战

背景与目标

  • 背景: 企业在落地生成式 AI 时,往往需要在不同平台(如 DeepSeek、阿里云百炼 DashScope、本地 Ollama)与多款模型之间灵活切换,以平衡效果、成本与可用性。
  • 目标 : 基于 Spring AI 提供统一的服务,支持:
    • 通过端点 /chat 按名称直切多个模型(固定预置的 ChatClient)。
    • 通过端点 /chat2 指定平台与模型并自定义 temperature,支持流式返回。

本文基于模块 03-multi-model-switch 的完整实现展开说明,覆盖依赖、配置、核心代码、接口调用与常见问题。


模块与依赖

模块:03-multi-model-switch

关键依赖包括 Spring Boot Web、Spring AI DeepSeek、Alibaba DashScope、Ollama、测试、Lombok:

xml 复制代码
<dependencies>
    <!--deepseek-->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-deepseek</artifactId>
    </dependency>

    <!--百炼-->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    </dependency>

    <!--ollama-->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-ollama</artifactId>
    </dependency>

    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--单元测试-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

配置文件

src/main/resources/application.properties 中配置了 API Key、默认模型与本地 Ollama 地址等:

properties 复制代码
spring.application.name=chat-client

# deepseek
spring.ai.deepseek.api-key=${DEEPSEEK_API_KEY}
spring.ai.deepseek.chat.options.model=deepseek-reasoner

# dashscope (阿里云百炼)
spring.ai.dashscope.api-key=${DASHSCOPE_API_KEY}
spring.ai.dashscope.chat.options.model=qwen-plus

# ollama (本地)
spring.ai.ollama.base-url=http://127.0.0.1:11434
spring.ai.ollama.chat.model=qwen3:0.6b

环境变量说明:

  • DEEPSEEK_API_KEY: DeepSeek 平台密钥
  • DASHSCOPE_API_KEY: 阿里云百炼密钥
  • 如果使用本地 Ollama,请确保 http://127.0.0.1:11434 可访问,且事先拉取好模型(如 qwen3:0.6b)。

核心设计与代码解读

1) 统一模型装配与客户端暴露 AiConfig

提供多个 ChatClient Bean:deepseekR1(推理/Reasoner)、deepseekV3(聊天/Chat)、ollama(本地模型)。通过不同 ChatModel 与默认 options,形成"即插即用"的客户端:

java 复制代码
@Configuration
public class AiConfig {

    @Bean
    public ChatClient deepseekR1(DeepSeekChatProperties chatProperties) {
        DeepSeekApi deepSeekApi = DeepSeekApi.builder()
                .apiKey(System.getenv("DEEPSEEK_API_KEY"))
                .build();

        DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
                .deepSeekApi(deepSeekApi)
                .defaultOptions(DeepSeekChatOptions.builder()
                        .model(DeepSeekApi.ChatModel.DEEPSEEK_REASONER)
                        .build())
                .build();

        return ChatClient.builder(deepSeekChatModel).build();
    }

    @Bean
    public ChatClient deepseekV3() {
        DeepSeekApi deepSeekApi = DeepSeekApi.builder()
                .apiKey(System.getenv("DEEPSEEK_API_KEY"))
                .build();

        DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
                .deepSeekApi(deepSeekApi)
                .defaultOptions(DeepSeekChatOptions.builder()
                        .model(DeepSeekApi.ChatModel.DEEPSEEK_CHAT)
                        .build())
                .build();

        return ChatClient.builder(deepSeekChatModel).build();
    }

    @Bean
    public ChatClient ollama(@Autowired OllamaApi ollamaApi,
                             @Autowired OllamaChatProperties options) {
        OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
                .ollamaApi(ollamaApi)
                .defaultOptions(OllamaOptions.builder()
                        .model(options.getModel())
                        .build())
                .build();

        return ChatClient.builder(ollamaChatModel).build();
    }
}

要点:

  • DeepSeek 通过 DeepSeekApi + DeepSeekChatModel 以不同默认 model 暴露为两个 ChatClient
  • Ollama 使用 OllamaApi 和配置中的 model 作为默认本地模型。

2) 简单模型切换端点 /chat(按 Bean 名直查)

通过自动注入 Map<String, ChatClient>,用 model 参数直接索引对应 ChatClient

java 复制代码
@Slf4j
@RestController
public class MultiModelsController {

    @Autowired
    private Map<String, ChatClient> chatClientMap;

    @PostConstruct
    public void init(){
        chatClientMap.forEach((key, value) ->
                log.info("model: {} : {}", key, value.getClass().getName()));
    }

    @GetMapping("/chat")
    String generation(@RequestParam String message,
                      @RequestParam String model) {
        ChatClient chatClient = chatClientMap.get(model);
        String content = chatClient.prompt().user(message).call().content();
        log.info("model: {} : {}", model, content);
        return content;
    }
}

可用 model 值示例(取决于 Spring 注入命名):deepseekR1deepseekV3ollama

调用示例:

bash 复制代码
curl 'http://localhost:8080/chat?model=deepseekV3&message=用50字解释大语言模型'

3) 平台+模型+温度+流式端点 /chat2

支持 platformmodeltemperature 的动态指定,并以流式文本输出:

java 复制代码
@RestController
public class MultiPlatformAndModelController {

    HashMap<String, ChatModel> platforms = new HashMap<>();

    public MultiPlatformAndModelController(DashScopeChatModel dashScopeChatModel,
                                           DeepSeekChatModel deepSeekChatModel,
                                           OllamaChatModel ollamaChatModel) {
        platforms.put("dashscope", dashScopeChatModel);
        platforms.put("ollama", ollamaChatModel);
        platforms.put("deepseek", deepSeekChatModel);
    }

    @RequestMapping(value = "/chat2", produces = "text/stream;charset=UTF-8")
    public Flux<String> chat(String message, MultiPlatformAndModelOptions options) {
        String platform = options.getPlatform();
        ChatModel chatModel = platforms.get(platform);

        ChatClient chatClient = ChatClient.builder(chatModel)
                .defaultOptions(ChatOptions.builder()
                        .temperature(options.getTemperature())
                        .model(options.getModel())
                        .build())
                .build();

        return chatClient.prompt().user(message).stream().content();
    }
}

options 参数对象:

java 复制代码
@Data
public class MultiPlatformAndModelOptions {
    private String platform;
    private String model;
    private Double temperature;
}

调用示例(浏览器/curl 均可):

  • DeepSeek(Chat):/chat2?platform=deepseek&model=deepseek-chat&temperature=0.7&message=你是谁
  • Ollama(本地 qwen3):/chat2?platform=ollama&model=qwen3:0.6b&temperature=0.7&message=你是谁
  • DashScope(通义千问):/chat2?platform=dashscope&model=qwen-plus&temperature=0.7&message=你是谁
bash 复制代码
curl -N 'http://localhost:8080/chat2?platform=ollama&model=qwen3:0.6b&temperature=0.3&message=用三点概括面向对象编程核心'

4) 启动类

java 复制代码
@SpringBootApplication
public class MultiModelApplication {
    public static void main(String[] args) {
        SpringApplication.run(MultiModelApplication.class, args);
    }
}

运行与验证

  1. 设置环境变量(macOS/zsh 举例):
bash 复制代码
export DEEPSEEK_API_KEY='你的deepseek_key'
export DASHSCOPE_API_KEY='你的dashscope_key'
  1. 确保本地 Ollama(可选)正常运行,并已拉取模型:
bash 复制代码
ollama run qwen3:0.6b
  1. 启动应用:
bash 复制代码
cd /Volumes/artisan/code/2025/spring-ai-artisan/03-multi-model-switch
./mvnw spring-boot:run
  1. 验证直切端点:
bash 复制代码
curl 'http://localhost:8080/chat?model=deepseekR1&message=解释下RAG是什么'
  1. 验证平台+模型+流式端点:
bash 复制代码
curl -N 'http://localhost:8080/chat2?platform=dashscope&model=qwen-plus&temperature=0.2&message=用两句话说明微服务的优缺点'

设计要点与最佳实践

  • 解耦平台与模型 :通过 platforms 映射与 ChatOptions.model,将平台选择与具体模型名解耦,便于扩展。
  • 流式输出:对长文本响应更友好,前端可边收边显,降低等待感。
  • 多 Bean 注入 Map :按名称索引 ChatClient 实例,快速实现"按名切换"。
  • 安全与配置:生产环境使用更安全的密钥管理(KMS、Vault)。模型名应与平台兼容,不匹配会报错或无效。
  • 容错 :对 platformmodel 判空与合法性校验;上游平台不通时返回可读错误并降级(如回退到本地 Ollama)。

常见问题(FAQ)

  • 为什么 /chatmodel 用 Bean 名而非实际模型名? /chat 是"按 Bean 名直切"的简单方式,适合预置几个典型配置;实际模型名通过 Bean 内默认 options 指定。
  • /chat2model 必须与平台支持的模型名一致吗? 是。比如 platform=ollamamodel 应是本地已就绪的 Ollama 模型名。
  • 如何新增平台? 新增对应 ChatModel 的依赖与 Bean,将其放入 platforms 映射,并在配置中补充默认参数即可。
  • 如何控制输出长度、系统提示词等?ChatOptions 中继续填充如 maxTokenssystem 等参数;或在 prompt() 中添加 system/assistant 指令。

至此,基于 Spring AI 的多平台多模型动态切换方案已经打通:可在 DeepSeek、DashScope 与本地 Ollama 之间灵活切换,支持按名直切与平台+模型+温度的流式对话,便于在实际项目中快速对比与落地。

相关推荐
咸菜一世3 小时前
scala中class的使用
后端
豆浆Whisky3 小时前
反射还是代码生成?Go反射使用的边界与取舍|Go语言进阶(11)
后端·go
Penge6663 小时前
MySQL 分页优化
后端
华仔啊3 小时前
前后端防重复提交的 6 种落地实现:从按钮禁用到 AOP 全自动防护
java·后端
程序新视界3 小时前
MySQL的OR条件查询不走索引及解决方案
数据库·后端·mysql
Pr Young4 小时前
MVCC 多版本并发控制
数据库·后端·mysql
IT_陈寒4 小时前
Java并发编程避坑指南:7个常见陷阱与性能提升30%的解决方案
前端·人工智能·后端
牧码岛4 小时前
服务端之NestJS接口响应message编写规范详解、写给前后端都舒服的接口、API提示信息标准化
服务器·后端·node.js·nestjs
星秀日5 小时前
框架--SpringBoot
java·spring boot·后端