恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
SpringAI 集成 DeepSeek 与多模型切换 demo:TaoToken 统一 Key 配置实战
首页
资讯中心
/
SpringAI 集成 DeepSeek 与多模型切换 demo:TaoToken 统一 Key 配置实战
SpringAI 集成 DeepSeek 与多模型切换 demo:TaoToken 统一 Key 配置实战
发布时间:2026/9/26 13:17:29
1. 从一堆 Key 说起SpringAI 多模型切换的真实痛点如果你正在用 SpringAI 做 AI 应用大概率遇到过这个场景项目里先接了 DeepSeek跑通了对话过两天产品说想加个别的模型做对比或者代码场景想换一个更擅长补全的模型。于是你打开application.yml开始复制粘贴第二份api-key、第二个base-url再写一个Bean再改一遍 Service 里的WebClient。等到第三个、第四个模型进来配置文件已经变成一锅粥每个厂商的 Key 散落在不同段落改一个环境变量要翻半天。这就是多厂商 Key 分散管理的典型问题。它不只是看着乱而是会实打实带来几个麻烦环境切换时容易漏改某个 Key不同模型的base_url格式不统一有的带/v1有的不带想临时切个模型验证效果得改代码重新打包。对于 SpringAI 这种强调一套 API 抽象多家模型的框架来说配置层反而成了最不优雅的地方。这篇要解决的就是让 SpringAI 项目接入 DeepSeek 并实现多模型切换时只维护一份统一 Key 和一份 base_url通过ChatClient的运行时参数完成模型切换。适合已经跑通过 SpringAI 基础对话、想进一步做多模型 demo 的开发者。核心思路是把厂商差异收敛到配置层把模型选择暴露到调用层。下面从环境准备开始一步步给出可复制的配置骨架和验证步骤。2. 前置准备TaoToken 统一 Key 与依赖骨架在动手改配置之前先把统一入口这件事定下来。多模型切换之所以痛苦根源是每个厂商一套鉴权。如果有一个兼容 OpenAI 协议的统一网关把 DeepSeek 等模型的调用都收敛到同一个base_url和同一个 Key 上SpringAI 侧就只需要认一个地址。TaoToken 在这里扮演的就是这个统一入口的角色。它的 API 地址是https://taotoken.net/api兼容 OpenAI 的/chat/completions协议所以 SpringAI 的 OpenAI Starter 可以直接对接不需要为 DeepSeek 单独写一套 WebClient。你需要先去控制台创建一个 API Key这个 Key 会在后面的application.yml里作为唯一凭证使用。创建 Key 的入口在控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite。拿到形如sk-xxxx的 Key 之后先放一边我们来看依赖。SpringAI 的版本迭代比较快建议用 1.0.0 及以上的 milestone 或正式版。pom.xml里核心就两个依赖SpringAI 的 OpenAI Starter 和 WebFlux因为ChatClient的流式返回依赖 Reactor。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency如果你用的是 Gradle对应写法是implementation org.springframework.ai:spring-ai-openai-spring-boot-starter:1.0.0。注意 SpringAI 的仓库需要额外配置 milestone 地址如果你拉不到依赖检查一下repositories里有没有加 Spring 的 milestone 仓库。这一步踩过坑的人不少依赖拉不下来八成是仓库没配对。依赖就绪后项目结构建议保持简单一个config包放配置类一个service包放对话服务一个controller包放接口。demo 阶段不需要过度分层能跑通多模型切换才是重点。3. 可复制配置application.yml 统一 Key 与模型清单这一节是全文的核心配置写对了后面代码就顺了。关键点有两个一是把base-url指向 TaoToken 的统一地址二是把可选模型列表做成配置项而不是硬编码在 Java 里。先看application.yml的完整骨架spring: ai: openai: # 统一入口所有模型共用这一个 base-url base-url: https://taotoken.net/api # 统一 Key所有模型共用这一个凭证 api-key: ${TAOTOKEN_API_KEY} chat: options: # 默认模型不指定时用它 model: deepseek-chat temperature: 0.7 # 自定义可切换的模型清单 springai: models: default-model: deepseek-chat available-models: - deepseek-chat - deepseek-coder - deepseek-reasoner这里有几个设计取舍值得说明。api-key用${TAOTOKEN_API_KEY}占位是为了不把密钥写死在文件里本地开发可以配环境变量线上走配置中心。base-url只写https://taotoken.net/api不要自己拼/v1SpringAI 的 OpenAI 客户端会自动补全路径手动加反而容易 404。springai.models这一段是我自己加的命名空间用来承载模型清单这个业务概念。default-model决定不传参时用哪个available-models则是白名单——后面 Controller 收到模型名时会先校验是否在清单里避免用户传个不存在的模型导致下游报错。这个白名单机制在多模型 demo 里很实用相当于把能切哪些模型变成可配置项加模型不用改代码。如果你想把模型清单放到 Nacos 或 Apollo 做动态刷新只需要把springai.models这段挪过去Java 侧用ConfigurationProperties或RefreshScope绑定即可结构不用动。这就是把清单外置的好处。配置类负责把这段 YAML 绑成对象Configuration ConfigurationProperties(prefix springai.models) Data public class ModelProperties { private String defaultModel; private ListString availableModels new ArrayList(); }Data来自 Lombok省掉 getter/setter。绑定完成后ModelProperties就能在 Service 里注入了。到这里统一 Key 和模型清单都就位了接下来写对话服务。4. 核心实现ChatClient 动态切换 DeepSeek 与其他模型SpringAI 的ChatClient是推荐的高层 API比直接操作OpenAiChatModel更顺手。多模型切换的关键在于ChatClient支持在每次请求时通过options覆盖模型名而不需要为每个模型建一个 Bean。先看 Service 的实现Service public class MultiModelChatService { private final ChatClient chatClient; private final ModelProperties modelProperties; public MultiModelChatService(ChatClient.Builder builder, ModelProperties modelProperties) { this.chatClient builder.build(); this.modelProperties modelProperties; } /** * 指定模型对话modelName 为空则用默认模型 */ public String chat(String message, String modelName) { String target resolveModel(modelName); return chatClient.prompt() .user(message) .options(OpenAiChatOptions.builder() .model(target) .build()) .call() .content(); } /** * 校验并解析模型名不在白名单则回退默认 */ private String resolveModel(String modelName) { if (modelName null || modelName.isBlank()) { return modelProperties.getDefaultModel(); } if (!modelProperties.getAvailableModels().contains(modelName)) { // 不在清单里回退默认避免下游 400 return modelProperties.getDefaultModel(); } return modelName; } }这段代码里OpenAiChatOptions.builder().model(target)就是切换模型的开关。因为base-url和api-key已经在全局配置里统一了这里只需要改模型名请求就会打到 TaoToken 的统一入口由它路由到对应的 DeepSeek 模型。这就是一次配置跑通多模型的落地方式。resolveModel做了两层保护空值走默认非法值也走默认。实测下来这个回退逻辑能挡掉大部分因为前端传错参数导致的 400 错误。如果你想要更严格的策略比如非法模型直接抛异常把回退那行改成throw new IllegalArgumentException(...)即可。Controller 层就很简单了暴露两个入口RestController RequestMapping(/api/chat) public class ChatController { private final MultiModelChatService chatService; public ChatController(MultiModelChatService chatService) { this.chatService chatService; } GetMapping public MapString, String chat(RequestParam String message, RequestParam(required false) String model) { String reply chatService.chat(message, model); return Map.of(model, model null ? default : model, reply, reply); } }调用方式有两种GET /api/chat?message你好走默认模型GET /api/chat?message写个快排modeldeepseek-coder显式指定。返回体里带上实际使用的模型名方便验证切换是否生效。如果你需要流式输出把.call().content()换成.stream().content()返回FluxStringController 返回类型改成FluxString即可模型切换逻辑完全不变。这一点是ChatClient抽象带来的便利——切换模型和切换返回模式互不干扰。5. 验证请求确认多模型切换真的生效配置和代码都写完了接下来要验证。启动项目后先用默认模型打一发curl http://localhost:8080/api/chat?message用一句话解释什么是递归预期返回类似{model:default,reply:递归是指一个函数在定义中调用自身的编程技巧……}再显式指定deepseek-coder问一个偏代码的问题curl http://localhost:8080/api/chat?message用Java写一个二分查找modeldeepseek-coder如果返回的reply里包含完整的 Java 方法实现说明模型切换生效了。你可以对比两次返回的风格差异——deepseek-chat偏通用解释deepseek-coder在代码任务上通常更聚焦。这种对比本身就是多模型 demo 的价值所在。再测一下白名单回退故意传一个不存在的模型名。curl http://localhost:8080/api/chat?message你好modelnot-exist-model预期它不会报错而是回退到默认模型正常返回。这说明resolveModel的保护逻辑起作用了。如果你想更直观地看到模型切换可以在 Service 里加一行日志打印实际使用的模型名log.info(chat request routed to model{}, target);启动时把日志级别调到 INFO每次请求都能在控制台看到路由到了哪个模型。这个技巧在排查为什么切换没生效时特别有用——如果日志里始终是默认模型那问题多半出在参数没传进来或者白名单校验把它挡了。验证通过后一个统一 Key、一份配置、多模型切换的 SpringAI demo 就跑通了。整个过程没有为 DeepSeek 单独写 WebClient也没有维护第二份鉴权信息。6. 常见报错排查从 401 到模型不存在的定位思路多模型接入最容易卡在几个固定位置这里按报错现象整理排查路径。401 Unauthorized九成是 Key 的问题。先确认TAOTOKEN_API_KEY环境变量真的注入了可以在启动日志里打印一下 Key 的前几位别打全。如果 Key 是对的检查base-url有没有多写或少写路径——正确值是https://taotoken.net/api不要带/v1也不要带/chat/completions。SpringAI 会自动拼接手动加会变成双重路径导致鉴权失败。404 Not Found通常是base-url写错。有人习惯性写成https://taotoken.net/api/v1结果请求打到/api/v1/chat/completions而实际路径是/api/chat/completions。把/v1去掉即可。模型不存在或 400 Bad Request先确认你传的模型名在available-models清单里且拼写和上游一致。deepseek-chat、deepseek-coder这些名字区分大小写写成DeepSeek-Chat可能就找不到。如果清单里有但依然报错去模型对话页面手动发一条消息验证该模型当前是否可用https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite。手动能通说明是代码侧参数没传对手动也不通就是模型侧的问题。切换不生效始终返回默认模型按这个顺序查。第一Controller 的RequestParam名字和 URL 参数是否一致model对model别一个叫modelName一个传model。第二resolveModel的白名单校验是否把合法模型误判了打印一下availableModels的内容确认配置绑定成功。第三OpenAiChatOptions是否真的被应用可以在 Service 里打印target确认。依赖冲突导致启动失败SpringAI 对 Spring Boot 版本有要求如果启动时报NoSuchMethodError或ClassNotFoundException多半是版本不匹配。建议 Spring Boot 3.2 配 SpringAI 1.0.0。用mvn dependency:tree看一下有没有旧版 OpenAI SDK 混进来。排查的核心思路是先确认统一入口base-url Key通不通再确认模型名对不对最后确认参数有没有传到。这三层分开验证比一股脑改代码高效得多。接入相关的完整参数说明可以对照文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。7. 从 demo 到长期编码把统一 Key 用起来跑通这个 demo 之后你会发现统一 Key 的价值不只是少写几行配置。当你把 SpringAI 项目从验证阶段推进到日常编码辅助、Agent 工具链时模型调用会变得高频且多样——写代码用 coder 类模型写文档用通用模型复杂推理用 reasoner 类模型。如果每个模型一套 Key运维成本会随模型数量线性增长统一入口之后加模型只是往available-models里加一行。如果你打算把这类多模型调用长期用在编码场景可以了解一下 Coding Plan它针对高频编码调用做了额度规划配合这里的统一配置能进一步简化成本管理https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite。对于需要频繁切换模型做对比验证的场景模型对话页面可以快速手动试跑省去每次改代码重启的麻烦。回到代码本身这个 demo 还有两个可以继续打磨的方向。一是把resolveModel的策略从白名单回退升级成按请求内容自动选模型比如检测到 prompt 里有代码块就路由到 coder 模型二是把模型清单接到配置中心实现不重启动态增删模型。这两步做完多模型切换就从 demo 级别变成了可上生产的基础设施。而这一切的前提都是先把统一 Key 和统一 base_url 这层地基打牢——地基稳了上面怎么搭都快。