大模型 RAG 后端架构:向量数据库选型与检索优化
大模型 RAG 后端架构:向量数据库选型与检索优化

一、大模型的知识边界:训练数据截止与领域知识缺失
大语言模型的知识来源于训练数据,存在两个根本性限制:训练数据有截止日期(无法获取最新信息),且通用模型缺乏垂直领域的专业知识。RAG(Retrieval-Augmented Generation)通过"先检索、后生成"的范式弥补这些缺陷——先从外部知识库中检索相关文档,再将检索结果注入模型上下文,让模型基于真实数据生成回答。
RAG 的后端架构核心是向量数据库的选型与检索优化。向量数据库负责存储文档的嵌入向量并支持高效的相似度检索,其性能直接决定 RAG 系统的响应延迟和检索质量。但向量数据库的选型不是简单的"性能对比"——不同的数据规模、查询模式和部署约束,适合不同的数据库方案。
二、RAG 后端架构与向量检索流程
RAG 系统的后端架构分为离线索引和在线检索两条路径。离线阶段负责文档的采集、分块、嵌入编码和索引构建;在线阶段处理用户查询的实时嵌入、向量检索和上下文注入。
flowchart TB
A[文档数据源] --> B[文档采集与清洗]
B --> C[文本分块策略]
C --> D[嵌入编码]
D --> E[向量数据库写入]
F[用户查询] --> G[查询嵌入编码]
G --> H[向量相似度检索]
E --> H
H --> I[Top-K 候选文档]
I --> J[语义重排序]
J --> K[上下文窗口截断]
K --> L[Prompt 组装]
L --> M[LLM 生成回答]
subgraph 向量数据库选型
N[小规模: FAISS/Chroma]
O[中规模: Milvus/Qdrant]
P[大规模: Pinecone/Weaviate]
end
E --> N
E --> O
E --> P
上图展示了 RAG 系统的完整数据流。向量数据库选型是架构决策的核心:FAISS 适合单机小规模场景(< 100 万条),Milvus 适合分布式中等规模(100 万-1 亿条),Pinecone 等托管服务适合不想自运维的团队。
三、生产级实现:RAG 检索引擎
// RAGRetrievalEngine.java — RAG 检索引擎核心实现
import java.util.*;
import java.util.concurrent.*;
// 文档分块记录
record DocumentChunk(
String id,
String content,
String source,
Map<String, String> metadata,
float[] embedding
) {}
// 检索结果
record RetrievalResult(
DocumentChunk chunk,
double score,
String rerankReason
) {}
// 文档分块器:按语义边界切分
// 设计意图:固定长度分块会截断语义完整的段落,
// 按段落和句子边界分块可以保留语义完整性
class SemanticChunker {
private static final int MAX_CHUNK_TOKENS = 400;
private static final int OVERLAP_TOKENS = 50;
List<String> chunk(String text) {
List<String> paragraphs = Arrays.asList(text.split("\\n{2,}"));
List<String> chunks = new ArrayList<>();
StringBuilder currentChunk = new StringBuilder();
for (String para : paragraphs) {
if (currentChunk.length() + para.length() > MAX_CHUNK_TOKENS * 2) {
if (currentChunk.length() > 0) {
chunks.add(currentChunk.toString().trim());
// 保留重叠区域
String overlap = currentChunk.substring(
Math.max(0, currentChunk.length() - OVERLAP_TOKENS * 2)
);
currentChunk = new StringBuilder(overlap);
}
}
currentChunk.append(currentChunk.length() > 0 ? "\n\n" : "").append(para);
}
if (currentChunk.length() > 0) {
chunks.add(currentChunk.toString().trim());
}
return chunks;
}
}
// 检索引擎:向量检索 + 语义重排序
class RAGRetrievalEngine {
private final EmbeddingClient embeddingClient;
private final VectorStore vectorStore;
private final Reranker reranker;
private final int topK;
private final int rerankTopN;
RAGRetrievalEngine(EmbeddingClient embeddingClient,
VectorStore vectorStore,
Reranker reranker,
int topK, int rerankTopN) {
this.embeddingClient = embeddingClient;
this.vectorStore = vectorStore;
this.reranker = reranker;
this.topK = topK;
this.rerankTopN = rerankTopN;
}
// 完整检索流程:查询嵌入 → 向量检索 → 重排序 → 截断
// 设计意图:向量检索是粗筛(召回率高但精度有限),
// 重排序是精排(基于交叉编码器提升精度)
List<RetrievalResult> retrieve(String query) {
// 步骤 1:查询嵌入
float[] queryEmbedding = embeddingClient.embed(query);
// 步骤 2:向量相似度检索(粗筛,取 topK 条)
List<RetrievalResult> candidates = vectorStore.search(
queryEmbedding, topK
);
// 步骤 3:语义重排序(精排,取 rerankTopN 条)
// 设计意图:向量检索使用双编码器(Bi-Encoder),
// 查询和文档独立编码,速度虽快但精度有限。
// 重排序使用交叉编码器(Cross-Encoder),
// 联合编码查询和文档,精度更高但速度较慢。
List<RetrievalResult> reranked = reranker.rerank(
query, candidates, rerankTopN
);
return reranked;
}
// 构建注入 LLM 的上下文
// 设计意图:检索到的文档可能超过 LLM 上下文窗口限制,
// 需要按优先级截断并标注来源
String buildContext(List<RetrievalResult> results, int maxContextTokens) {
StringBuilder context = new StringBuilder();
int usedTokens = 0;
for (RetrievalResult result : results) {
int chunkTokens = estimateTokens(result.chunk().content());
if (usedTokens + chunkTokens > maxContextTokens) {
break; // 超出上下文窗口,截断
}
context.append("[来源: ").append(result.chunk().source())
.append("]\n")
.append(result.chunk().content())
.append("\n\n");
usedTokens += chunkTokens;
}
return context.toString();
}
private int estimateTokens(String text) {
return (int) Math.ceil(text.length() / 2.0);
}
}
// 向量存储接口:抽象不同向量数据库的实现
interface VectorStore {
void upsert(List<DocumentChunk> chunks);
List<RetrievalResult> search(float[] queryEmbedding, int topK);
void deleteBySource(String source);
}
// 重排序器接口
interface Reranker {
List<RetrievalResult> rerank(String query, List<RetrievalResult> candidates, int topN);
}
四、边界分析与架构权衡
RAG 后端架构在生产落地中需要正视以下 Trade-off:
向量数据库的选型决策。FAISS 是最简单的方案(单机内存索引,零运维),但无法水平扩展且不支持持久化。Milvus 支持分布式部署和持久化,但运维复杂度高(依赖 etcd、MinIO、Pulsar)。Pinecone 是全托管服务,零运维但成本较高(按向量数量和查询次数计费)。选型建议:< 50 万条用 FAISS,50 万-5000 万条用 Milvus/Qdrant,> 5000 万条或不想运维用 Pinecone。
检索精度与延迟的矛盾。双编码器检索延迟低(< 10ms),但精度有限;交叉编码器重排序精度高,但延迟高(每条 50-100ms)。Top-K=20 的重排序需要 1-2 秒。建议:粗筛取 Top-50,重排序取 Top-5,在精度和延迟间取得平衡。
分块策略的影响。分块大小直接影响检索质量:块太大导致嵌入向量"稀释"关键信息,块太小丢失上下文。固定长度分块最简单但效果最差,按段落分块保留语义完整性,按语义边界分块(使用 NLP 句子分割器)效果最好但实现复杂。建议从段落分块开始,根据检索效果逐步优化。
适用边界:RAG 最适合知识密集型问答场景(技术文档、法律条文、产品手册)。对于需要精确匹配的场景(如库存查询、价格计算),传统数据库查询比 RAG 更可靠。
五、总结
RAG 后端架构将大模型从"封闭推理"扩展到"开放检索"。核心要点:向量数据库选型需根据数据规模和运维能力决策,双编码器粗筛 + 交叉编码器精排的两阶段检索在精度和延迟间取得平衡,分块策略直接影响检索质量。落地建议:第一,从 FAISS + 段落分块快速验证 RAG 效果;第二,根据数据规模决定是否迁移到分布式向量数据库;第三,引入重排序提升检索精度,但需控制延迟在可接受范围内。关键原则:RAG 的价值不在于"检索到文档",而在于"检索到正确的文档"——检索质量决定生成质量。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)