JeecgBoot 多模型统一管理与路由
JeecgBoot 多模型统一管理与路由
学习目标
通过本章学习,你将能够:
- 理解 JeecgBoot 如何统一管理 10+ 个 AI 模型提供商
- 掌握模型参数合并的优先级策略(应用级 > 模型级 > 系统默认)
- 实现模型未激活时的自动降级到默认模型
- 了解 DeepSeek 推理模型的特殊适配方案
前置知识
本章涉及多模型适配的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 JeecgBoot 的具体实现。
项目实践
模型注册与实体设计
JeecgBoot 通过 AiragModel 实体统一管理所有 AI 模型:
| 字段 | 说明 |
|---|---|
provider | 模型提供商(openai、anthropic、dashscope、qianfan、zhipu、ollama、gemini 等) |
modelName | 模型名称(gpt-4o、claude-sonnet-4、deepseek-v4-flash 等) |
baseUrl | API 基础地址(支持私有部署) |
credential | JSON 存储凭证(apiKey、secretKey、httpVersionOne) |
modelParams | JSON 存储模型参数(temperature、topP、maxTokens、timeout 等) |
activateFlag | 激活状态(0=未激活,1=已激活) |
modelType | 模型类型(LLM、EMBED、IMAGE) |
设计亮点:凭证和参数全部以 JSON 字符串存储,而非独立数据库字段。这样新增模型时无需修改表结构,只需在前端配置界面扩展即可。
统一调用层:AIChatHandler
AIChatHandler 是平台的核心聊天入口,它实现了 IAIChatHandler 接口,提供:
- 非流式调用:
completions(modelId, messages)— 一次性返回完整响应 - 流式调用:
chat(modelId, messages)— 返回TokenStream,支持逐 token 推送 - 默认模型调用:
chatByDefaultModel(messages)— 不指定模型时使用系统默认
调用链路:
Controller → AIChatHandler → LLMHandler → langchain4j → 具体模型 APILLMHandler 是基于 langchain4j 的统一封装层,负责根据 AIChatParams.provider 和 AIChatParams.modelName 选择对应的 langchain4j 模型实现。
参数合并策略:mergeParams
模型参数的合并遵循明确的优先级:
最终参数 = 应用级参数 > 模型级参数 > 系统默认值// 伪代码:mergeParams 逻辑private AIChatParams mergeParams(AiragModel airagModel, AIChatParams params) { // 1. 模型级参数作为基础 if (airagModel 有 modelParams) { params.temperature = params.temperature ?? airagModel.temperature; params.topP = params.topP ?? airagModel.topP; params.maxTokens = params.maxTokens ?? airagModel.maxTokens; // ... 其他参数 }
// 2. 凭证透传 params.apiKey = airagModel.credential.apiKey; params.secretKey = airagModel.credential.secretKey;
// 3. RAG 配置(如果应用绑定了知识库) if (params.knowIds 非空) { params.queryRouter = embeddingHandler.getQueryRouter( params.knowIds, params.topNumber, params.similarity ); }
// 4. MCP/插件配置(非推理模型) if (!是 DeepSeek 推理模型) { buildPlugins(params); }
// 5. DeepSeek 推理模型特殊处理 if (是 DeepSeek 推理模型) { params.returnThinking = true; params.sendThinking = true; }
return params;}模型降级策略
当用户选择的模型未激活(activateFlag = 0)时,系统不会报错,而是自动降级:
// 流式聊天降级逻辑AiragModel airagModel = airagModelMapper.getByIdIgnoreTenant(modelId);if (airagModel == null || airagModel.getActivateFlag() == 0) { log.warn("模型未激活,采用默认模型"); return chatByDefaultModel(messages, params);}默认模型通过 AiChatConfig YAML 配置:
jeecg: ai: chat: ai-model-draw: provider: openai model: dall-e-3 api-key: ${OPENAI_API_KEY} api-host: https://api.openai.com适用场景:多租户环境中,管理员配置了多种模型供用户选择。当某个模型的 API Key 过期或余额不足时,用户不会立即感知到中断,系统自动切换到默认模型继续服务。
DeepSeek 推理模型适配
这是 JeecgBoot 源码中发现的一个经典实战案例。
问题:DeepSeek 推理模型(deepseek-v4-flash 等)要求多轮工具调用时,每次请求必须回传上一次的 reasoning_content 字段。但历史消息持久化层不保存该字段,导致 API 校验失败。
解决方案:在发送前,为历史 AI 消息注入占位 thinking:
// 为 DeepSeek 推理模型的历史 AI 消息注入占位 thinkingprivate static List<ChatMessage> injectThinkingPlaceholderIfNeeded(messages, modelName) { if (!isDeepSeekThinkingModel(modelName)) return messages;
for msg in messages: if msg is AiMessage and msg.thinking() 为空: rebuilt = AiMessage.builder() .text(msg.text()) .thinking("...") // 占位 .toolExecutionRequests(msg.toolExecutionRequests()) .build() result.add(rebuilt) return result}模型识别采用精确匹配 + 关键字包含的双重策略:
DEEPSEEK_THINKING_MODELS = {"deepseek-reasoner", "deepseek-v4-flash", "deepseek-v4-pro"}
isDeepSeekThinkingModel(name): 1. DEEPSEEK_THINKING_MODELS 精确匹配 2. 关键字包含:reasoner / v4-flash / v4-pro 3. 兼容版本后缀(如 deepseek-v4-flash-0428)问题与规避
| 陷阱 | 表现 | 对策 |
|---|---|---|
| 模型未配置默认降级 | AiChatConfig 未配置默认模型 → 图像生成直接报错 | 启动时检查默认模型配置,缺失时给出友好提示 |
| 通义千问 null 消息内容 | Qwen API 不接受 null 消息内容 | 重建 ToolExecutionResultMessage 时确保 text 不为 null(PR#9539 修复) |
| 工具调用消息序列不完整 | 历史消息数量设置过小 → 工具调用上下文丢失 | 友好提示用户增加历史消息条数 |
| DeepSeek 联网搜索后异常 | 联网搜索工具的响应格式与预期不符 | 强制 returnThinking=true 确保深度思考模式(issues/9607) |
设计取舍
JSON 存储 vs 独立字段
JeecgBoot 选择:credential 和 modelParams 使用 JSON 字符串存储。
| 方案 | 优势 | 代价 |
|---|---|---|
| JSON 字符串 | 新增模型无需改表结构,前端配置灵活 | 无法在数据库层面做索引和查询 |
| 独立字段 | 可索引、可查询 | 每增加一种凭证类型都要改表结构 |
在模型管理场景中,管理员更关注的是”有哪些模型”而非”按 apiKey 搜索模型”,JSON 存储是合理选择。
降级到默认 vs 报错提示
JeecgBoot 选择:模型未激活时静默降级到默认模型。
| 方案 | 体验 | 风险 |
|---|---|---|
| 静默降级 | 用户无感知,服务不中断 | 用户可能不知道自己的响应来自不同的模型 |
| 报错提示 | 用户明确知道问题 | 服务中断,用户体验差 |
生产系统中,服务连续性优先于透明度。JeecgBoot 通过 log.warn 记录降级事件,管理员可在后台日志中追踪。
参考来源
- langchain4j 官方文档
- JeecgBoot 源码 v3.9.2,
AIChatHandler.java、AiragModel.java