Spring AI 多模型集成实战:OpenAI/DeepSeek/通义千问/智谱一套代码全搞定
🌐 演示地址:http://ruoyioffice.com | 📦 源码1·GitHub:ruoyi-office | 📦 源码2·GitCode:ruoyi-office | 📦 源码3·Gitee:ruoyi-office | 💬 微信:17156169080(备注「RuoYi Office」)
业务方今天要 DeepSeek、明天要通义千问、后天又说"内网不能出公网,得换成本地 Ollama"。如果你的代码里到处是
new OpenAiChatModel(...),每换一家模型就要改代码、发版、重测一遍。企业级 AI 集成真正的难点,从来不是"调通一个模型",而是"让十几家模型在同一套代码里随时热切换、各租户用各自的 Key、参数可配、调用方零感知"。 本文以 RuoYi Office 的 AI 模块为样本,从两张配置表讲起,拆解如何用 Spring AI + 工厂模式把 OpenAI、DeepSeek、通义千问、智谱、文心、豆包、Ollama 等模型统一收口。
▲ 多模型集成能力地图:① 配置层(ai_api_key 存密钥 + ai_model 存模型参数)→ ② 工厂层(AiModelFactory 按平台 switch 构建 Spring AI ChatModel/EmbeddingModel/ImageModel 并缓存)→ ③ 能力层(对话/写作/知识库/绘图统一调用)→ ④ 平台矩阵(17 家大模型平台,国内国外多媒体全覆盖)
引言:企业接大模型,到底难在哪?
每个想给系统加 AI 能力的团队,几乎都会在"接模型"这步反复返工:
难点一:模型 SDK 各不相同。OpenAI、通义 DashScope、智谱、文心 QianFan、豆包,每家的 SDK 类名、鉴权方式、参数结构都不一样,硬编码一家,换家就重写。
难点二:Key 和参数写死在配置文件。application.yml 里写死一个 api-key,多租户场景下没法让 A 公司用自己的 Key、B 公司用另一个 Key;改个温度值还得重启。
难点三:换模型要发版。运营想把对话模型从 GPT 换成 DeepSeek 省成本,结果是一次开发改代码 + 测试 + 上线的完整流程,业务等不起。
难点四:本地与云端混用难。涉密场景要求模型部署在内网(Ollama / 本地推理),公开场景又想用云端大模型,两套逻辑各写一遍。
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
RuoYi Office 的解法是把"模型"做成可运营配置的数据:用 ai_api_key + ai_model 两张表存配置,用 AiModelFactory 工厂按平台动态构建 Spring AI 的标准 ChatModel,调用方只认 modelId、完全不感知底层是哪家。下面从设计讲起。
一、整体设计:把"模型"变成一行可配置的数据
Spring AI 是 Spring 官方推出的 AI 应用开发框架,它把不同厂商的大模型抽象成统一接口:ChatModel(对话)、EmbeddingModel(向量化)、ImageModel(绘图)、VectorStore(向量库)。只要拿到一个 ChatModel 实例,上层调用就完全一致,不用关心背后是 OpenAI 还是 DeepSeek。
RuoYi Office 在 Spring AI 之上再封一层"配置 + 工厂",把模型集成拆成清晰的三层:
|
|
|
|
|---|---|---|
| 配置层 |
|
ai_api_key
ai_model
|
| 工厂层 |
|
AiModelFactory |
| 能力层 |
|
AiModelService
|
一句话概括设计哲学:「密钥和参数交给数据库、对象构建交给工厂、能力调用交给 Spring AI 标准接口」。三层解耦后,新增一家模型只需在枚举里加一项、在工厂里加一个分支,上层业务零改动。
Spring AI 即 Spring 官方的 AI 集成框架,用于在 Spring Boot 应用中以统一接口调用各家大模型,例如把"对话"统一抽象成
ChatModel.call(Prompt),屏蔽厂商差异。
二、配置层:两张表撑起多模型
2.1 ai_api_key:密钥与平台
第一张表 ai_api_key 只管"用哪个平台、哪把钥匙、连哪个地址"。把密钥单独拆一张表,是为了让多个模型复用同一把 Key(比如通义的对话模型和向量模型共用一个 DashScope Key):
("ai_api_key")public class AiApiKeyDO extends BaseDO {private Long id;private String name; // 名称,便于运营辨认private String apiKey; // 密钥private String platform; // 平台,对应 AiPlatformEnumprivate String url; // API 地址(可空,用默认;自建/代理时填)private Integer status; // 状态:开启/关闭}
2.2 ai_model:模型与调用参数
第二张表 ai_model 描述"具体哪个模型 + 调用参数"。一把 Key 下可以挂多个模型(如 deepseek-chat 和 deepseek-reasoner),对话类参数(温度、最大 token、上下文条数)也存在这里,运营随时可调:
public class AiModelDO extends BaseDO {private Long id;private Long keyId; // 关联 ai_api_keyprivate String name; // 模型名称private String model; // 模型标识,如 deepseek-chat、qwen-maxprivate String platform; // 平台private Integer type; // 类型:对话/向量/图片/语音…(AiModelTypeEnum)private Integer sort;private Integer status;// 对话参数:调用时透传给大模型private Double temperature; // 温度:越高越发散private Integer maxTokens; // 单次回复最大 tokenprivate Integer maxContexts; // 携带的历史消息条数}
为什么拆成两张表而不是一张? 因为"密钥"和"模型"是一对多关系——一把 DashScope Key 下有 qwen-max、qwen-plus、text-embedding-v3 多个模型,密钥轮换时只改一行,所有挂在它下面的模型自动生效。这是典型的"配置归一化"。
三、平台枚举:一张表看懂支持哪些模型
所有支持的平台集中在 AiPlatformEnum 一个枚举里,新增厂商先在这里登记。RuoYi Office 当前覆盖国内主流、国外主流与多媒体三类共 17 个平台:
|
|
|
|
|---|---|---|
| 国内对话 |
|
|
| 国内对话 |
|
|
| 国外对话 |
|
|
| 本地 |
|
|
| 多媒体 |
|
|
模型类型用另一个枚举 AiModelTypeEnum 区分:CHAT(1) 对话、IMAGE(2) 绘图、VOICE(3) 语音、VIDEO(4) 视频、EMBEDDING(5) 向量、RERANK(6) 重排。同一个平台可以同时提供对话和向量模型,靠 type 区分用途。
关键设计:平台与类型用枚举集中管理,意味着"支持哪些模型"是一份可枚举、可校验的清单,而不是散落在各处的 if-else,这对后续维护和排错极其友好。
▲ AI 对话页面:右上角可随时切换底层模型(通义千问 / DeepSeek / 智谱等),温度、上下文条数等参数随模型配置生效,调用方完全无感。
四、工厂层:AiModelFactory 是整套设计的心脏
这是整个多模型集成最关键的一环。AiModelFactory 是一个工厂接口,负责把"平台 + Key + URL"翻译成 Spring AI 的标准模型对象。它的接口定义就是一份"能造什么"的清单:
public interface AiModelFactory {// 按平台 + 密钥构建对话模型(核心)ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);// 向量模型,用于知识库 EmbeddingEmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model);// 绘图模型ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url);// 向量库(知识库 RAG 用)VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,EmbeddingModel embeddingModel, Map<String, Class<?>> metadataFields);}
4.1 按平台 switch,构建对应 ChatModel
实现类 getOrCreateChatModel 的核心就是一个 switch:拿到平台枚举,分发到对应厂商的构建方法。每个 case 内部用对应的 Spring AI Starter 或自研包装类创建实例:
public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) {// 用「类型 + 平台 + Key + URL」拼出缓存 Key,命中则复用,避免重复创建String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url);return Singleton.get(cacheKey, (Func0<ChatModel>) () -> {switch (platform) {case DEEP_SEEK: return buildDeepSeekChatModel(apiKey, url);case TONG_YI: return buildTongYiChatModel(apiKey); // 阿里 DashScopecase ZHI_PU: return buildZhiPuChatModel(apiKey, url);case YI_YAN: return buildYiYanChatModel(apiKey); // 文心 QianFancase DOU_BAO: return buildDouBaoChatModel(apiKey);case OPENAI: return buildOpenAiChatModel(apiKey, url);case ANTHROPIC: return buildAnthropicChatModel(apiKey, url);case OLLAMA: return buildOllamaChatModel(url); // 本地,无需 Key// … 通义/星火/混元/硅基流动/MiniMax/月之暗面/百川/Gemini/Grok 等default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));}});}
两个值得学习的设计点:
- 客户端缓存(
Singleton.get):同一组「平台 + Key + URL」只构建一次模型客户端,后续命中缓存直接复用。大模型客户端内部往往持有连接池、HTTP 客户端,频繁 new 既慢又浪费资源。 - 统一返回 Spring AI 的
ChatModel:无论底层是官方 Starter、阿里 DashScope SDK,还是自研的 DouBaoChatModel、XingHuoChatModel包装类,对外都收敛成同一个接口,上层零差异。
4.2 业务入口:只认 modelId
上层业务从不直接碰工厂,而是通过 AiModelService.getChatModel(Long id)——传一个模型 ID,内部完成"查模型 → 查密钥 → 调工厂"的全过程:
public ChatModel getChatModel(Long id) {// 1. 校验模型存在 + 校验密钥可用AiModelDO model = validateModel(id);AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());// 2. 委托工厂构建(命中缓存则复用)return modelFactory.getOrCreateChatModel(AiPlatformEnum.validatePlatform(apiKey.getPlatform()),apiKey.getApiKey(), apiKey.getUrl());}
这一步是"调用方零感知"的关键:对话、写作、知识库问答等所有能力,拿到的都是一个标准 ChatModel,它们根本不知道(也不需要知道)背后是 DeepSeek 还是通义。换模型?改一下会话/角色绑定的 modelId 即可,代码一行不动。
五、能力层:流式对话怎么调
拿到 ChatModel 后,发起一次对话就是 Spring AI 的标准用法。企业场景几乎都要"打字机"式流式输出,用 stream() 返回 Flux,配合 SSE 推给前端:
// 1. 构建消息:系统设定 + 用户输入List<Message> messages = new ArrayList<>();messages.add(new SystemMessage(systemPrompt)); // 角色设定messages.add(new UserMessage(userInput)); // 用户问题// 2. 构建参数:温度、最大 token 来自 ai_model 配置ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(),model.getTemperature(), model.getMaxTokens());// 3. 流式调用,逐块返回Flux<ChatResponse> stream = streamingChatModel.stream(new Prompt(messages, options));return stream.map(chunk -> {String text = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : "";return success(StrUtil.nullToDefault(text, "")); // 包成 CommonResult 推给前端});
这段代码的价值在于:它对所有平台都成立。无论 streamingChatModel 背后是 GPT 还是通义,流式输出的写法完全一致——这正是 Spring AI 统一抽象带来的红利。
六、RuoYi Office 多模型设计的创新点
6.1 配置即模型,换模型不发版
模型平台、密钥、参数全部入库,管理后台增删改即时生效。运营把对话模型从 GPT 换成 DeepSeek,只是改一行数据,不动代码、不发版。
6.2 工厂 + 缓存,新增厂商成本极低
接入一家新模型,只需:枚举加一项、工厂加一个 case 分支。客户端按「平台+Key+URL」缓存,性能与资源都有保障。
6.3 多租户各用各的 Key
ai_api_key 继承平台基础能力支持租户隔离,A 公司用自己的通义 Key、B 公司用自己的 DeepSeek Key,互不串号,计费清晰。
6.4 云端与本地一视同仁
Ollama 作为本地平台与云端模型走同一套工厂逻辑。涉密数据走内网 Ollama、公开问答走云端大模型,由模型配置决定,代码无分叉。
七、数据结构
ai_api_key(密钥表)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ai_model(模型表)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
设计要点:
- 密钥与模型 1:N
:一把 Key 挂多个模型,密钥轮换改一行; - 参数随模型存
:温度/上下文条数等可运营调,不写死配置; - 继承 BaseDO
:自带 creator/create_time/tenant_id,天然多租户隔离。
八、技术亮点总结
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
九、快速体验
在线演示:http://ruoyioffice.com/web/(账号 admin / admin123),进入「AI 大模型 → 模型配置 / AI 对话」。
推荐体验流程:
-
进入「AI 大模型 → API 密钥」,新增一个平台密钥(如 DeepSeek / 通义千问); -
进入「模型管理」,在该密钥下新增一个对话模型,设置温度、最大 token; -
打开「AI 对话」,新建会话并选中刚配置的模型,发起一次提问; -
切换到另一个平台的模型,发同样的问题,对比效果与速度; -
体会"换模型只是切配置、代码零改动"的设计价值。
源码仓库:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
结语
多模型集成的精髓,不在于"会调某一家模型的 API",而在于把密钥、模型、能力三件事彻底解耦:密钥与参数交给数据库、对象构建交给工厂、能力调用交给 Spring AI 标准接口。一旦解耦,“接大模型"就从"绑死一家厂商的工程"变成了"配一条数据的运营动作”。这套「配置即模型 + 工厂动态构建」的思路,同样适用于支付渠道、短信通道、对象存储等任何"多厂商、需热切换"的集成场景。
你们的系统接 AI 时是怎么管理多模型的?是写死一家,还是也做了可配置切换?欢迎在评论区聊聊。
常见问题(FAQ)
RuoYi Office 支持哪些大模型?可以体验吗?
支持。后端基于 Spring Boot 3.5 + Spring AI,已接入通义千问、文心一言、DeepSeek、智谱、星火、豆包、混元、硅基流动、MiniMax、月之暗面、百川,以及 OpenAI、Azure、Anthropic、Gemini、Grok 与本地 Ollama 等共 17 个平台,提供在线演示与本地部署体验;完整能力与持续维护由商业版提供。
怎么从 GPT 换成 DeepSeek?要改代码吗?
不用改代码。在管理后台「API 密钥」里新增 DeepSeek 密钥、「模型管理」里新增对应模型,然后把会话或角色绑定的模型改成 DeepSeek 即可,底层由 AiModelFactory 自动构建,上层无感。
数据不能出公网怎么办?
用本地模型。把平台选为 Ollama,填内网推理服务地址,对话/向量化全部在内网完成,数据不出网;它与云端模型走同一套工厂逻辑,业务代码不分叉。
多个租户能各用各的模型密钥吗?
可以。密钥表按租户隔离,每个租户配置自己的平台 Key 与模型,互不影响,计费与额度也各自独立。
Spring AI 和直接调各家 SDK 有什么区别?
Spring AI 把各厂商模型抽象成统一的 ChatModel/EmbeddingModel/ImageModel 接口,屏蔽了 SDK 差异。配合工厂模式,新增一家厂商只需加一个枚举与一个构建分支,上层"对话/写作/知识库"代码完全不用动。
💡 想要体验 RuoYi Office 的强大功能?
🌐 在线演示:http://ruoyioffice.com/web/(账号 admin / admin123)
📦 源码仓库:GitHub | GitCode | Gitee
💬 技术咨询:添加微信 17156169080,备注「RuoYi Office」
⭐ 如果觉得不错,请给个 Star 支持一下!


