☰
Spring AI 多模型适配实战:通义千问与 DeepSeek 统一 ChatModel 配置
2026/9/28 18:31:34 网站建设 项目流程

1. 为什么 Spring AI 多模型适配总在配置阶段翻车

Spring AI 把不同厂商的大模型 API 抽象成统一的ChatModel接口,这件事本身很优雅。但真正落到 Java 后端项目里,同时接入通义千问和 DeepSeek 时,问题往往不在业务代码,而在配置层:两个模型走的是不同的 starter、不同的 base-url、不同的参数命名,甚至同一个ChatModel类型在容器里出现多个 Bean 时,Spring 直接抛No qualifying bean。

我见过太多项目卡在这一步——单接一个模型跑得挺好,一旦要"通义千问 + DeepSeek 双活",application.yml就开始打架,@Autowired不知道注入哪个,切换模型要改代码重新打包。这跟 Spring AI 想表达的"改一行配置就换模型"完全背道而驰。

这篇面向需要多模型切换的 Java 后端场景,目标很明确:给你一套可复制的application.yml与ChatModel骨架配置,通过统一 Key/API 通道 TaoToken 完成双模型调用与切换验证,一次配置跑通两个模型。适合正在做 AI 应用、需要成本与质量动态权衡、又不想为每个厂商维护一套对接代码的后端同学。

核心检索词先摆出来:Spring AI 是 Spring 生态里对接大模型的统一抽象层,能做什么——把通义千问、DeepSeek 这类厂商 API 收敛成同一个ChatModel接口;适合谁——需要多模型路由、故障切换、成本控制的 Java 后端团队。

2. TaoToken 前置:统一 Key 与 API 通道

多模型适配最烦的一点是 Key 管理。通义千问一个 Key、DeepSeek 一个 Key,每个都要单独申请、单独配置、单独轮换,项目里环境变量越堆越多。TaoToken 在这里的作用是提供一条统一的 API 通道和一个统一 Key,让 Spring AI 侧只需要维护一份凭证,就能同时调用通义千问和 DeepSeek。

官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=

API 基础地址:https://taotoken.net/api

需要提前准备的东西不多:一个 TaoToken 账号、一个 API Key、以及本地能跑起来的 Spring Boot 项目。API Key 在控制台的 API Keys 页面创建,创建后立刻复制保存,页面刷新后就看不到完整 Key 了。

  • 模型对话(验证模型是否通):https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat
  • API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc

注意:TaoToken 是合规的 API 聚合通道,Key 只放在服务端环境变量或配置中心,绝对不要写进前端代码或提交到 Git 仓库。

3. 可复制配置:application.yml 与 ChatModel 骨架

3.1 Maven 依赖

Spring AI 目前建议锁定1.0.0-M6,不要写latest,里程碑版本之间 API 差异不小。通义千问用 DashScope starter,DeepSeek 走 OpenAI 兼容 starter,两者都通过 TaoToken 的 base-url 接入。

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-dashscope</artifactId> <version>1.0.0-M6</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-openai</artifactId> <version>1.0.0-M6</version> </dependency>

3.2 application.yml 双模型配置

关键点在于:两个模型都指向 TaoToken 的 API 地址,用同一个 Key,只是model字段不同。这样配置层就统一了,切换模型只改model值。

spring: ai: dashscope: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: enabled: true options: model: qwen-plus temperature: 0.7 max-tokens: 2000 openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api/v1 chat: enabled: true options: model: deepseek-chat temperature: 0.7 max-tokens: 4096

环境变量在启动时注入:

export TAOTOKEN_API_KEY=sk-你的Key

3.3 多 ChatModel Bean 骨架配置

当容器里同时存在多个ChatModel实现时,必须显式区分,否则注入报错。用@Qualifier给每个 Bean 命名,业务层按名字取用。

@Configuration public class MultiModelConfig { @Bean("qwenChatModel") @Primary public ChatModel qwenChatModel(DashScopeChatProperties properties) { DashScopeChatOptions options = DashScopeChatOptions.builder() .withModel("qwen-plus") .withTemperature(0.3) .build(); return new DashScopeChatModel( new DashScopeApi(properties.getApiKey()), options); } @Bean("deepseekChatModel") public ChatModel deepseekChatModel(OpenAiChatProperties properties) { OpenAiApi api = new OpenAiApi( "https://taotoken.net/api/v1", properties.getApiKey()); OpenAiChatOptions options = OpenAiChatOptions.builder() .withModel("deepseek-chat") .withTemperature(0.7) .build(); return new OpenAiChatModel(api, options); } }

@Primary标记通义千问为默认模型,这样没有@Qualifier的地方也能正常注入,避免启动失败。

3.4 业务层统一调用骨架

业务代码只依赖ChatModel接口,通过@Qualifier选择实现。下面这个 Controller 演示了同一套逻辑如何切换两个模型。

@RestController @RequestMapping("/api/chat") public class ChatController { private final ChatModel qwenChatModel; private final ChatModel deepseekChatModel; public ChatController( @Qualifier("qwenChatModel") ChatModel qwenChatModel, @Qualifier("deepseekChatModel") ChatModel deepseekChatModel) { this.qwenChatModel = qwenChatModel; this.deepseekChatModel = deepseekChatModel; } @PostMapping("/qwen") public String chatWithQwen(@RequestBody String question) { return qwenChatModel.call(question); } @PostMapping("/deepseek") public String chatWithDeepSeek(@RequestBody String question) { return deepseekChatModel.call(question); } }

到这里,配置层已经统一:一个 Key、一条通道、两个模型,业务代码零重复。

4. 验证请求:一次配置跑通两个模型

4.1 启动与健康检查

启动 Spring Boot 应用,观察日志里两个ChatModelBean 是否都注册成功。如果看到No qualifying bean of type 'ChatModel',说明@Qualifier名字对不上,回到 3.3 检查 Bean 名称。

4.2 用 curl 验证双模型

先验证通义千问:

curl -X POST http://localhost:8080/api/chat/qwen \ -H "Content-Type: text/plain" \ -d "用一句话解释什么是依赖倒置"

再验证 DeepSeek:

curl -X POST http://localhost:8080/api/chat/deepseek \ -H "Content-Type: text/plain" \ -d "用一句话解释什么是依赖倒置"

两个请求都返回正常文本,说明统一 Key 通道生效,双模型跑通。如果其中一个返回 401,检查TAOTOKEN_API_KEY是否注入成功;返回 404,检查 base-url 是否写成了/api/v1还是/api,DashScope 和 OpenAI 兼容端点的路径规则不同。

4.3 流式输出验证

多模型适配里流式输出是高频需求,验证一下stream方法:

@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream(@RequestBody String question) { Prompt prompt = new Prompt(new UserMessage(question)); return deepseekChatModel.stream(prompt) .map(resp -> resp.getResult().getOutput().getContent()); }

用浏览器或 curl 访问,能看到逐字返回,说明流式通道也走通了。

4.4 运行时动态切换

如果不想为每个模型写一个接口,可以注入Map<String, ChatModel>,运行时按名字选:

@Autowired private Map<String, ChatModel> chatModels; public String route(String modelName, String question) { ChatModel model = chatModels.getOrDefault(modelName, chatModels.get("qwenChatModel")); return model.call(question); }

这样新增模型只需要加一个 Bean,路由层不用改。

5. 本篇常见错排查

5.1 多 Bean 注入冲突

报错expected single matching bean but found 2,原因是容器里有两个ChatModel,Spring 不知道注入哪个。三种解法:给默认模型加@Primary;注入处加@Qualifier("beanName");注入Map<String, ChatModel>按名取。推荐组合使用@Primary+@Qualifier。

5.2 base-url 路径写错

DashScope 和 OpenAI 兼容端点的 base-url 规则不同。DashScope 用https://taotoken.net/api,OpenAI 兼容用https://taotoken.net/api/v1。写错会返回 404 或路径拼接异常。实测下来,把两个 base-url 分别配置、不要共用同一个变量,最省心。

5.3 API Key 未注入

api-key读的是${TAOTOKEN_API_KEY},如果环境变量没设置,启动时不会报错,但调用时返回 401。排查方法:在启动类里打印System.getenv("TAOTOKEN_API_KEY")是否为 null,或者改用application-local.yml本地覆盖。

5.4 模型名不匹配

通义千问的模型名是qwen-plus、qwen-max这类,DeepSeek 是deepseek-chat、deepseek-reasoner。写错模型名会返回模型不存在。切换模型时只改model字段,不要动 base-url 和 Key。

5.5 超时与限流

高峰期调用慢或返回 429,说明触发了限流。给RestClient配置连接超时 5 秒、读取超时 60 秒,并在路由层加降级:通义千问限流时自动切到 DeepSeek。熔断器连续失败 5 次后打开,2 分钟后半开重试。

5.6 Spring AI 版本差异

0.8.x和1.0.x的 API 差异很大,比如ChatClient构造方式、Prompt包装方式都变了。锁死1.0.0-M6,不要混用不同版本的 starter,否则会出现方法找不到的编译错误。

6. 多模型路由与长期编码场景的落地建议

配置跑通只是第一步,生产环境真正需要的是路由策略。成本敏感场景用 DeepSeek,质量优先场景用通义千问,敏感数据走本地模型,故障时自动切换。这些策略都可以在ChatModel抽象之上用策略模式实现,业务代码依然只依赖接口。

如果你打算把多模型能力接到长期编码或 Agent 工作流里,建议用 Coding Plan 统一管理调用配额与模型编排:

  • Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
  • 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console
  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc

最后留一个我踩过的坑:多模型项目里,@ConfigurationProperties统一管理配置比散落在各个@Value里强太多。把每个模型的 api-key、base-url、model、temperature、timeout 收进一个Map<String, ModelConfig>,新增模型只加一段 yaml,代码零改动。这一步做完,你的 Spring AI 多模型适配才算真正可维护。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询