一、先看一个问题:大模型为什么需要"查资料"?
假设你问 ChatGPT:
"知行科技 2025 年的年会主题是什么?"
如果你之前从没告诉过它这家公司的任何信息,它会怎么回答?大概率是一本正经地编一个------因为它的知识截止在训练数据日期,它不知道 2025 年的事,更不知道一家它没见过的公司。
这不是 ChatGPT 的问题。这是所有大语言模型的共同局限:它们只知道训练时见过的内容,不知道你手里的私有文档。
传统解决方案是微调(Fine-tuning)------把新知识"背"进模型参数。但微调有三个致命缺陷:每次更新知识都要重新训练、无法快速追加新数据、模型学会"编得更像真的"而不是"说不知道"。
RAG(Retrieval-Augmented Generation,检索增强生成)换了一个思路:不靠背,靠查。 让模型在回答问题之前,先去一个专门的知识库里检索相关文档,然后把查到的内容拼进 Prompt,再让模型基于这些资料回答。
这个思路的核心公式只有八个字:先查资料,再答题。
二、RAG 是怎么工作的?
RAG 的工作流程分为四个步骤:
| 步骤 | 做什么 | 类比 |
|---|---|---|
| 1. 索引(Indexing) | 把文档切成段落,用 Embedding 模型转成向量,存入向量数据库 | 把书拆成纸条,贴上标签,放进分类柜 |
| 2. 检索(Retrieval) | 用户提问时,把问题也转成向量,在向量数据库中找最相似的 Top-K 个段落 | 拿着关键词去柜子里翻出最相关的几条纸条 |
| 3. 增强(Augmentation) | 把检索到的段落拼接到用户的原始问题后面,组成一个完整的 Prompt | 把纸条贴在问题上,一起递给答题人 |
| 4. 生成(Generation) | LLM 基于增强后的 Prompt 生成答案,可以标注引用来源 | 答题人看完纸条,给出有据可查的答案 |
为什么步骤 2 要用向量数据库而不是普通的关键词搜索?
因为向量相似度能理解"语义"。当你问"公司多少人"时,传统关键词搜索可能只匹配含"人"或"多少"的句子,但向量搜索能找出"团队规模"、"员工数量"这种用词不同但意思相同的段落。这是 RAG 区别于传统全文搜索的关键。
在实际项目中,有几个关键设计选择直接影响效果:
- Chunk Size(分块大小):太小丢失上下文("根据第3条"------第3条是什么?),太大检索精度下降。典型值是 512-1024 个字符。
- Embedding 模型:中文场景推荐 BGE/M3E 系列,英文推荐 text-embedding-3。维度越高表达能力越强,但检索速度越慢。
- 检索策略:纯向量检索有精度损失,混合检索(向量 + BM25 关键词 + 重排序)是生产级 RAG 的标配。
三、一个能跑起来的 Demo:rag_demo
理论讲完了,我们看一个能真正跑起来的项目。代码在 https://gitee.com/XiaoYRecluse/rag_demo.git ,技术栈是 FastAPI + ChromaDB + 多提供商 LLM/Embedding。
3.1 项目做了什么?
这个 Demo 实现了一个完整的 RAG 系统,把一份公司的员工手册(约 9000 字)做成了可问答的知识库。
核心特性:
- FastAPI REST API :提供
/api/v1/ask(问答)、/api/v1/upload(文档上传)、/api/v1/health(健康检查)等接口 - 多提供商架构:LLM 和 Embedding 都可以在 DeepSeek API / LM Studio / Ollama / SentenceTransformers 之间自由切换------本地跑还是用云端 API,改一个环境变量就行
- ChromaDB 向量存储:持久化到本地文件夹,无需额外安装数据库服务
- 64 题自动化测试套件:覆盖 8 个检索维度,可复现的量化评估
3.2 三层架构
┌─────────────────────────────────────┐
│ API 层 (FastAPI) │ ← 接收 HTTP 请求
│ /ask /upload /docs /health │
└───────────────┬─────────────────────┘
│
┌───────────────▼─────────────────────┐
│ 业务逻辑层 │
│ QueryPipeline IngestionPipeline │ ← 编排查询和摄入流程
└───────────────┬─────────────────────┘
│
┌───────────────▼─────────────────────┐
│ 核心服务层(多提供商) │
│ LLMService EmbeddingService │ ← 适配 4 种 LLM + 4 种 Embedding
│ RetrievalService → ChromaDB │
└─────────────────────────────────────┘
最巧妙的设计是provider 切换 ------只需要修改 .env 文件中的一个配置项:
| 场景 | EMBEDDING_PROVIDER | LLM_PROVIDER | 网络 |
|---|---|---|---|
| 纯本地离线 | sentence_transformers |
lm_studio |
离线 |
| 本地 API | ollama |
ollama |
本地 |
| 云端 | deepseek |
deepseek |
需要 |
这种设计的实际意义:开发时用免费的本地模型调试,上线后切换到更精准的云端 API------代码一行不改。
3.3 真实测试数据
这个 Demo 不是"能跑就行"的玩具------它配了一套 64 题的测试集,覆盖精确事实、概括总结、多段落推理等 8 个维度。最新一轮测试结果:
| 指标 | 数值 | 解读 |
|---|---|---|
| 检索命中率 | 67.2% (43/64) | 64 题中有 43 题找到了正确答案所在的段落 |
| 答案正确率 | 48.4% (31/64) | 找到段落后,LLM 能正确回答的有 31 题 |
| 平均延迟 | 4057ms/题 | 本地 Qwen3.5-9B + RTX 5090 |
67.2% 的检索命中率暴露了 Naive RAG 的核心瓶颈:检索不到正确段落,后续的增强和生成都是空中楼阁。 这也是为什么生产级 RAG 需要混合检索 + 重排序------这些正是下文要谈的扩展方向。
四、从 Demo 到生产:五个扩展方向
这个 Demo 是一个干净的起点。如果你想让它在真实场景中可用,以下是五个递进的扩展方向。
扩展 1:混合检索(难度:低,效果:显著)
当前状态 :只用了向量相似度检索(ChromaDB query → Top-K 段落)。
问题 :纯向量检索对精确匹配(人名、日期、编号)不敏感------你问"第 3.2 条是什么",它可能返回完全不相关的段落。
方案 :加入 BM25 关键词检索作为补充,两路结果通过 RRF(Reciprocal Rank Fusion)合并,再用一个轻量级 Reranker 模型二次排序。
预期提升:检索命中率 67% → 85%+
扩展 2:文档格式支持(难度:低)
当前状态 :只支持 Markdown 文档摄入,按 ## 标题分块。
扩展:加入 PDF(PyMuPDF)、Word(python-docx)、网页抓取(trafilatura)的支持,覆盖更多真实文档来源。
扩展 3:对话历史记忆(难度:中)
当前状态 :每次问答独立,没有上下文记忆。用户追问"那第二条呢?",系统不知道"那"指什么。
方案:加入对话历史管理------将最近 N 轮对话压缩后注入 Prompt,同时支持"基于之前的回答继续追问"。这是从"单轮问答"到"对话式 RAG"的跨越。
扩展 4:多文档知识库(难度:中)
当前状态 :只有一个 collection(documents),所有文档混在一起。
扩展:支持创建多个 collection(如"员工手册"、"技术文档"、"规章制度"),用户提问时可以指定检索范围,或者让系统自动路由到最相关的 collection。
扩展 5:Agentic RAG(难度:高)
当前状态 :固定流程------提问 → 检索 → 拼接 Prompt → 生成。
Agentic RAG:让 LLM 自己决定检索策略。"这个问题需要去哪个知识库查?"、"第一次检索结果不够精确,换一个角度再查"、"用户问的是对比性问题,需要从两个知识库各取一段"------Agent 自主规划多步检索,而不是被动的单次查询。
这是 RAG 的最终形态:不是"给模型查资料",而是"模型自己去查资料"。
五、总结
RAG 解决的是一个朴素但关键的问题:大模型不知道怎么回答它没学过的东西。 与其让它背下来(微调),不如让它去查(检索增强)。
这个 Demo 把 RAG 的核心 pipeline 完整落地了------从文档分块到向量检索到 LLM 生成,所有环节都可配置、可测试。67.2% 的检索命中率不是终点,而是告诉你下一步该往哪走的起点。
如果你拿到这个 Demo,建议的改动优先级: 先加混合检索(让检索更准)→ 再支持 PDF/Word(让文档来源更广)→ 然后加对话记忆(让交互更自然)→ 最后探索 Agentic RAG(让系统更智能)。
以上的优化方向已经在项目中规划了,正在实施中,更强大的v2.0版本敬请关注;
下一篇,我们讲讲如何实现 Agentic RAG