记一次RAG(检索增强生成)全链路跑通
在使用了那么多大模型之后,心里打起了自己构建一个具备搜索能力的大模型,也就是RAG。
RAG = Retrieval-Augmented Generation,检索增强生成。
一句话:让大模型在回答前,先去你的知识库里“查资料”,再根据查到的内容回答**。类似开卷考试,而不是让模型凭记忆瞎编。
它主要解决:LLM 知识过期、幻觉、无法访问私有数据、答案不可溯源等问题。RAG 不是微调;RAG 负责“外挂知识”,微调更多负责“改变风格/能力”。
这个RAG主要的功能就是它可以先搜索我的代码库以及参数配置文档,然后将结果反馈给大模型进行整合输出。
一、RAG 的核心流程
1 | 离线索引: |
关键组件:
- 文档加载:PDF、Word、网页、数据库、Markdown、代码等
- 切块 Chunking:把长文档切成小段
- Embedding:把文本转成向量
- 向量数据库:FAISS、Chroma、Milvus、Qdrant、pgvector 等
- 检索器:向量检索、关键词检索、混合检索
- 重排 Reranker:对召回结果精排
- LLM:根据上下文生成答案
- Prompt:约束模型“只根据上下文回答”
二、如何开发一个 RAG?
1. 明确需求
先确定:
- 面向什么场景:客服、法律、医疗、代码、企业知识库?
- 数据来源是什么:PDF、网页、数据库、API?
- 是否需要权限控制?
- 是否要求引用来源?
- 数据更新频率?
- 用云端模型还是本地模型?
2. 准备数据
把数据解析成纯文本,并保留元数据:
1 | { |
元数据很重要,后面可以做过滤、引用、权限控制。
3. 切块
常见策略:
- 固定长度:如 500~1000 tokens
- 递归切分:按段落、标题、句子
- 语义切分:按语义边界
- 父子块:检索小块,返回大块
- 表格、代码单独处理
建议:chunk_size=500~1000,overlap=50~150,具体要实验。
4. 向量化并存入向量库
选择 Embedding 模型:
- 云端:OpenAI
text-embedding-3-small/large - 开源:BGE、GTE、m3e、Jina Embeddings
- 多语言场景要选多语言模型
然后存入:
- 本地小项目:FAISS、Chroma
- 生产:Milvus、Qdrant、Weaviate、pgvector
5. 实现检索
最基础的是向量相似度检索:
1 | 问题向量 → 在向量库找最相似的 top k 个 chunk |
但生产级通常用混合检索:
- 向量检索:擅长语义
- BM25/关键词检索:擅长专有名词、编号、错误码
- 混合后融合排序
再加重排:
1 | 先召回 top 20~100 → Reranker 精排 → 取 top 3~8 给 LLM |
6. 拼接 Prompt 并生成
Prompt 示例:
1 | 你是一个严谨的助手。请只根据下面提供的上下文回答问题。 |
7. 评估与优化
构建测试集,至少 50~100 条问答,评估:
- 检索召回率:该找的文档找到了吗?
- 答案忠实度:是否基于上下文?
- 答案相关性:是否回答了问题?
- 引用准确率
- 延迟和成本
工具:RAGAS、TruLens、人工评估。
三、最小可用 RAG 代码示例
用 Python + LangChain + FAISS + OpenAI:
1 | pip install langchain langchain-openai langchain-community langchain-text-splitters faiss-cpu |
1 | import os |
这就是一个最小 RAG。它已经能跑通“文档 → 检索 → 生成”。
四、生产级 RAG 还要加什么?
最小 RAG 很容易做,难的是效果稳定。生产级通常需要:
- 混合检索:向量 + BM25
- 重排 Reranker:BGE-reranker、Cohere Rerank
- 查询改写:多查询、HyDE、结合历史改写
- 多路召回:不同索引、不同粒度
- 父子块 / 句子窗口:检索小块,给大上下文
- 元数据过滤:时间、产品、租户、权限
- 上下文压缩:去掉无关内容,节省 token
- 引用来源:输出
[1][2]并链接原文 - 权限控制:检索前过滤,防止越权泄露
- 增量更新:文档变更后重新索引、删除旧向量
- 缓存:Embedding 缓存、检索缓存、答案缓存
- 监控:延迟、成本、召回率、用户反馈
- 评估体系:RAGAS、人工标注、A/B 测试
五、技术选型参考
| 模块 | 常见选择 |
|---|---|
| 框架 | LangChain、LlamaIndex、Haystack |
| 向量库 | FAISS、Chroma、Milvus、Qdrant、pgvector |
| Embedding | OpenAI、BGE、GTE、m3e、Jina |
| LLM | GPT、Claude、Qwen、DeepSeek、Llama |
| 重排 | BGE-reranker、Cohere Rerank |
| 评估 | RAGAS、TruLens |
| 部署 | FastAPI、Flask、Docker、K8s |
六、常见坑
- 切块太大:噪声多,检索不准
- 切块太小:语义不完整
- 只用向量检索:专有名词、编号容易漏
- 不做重排:top k 里噪声太多
- Prompt 不约束:模型开始编造
- 不更新索引:答案过期
- 不做权限过滤:数据泄露
- 没有评估:不知道改好还是改坏
总结
RAG 的本质是:搜索系统 + Prompt 工程 + LLM 生成。
开发顺序建议:
- 先做最小 MVP:加载文档 → 切块 → 向量化 → 检索 → 生成
- 构建测试集,评估检索和答案质量
- 再加混合检索、重排、查询改写、权限、引用
- 最后做部署、监控、增量更新
如果你告诉我你的具体场景,比如“用本地模型做企业 PDF 知识库”或“用 OpenAI 做客服机器人”,我可以给你一套具体的项目结构和技术方案。
我的最小的RAG的主要功能就是将业务代码和参数配置文档作为资料库,让大模型在回答问题之前现在资料库中进行搜索,然后再进行回答。
我的RAG采用的技术选型分别是:
📊 RAG 技术选型一览表
| 模块 | 最终选型 | 状态 | 选型理由 / 备注 |
|---|---|---|---|
| 开发框架 | LlamaIndex | ✅ 已落地 | 相较于 LangChain 和 Haystack,LlamaIndex 专为 RAG 设计,API 更精简,检索性能好,内存占用相对低。 |
| 大语言模型 | qwen3:1.7b | ✅ 已落地 | 经历了 qwen3:4b(太慢、思考啰嗦)的折磨后,换为 1.7B 版本。无思考输出,纯 CPU 推理速度快,响应时间从几分钟降至 50 秒左右。 |
| 嵌入模型 | BGE-M3 | ✅ 已落地 | 通过 Ollama 调用。中文语义理解强,支持稠密+稀疏检索,对技术文档中的专有名词和错误码召回效果好。 |
| 向量数据库 | ChromaDB | ✅ 已落地 | 作为嵌入式数据库,零配置,安装简单,与 LlamaIndex 集成度高。当前 2551 个切片的规模完全够用,后续可平滑迁移至 Qdrant。 |
| 文档加载器 | SimpleDirectoryReader | ✅ 已落地 | 统一加载 Word、TXT、C/C++ 代码文件,自动附加 file_name、file_path 等元数据。 |
| 文本切片器 | SentenceSplitter | ✅ 已落地 | 用于 Word 和 TXT 文件,按句子边界切分,chunk_size=512,chunk_overlap=64,避免语义截断。 |
| 代码切片器 | CodeSplitter | ✅ 已落地 | 针对 C/C++ 代码,使用 AST 语法树按函数/类切分。关键踩坑:需传入 tree_sitter_cpp 预构建的 parser 对象,绕开网络下载超时问题。 |
| 重排模型 | BGE-reranker-v2-m3 | ⏸️ 规划中 | 能进一步提升检索精度,但纯 CPU 下运行会增加延迟,暂未启用。 |
| 评估工具 | RAGAS | ⏸️ 规划中 | 用于构建测试集,量化评估忠实度、答案相关性等指标,待流程稳定后再引入。 |
| 部署方式 | Python 本地脚本 | ✅ 当前阶段 | 目前以 RAG_One.py 单文件脚本运行。后续可用 FastAPI 封装 API,搭配 Docker Compose 实现一键部署。 |
💻 硬件环境配置(附加信息)
| 环境 | 配置 | 说明 |
|---|---|---|
| 主力开发机 | Intel i7-1255U / 32GB DDR4 / Intel Iris Xe 核显 | 纯 CPU 推理,无独立显卡加速。16GB 内存时极易触发瓶颈,32GB 下运行较为稳定。 |
🖥️ 阶段一:环境搭建与数据准备(2-3天)
1. 安装 Ollama for Windows
Ollama 官方提供了原生 Windows 应用,安装非常简单:
下载:访问 Ollama 官网 https://ollama.com/download,点击下载 Windows 版安装程序
OllamaSetup.exe。安装:双击运行
OllamaSetup.exe,一路“下一步”即可。它默认安装在你的用户目录下,不需要管理员权限。验证:安装完成后,Ollama 会自动在后台运行,右下角系统托盘会出现一个羊驼图标。打开 PowerShell 或 CMD,输入以下命令验证:
1
ollama -v
如果显示版本号,说明安装成功。
⚠️ 重要提示:更改模型存储位置
模型文件可能高达数十 GB。如果你的 C 盘空间紧张,建议在安装前或安装后设置环境变量OLLAMA_MODELS来更改模型存储路径。
- 打开“系统属性” -> “环境变量”。
- 在“用户变量”中,新建一个变量,名为
OLLAMA_MODELS,值设置为你的目标路径,例如D:\OllamaModels。- 保存后,重启 Ollama(在系统托盘右键退出,再从开始菜单重新打开)。
2. 拉取模型
在 PowerShell 中执行以下命令,拉取 Qwen3 4B 和嵌入模型 BGE-M3:
1 | ollama pull qwen3:4b |
3. 创建 Python 虚拟环境并安装依赖
在 PowerShell 中,进入你的项目目录,然后执行:
1 | # 创建虚拟环境 |
4. 准备数据与解析
将你的 Word、Excel、代码文件放入 ./data 目录。使用 SimpleDirectoryReader 统一加载:
1 | from llama_index.core import SimpleDirectoryReader |
切片策略(与平台无关,但很重要):
- Word:按标题层级切分,每个标题下的段落作为一个 chunk。
- Excel:按 Sheet 切分,并确保每个 chunk 都包含表头信息。
- 代码:按函数/类切分,保留文件路径和语言标记。
🔗 阶段二:核心 RAG 链路开发(3-5天)
5. 配置本地模型与嵌入
1 | from llama_index.core import Settings |
6. 构建向量索引并持久化(ChromaDB)
在 Windows 上,ChromaDB 以 Python 库的形式在本地运行,数据会持久化到指定目录。
1 | from llama_index.core import VectorStoreIndex, StorageContext |
7. 创建检索器并实现问答
1 | from llama_index.core.retrievers import VectorIndexRetriever |
此时已经跑通了最小可用的 RAG 系统。
在这里附一个我的完整的代码:
1 | from llama_index.core import SimpleDirectoryReader |
踩坑记录:
F:\流离火\AGI\RAG开发>python RAG_One.py
共加载 5 个代码文件
Could not get parser for language cpp. Check https://github.com/Goldziher/tree-sitter-language-pack?tab=readme-ov-file#available-languages for a list of valid languages.
Traceback (most recent call last):
File “F:\流离火\AGI\RAG开发\RAG_One.py”, line 16, in
cpp_splitter = CodeSplitter(
^^^^^^^^^^^^^
File “F:\Python\Lib\site-packages\llama_index\core\node_parser\text\code.py”, line 116, in init
parser = tree_sitter_language_pack.get_parser(language) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “F:\Python\Lib\site-packages\tree_sitter_language_pack\api.py”, line 198, in get_parser
return cast(“Parser”, _rust.get_parser(name=name))
^^^^^^^^^^^^^^^^^^^^^^^^^^^
tree_sitter_language_pack.DownloadError: Download error: Failed to download https://github.com/xberg-io/tree-sitter-language-pack/releases/download/v1.20.0/parsers-windows-x86_64.tar.zst: io: 你的主机中的软件中止了一个已建立的连接。 (os error 10053)
不知道是谁中止的,nnd,大概率是数据卫士,看了一眼路径,然后问了ds,就直接把包解压放在了F:\Python\Lib\site-packages\tree_sitter_language_pack目录下,再跑一次,竟然可以了。
📝 RAG 开发“数据准备与切片”阶段问题总结
以下是我们在这个阶段遇到的坑、原因和解决方案的完整总结,你可以直接存档:
- 代码文件:5 个,生成 285 个切片(使用
CodeSplitter)- Word 文档:2 个,生成 2264 个切片(使用
SentenceSplitter+DocxReader) - txt 文件:1 个(使用
SentenceSplitter) - 总计:2551 个知识切片
- Word 文档:2 个,生成 2264 个切片(使用
问题 1:Ollama 拉取模型时报错 file does not exist / 网络超时
现象:
ollama pull qwen3:4b-instruct-2507报错Error: pull model manifest: file does not exist后续下载 BGE 模型时报错
wsarecv: An established connection was aborted by the software in your host machine.
原因:
模型名称错误:
qwen3:4b-instruct-2507是 Hugging Face/ModelScope 的命名,Ollama 官方库对应的 tag 是qwen3:4b。网络问题:国内直连 Ollama 官方源不稳定,导致下载中断。
残留文件:下载中断后,本地残留了不完整的 manifest 文件。
解决方案:
修正模型名称:使用
ollama pull qwen3:4b和ollama pull bge-m3。使用国内镜像(如果官方源依然超时):
1
2ollama pull modelscope.cn/Qwen/Qwen3-4B-Instruct-GGUF
ollama pull modelscope.cn/BAAI/bge-m3清理残留:进入
C:\Users\你的用户名\.ollama\models\manifests,删除对应的qwen3文件夹。配置环境变量:设置
OLLAMA_MODELS到非 C 盘路径(如H:\ollama),并彻底退出并重启 Ollama 使其生效。
问题 2:CodeSplitter 报错 ModuleNotFoundError: No module named 'tree_sitter_language_pack'
现象:
1 | ImportError: Please install tree_sitter_language_pack to use CodeSplitter. |
原因:CodeSplitter 依赖 tree-sitter-language-pack 来解析代码结构,但环境中未安装该库。
解决方案:
1 | pip install tree-sitter-language-pack |
若安装后报版本不兼容,可降级安装:
1 | pip install "tree-sitter-language-pack<1.0" |
问题 3:CodeSplitter 报错 Could not get parser for language c++
现象:
1 | DownloadError: Download error: Language 'c++' is not in the download manifest |
原因:tree-sitter-language-pack 中 C++ 的标识符是 cpp,而不是 c++。
解决方案:
修改 CodeSplitter 的参数:
1 | cpp_splitter = CodeSplitter( |
问题 4:CodeSplitter 报错 DownloadError: Failed to download ... io: 你的主机中的软件中止了一个已建立的连接
现象:tree-sitter-language-pack 尝试从 GitHub Releases 动态下载 parsers-windows-x86_64.tar.zst,因网络问题超时。
原因:
国内网络访问 GitHub 不稳定,导致动态下载解析器失败。
解决方案(推荐方案):
使用轻量级的 tree_sitter_cpp 包,并手动将解析器对象传给 CodeSplitter,彻底绕开动态下载。
1 | pip install tree_sitter tree_sitter_cpp |
1 | import tree_sitter |
问题 5:Word 文档切片出现严重乱码
现象:
切片内容显示为 /+&W;'rβu&~k8ekmX+1wUm۲M9aRTDYwSW7_.Xz@6E6k7pZBsMs... 等随机字符。
原因:.docx 本质是 ZIP 压缩包(二进制格式),不是纯文本。SimpleDirectoryReader 在未显式指定 file_extractor 时,自动推断机制可能回退到通用文本读取器,强行用某种编码(UTF-8/GBK)解码 ZIP 的二进制内容,导致乱码。
解决方案(方案A):
显式指定 DocxReader,强制使用 python-docx 解析。
1 | pip install llama-index-readers-file python-docx |
1 | from llama_index.readers.file import DocxReader |
经验总结
Ollama 模型名要用官方 tag:不要混用 Hugging Face/ModelScope 的命名,
qwen3:4b才是官方 tag。显式指定读取器优于自动推断:尤其是处理
.docx、.pdf、.xlsx等二进制格式时,显式传入file_extractor能避免回退到通用文本读取器导致的乱码。tree-sitter-language-pack动态下载是坑:国内网络环境下,推荐用独立包(如tree_sitter_cpp)并手动传入 parser 对象。代码切片和文本切片策略不同:代码用
CodeSplitter(按 AST 结构切),文本用SentenceSplitter(按句子边界切)。环境变量修改后必须重启服务:Ollama 只在启动时读取一次环境变量,修改后必须彻底退出并重启。




