欢迎光临
我们一直在努力

Spring AI 多模型集成实战:OpenAI/DeepSeek/通义千问/智谱一套代码全搞定

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 等模型统一收口。 spring-ai-multi-model-architecture.png

▲ 多模型集成能力地图:① 配置层(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 / 本地推理),公开场景又想用云端大模型,两套逻辑各写一遍。

现状后果
每家模型 SDK 硬编码 换模型 = 改代码 + 发版
Key/参数写死配置文件 多租户无法各用各的 Key
模型不可运营配置 调温度、换模型都要重启
云端/本地两套逻辑 维护成本翻倍,易出错

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
工厂层 按平台动态构建并缓存 Spring AI 模型实例 AiModelFactory
能力层 对话/写作/知识库/绘图统一调用,只认 modelId AiModelService 等

一句话概括设计哲学:「密钥和参数交给数据库、对象构建交给工厂、能力调用交给 Spring AI 标准接口」。三层解耦后,新增一家模型只需在枚举里加一项、在工厂里加一个分支,上层业务零改动。

Spring AI 即 Spring 官方的 AI 集成框架,用于在 Spring Boot 应用中以统一接口调用各家大模型,例如把"对话"统一抽象成 ChatModel.call(Prompt),屏蔽厂商差异。


二、配置层:两张表撑起多模型

2.1 ai_api_key:密钥与平台

第一张表 ai_api_key 只管"用哪个平台、哪把钥匙、连哪个地址"。把密钥单独拆一张表,是为了让多个模型复用同一把 Key(比如通义的对话模型和向量模型共用一个 DashScope Key):

@TableName("ai_api_key")
@Data
public class AiApiKeyDO extends BaseDO {
@TableId
private Long id;
private String name; // 名称,便于运营辨认
private String apiKey; // 密钥
private String platform; // 平台,对应 AiPlatformEnum
private String url; // API 地址(可空,用默认;自建/代理时填)
private Integer status; // 状态:开启/关闭
}

image.png

2.2 ai_model:模型与调用参数

第二张表 ai_model 描述"具体哪个模型 + 调用参数"。一把 Key 下可以挂多个模型(如 deepseek-chat 和 deepseek-reasoner),对话类参数(温度、最大 token、上下文条数)也存在这里,运营随时可调:

@TableName("ai_model")
@Data
public class AiModelDO extends BaseDO {
@TableId
private Long id;
private Long keyId; // 关联 ai_api_key
private String name; // 模型名称
private String model; // 模型标识,如 deepseek-chat、qwen-max
private String platform; // 平台
private Integer type; // 类型:对话/向量/图片/语音…(AiModelTypeEnum)
private Integer sort;
private Integer status;
// 对话参数:调用时透传给大模型
private Double temperature; // 温度:越高越发散
private Integer maxTokens; // 单次回复最大 token
private Integer maxContexts; // 携带的历史消息条数
}

image.png

为什么拆成两张表而不是一张? 因为"密钥"和"模型"是一对多关系——一把 DashScope Key 下有 qwen-max、qwen-plus、text-embedding-v3 多个模型,密钥轮换时只改一行,所有挂在它下面的模型自动生效。这是典型的"配置归一化"。


三、平台枚举:一张表看懂支持哪些模型

所有支持的平台集中在 AiPlatformEnum 一个枚举里,新增厂商先在这里登记。RuoYi Office 当前覆盖国内主流、国外主流与多媒体三类共 17 个平台:

分类平台(platform 值)说明
国内对话 TongYi / YiYan / DeepSeek / ZhiPu / XingHuo / DouBao / HunYuan 通义千问 / 文心 / DeepSeek / 智谱 / 星火 / 豆包 / 混元
国内对话 SiliconFlow / MiniMax / Moonshot / BaiChuan 硅基流动 / MiniMax / 月之暗面 / 百川
国外对话 OpenAI / AzureOpenAI / Anthropic / Gemini / Grok GPT / Azure / Claude / Gemini / Grok
本地 Ollama 内网私有化部署,数据不出网
多媒体 StableDiffusion / Midjourney / Suno 绘图 / 绘图 / 音乐

模型类型用另一个枚举 AiModelTypeEnum 区分:CHAT(1) 对话、IMAGE(2) 绘图、VOICE(3) 语音、VIDEO(4) 视频、EMBEDDING(5) 向量、RERANK(6) 重排。同一个平台可以同时提供对话和向量模型,靠 type 区分用途。

关键设计:平台与类型用枚举集中管理,意味着"支持哪些模型"是一份可枚举、可校验的清单,而不是散落在各处的 if-else,这对后续维护和排错极其友好。

image.png

▲ AI 对话页面:右上角可随时切换底层模型(通义千问 / DeepSeek / 智谱等),温度、上下文条数等参数随模型配置生效,调用方完全无感。


四、工厂层:AiModelFactory 是整套设计的心脏

这是整个多模型集成最关键的一环。AiModelFactory 是一个工厂接口,负责把"平台 + Key + URL"翻译成 Spring AI 的标准模型对象。它的接口定义就是一份"能造什么"的清单:

public interface AiModelFactory {
// 按平台 + 密钥构建对话模型(核心)
ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);
// 向量模型,用于知识库 Embedding
EmbeddingModel 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 或自研包装类创建实例:

@Override
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); // 阿里 DashScope
case ZHI_PU: return buildZhiPuChatModel(apiKey, url);
case YI_YAN: return buildYiYanChatModel(apiKey); // 文心 QianFan
case 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,内部完成"查模型 → 查密钥 → 调工厂"的全过程:

    @Override
    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(密钥表)

    字段类型说明
    id bigint 主键
    name varchar 密钥名称
    api_key varchar 密钥(敏感,建议加密存储)
    platform varchar 平台标识,对应 AiPlatformEnum
    url varchar API 地址,可空走默认
    status tinyint 状态:0 开启 / 1 关闭

    ai_model(模型表)

    字段类型说明
    id bigint 主键
    key_id bigint 关联 ai_api_key
    name varchar 模型名称
    model varchar 模型标识,如 deepseek-chat
    platform varchar 平台
    type tinyint 类型:1 对话 / 2 图片 / 5 向量…
    temperature double 温度
    max_tokens int 单次最大 token
    max_contexts int 携带历史消息条数
    status tinyint 状态

    设计要点:

    • 密钥与模型 1:N:一把 Key 挂多个模型,密钥轮换改一行;
    • 参数随模型存:温度/上下文条数等可运营调,不写死配置;
    • 继承 BaseDO:自带 creator/create_time/tenant_id,天然多租户隔离。

    八、技术亮点总结

    设计要点实现方式价值
    配置入库 ai_api_key + ai_model 两表 换模型不发版,运营可自助
    统一抽象 Spring AI ChatModel/EmbeddingModel 上层调用零差异
    工厂构建 AiModelFactory 按平台 switch 新增厂商只加一个分支
    客户端缓存 Singleton 按 平台+Key+URL 缓存 复用连接,省资源
    流式输出 ChatModel.stream → Flux + SSE 打字机式体验,全平台一致
    多租户 密钥按租户隔离 各用各的 Key,计费清晰
    本地/云端 Ollama 与云端同走工厂 涉密内网与公网统一逻辑

    九、快速体验

    在线演示:http://ruoyioffice.com/web/(账号 admin / admin123),进入「AI 大模型 → 模型配置 / AI 对话」。

    推荐体验流程:

  • 进入「AI 大模型 → API 密钥」,新增一个平台密钥(如 DeepSeek / 通义千问);
  • 进入「模型管理」,在该密钥下新增一个对话模型,设置温度、最大 token;
  • 打开「AI 对话」,新建会话并选中刚配置的模型,发起一次提问;
  • 切换到另一个平台的模型,发同样的问题,对比效果与速度;
  • 体会"换模型只是切配置、代码零改动"的设计价值。
  • 源码仓库:

    仓库地址
    GitHub github.com/yuqing2026/ruoyi-office
    GitCode gitcode.com/zhouzhongyan/ruoyi-office
    Gitee gitee.com/yqzy1688/ruoyi-office

    结语

    多模型集成的精髓,不在于"会调某一家模型的 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 支持一下!

    image.png

    赞(0)
    未经允许不得转载:171主机测评 » Spring AI 多模型集成实战:OpenAI/DeepSeek/通义千问/智谱一套代码全搞定
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址