AI基础设施层宏观设计
前面的 RAG 理论系列和知识库实战系列,我们从 RAG 的概念讲到了评估优化,从文件上传讲到了分块存储。到这里,你应该已经清楚一个 RAG 系统的完整链路:用户提问 → 检索知识库 → 重排序 → 喂给大模型生成回答。
但有一个问题一直被我们当作黑盒处理——模型调用。
前面的文章里,每次需要调大模型的时候,我们都是直接写 OkHttp 请求,往百炼或者硅基流动发一个 HTTP 调用,拿到结果就完事。这在教学环境里没问题,但放到 Ragent 这样一个需要同时支持百炼、硅基流动、Ollama 等多个供应商,并且覆盖 Chat、Embedding、Rerank 三种模型能力的项目里,事情就没这么简单了。
这些模型调用在项目中到底是怎么组织的?直接在 Service 里写 OkHttp 调用?如果百炼挂了怎么办?如果要加一个新的供应商怎么办?
从这篇开始,我们进入大模型调度引擎实战系列。这个系列会逐篇拆解 Ragent 项目的 infra-ai 模块——它是整个项目的 AI 基础设施层,负责屏蔽供应商差异、实现多模型路由和故障转移。这一篇先从宏观视角讲清楚它的整体设计。
为什么需要 AI 基础设施层
1. 没有 infra 层的世界
假设你在做一个电商客服知识库系统。产品需求很明确:用户提问时,先调 Embedding 模型把问题向量化,再去向量数据库检索相关文档,用 Reranker 精排一遍,最后把 chunk 喂给 Chat 模型生成回答。
一开始,你选了百炼作为模型供应商,直接在业务 Service 里写调用代码:
// ChatService.java —— 直接调百炼 Chat API
public String chat(String prompt) {
JsonObject body = new JsonObject();
body.addProperty("model", "qwen-plus");
body.add("messages", buildMessages(prompt));
Request request = new Request.Builder()
.url("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions")
.addHeader("Authorization", "Bearer " + BAILIAN_API_KEY)
.post(RequestBody.create(body.toString(), JSON))
.build();
Response response = httpClient.newCall(request).execute();
// ... 解析响应
}
能跑,没毛病。Embedding 调用也差不多,换个 URL 和请求格式就行。
然后问题来了。
场景一:百炼 API 挂了。 某天下午百炼服务抖了一下,返回 500 错误。你的整个客服系统直接瘫痪——因为所有对话都走百炼,没有备选方案。老板问你为什么系统挂了,你说供应商挂了。老板说那你怎么不做个备份?
场景二:加一个硅基流动做备份。 老板的要求合理,你开始加硅基流动。但硅基 流动的 API 地址不一样(https://api.siliconflow.cn/v1/chat/completions),认证方式一样但模型名不同(deepseek-ai/DeepSeek-V3)。你在 ChatService 里加了一堆 if-else:
public String chat(String prompt) {
if ("bailian".equals(currentProvider)) {
// 百炼的 URL、Header、模型名
} else if ("siliconflow".equals(currentProvider)) {
// 硅基流动的 URL、Header、模型名
}
// 失败了还要 try-catch 切换到另一个供应商...
}
场景三:再加一个 Ollama 做本地部署。 测试环境不想烧钱调云端 API,装了一个 Ollama 跑本地模型。但 Ollama 不需要 API Key,端点路径也不一样(http://localhost:11434/v1/chat/completions)。又是一套 if-else。
场景四:Embedding 和 Rerank 也要同样的逻辑。 你发现 EmbeddingService 也需要做供应商切换和容错,RerankService 也要。三个 Service 里面,每个都有一堆重复的 HTTP 调用代码、供应商判断逻辑、错误处理代码。
到这里,代码已经变成了这个样子:三个 Service 里散落着三个供应商的 if-else 判断,每个 Service 都在重复做 HTTP 请求构建、响应解析、错误处理。供应商的 URL、API Key、模型名硬编码在代码里。想加个新供应商?每个 Service 都得改。想调整优先级?改代码重新部署。
这就是没有基础设施层的代价。
2. 我们需要什么
从上面的反面例子里,可以提炼出四个核心需求:
统一接口,屏蔽差异。 业务层不应该关心当前调的是百炼还是硅基流动,更不应该知道各家 API 的请求格式有什么区别。业务层只需要说我要做一次 Chat 调用,传入 Prompt,拿到结果。
多模型路由与故障转移。 同一种能力(比如 Chat)可以配置多个候选模型,按优先级排序。高优先级的模型挂了,自动切换到下一个,业务层完全无感知。
配置驱动,零代码切换。 加一个新供应商、调整模型优先级、切换默认模型——这些操作不应该改代码,改配置文件就够了。
新供应商可插拔扩展。 哪天要接入一个 vLLM 私有部署的推理服务,只需要实现一个客户端类,不需要动任何已有代码。
这就是 AI 基础设施层要解决的问题。
3. infra-ai 模块的定位
用一句话概括:infra-ai 是业务层和模型供应商之间的中间层。
业务层只依赖 infra-ai 暴露的三个接口(LLMService、EmbeddingService、RerankService),完全不感知具体供应商。供应商是谁、优先级怎么排、挂了怎么切——这些全部由 infra-ai 内部处理。
整体架构总览
1. 模块包结构
infra-ai 模块共有 9 个包,每个包有明确的职责边界:
| 包名 | 职责 | 核心类 |
|---|---|---|
config | 配置根 | AIModelProperties——将 YAML 配置绑定为类型安全的 Java 对象 |
enums | 类型词汇表 | ModelProvider(供应商枚举)、ModelCapability |