如果你正在做一个企业级大模型项目,有一个场景大概率会遇到:老板丢过来一批 PDF、Word、企业制度文档,说“把这些做成一个智能问答系统”,还要“答案必须准,不许瞎编”。
你第一反应可能是微调一个私有模型。但很快你会发现,微调的成本、周期、GPU 资源、数据标注量都高得惊人,而且微调模型本质上是在“背新知识”,它改不了模型“不知道就编”的本性。此时 RAG 提供的思路完全不同:模型不需要记住你的文档,它只需要在回答问题时,现场去你的知识库里检索相关片段,再根据这些片段组织答案。
这篇文章的目的很直接:帮你从零到一搭建一套完整的 RAG 项目,讲清楚工作原理,也覆盖企业落地时的工程细节。我不会只贴一堆概念,而是给你一套可以照着跑的代码、可验证的流程,以及我在实际项目中踩过的坑。
读完你应该能回答三个问题:
- RAG 到底是怎么工作的,和微调有什么区别?
- 一个最小的 RAG 系统,代码上需要哪几块?
- 从“能跑”到“好用”,企业级 RAG 还要补哪些课?
1. 为什么大模型应用绕不开 RAG
先说一个基本事实:大语言模型的知识截止时间是训练数据决定的。你问它 2024 年之后发生的事,它大概率不知道;你问它企业内部制度、某个产品的私有文档、某个项目的技术报告,它更不可能知道。硬问,它就会一本正经地生成一段看似合理、实际是编造的内容,这就是所谓的“幻觉”。
针对这个问题,业界有两条主流路线:
- 微调(Fine-tuning):用业务数据继续训练模型,把知识“塞进”模型参数。适合改变模型的行为风格、输出格式、领域术语表达,但不适合频繁更新知识。
- RAG(Retrieval-Augmented Generation,检索增强生成):不改变模型参数,而是在生成之前增加一个“检索”步骤,先从外部知识库找到与问题相关的资料,再把资料和问题一起交给模型,让模型“带着资料答题”。
两者不是互斥关系,但对企业最常见的“知识库问答”需求,RAG 是性价比高得多、落地速度也快得多的方案。原因很朴素:
- 知识更新方便。今天改一篇文档,重新构建对应索引即可,不用重新训练模型。
- 答案可溯源。RAG 可以把命中的原文片段作为依据返回给用户,这是微调难以做到的。
- 资源门槛低。RAG 最大的计算开销在向量检索,这部分不需要训练大模型,普通 CPU 机器也能跑,GPU 只对生成阶段有需求,甚至可以直接调用 API 模型。
做深度学习的人常说“No Free Lunch”,RAG 同样如此。它没有解决所有问题,比如检索质量差时答案照样会错、上下文窗口被填充大量无关片段时模型反而会困惑。但这不妨碍它成为当前企业接入 LLM 最务实的起点。
RAG 的核心价值,一句话总结:它让模型的输出从“凭记忆发挥”变成“基于证据作答”。
2. RAG 工作原理拆解:从“背课文”到“带着笔记答题”
我习惯用一个比喻来解释 RAG——它像一个开卷考试。
没有 RAG 的大模型,相当于闭卷考试。模型只能靠训练阶段“记下来的知识”答题,一旦题目超出记忆范围,就只能蒙。
有了 RAG,模型变成开卷考试:你给它一段提示词(Prompt),里面附带一份“参考笔记”,笔记内容来自企业知识库中与当前问题最相关的片段。模型作答时必须先读完笔记,再结合自己的语言组织能力写出答案。它依然不会的知识仍然不会,但它可以“照着笔记回答”它曾经不知道的内容。
把这个过程工程化,RAG 系统分为两个大阶段、五个小步骤。
2.1 索引阶段(离线构建)
索引阶段的任务是把原始文档处理成计算机可以快速检索的形式。
- 文档加载(Loading):读取 PDF、Word、Markdown、HTML、纯文本等格式的文档,转换成纯文本内容。
- 文本切分(Splitting/Chunking):把长文档切分成固定大小或语义完整的文本块,也就是 Chunk。切分的好坏直接影响后续检索效果。
- 向量化(Embedding):用 Embedding 模型把每个 Chunk 转换成一个高维向量。语义相近的文本,向量之间的距离也近。
- 存储(Storing):把向量和原文、元数据一起存入向量数据库,如 FAISS、Chroma、Milvus、pgvector 等。
2.2 查询阶段(在线推理)
查询阶段处理用户的每一次提问:
- 检索(Retrieval):把用户问题也用同一个 Embedding 模型转换成向量,然后在向量数据库中做相似度搜索,找出最相关的 Top-K 个 Chunk。
- 增强生成(Augmented Generation):把检索到的 Chunk 拼装进 Prompt,连同用户问题一起发送给 LLM,让 LLM 基于这些参考片段生成最终答案。
你直接问模型“我们公司的请假流程是什么”,模型答不上来。但如果你先把《员工手册》里关于请假制度的几个片断检索出来,再把它们粘进 Prompt,模型就能给你一条有条理的请假流程。这就是整个 RAG 最核心的机制,没有玄学,纯粹是工程组合。
技术上有几个细节值得注意:
- Embedding 模型要统一。写入向量库和查询问题时,必须使用同一个 Embedding 模型,否则向量空间不对齐,检索结果会莫名其妙。
- 相似度度量要选对。常见的有余弦相似度(Cosine Similarity)、欧氏距离、内积。绝大多数文本场景用余弦相似度最稳妥。
- Top-K 要控制好。K 太小可能漏掉关键片段,K 太大则会把不相关的内容塞进提示词,稀释模型的注意力。
3. RAG 的四代演进:从朴素到 Agentic RAG
你在日常工作里听到的 RAG 其实已经不是同一种东西。过去一年左右,RAG 的架构经历了好几轮演进,搞懂这些概念能让你在项目方案设计时不被技术名词带偏。
3.1 朴素 RAG(Naive RAG)
这是最经典的“检索 + 生成”两段式架构:文档切块 -> 向量化 -> 向量检索 -> 拼 Prompt -> 生成。
优点是结构简单,适合快速验证;缺点是检索质量完全依赖 Chunk 切分和 Embedding 模型,面对复杂问题命中率不稳定。很多刚入门的人以为 RAG 就是这个样子,其实这只是起点。
3.2 高级 RAG(Advanced RAG)
针对朴素 RAG 的弱点,工程上做了大量优化。常见手段包括:
- 查询改写:用户问题太模糊时,先用 LLM 把问题改写得更清晰,甚至拆解成多个子问题再检索。
- 混合检索:同时用稀疏检索(BM25,擅长关键词精确匹配)和稠密检索(向量相似度,擅长语义匹配),再融合结果。
- 重排序(Rerank):第一轮召回 Top-50 候选片段,用一个重排序模型对结果精排,只把最相关的 Top-5 交给 LLM。
这部分是企业级 RAG 的主要工作区,后面我会再展开。
3.3 模块化 RAG
模块化 RAG 的思路是:把检索、记忆、路由、查询改写、重排、验证等能力设计成可自由组合的模块,按需组装。
例如,系统先判断用户问题属于“闲聊”还是“知识库问答”,再决定走对话流程还是检索流程。或者对复杂问题先拆解成子问题,分别检索,最后汇总。类似 LangChain 的 LCEL 表达式语言,就是用来组合这些模块的。
3.4 Agentic RAG
最前沿的方向是把 RAG 和 Agent(智能体)结合:模型不只是被动接收检索结果,而是能够自主决定“要不要检索”“检索几次”“查完还不确定要不要追问用户”,甚至调用多个工具交叉验证。
从材料中也能看到,Agentic RAG(智能体式 RAG)已经成为热点方向。它的工程复杂度明显更高,通常涉及规划、工具调用、记忆管理,适合问答场景复杂、需要多轮验证的落地项目。
对多数团队的判断:如果你刚开始做知识库问答,不要一上来就追 Agentic RAG。先把朴素 RAG 跑通,再用高级 RAG 的混合检索 + Rerank 提升准确率,最后按需求引入 Agent 能力。RAG 是一项系统工程,检索质量、提示词设计、评估体系都比架构名词重要。
4. 环境准备与依赖选型
动手之前,先把环境准备好。本文示例采用 Python 技术栈,因为这是目前 LLM 生态最成熟的语言。
4.1 基础环境
- Python 3.10 或以上版本,建议 3.10/3.11,部分依赖对 3.12 的兼容仍不稳定。
- pip 包管理工具。
- 可以访问 Embedding 模型和 LLM 服务的网络环境。如果使用开源模型私有化部署,需要准备 GPU 或足够的 CPU 内存。具体版本请以实际项目为准,这里演示通用思路。
4.2 技术选型说明
下面这些库是当前 RAG 项目常见的组合:
| 组件 | 用途 | 推荐选型 |
|---|---|---|
| 框架 | 编排 RAG 流程 | LangChain / LlamaIndex |
| 文档加载 | 解析 PDF、Word 等 | LangChain 内置 Loader / unstructured |
| 文本切分 | 处理超长文本 | LangChain Text Splitter |
| 向量数据库 | 存储和检索向量 | Chroma(轻量)/ FAISS(单机)/ Milvus(生产) |
| Embedding | 文本转向量 | OpenAI Embedding 或开源模型 BGE、M3E 等 |
| LLM | 最终答案生成 | OpenAI、通义、智谱或本地部署模型 |
第一次实践建议用 Chroma 或 FAISS,两者都能在本地快速跑通,不需要额外启动服务。
4.3 安装依赖
创建一个虚拟环境,并安装以下依赖:
python -m venv rag_env source rag_env/bin/activate # Windows 下使用 rag_env\Scripts\activate pip install langchain langchain-community langchain-openai chromadb faiss-cpu pypdf如果你需要使用 OpenAI 的模型,再安装openai:
pip install openai tiktoken国内开发者如果希望降低模型调用成本,也可以选择开源或国产 Embedding 模型和 LLM 服务,LangChain 对各家都有对接封装,接口思路是类似的。
5. 零基础完整示例:先跑通一个最小 RAG 系统
我建议采用“最小可用”原则:先不管各种高级优化,用不到 100 行代码,把“加载文档 -> 切分 -> 向量化 -> 检索 -> 生成”整条链路跑通。这样做有两个好处:
- 你能直观感受每个环节的存在意义。
- 后续所有优化,都是在这一条主链路上做增强。
5.1 示例代码实现
先创建一个项目目录:
mkdir rag_demo cd rag_demo mkdir docs把你要测试的文档(PDF 或 txt)放到docs目录下。这里假设你有一份company_policy.txt。
创建主程序文件rag_pipeline.py:
# 文件路径:rag_demo/rag_pipeline.py import os from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import OpenAIEmbeddings from langchain_community.vectorstores import Chroma from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate from langchain.schema import StrOutputParser from langchain.schema.runnable import RunnablePassthrough # 1. 加载文档 loader = TextLoader("docs/company_policy.txt", encoding="utf-8") documents = loader.load() # 2. 文本切分 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", " ", ""] ) chunks = text_splitter.split_documents(documents) print(f"切分完成,共 {len(chunks)} 个文本块") # 3. 向量化 embedding_model = OpenAIEmbeddings( model="text-embedding-ada-002", openai_api_key=os.environ.get("OPENAI_API_KEY") ) # 4. 存储到向量数据库 vector_store = Chroma.from_documents( documents=chunks, embedding=embedding_model, persist_directory="./chroma_db" ) vector_store.persist() print("文档向量化并存储完成") # 5. 构建检索器 retriever = vector_store.as_retriever(search_kwargs={"k": 4}) # 6. 定义 Prompt prompt = ChatPromptTemplate.from_template( """你是一名企业知识库助手。请根据以下参考片段回答问题。 如果参考片段中没有足够信息,请明确回答“知识库中没有找到相关信息”,不要编造。 参考片段: {context} 问题:{question} """ ) # 7. 初始化 LLM llm = ChatOpenAI( model="gpt-4o-mini", temperature=0, openai_api_key=os.environ.get("OPENAI_API_KEY") ) # 8. 组装 RAG 链路 def format_docs(docs): return "\n\n".join([d.page_content for d in docs]) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # 9. 测试问答 question = "公司请假流程是什么?" response = rag_chain.invoke(question) print("用户问题:", question) print("系统回答:", response)这段代码把 RAG 主链路完整呈现出来了。我拆开解释几个关键点。
文本切分参数chunk_size=500, chunk_overlap=50的含义是:每个文本块约 500 个字符,相邻两个块之间有 50 个字符的重叠。重叠的目的是避免一句话恰好被拦腰切断,导致语义不完整。后面我会专门说这个参数怎么调。
retriever | format_docs使用了 LangChain 的 LCEL 表达式。它表示:先从向量库检索出相关文档,再用format_docs函数把文档列表拼成纯文本,作为 Prompt 中的{context}变量。
temperature=0很重要。知识库问答要求确定性,温度过高会让模型发挥太多,出现偏离原文的表述。知识类场景我一般直接设成 0。
5.2 运行命令
export OPENAI_API_KEY="你的 API Key" python rag_pipeline.pyWindows 下用:
$env:OPENAI_API_KEY="你的 API Key" python rag_pipeline.py5.3 预期输出
正常运行时会看到类似输出:
切分完成,共 12 个文本块 文档向量化并存储完成 用户问题: 公司请假流程是什么? 系统回答: 根据公司制度,员工请假需提前一天提交请假申请,经部门负责人审批后生效。特殊情况需及时电话通知上级......到这一步,你的第一套 RAG 系统已经跑通了。
5.4 如何自行验证效果
跑通代码只是第一步,你需要验证检索环节是否真的找对了文档。建议在正式问答之前,先把检索结果打印出来检查,增加一段调试代码:
# 文件路径:rag_demo/debug_retrieval.py import os from langchain_community.embeddings import OpenAIEmbeddings from langchain_community.vectorstores import Chroma embedding_model = OpenAIEmbeddings( model="text-embedding-ada-002", openai_api_key=os.environ.get("OPENAI_API_KEY") ) vector_store = Chroma( persist_directory="./chroma_db", embedding_function=embedding_model ) retriever = vector_store.as_retriever(search_kwargs={"k": 4}) docs = retriever.invoke("公司请假流程是什么?") for i, doc in enumerate(docs): print(f"--- 第 {i + 1} 个检索结果 ---") print(doc.page_content) print()如果检索结果和问题主题不相关,那么不管 LLM 多强,答案也不会对。记住这条经验:RAG 系统的天花板在检索质量,LLM 只是把检索到的内容翻译成答案。排查问题先查检索。
6. 从“能跑”到“好用”:企业级 RAG 进阶实践
最小系统跑通后,真正的挑战才开始。企业级 RAG 和 Demo 级 RAG 的差距,主要体现在检索质量、评估体系、性能和成本控制几个维度。
6.1 检索质量优化:混合检索 + Rerank
纯向量检索有一个明显短板:它擅长语义匹配,但不擅长精确匹配。比如你搜产品型号“A100-X”,向量检索可能返回一堆关于“A100”的泛泛内容,而 BM25 这种基于关键词的检索能精确命中型号字符串。
企业级系统通常采用“混合检索 + 重排序”方案:
- 第一路:向量检索召回 Top-50。
- 第二路:BM25 关键词检索召回 Top-50。
- 合并两路结果,去重。
- 使用 Rerank 模型对候选片段逐一打分,取 Top-5 交给 LLM。
从相关热词中也能看到,rag多路召回、embedding rerank都是 RAG 实战中的高频关键词,说明这正是工程里大家关心的重点。
LangChain 中可以通过EnsembleRetriever实现混合检索思路:
# 文件路径:rag_demo/hybrid_retriever.py from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OpenAIEmbeddings from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter loader = TextLoader("docs/company_policy.txt", encoding="utf-8") documents = loader.load() text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) chunks = text_splitter.split_documents(documents) # 构建稠密检索器(向量) embedding_model = OpenAIEmbeddings(model="text-embedding-ada-002") vector_store = Chroma.from_documents(chunks, embedding_model) dense_retriever = vector_store.as_retriever(search_kwargs={"k": 50}) # 构建稀疏检索器(BM25) bm25_retriever = BM25Retriever.from_documents(chunks) bm25_retriever.k = 50 # 融合成混合检索器 ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, dense_retriever], weights=[0.3, 0.7] ) # 测试一次检索 query = "公司请假流程是什么?" docs = ensemble_retriever.invoke(query) for i, doc in enumerate(docs[:5]): print(f"Top {i + 1}: {doc.page_content[:100]}")需要说明的是,实际生产项目中我更推荐将 Rerank 单独作为一层。Rerank 模型通常是一个交叉编码器,把“问题 + 候选片段”拼接后打分,效果比双塔向量相似度更精确,但计算成本更高,所以只用于精排阶段是合理的工程取舍。
6.2 RAG 效果评估:别靠感觉,用 RAGAS
很多团队在开发 RAG 系统时有个通病:问几个问题觉得“回答得还行”,就直接上线。结果用户一用,各种答非所问。原因很简单——你测试的 5 个问题覆盖不了真实用户的上百种问法。
RAG 需要像传统搜索系统一样建立评测集。业界常用的评估框架是 RAGAS(Retrieval-Augmented Generation Assessment),它从三个核心维度衡量系统质量:
- 忠实度(Faithfulness):回答是否严格基于检索到的上下文,有没有凭空编造。
- 答案相关性(Answer Relevance):回答是否切题,是否回答了用户的真实问题。
- 上下文相关性(Context Relevance):检索回来的文本块是不是真的和问题相关。
评估时需要准备一组“问题 - 标准答案”的测试集。如果已经有标准答案,还可以计算答案与标准答案的相似度。RAGAS 的用法大致如下:
pip install ragas# 文件路径:rag_demo/evaluate_rag.py from ragas import evaluate from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall from datasets import Dataset # 准备评估数据,需要从RAG链路中获取对应输出 eval_data = { "question": ["公司请假流程是什么?"], "answer": ["根据制度,员工需提前一天提交请假申请..."], "contexts": [["根据公司制度,员工请假需提前一天提交请假申请..."]], "ground_truth": ["员工提前一天提交申请,经部门负责人审批后生效。"] } dataset = Dataset.from_dict(eval_data) result = evaluate( dataset, metrics=[faithfulness, answer_relevancy, context_precision, context_recall] ) print(result)建议把评估集成到代码发布流程中。每次修改切分策略、Embedding 模型或 Prompt 模板后,都要在固定评测集上跑一遍,对比指标变化,用数据而不是感觉来决定是否上线。
6.3 知识更新与索引管理
企业知识库是动态变化的,文档会新增、修改、删除。很多初版 RAG 项目会踩一个坑:向量库里存了旧数据,文档更新后没重建索引,导致答案还是旧的。
生产环境需要明确索引策略:
- 小规模知识库(几百个文档以内):可以定期全量重建索引,简单可靠。
- 大规模知识库:需要增量更新。新增文档直接写入向量库;修改文档时要先按文档 ID 删除旧向量,再写入新向量;删除文档时同步删除对应向量。
文档 ID 的规范尤其重要。推荐使用“来源文件路径 + 切分序号”作为向量记录的元数据,方便追溯和定向删除:
# 示意:为每个Chunk添加元数据 for idx, chunk in enumerate(chunks): chunk.metadata["doc_id"] = f"{chunk.metadata['source']}#{idx}"6.4 安全与权限管理
RAG 系统直接面向企业知识内容,权限问题不能忽视。如果你的用户分为普通员工和管理层,不同级别可访问的文档不同,那么必须在检索阶段就做权限过滤,而不是在生成阶段靠提示词控制。否则,LLM 一旦检索到敏感内容,用户通过追问就可能套出越权信息。
实现上,可以在文档元数据中标记可见范围,检索时拼接过滤条件:
# 示意:按权限过滤检索结果 from langchain.schema import Document # 元数据示例:access_level = "admin" 或 "employee" filtered_docs = [d for d in docs if d.metadata.get("access_level", "employee") in user_roles]从合规角度看,涉及用户个人数据、敏感商业信息时,建议在 RAG 上层再叠加审计模块,记录每个用户查询过哪些内容、模型返回了什么。这一点在政务、金融等合规要求高的 RAG 项目中尤其重要,相关材料中也能看到政务 RAG 知识库项目的实例。
6.5 成本控制与响应延迟
企业上线 RAG,逃不过两个指标:成本和延迟。
- Embedding 调用次数:每次文档更新都要重新向量化,文档量大时会消耗大量 API 费用或 GPU 时间。可以引入缓存机制,对文本内容求哈希,一模一样的内容不重复计算向量。
- 提示词长度:检索到的 Chunk 数量越多,Prompt 越长,Token 费用越高。实际项目中我一般控制 3 到 5 个 Chunk,单个 Chunk 500 字符左右,既能保证覆盖面,又不会让 Prompt 过长。
- 缓存高频问题:相似的问题可以缓存答案,设置按天过期。这能显著降低 LLM 调用量,尤其适合问法相对固定的企业场景。
7. 常见问题与排查方法
我把 RAG 项目中最常遇到的几个问题整理成一张排查表。出现异常时,先看现象,再定位原因。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 答案明显与文档内容不符 | 检索到的 Chunk 不相关 | 打印检索结果,检查 Top-K 命中内容 | 优化切分策略,引入混合检索或 Rerank |
| 回答仍然在编造 | 检索结果不足以支撑答案,或 Prompt 约束不足 | 检查上下文片段是否完整覆盖问题要点 | 在 Prompt 中明确要求“无信息时如实说明”;增加检索 Chunk 数 |
| 检索返回结果太少 | Chunk 切分过大,向量粒度太粗 | 查看 Chunk 数量和各块长度分布 | 调小 chunk_size,增加 chunk_overlap |
| 检索结果语义接近但关键词不精确 | 纯向量检索无法匹配精确词 | 尝试查询“A100-X”这类型号或编号 | 增加 BM25 稀疏检索做混合召回 |
| 文档更新后答案仍是旧内容 | 向量库没有重建或增量更新 | 检查向量库中记录的文档时间戳 | 建立索引更新机制,文档变更后重建对应向量 |
| Embedding 消耗费用过高 | 每次启动都全量向量化 | 检查代码是否重复创建向量库 | 使用持久化向量库,并做文本内容哈希缓存 |
| 中文文档切出乱码或错词 | 编码问题或切分器不识别中文标点 | 检查原始文件编码 | 统一保存为 UTF-8;切分器增加中文标点分隔符 |
| LLM 返回内容过长或格式不符合预期 | 没有在 Prompt 中约束输出格式 | 检查 Prompt 模板 | 增加“请用简洁段落回答”“输出 Markdown 列表”等约束 |
排错时的第一原则是“分层定位”:先确认检索层是否输出正确,再检查 LLM 层的生成是否忠实。不要一上来就去调 Prompt,你连模型拿到的原材料对不对都还没确认。
8. 企业级 RAG 工程落地最佳实践
不同团队做 RAG 的差异,往往不在算法理解,而在工程方法。下面这组建议来自实际项目沉淀,可以帮你少走弯路。
8.1 从业务问题出发选架构
很多项目失败的原因是技术选型脱离业务。先搞清楚你的知识库是什么形态:
- 纯文本制度文档,问题相对固定,朴素 RAG 就够。
- 文档格式复杂,包含大量表格、图片,需要先做版面解析,再做 RAG。
- 问题复杂,经常需要多步推理,才需要考虑 Agentic RAG。
不要为了用新框架而用新框架。RAG 的成功标准是“用户能快速找到准确答案”,不是“我们的架构足够酷”。
8.2 对文档做预处理,而不是直接切块
原始 PDF 直接切块,效果一般不会好。企业文档常见问题包括:PDF 里有页眉页脚、表格被拆散、扫描件没有文本层。
建议在进入 RAG 链路之前,先做一轮文档清洗:
- 去除页眉页脚、页码、水印文字。
- 对表格做结构化解析,行和列保持对应关系。
- 扫描件先做 OCR。
- Markdown 格式的文档优先保留标题层级,切分时可以按标题切,保持语义完整。
这一步看似繁琐,却对最终效果有决定性影响。检索质量差,很多时候不是模型不行,是原始文档压根没解析好。
8.3 切分策略要按文档类型调整
没有万能切分参数。长文本和短文本、制度文档和技术手册,最优参数都不一样。
我的经验是:
- 制度类文档,按章节语义切块,
chunk_size=500左右比较稳妥。 - 技术手册类,按 Markdown 标题层级切块,优先保证每个 Chunk 内容完整。
- 对话记录或问答对,尽量保持一问一答一个 Chunk,不要拆散。
调参时围绕三个指标:检索命中率、答案忠实度、用户满意度。前两个可以通过评测集量化,最后一个是长期观察指标。
8.4 Prompt 模板要提前设计可复用结构
在实际项目中,Prompt 通常会被多个系统复用,建议把 Prompt 模板做成独立配置,不要散落在代码里。
一个生产级 RAG Prompt 至少应该包含:
- 角色定位:你是企业知识库助手。
- 任务说明:根据参考片段回答用户问题。
- 语气与格式要求:简洁、专业、列表输出等。
- 边界与兜底:信息不足时明确说明,不要编造。
- 可溯源要求:列出引用的文档或片段来源。
你是一名企业知识库助手。请根据参考片段回答问题。 要求: 1. 优先使用参考片段中的信息,语言精练。 2. 如果参考片段信息不足,回复:“知识库中没有找到相关信息。” 3. 在回复末尾列出引用的文档来源编号。 参考片段: {context} 问题: {question} 来源列表: {source_info}8.5 建立评测集和回归机制
这一点我会反复强调:RAG 上线后,效果只会随着文档更新、模型升级而漂移。没有评测集,就没有质量回归机制。
建议按业务场景整理 50 到 100 个高频问题作为评测集,覆盖:
- 直接命中的简单问题。
- 需要多片段拼装的复杂问题。
- 知识库中不存在信息的越界问题。
每次改动系统后跑一遍评测集,把三个 RAGAS 指标记录下来。指标下降就回滚,指标上升再继续。
9. 从 RAG 到 Agentic RAG:值得关注的未来方向
文章最后谈一下方向问题。在我写这篇文章时,RAG 的下一个热点是 Agentic RAG,也就是把 RAG 从“一次检索、一次生成”的流水线,升级成“模型自主决定检索策略”的智能体。
典型场景是:用户问了一个复杂问题,比如“对比今年和去年公司营收变化原因”,普通 RAG 只会检索一次,很可能漏掉关键数据。而 Agentic RAG 中的模型可以自己规划:
- 先检索今年营收数据。
- 识别数据有变化,再检索原因分析文档。
- 如果两个来源存在矛盾,再检索第三条资料交叉验证。
- 最后组织成回答。
这种模式适合复杂问答、多步推理、需要工具调用的场景。但它对模型能力要求更高,同时需要考虑规划失败时的兜底策略、工具调用的异常处理、成本控制等问题。
对于大多数刚开始接触 RAG 的开发者,我的建议是:先把手动流程跑熟,再用 LangGraph 等框架逐步引入 Agent 能力。不要跳过基础阶段直接上 Agentic RAG,否则你会被调试复杂度淹没。
10. 最后一个建议:从最小项目开始
如果你看完文章准备动手,我给的建议非常简单:不要一开始就追求企业级完美架构,而是先用 30 分钟跑通最小 RAG,再逐步添加混合检索、Rerank、评测、权限、缓存。
RAG 的学习曲线不算陡峭,但坑位很多。先把主链路跑通,你就能在后续每个优化点上做对比实验,真正理解每一步改动带来的效果变化。这和所有工程实践的规律一致:先把地基夯实,再盖高楼。带着你手头的文档,从安装依赖开始,跑通第一条链路,你就能感受到 RAG 从概念变成工具的过程。