1. 多模型混合调用到底解决什么问题
LangChain4j 做 Java AI 应用开发时,单模型跑通只是起点。真实项目里,你很快会遇到三个绕不开的场景:成本压力、单点故障、能力互补。比如智能客服每天处理十万次对话,全部走 GPT-4o 月成本可能上万;某天下午 OpenAI 接口抖动,整个系统跟着瘫痪;而中文合规问答又需要数据不出境的模型来兜底。
这些问题的共同解法,是在一个 Spring Boot 项目里同时接入 OpenAI、DeepSeek、阿里百炼三家模型,按任务类型、用户等级或可用性动态切换。LangChain4j 的 Spring Boot Starter 提供了@AiService(wiringMode = EXPLICIT)显式绑定机制,让多个 ChatModel Bean 共存于同一容器,每个 AiService 接口精确指向一个模型。
但多模型接入的第一个拦路虎不是代码,而是配置管理:三家模型意味着三套 API Key、三个 BaseURL、三份模型名,散落在 application.yml 里既难维护又容易冲突。这篇就聚焦这个配置骨架问题,给出一份可复制的 application.yml,用 TaoToken 统一 Key 收敛多 Key 管理,并演示一次三模型切换调用的验证动作。目标很明确:一份配置跑通三家模型。
适合正在用 LangChain4j 做 Java AI 应用、已经跑通单模型、准备上多模型的开发者。如果你还在纠结第一个模型怎么接,建议先看前面的基础篇。
2. TaoToken 前置:统一 Key 与 BaseURL 收敛
多模型配置最烦的地方在于:OpenAI 的 Key 格式、DeepSeek 的 Key 格式、阿里百炼的 Key 格式各不相同,环境变量要维护三套,CI/CD 里要注入三个 secret,换一个模型就要改一处配置。TaoToken 的作用是把这三家的接入点收敛成一个统一的 API 入口和一把 Key。
它的工作方式很直接:你拿到一把 TaoToken 的 API Key,把 BaseURL 指向https://taotoken.net/api,然后在请求里通过模型名区分要调用哪家模型。对 LangChain4j 来说,这意味着 OpenAI、DeepSeek、阿里百炼三家都可以复用同一个 OpenAI 兼容适配器,只是 model-name 不同。
这样做的好处有三个。第一,Key 管理从三套变一套,环境变量只维护TAOTOKEN_API_KEY一个。第二,BaseURL 统一,不用记三个域名。第三,切换模型只改 model-name 一个字段,配置骨架完全不变。
需要提前准备的东西:一个 TaoToken 账号,在控制台生成 API Key。如果你还没有 Key,可以先去官网注册,然后在 API Keys 页面创建。整个准备过程不超过五分钟,重点是把 Key 存到环境变量里,不要硬编码进代码。
注意:API Key 属于敏感凭证,务必通过环境变量或配置中心注入,不要提交到 Git 仓库。本地开发可以用
.env文件配合 IDE 的环境变量插件。
3. 可复制的配置骨架
这一节给出完整的依赖、application.yml 和 AiService 接口定义。你可以直接复制到项目里,改一下 Key 的环境变量名就能跑。
3.1 Maven 依赖
多模型接入只需要两个 starter:LangChain4j 核心 starter 和 OpenAI 兼容 starter。因为 TaoToken 走 OpenAI 兼容协议,DeepSeek 和阿里百炼也都能通过这个适配器接入,所以不需要额外引入 dashscope 或 ollama 的 starter。
<dependencies> <!-- LangChain4j Spring Boot 核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>1.0.0-beta3</version> </dependency> <!-- OpenAI 兼容适配器,同时覆盖 DeepSeek 和阿里百炼 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> <version>1.0.0-beta3</version> </dependency> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>版本号以你项目实际使用的为准,这里给的是 beta3 作为参考。如果你的项目还在用更早的版本,@AiService的包路径可能略有不同,注意调整 import。
3.2 application.yml 统一配置
这是本篇的核心。关键点在于:只配一个langchain4j.open-ai.chat-model前缀,BaseURL 指向 TaoToken,model-name 先给一个默认值。三家模型的切换通过代码里的 Bean 手动创建来实现,而不是靠三份 yml 配置。
spring: application: name: LangChain4j-Multi-Model server: port: 8082 # ========================================== # TaoToken 统一接入配置 # BaseURL 指向 TaoToken,一把 Key 覆盖三家模型 # ========================================== langchain4j: open-ai: chat-model: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api model-name: gpt-4o-mini log-requests: true log-responses: true timeout: PT60S这里model-name给的是gpt-4o-mini,作为默认 BeanopenAiChatModel的模型。DeepSeek 和阿里百炼的 Bean 我们在 Java 配置类里手动创建,这样三个 Bean 名称互不冲突。
3.3 手动创建 DeepSeek 与百炼 Bean
因为三家共用同一个langchain4j.open-ai前缀,自动配置只能生成一个 Bean。要同时存在三个,需要在@Configuration类里手动构建另外两个。
package com.langchain4j.config; import dev.langchain4j.model.openai.OpenAiChatModel; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MultiModelConfig { @Value("${TAOTOKEN_API_KEY}") private String apiKey; private static final String BASE_URL = "https://taotoken.net/api"; /** * DeepSeek 模型 Bean * Bean 名称:deepSeekChatModel */ @Bean("deepSeekChatModel") public OpenAiChatModel deepSeekChatModel() { return OpenAiChatModel.builder() .apiKey(apiKey) .baseUrl(BASE_URL) .modelName("deepseek-chat") .logRequests(true) .logResponses(true) .build(); } /** * 阿里百炼通义千问模型 Bean * Bean 名称:qwenChatModel */ @Bean("qwenChatModel") public OpenAiChatModel qwenChatModel() { return OpenAiChatModel.builder() .apiKey(apiKey) .baseUrl(BASE_URL) .modelName("qwen-turbo") .logRequests(true) .logResponses(true) .build(); } }注意三个 Bean 的 model-name 分别是gpt-4o-mini、deepseek-chat、qwen-turbo,BaseURL 和 apiKey 完全一致。这就是统一接入的核心:差异只在模型名。
3.4 三个 AiService 接口显式绑定
每个接口通过wiringMode = EXPLICIT和chatModel属性精确绑定到对应的 Bean。
package com.langchain4j.assistant; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.spring.AiService; import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT; @AiService(wiringMode = EXPLICIT, chatModel = "openAiChatModel") public interface OpenAiAssistant { @SystemMessage("你是一个擅长深度推理和复杂分析的AI助手") String chat(String message); } @AiService(wiringMode = EXPLICIT, chatModel = "deepSeekChatModel") public interface DeepSeekAssistant { @SystemMessage("你是一个擅长代码生成和技术问答的AI助手") String chat(String message); } @AiService(wiringMode = EXPLICIT, chatModel = "qwenChatModel") public interface QwenAssistant { @SystemMessage("你是一个擅长中文理解和快速响应的AI助手") String chat(String message); }三个接口放在同一个包下即可,Spring 会自动扫描并生成代理实现。到这里配置骨架就完整了,接下来验证。
4. 验证请求与成功结果
配置写完不验证等于没写。这一节用一个 Controller 暴露三个端点,分别调用三家模型,然后通过 curl 确认每个模型都返回了符合预期的响应。
4.1 验证用 Controller
package com.langchain4j.controller; import com.langchain4j.assistant.DeepSeekAssistant; import com.langchain4j.assistant.OpenAiAssistant; import com.langchain4j.assistant.QwenAssistant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class MultiModelController { @Autowired private OpenAiAssistant openAiAssistant; @Autowired private DeepSeekAssistant deepSeekAssistant; @Autowired private QwenAssistant qwenAssistant; @GetMapping("/model/openai") public String openai(@RequestParam(defaultValue = "你好") String message) { return openAiAssistant.chat(message); } @GetMapping("/model/deepseek") public String deepseek(@RequestParam(defaultValue = "你好") String message) { return deepSeekAssistant.chat(message); } @GetMapping("/model/qwen") public String qwen(@RequestParam(defaultValue = "你好") String message) { return qwenAssistant.chat(message); } }4.2 启动与调用
启动应用前确认环境变量已设置:
export TAOTOKEN_API_KEY=你的Key mvn spring-boot:run然后依次调用三个端点:
curl "http://localhost:8082/model/openai?message=用一句话介绍你自己" curl "http://localhost:8082/model/deepseek?message=用一句话介绍你自己" curl "http://localhost:8082/model/qwen?message=用一句话介绍你自己"4.3 预期结果
三个请求都应该返回 200,响应体是模型生成的自我介绍。因为log-requests和log-responses都开了,控制台会打印每次请求的 URL、模型名和响应内容。你可以从日志里确认:
/model/openai的请求体里model字段是gpt-4o-mini/model/deepseek的请求体里model字段是deepseek-chat/model/qwen的请求体里model字段是qwen-turbo
如果三个都返回了内容,说明一份配置跑通了三家模型。如果某个端点报错,对照下一节的排查表定位。
提示:验证阶段建议把
log-requests和log-responses打开,生产环境可以关掉以减少日志量。日志里能看到实际发出的模型名,这是确认路由是否正确的最直接方式。
5. 本篇常见错排查
多模型配置的报错集中在 Bean 绑定和模型名两个环节。下面按症状、原因、解决方案三列整理,遇到问题直接查表。
5.1 Bean 名称找不到
症状是启动时报NoSuchBeanDefinitionException: No bean named 'xxxChatModel' available。原因通常是@AiService的chatModel属性值和实际 Bean 名称不匹配。比如你写的是chatModel = "deepseekChatModel",但配置类里@Bean("deepSeekChatModel")大小写不一致。
排查方法是在启动类里加一个 CommandLineRunner,打印容器里所有 ChatModel 类型的 Bean 名称:
@Bean public CommandLineRunner listChatModels(ApplicationContext context) { return args -> { String[] beans = context.getBeanNamesForType(ChatModel.class); System.out.println("可用的 ChatModel Bean:"); for (String bean : beans) { System.out.println(" - " + bean); } }; }启动后控制台会列出openAiChatModel、deepSeekChatModel、qwenChatModel三个名称,照着改@AiService的绑定值即可。
5.2 多个 OpenAI 兼容模型冲突
症状是三个端点返回的内容都来自同一个模型,或者只有默认 Bean 生效。原因是自动配置只生成了一个openAiChatModel,如果你没有手动创建另外两个 Bean,三个@AiService都绑到了同一个模型上。
解决方案就是本篇 3.3 节的手动 Bean 创建。关键点是给每个 Bean 显式命名,并且@AiService的chatModel属性值要和 Bean 名称严格一致。
5.3 模型名不被识别
症状是请求返回 400 或 404,错误信息里提到 model not found。原因是 model-name 写错了,比如把deepseek-chat写成了deepseek-v4,或者把qwen-turbo写成了qwen-turbo-latest。
排查方法是打开log-requests,看实际发出的请求体里 model 字段是什么,然后对照 TaoToken 文档里支持的模型名列表。模型名是区分大小写的,gpt-4o-mini和GPT-4O-MINI不一样。
5.4 排查速查表
| 症状 | 原因 | 解决方案 |
|---|---|---|
| 启动报 Bean 找不到 | chatModel 属性值与 Bean 名称不匹配 | 用 getBeanNamesForType 列出所有 Bean 后对照修改 |
| 三个端点返回同一模型 | 只生成了一个 OpenAI Bean | 手动创建 DeepSeek 和百炼 Bean 并显式命名 |
| 请求返回 400/404 | model-name 写错或不被支持 | 打开 log-requests 看实际模型名,对照文档修正 |
| 请求超时 | 网络问题或 Key 无效 | 检查 TAOTOKEN_API_KEY 环境变量是否正确注入 |
| 中文返回乱码 | 响应编码问题 | 确认 Controller 返回 String 且 Spring 默认 UTF-8 |
6. 接入文档与后续动作
配置跑通之后,下一步通常是把它接到真实业务里。如果你在排障阶段需要确认某个模型名是否支持、某个参数是否可用,可以直接在模型对话页面手动发一条请求验证,比改代码重启快得多。接入文档里有完整的模型名列表和参数说明,遇到不确定的字段先查文档再改配置。
对于准备把多模型用到长期编码或 Agent 场景的,建议看一下 Coding Plan,它把多模型路由和成本控制做成了可复用的方案,不用自己从零搭。如果你还在验证阶段,先把本篇的三个端点跑通,确认三家模型都能正常返回,再考虑往业务里集成。
API Key 的管理建议单独走 API Keys 页面,给不同环境生成不同的 Key,方便按环境排查问题。控制台里能看到每个 Key 的调用量,多模型场景下这个数据对成本分析很有用。
配置骨架本身不复杂,难的是把三家的差异收敛到一个入口。TaoToken 在这里扮演的角色就是那个收敛点:一把 Key、一个 BaseURL、三个模型名。剩下的就是 LangChain4j 的显式绑定机制,把每个 AiService 精确指向对应的 Bean。这套组合跑通之后,加第四个、第五个模型也只是多一个 Bean 和一行绑定的事。