多模态和 RAG 上生产,瓶颈经常在模型外面
很多 AI 应用最初是一个 chat endpoint,后来慢慢变成 RAG,再加图片、PDF、表格、视频帧。模型没换几次,基础设施却已经从“推理服务”变成“数据处理和推理混合系统”。
多模态和 RAG 的核心问题是:token 只是最后一公里。在进入 LLM 前,系统已经搬运了大量字节、图片、索引和中间特征。
RAG 的生产链路
一个稍微靠谱的 RAG 不只是 vector search:
每一段都可能跑在 Kubernetes 上,也都需要独立伸缩。Embedding 是 GPU/CPU 混合负载,OCR 可能吃 CPU 或专用模型,reranker 可能是小 GPU 模型,LLM 推理是大 GPU 负载。把它们塞进同一个 Pod,只会让扩容和排错变复杂。
多模态不是“图片转文字”这么简单
视觉语言模型通常包含视觉编码器、投影层和语言模型。图片输入会带来几个基础设施问题:
- 图片体积远大于文本,请求入口要限制大小;
- 预处理需要 CPU/GPU,不能和 LLM decode 互相抢;
- 图像 embedding 或视觉 token 会增加上下文长度;
- 同一请求可能同时访问对象存储和 GPU;
- 缓存策略要按图片 hash,而不是按 prompt 文本。
如果业务允许,图片解析、OCR、caption、embedding 可以先异步化。不要所有图片都实时塞进最贵的大模型。
Embedding 和 reranker 应该单独池化
Embedding 请求通常短、密集、可批量,适合高吞吐。Reranker 请求更像小模型推理,延迟敏感但显存需求低。它们不该和聊天大模型争同一组 H100。
常见资源池:
| 组件 | 资源建议 |
|---|---|
| OCR / parse | CPU + 少量 GPU |
| embedding | L4/L40S/A10 或 MIG |
| reranker | 小 GPU / MIG |
| vector DB | CPU、内存、NVMe |
| LLM | 大显存 GPU |
这样做的好处是成本清楚。Embedding 量暴涨时,不会直接挤掉聊天推理;向量库 rebuild 时,也不会影响 LLM 的 SLA。
索引流水线要版本化
RAG 最大的隐性问题是索引版本。文档更新、chunk 策略变化、embedding 模型升级、reranker 调参,都会让检索结果变化。
索引任务应该带版本:
dataset_version + parser_version + chunker_version + embedding_model + index_params
线上查询也要记录命中的 index version。否则用户说“昨天能搜到,今天搜不到”,平台很难复现。
Kubernetes 里可以用 Job/Workflow 跑索引,用队列限制并发,用对象存储保存中间结果,用事件触发增量索引。不要让在线 Pod 顺手做重索引。
RAG 和长上下文的关系
长上下文模型让 RAG 变简单了吗?只简化了一部分。它可以减少 aggressive chunking,但没有消除检索和排序。原因是成本。
把 200 页 PDF 全塞进 prompt,技术上也许能跑,经济上不一定合理。更好的做法是:
- 检索先缩小候选;
- reranker 做精排;
- prompt builder 保留证据和出处;
- 长上下文用来容纳更完整的证据,而不是吞全部原文。
K8s 上的服务拆分
一个生产多模态 RAG 平台可以拆成:
- ingestion namespace:解析、OCR、清洗;
- indexing namespace:embedding、索引构建;
- retrieval namespace:vector DB、reranker;
- serving namespace:LLM runtime、gateway;
- observability namespace:trace、metrics、日志。
每个 namespace 配不同 GPU 队列和限额。LLM 推理池要保护,不能被索引重建任务抢走。
多模态和 RAG 的难点不在模型调用,而在数据和推理之间的工程边界。边界清楚,系统才有机会稳定。