RAG技术栈全景:从向量数据库到LLM
上篇我们聊了RAG是什么,这篇我们直接上硬菜:把RAG技术栈的每一个组件拆开看清楚。不夸张地说,90%的人做RAG卡住,都是因为组件选型没想清楚就开干。看完这篇,你至少能画出自己系统的架构图。
大家好,我是黒漂技术佬。
一、一张图看清RAG全貌
先把全景图甩出来。一个企业级RAG系统包含6个核心层:
┌─────────────────────────────────────────────────┐ │ 【应用层】 │ │ 问答对话 │ 文档搜索 │ 数据助手 │ 标注平台 │ ├─────────────────────────────────────────────────┤ │ 【生成层】 │ │ Prompt模板 │ 上下文组装 │ 流式输出 │ 引用追溯 │ │ │ │ │ LLM(大语言模型) │ │ GPT-4 │ DeepSeek │ Qwen │ 私有部署模型 │ ├─────────────────────────────────────────────────┤ │ 【检索层】 │ │ 混合检索 │ Query改写 │ 重排序 │ 结果过滤 │ ├─────────────────────────────────────────────────┤ │ 【向量层】 │ │ Embedding模型 │ 向量数据库 │ 索引管理 │ ├─────────────────────────────────────────────────┤ │ 【数据层】 │ │ 文档解析 │ 文本分块 │ 元数据提取 │ 数据清洗 │ ├─────────────────────────────────────────────────┤ │ 【基础设施层】 │ │ Python/FastAPI │ Docker │ Redis │ MinIO │ └─────────────────────────────────────────────────┘看起来层很多?别慌,我们一层层拆。
二、数据层:垃圾进,垃圾出
RAG系统有一条铁律:知识库的质量上限 = 你喂进去的文档质量。
LLM再强,检索算法再精妙,如果文档本身就是一坨——表格乱码、图片丢失、层级全乱——那最终答案能好才见鬼了。
文档解析引擎
不同格式需要不同的解析器:
| 文档格式 | 推荐工具 | 难点 |
|---|---|---|
| PDF(文字型) | PyMuPDF / pdfplumber | 表格提取、双栏布局 |
| PDF(扫描型) | Tesseract OCR + pdf2image | 手写体、印章遮盖 |
| Word (.docx) | python-docx | 嵌入图片、复杂表格 |
| Markdown | 直接读文本 | 无 |
| HTML | BeautifulSoup | 导航栏、广告等噪音 |
| 飞书/语雀 | 各自API + HTML解析 | 富文本嵌套 |
| 微信群聊记录 | 正则解析 | 大量表情、无关消息 |
实操建议:
- 优先用
pdfplumber,它比 PyMuPDF 更擅长处理中文表格 - 扫描件必须过OCR,但别期望太高——扫描质量差的文档 OCR 准确率不到 80%
- 能用Markdown就别用Word——未来你的知识库一定是日积月累的,让员工从源头上用结构化的格式
文本分块(Chunking)
解析出纯文本后,不能整篇喂给Embedding模型——那就像把一本书扔进搜索框。需要切块。
常用的分块策略:
策略1:固定长度分块(naive,不推荐) 按 500 字切一块,遇到句号才断。 问题:可能在「第3章……第4章」中间一刀砍断。 策略2:语义分块(推荐) 按标题层级切:一二级标题作为独立块,正文段落聚合到对应标题下。 工具:LangChain 的 RecursiveCharacterTextSplitter + MarkdownHeaderTextSplitter 策略3:滑窗重叠(折中方案) 每块 500 字,相邻两块重叠 100 字。 好处:不会因为切在句子中间而丢掉语义。我的实战经验:
- 中文文档,块大小 300~500 字比较合适(太短缺少上下文,太长稀释语义)
- 每个块必须带元数据:来源文件名、页码、章节标题、更新时间
- 元数据在检索时会派上大用场——比如「只搜 2024 年后的技术文档」
三、向量层:RAG的发动机
上一篇文章讲了,Embedding是把文字变成数字数组。这层有两个关键选型。
Embedding 模型怎么选?
截至 2026 年,中文 Embedding 模型已经卷得不成样了。选型核心看三点:语义质量、推理速度、向量维度。
| 模型 | 维度 | 中文效果 | 特点 |
|---|---|---|---|
| text-embedding-3-small (OpenAI) | 1536 | ⭐⭐⭐ | 云端调用,付费,效果好但数据要流出 |
| bge-large-zh-v1.5 (BAAI) | 1024 | ⭐⭐⭐⭐ | 开源,中文顶流,本地部署 |
| m3e-large (moka-ai) | 1024 | ⭐⭐⭐⭐ | 开源,中英双语,社区活跃 |
| stella-large-zh (infgrad) | 1024 | ⭐⭐⭐⭐ | 开源,速度快 |
| jina-embeddings-v3 | 1024 | ⭐⭐⭐⭐ | 支持 8192 token 长文本 |
| GTE-Qwen2-7B-instruct | 3584 | ⭐⭐⭐⭐⭐ | 大模型做Embedding,效果最好但资源吃紧 |
我的选择:公司内部用bge-large-zh-v1.5,效果和速度都OK;如果预算充足且对数据安全没那么敏感,text-embedding-3-small的 API 调用是零运维成本的选择。
详细评测(同一份测试集、同一检索任务下的 mAP@10 对比)留到第 5 篇再展开。
向量数据库怎么选?
向量数据库是RAG的"记忆仓库"。市面上的选择大致分三类:
| 类型 | 代表产品 | 适用场景 |
|---|---|---|
| 轻量级向量库 | Chroma | 开发测试、Demo、单机小规模 |
| 专用向量数据库 | Milvus / Qdrant / Weaviate | 百万级以上向量、需要分布式 |
| 全文+向量融合 | Elasticsearch 8.x | 已有ES基础设施、需要混合检索 |
具体选哪个,我们放到第 6 篇详细对比。这里只给结论:Demo 阶段用 Chroma 最快跑通,生产环境优先看 Milvus 或带向量能力的 ES 8.x。
四、检索层:让系统真正懂你
检索层是RAG的"大脑",也是调优空间最大的环节。几个核心技术:
1. 混合检索(Hybrid Search)
单独用向量检索有一个经典问题:你问"张三的报销单",向量可能匹配到一堆讲报销流程的文档,但就是找不到张三那份具体的报销单。
原因在于:向量擅长语义类比,但不擅长精确匹配人名、工号、金额这类实体。
所以企业级RAG几乎都用「混合检索」:
用户问题 │ ├──→ 向量检索(语义召回) → Top 20 │ └──→ 关键词检索(BM25/ES)→ Top 20 │ └──→ RRF(倒数排名融合)/ 加权融合 → Top 10 │ └──→ 重排序(Reranker)→ Top 5 → 送给LLM2. Query 改写
用户不会写完美的搜索查询。他们可能会问:“上次那个关于代码评审的,就是那个谁来着,你说要加的那个检查项……”
这种问题直接向量化,几乎搜不到任何东西。所以需要Query改写——在检索之前,用一个小型的LLM把用户的模糊问题重写为检索友好的形式:
原文:“上次那个关于代码评审的检查项”
改写后:“代码评审(Code Review)检查项清单 规范”
3. 重排序(Reranker)
向量检索返回的 Top-K 只是"大概相关",还需要一个更强的模型重新打分排序。推荐用bge-reranker-large,它在 MTEB 中文排行榜上长期霸榜。
流程:向量检索出 20 条 → Reranker 精排后取前 5 条 → 送给 LLM。
五、生成层:把材料变成答案
生成层看似最简单——把文档片段拼进Prompt就行。但实际上,Prompt写得好不好,答案质量差一个档次。
一份好的RAG Prompt 至少包含:
【角色设定】你是一个企业知识库助手,服务于XX公司内部员工。 【核心规则】 1. 只根据提供的文档内容回答,文档中没有的信息明确说"不知道" 2. 回答末尾必须标注引用的文档名称和页码 3. 如果文档中有矛盾信息,如实指出并说明分别来自哪份文档 【相关文档】(由检索系统自动填充) ... 【用户问题】(用户的原话)LLM 选型
| 模型 | 适用场景 | 成本 |
|---|---|---|
| GPT-4o | 最高质量,复杂推理 | $$ |
| DeepSeek-V3 | 性价比极高,中文友好 | $ |
| Qwen2.5-72B | 私有化部署首选 | 自建GPU |
| GLM-4 | 中文效果好 | $ |
实操建议:内部问答场景用 DeepSeek 完全够用,成本只有 GPT-4o 的十分之一不到;如果数据安全要求高,部署一个 Qwen2.5-32B 本地跑也不是不行。
六、企业级架构全景(附带一张真实部署图)
讲完所有组件,来看一张我实际部署的企业级RAG架构:
┌─────────────┐ │ Nginx │ 负载均衡 + SSL └──────┬──────┘ │ ┌───────────┴───────────┐ │ │ ┌──────▼──────┐ ┌──────▼──────┐ │ FastAPI │ │ FastAPI │ API服务(多副本) │ (问答API) │ │ (管理API) │ └──────┬──────┘ └──────┬──────┘ │ │ ┌─────────┼───────────────┬───────┼─────────┐ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │Milvus│ │Redis │ │ LLM │ │Embed│ │MinIO │ │向量库│ │ 缓存 │ │ 服务 │ │ ding│ │文件存│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ 文档源 (PDF/Word/MD)关键设计点:
- 向量库独立部署:Milvus 跑在独立服务器上,不跟 API 服务耦合
- Redis 做两层缓存:问题→答案缓存(完全相同的提问秒回)+ Embedding 结果缓存(节省反复调 Embedding 服务的开销)
- MinIO 存原始文件:文档的原始 PDF/Word 存在对象存储里,方便追溯原文
- 异步文档处理:上传文档后走消息队列(Redis Stream / RabbitMQ),解析→分块→向量化→入库,全部异步,上传秒回
七、这里选型,最容易踩的3个坑
坑1:上来就搞微服务
Demo 阶段一个 FastAPI + Chroma + SQLite 完全够用。不要一上来就拆微服务、搞 K8s、配服务网格——你连用户到底怎么用都还没搞清楚。
先跑通一个单机的端到端流程,再考虑架构拆分。
坑2:Embedding 模型随便选
选了英文优化的模型贴到中文场景上,语义检索基本等于随机猜。中文必须选中英双语或纯中文优化的模型。
坑3:文档不处理就直接扔
“我试了试,把 200 页的 PDF 直接 Embedding,效果不好。”——那是当然的。文档必须经过解析→清洗→分块的流水线。跳过这一步,就是在给后续所有环节埋雷。
💬 你的技术栈选好了吗?Embedding 模型和向量数据库准备用哪个?评论区分享一下,我帮你参谋参谋!