RAG 效果差怎么办从文档切分到召回参数的 10 个优化点作者AI 爪客类型RAG / 知识库 / 检索增强生成 / 工程实践说明本文聚焦可落地的 RAG 优化方法不虚构压测数据或实验结论。文中的效果预览以“可观察现象”和“评估方式”为主实际收益需结合业务语料、模型、向量库和评测集验证。摘要很多团队第一次搭建 RAGRetrieval-Augmented Generation检索增强生成系统时常见感受是流程跑通了但回答质量不稳定。典型问题包括明明知识库里有答案模型却说“不知道”召回了很多片段但真正相关的内容排在后面文档切得太碎答案缺上下文文档切得太大噪声太多模型抓不住重点同一个问题换个问法召回结果明显波动引用来源不准确回答看起来“像对的”但无法验证。RAG 效果差通常不是单点问题而是由文档解析、切分策略、Embedding、向量索引、召回参数、重排、提示词和评测体系共同决定。本文围绕一个核心问题展开当 RAG 回答效果不理想时应该从哪些环节系统排查和优化我们将从“文档切分”到“召回参数”梳理 10 个高频优化点并给出工程实现思路、提示词模板、排查清单和可复制资产帮助你把 RAG 从“能跑”推进到“可调、可评估、可迭代”。一、问题背景1. RAG 为什么容易“看起来能用实际不好用”一个基础 RAG 链路通常包括用户问题 ↓ 问题改写 / 查询理解 ↓ 向量召回 / 关键词召回 / 混合召回 ↓ 候选片段重排 ↓ 构造提示词 ↓ 大模型生成答案 ↓ 返回答案与引用来源从架构上看并不复杂但每一步都可能引入误差。例如文档解析阶段丢失了表格结构切分阶段把一个完整条款拆成两段向量召回阶段只适合语义相似但不适合编号、术语、金额、版本号等精确匹配top_k 设置太小关键片段没进上下文top_k 设置太大噪声片段干扰生成提示词没有约束“只能基于上下文回答”导致模型自由发挥没有评测集每次调参只能凭感觉。因此RAG 优化的第一原则是不要只盯着大模型要先确认检索链路是否把正确上下文送到了模型面前。2. 一个常见错误把“生成差”误判成“模型差”很多 RAG 问题表面看是模型回答不好实际原因是正确答案没有被召回召回内容排序太靠后上下文中存在互相冲突的信息提示词没有要求引用依据用户问题需要多跳推理但召回只覆盖了单个片段。如果模型没有拿到正确资料再强的模型也只能猜。所以排查时建议先问三个问题知识库里是否真的有答案检索结果里是否包含答案所在片段答案片段是否被放入最终提示词上下文只有这三步都成立才进入生成侧优化。二、最终效果预览本文不提供虚构实验数值但一个经过系统优化的 RAG 通常应该在以下方面变得更可控1. 可观察改进方向维度优化前常见现象优化后期望现象召回完整性知识库有答案但召不回关键片段更稳定进入候选集上下文质量片段噪声多、重复多上下文更集中冲突更少回答可信度无引用或引用不准答案能绑定明确来源问法鲁棒性换个问法结果变化大同义问法召回结果更稳定排查效率凭感觉调参可通过日志和评测集定位问题2. 建议评估方式你可以构建一个小型评测集每条样本至少包含{question:用户问题,expected_answer:参考答案要点,expected_source:答案所在文档或片段 ID,question_type:事实型 / 解释型 / 对比型 / 多跳型 / 规则型}每次调整切分策略、召回参数或提示词后记录正确片段是否被召回正确片段在召回结果中的排名最终上下文是否包含正确片段模型答案是否覆盖参考答案要点引用来源是否准确。这样才能判断优化是否真的有效而不是“某几个问题看起来变好了”。三、技术方案总览本文总结的 10 个优化点如下01. 文档解析先保证内容被正确抽取 02. 清洗规范去掉页眉页脚、目录噪声和重复文本 03. 切分粒度控制 chunk size 与 overlap 04. 语义边界按标题、段落、条款、表格进行结构化切分 05. 元数据设计保留标题层级、页码、文档版本和权限信息 06. Embedding 选择匹配语言、领域和查询形态 07. 混合召回向量检索 关键词检索互补 08. 召回参数合理设置 top_k、score 阈值和多路召回比例 09. 重排模型用 rerank 提升候选片段顺序 10. 生成约束通过提示词要求基于上下文、给出引用和不确定性一条更稳健的 RAG 工程链路可以设计为文档上传 ↓ 解析PDF / Word / Markdown / HTML / Excel ↓ 清洗去噪、去重、规范换行 ↓ 结构化切分标题层级 段落 表格 overlap ↓ 元数据补全source、page、section、version、permission ↓ 索引构建向量索引 关键词索引 ↓ 查询处理问题改写、术语扩展、意图识别 ↓ 混合召回dense sparse ↓ 重排reranker 对候选排序 ↓ 上下文压缩去重、截断、合并相邻片段 ↓ 大模型生成严格基于上下文回答 ↓ 日志与评测召回命中、答案质量、引用准确性四、环境准备以下示例以 Python 工程为例具体向量库和模型供应商可按团队技术栈替换。1. 推荐组件模块可选方案说明文档解析pypdf、pdfplumber、python-docx、BeautifulSoup根据文档类型选择文本清洗re、ftfy、自定义规则处理空白、页眉页脚、重复文本向量模型bge、text-embedding、领域 embedding优先选择中文效果稳定的模型向量库FAISS、Milvus、Qdrant、pgvector、Elasticsearch vector根据数据规模和运维条件选择关键词检索BM25、Elasticsearch、OpenSearch适合精确词、编号、术语重排模型bge-reranker、cross-encoder、商业 rerank API提升 top 结果排序质量评测自建 QA 集、RAGAS、DeepEval、自定义脚本重点是可重复评估2. 最小目录结构示例rag-demo/ data/ raw/ # 原始文档 processed/ # 清洗后的文本 indexes/ vector/ # 向量索引 keyword/ # 关键词索引 src/ parse.py clean.py chunk.py embed.py retrieve.py rerank.py generate.py evaluate.py configs/ rag.yaml eval/ qa_set.jsonl3. 配置文件示例chunk:chunk_size:800chunk_overlap:120split_by_heading:truepreserve_table:trueretrieval:dense_top_k:20sparse_top_k:20final_top_k:6min_score:0.25use_rerank:trueprompt:require_citation:trueallow_unknown:true注意这些参数不是通用最优值只是起点。实际项目中应结合文档类型、问题类型和模型上下文长度调参。五、核心实现思路优化点 1文档解析要先过质量关RAG 的输入不是“文件”而是“可被模型理解的文本与结构”。如果解析质量差后续切分和召回都会受影响。常见解析问题PDF 分栏文本顺序错乱表格被解析成无意义的换行文本页眉页脚混入正文扫描件没有 OCRWord 标题层级丢失图片中的关键信息没有抽取。建议做法defvalidate_parsed_document(doc):checks{has_text:len(doc.text.strip())0,has_reasonable_length:len(doc.text)100,has_metadata:bool(doc.metadata.get(source)),no_excessive_blank:\n\n\n\nnotindoc.text,}returnchecks上线前建议人工抽查几类典型文档长 PDF带表格的制度文件多级标题 Word扫描件中英混合文档。如果文档解析阶段已经错了不要急着调 top_k。优化点 2清洗时去噪但不要误删信息清洗的目标是降低噪声而不是让文本“看起来整洁”。适合清理的内容包括重复页眉页脚页码目录中的大量点线无意义空白重复版权声明HTML 标签残留。但要谨慎处理编号如3.1.2金额如1,000,000日期如2024-06-01产品型号如A-100 Pro法规条款如第十二条。清洗函数示例importredefclean_text(text:str)-str:texttext.replace(\u00a0, )textre.sub(r[ \t], ,text)textre.sub(r\n{3,},\n\n,text)textre.sub(r第\s*\d\s*页\s*/\s*共\s*\d\s*页,,text)returntext.strip()建议保留清洗前后的样本对比避免规则过猛导致关键信息丢失。优化点 3chunk size 不是越大越好也不是越小越好切分粒度是 RAG 效果的关键变量。chunk 太小的问题上下文不完整条款前提和结论被拆开表格标题和表格内容分离模型无法理解“这段话在讲什么”。chunk 太大的问题向量表示被多个主题稀释召回片段包含大量无关内容进入提示词后占用上下文窗口重排时粒度太粗难以精确排序。一个较稳妥的做法defsplit_by_length(text,chunk_size800,overlap120):chunks[]start0whilestartlen(text):endmin(startchunk_size,len(text))chunktext[start:end]chunks.append(chunk)ifendlen(text):breakstartmax(0,end-overlap)returnchunks但实际工程中不建议只按字符数硬切。更推荐结合语义边界。优化点 4优先按标题、段落、条款和表格切分更好的切分策略是先尊重文档结构再控制长度。例如一级标题 二级标题 段落 1 段落 2 表格 1可以切成{text:二级标题 段落 1 段落 2,metadata:{h1:一级标题,h2:二级标题,type:paragraph}}表格建议保留为 Markdown 表格或结构化 JSON而不是直接压平成一行。| 产品 | 免费额度 | 超出计费 | |---|---:|---:| | A 服务 | 1000 次/月 | 0.01 元/次 | | B 服务 | 500 次/月 | 0.03 元/次 |对于制度、合同、API 文档建议按以下边界切分标题层级条款编号小节代码块表格FAQ 问答对Markdown heading。核心原则一个 chunk 最好表达一个相对完整的语义单元。优化点 5元数据决定了后续过滤、引用和权限控制很多 RAG 系统一开始只存text和embedding后面才发现很难做筛选和引用。建议每个 chunk 至少保留{chunk_id:doc001-p12-c03,text:...,metadata:{source:员工手册.pdf,page:12,section:请假制度/年假规则,doc_type:policy,version:2024-01,created_at:2024-01-15,permission:internal}}这些信息可以用于按文档类型过滤按版本过滤返回引用来源权限控制召回结果去重问题定位和日志排查。如果用户问“最新版规则是什么”但元数据没有版本字段系统就很难稳定回答。优化点 6Embedding 模型要匹配语言和业务领域Embedding 不是“随便选一个就行”。选择时建议关注中文语义效果中英混合能力长文本截断策略对专业术语、缩写、代码、编号的表达能力向量维度和存储成本推理速度是否支持批量处理是否便于私有化部署。如果你的知识库包含大量法律条款金融术语医疗术语工业设备编号API 参数代码片段就需要重点验证 Embedding 对这些内容的召回能力。评估方式可以很简单为每类问题准备 1020 条样本观察正确 chunk 是否进入 top_k以及排序是否靠前。优化点 7不要只依赖向量召回混合召回更稳向量召回擅长语义相似但对精确匹配不一定稳定。例如“ERR_1024 是什么错误”“A-17 型号支持哪些接口”“第 3.2.1 条规定了什么”“订单状态码 5031 代表什么”这类问题往往更适合关键词检索或 BM25。推荐使用混合召回候选集 向量召回 top_k 关键词召回 top_k ↓ 去重 ↓ 重排 ↓ 取 final_top_k伪代码defhybrid_retrieve(query):dense_hitsdense_search(query,top_k20)sparse_hitsbm25_search(query,top_k20)mergeddeduplicate(dense_hitssparse_hits,keychunk_id)rerankedrerank(query,merged)returnreranked[:6]混合召回可以显著提升以下场景的稳定性编号查询专有名词查询短问题用户原话与文档表达差异较大文档中存在大量相似条款。这里的“显著”需要用你自己的评测集验证不建议直接套用他人的结论。优化点 8召回参数要结合问题类型调节常见参数包括参数含义风险dense_top_k向量召回数量太小漏召回太大噪声多sparse_top_k关键词召回数量太小漏精确匹配太大重复多final_top_k最终进入上下文数量太小信息不足太大干扰生成score_threshold相似度阈值太高无结果太低噪声多max_context_tokens上下文 token 上限太低截断太高成本增加建议不要只设一组固定参数而是根据问题类型动态调整。例如defchoose_retrieval_config(query_type):ifquery_typecode_or_id:return{dense_top_k:10,sparse_top_k:30,final_top_k:6}ifquery_typeconcept_explain:return{dense_top_k:30,sparse_top_k:10,final_top_k:8}ifquery_typemulti_hop:return{dense_top_k:40,sparse_top_k:20,final_top_k:10}return{dense_top_k:20,sparse_top_k:20,final_top_k:6}如果没有问题分类器也可以先从固定配置开始再通过日志观察哪些问题容易失败。优化点 9重排是提升 top 结果质量的重要手段向量召回通常是“先粗召回”重排模型负责“精排序”。典型流程query 40 个候选 chunk ↓ reranker 逐个打分 ↓ 按相关性排序 ↓ 取前 58 个进入上下文重排尤其适合文档相似度高chunk 数量多问题表达复杂需要提高首位命中率召回结果中相关和半相关片段混杂。需要注意rerank 会增加延迟候选数量越大成本越高reranker 也需要评估不是一定比原排序好对超长 chunkreranker 可能被噪声影响。工程上可以设置开关retrieval:use_rerank:truererank_top_n:40final_top_k:6并在日志中记录 rerank 前后的排序变化方便判断是否值得保留。优化点 10提示词要明确“基于上下文回答”即使召回正确如果提示词过于宽松模型仍可能自由发挥。建议提示词至少约束只能基于给定上下文回答找不到答案时明确说不知道给出引用来源多个来源冲突时说明冲突不要编造条款、数字、链接或出处。还可以要求模型先判断上下文是否足够再回答。例如请先判断上下文是否足以回答问题 - 如果足够给出答案和引用 - 如果不足说明缺少哪些信息 - 不要使用上下文之外的知识补全事实。这样可以减少“看起来很流畅但不可验证”的回答。六、提示词模板下面给出一个适合 RAG 问答的基础模板。1. 严格引用版你是一个严谨的知识库问答助手。请仅根据【上下文】回答【用户问题】。 要求 1. 如果上下文中没有足够信息请回答“根据已提供资料无法确定”并说明缺少的信息。 2. 不要编造事实、数字、条款、链接、文档名或页码。 3. 回答中涉及事实结论时必须标注引用来源。 4. 如果多个上下文片段存在冲突请指出冲突并分别列出来源。 5. 回答尽量简洁但关键步骤和限制条件不能省略。 【用户问题】 {question} 【上下文】 {context} 【回答格式】 结论 依据 引用 不确定或缺失信息2. 多片段综合版你需要基于多个检索片段综合回答问题。 请按以下步骤处理 1. 识别哪些片段与问题直接相关 2. 忽略无关片段 3. 合并多个片段中的互补信息 4. 如果片段之间有冲突优先使用版本更新、来源更权威的片段并说明原因 5. 最终答案必须附带来源编号。 用户问题{question} 检索片段 {context} 请输出 - 直接答案 - 关键依据 - 来源编号 - 需要人工确认的点3. 无答案兜底版请判断上下文是否包含回答问题所需的信息。 如果不包含请不要猜测按以下格式回答 “根据当前知识库资料无法确定该问题的答案。” 然后补充 1. 已检索到的相关信息是什么 2. 仍缺少什么信息 3. 建议用户补充什么文档或条件。 问题{question} 上下文{context}七、常见错误与排查错误 1只看最终回答不看检索结果排查方式记录每次请求的完整链路日志。建议日志字段{query:用户问题,query_rewrite:改写后的问题,dense_hits:[chunk_1,chunk_2],sparse_hits:[chunk_3],rerank_hits:[chunk_3,chunk_1],final_context:[chunk_3,chunk_1],answer:模型回答,citations:[chunk_3]}有了日志才能定位是没召回、没排前、没进上下文还是生成阶段没用上。错误 2chunk 没有稳定 ID如果 chunk ID 每次重建都变化会导致评测结果难以对比引用链接失效缓存不可复用增量更新困难。建议 ID 包含文档 ID、页码、块序号或内容哈希。importhashlibdefmake_chunk_id(doc_id,section,text):digesthashlib.md5(text.encode(utf-8)).hexdigest()[:8]returnf{doc_id}-{section}-{digest}错误 3overlap 过大导致大量重复片段overlap 可以避免上下文断裂但过大会造成索引膨胀召回结果重复final context 被相似片段占满模型答案啰嗦或引用重复。排查方法计算召回结果之间的文本相似度或检查同一文档相邻 chunk 是否大量重复进入 final_top_k。错误 4没有处理表格和代码块表格、代码、配置项经常是知识库中最关键的信息。建议表格转 Markdown 或 JSON保留表头表格标题和说明不要与表格分离代码块不要被普通切分器切碎API 参数表要保留字段名、类型、是否必填、说明。错误 5score 阈值直接照搬不同向量模型、不同距离度量、不同归一化方式下score 含义可能不同。例如cosine similarity 越大越相似distance 越小越相似有些向量库返回的是距离不是相似度不同 embedding 模型的分布不同。因此阈值必须基于真实样本观察不建议复制别人的数值。错误 6没有做权限过滤企业知识库中RAG 不只是“找得到”还要“该不该给”。检索前或检索后都需要做权限控制deffilter_by_permission(chunks,user):return[cforcinchunksifc.metadata.get(permission)inuser.allowed_permissions]不要把用户无权访问的内容放进上下文后再指望模型不泄露。错误 7评测集只包含简单事实问答真实业务问题往往包括对比型A 和 B 有什么区别归纳型这份制度对外包员工有什么影响多跳型满足条件 1 和条件 2 时应该如何处理时效型最新版规则是什么反问型这个说法是否正确评测集应覆盖主要问题类型否则调参会偏向简单问答。八、优化方向当基础链路稳定后可以继续考虑以下方向。1. 查询改写与多查询召回用户问题可能很短或者表达不规范。可以让模型生成多个检索 query原问题报销打车有什么限制 改写 1员工打车报销规则 改写 2交通费报销限制 改写 3出租车 网约车 报销 标准然后多路召回、合并、去重、重排。适合场景用户问题口语化文档表述正式术语不统一召回结果不稳定。2. 上下文压缩当召回片段较多时可以先进行上下文压缩删除与问题无关的句子合并相邻片段去掉重复内容保留引用 ID控制 token 数。但要注意压缩过程不能丢失关键限定条件。3. 层级检索对于长文档可以先召回章节再在章节内召回段落。问题 → 章节级召回 → 段落级召回 → 重排 → 生成适合制度文档产品手册法规文件技术文档站点。4. 增量索引更新文档经常变更时不建议每次全量重建。可以记录doc_idversioncontent_hashchunk_hashupdated_at。仅对变化文档重建索引。5. 面向业务的 RAG 评测看板建议建立可视化看板跟踪检索命中率正确片段平均排名无答案问题比例引用准确率用户点踩问题高失败率文档高失败率问题类型。RAG 优化最终应进入持续运营而不是一次性调参。九、资产复制区本节整理可直接复制到项目中的模板。1. RAG 排查清单# RAG 问题排查清单 ## 一、知识库内容 - [ ] 原始文档中是否存在答案 - [ ] 文档是否为最新版本 - [ ] 用户是否有权限访问该文档 ## 二、解析与清洗 - [ ] 文本是否完整抽取 - [ ] 表格是否保留结构 - [ ] 页眉页脚是否影响正文 - [ ] 清洗规则是否误删编号、金额、日期 ## 三、切分 - [ ] chunk 是否表达完整语义 - [ ] chunk size 是否过大或过小 - [ ] overlap 是否导致重复过多 - [ ] 标题层级是否写入 metadata ## 四、召回 - [ ] 正确 chunk 是否进入 dense top_k - [ ] 正确 chunk 是否进入 sparse top_k - [ ] 是否需要混合召回 - [ ] score 阈值是否过高 ## 五、重排与上下文 - [ ] rerank 后正确 chunk 是否靠前 - [ ] final context 是否包含正确 chunk - [ ] 是否存在大量重复或冲突片段 ## 六、生成 - [ ] 提示词是否要求仅基于上下文回答 - [ ] 是否要求引用来源 - [ ] 无答案时是否允许回答不知道2. Chunk 数据结构模板{chunk_id:doc001-p003-c002,text:这里是切分后的文本内容。,metadata:{doc_id:doc001,source:产品手册.pdf,page:3,section_path:[产品说明,功能限制],chunk_index:2,doc_version:2024-06,content_hash:xxxxxxxx,permission:internal}}3. 检索配置模板retrieval:mode:hybriddense:top_k:20score_threshold:nullsparse:top_k:20rerank:enabled:truecandidate_top_n:40final:top_k:6max_context_tokens:60004. 评测样本模板{id:qa_001,question:年假最多可以结转多久,expected_answer:根据公司制度年假结转规则为……,expected_source:[employee-handbook-p12-c03],question_type:policy_fact} {id:qa_002,question:A 服务和 B 服务的免费额度有什么区别,expected_answer:A 服务免费额度为……B 服务免费额度为……,expected_source:[pricing-doc-p03-c01],question_type:compare}5. 召回日志模板{request_id:req_xxx,user_id:user_xxx,query:用户原始问题,query_type:concept_explain,rewrite_queries:[改写问题 1,改写问题 2],dense_hits:[{chunk_id:c1,score:0.78}],sparse_hits:[{chunk_id:c2,score:12.4}],rerank_hits:[{chunk_id:c2,score:0.91},{chunk_id:c1,score:0.86}],final_context_ids:[c2,c1],answer_has_citation:true,latency_ms:1200}十、结尾互动RAG 优化不是“调一个参数就万事大吉”而是一套围绕数据、检索、生成和评测的系统工程。如果你的 RAG 效果不稳定可以先不要急着换大模型而是按这条路径排查文档是否正确解析 ↓ chunk 是否完整表达语义 ↓ 正确片段是否被召回 ↓ 正确片段是否排在前面 ↓ 最终上下文是否包含正确证据 ↓ 提示词是否限制模型基于证据回答 ↓ 是否有评测集持续验证欢迎在评论区分享你遇到的 RAG 问题是召回不到是召回到了但回答不对是引用不准确还是文档结构太复杂后续可以继续展开RAG 评测集怎么设计混合召回如何工程落地表格型知识库如何切分企业知识库权限控制怎么做如何搭建 RAG 质量监控看板。如果这篇文章对你有帮助欢迎点赞、收藏、评论。这里是AI 爪客我们下一篇技术实践见。
RAG 效果差怎么办:从文档切分到召回参数的 10 个优化点
RAG 效果差怎么办从文档切分到召回参数的 10 个优化点作者AI 爪客类型RAG / 知识库 / 检索增强生成 / 工程实践说明本文聚焦可落地的 RAG 优化方法不虚构压测数据或实验结论。文中的效果预览以“可观察现象”和“评估方式”为主实际收益需结合业务语料、模型、向量库和评测集验证。摘要很多团队第一次搭建 RAGRetrieval-Augmented Generation检索增强生成系统时常见感受是流程跑通了但回答质量不稳定。典型问题包括明明知识库里有答案模型却说“不知道”召回了很多片段但真正相关的内容排在后面文档切得太碎答案缺上下文文档切得太大噪声太多模型抓不住重点同一个问题换个问法召回结果明显波动引用来源不准确回答看起来“像对的”但无法验证。RAG 效果差通常不是单点问题而是由文档解析、切分策略、Embedding、向量索引、召回参数、重排、提示词和评测体系共同决定。本文围绕一个核心问题展开当 RAG 回答效果不理想时应该从哪些环节系统排查和优化我们将从“文档切分”到“召回参数”梳理 10 个高频优化点并给出工程实现思路、提示词模板、排查清单和可复制资产帮助你把 RAG 从“能跑”推进到“可调、可评估、可迭代”。一、问题背景1. RAG 为什么容易“看起来能用实际不好用”一个基础 RAG 链路通常包括用户问题 ↓ 问题改写 / 查询理解 ↓ 向量召回 / 关键词召回 / 混合召回 ↓ 候选片段重排 ↓ 构造提示词 ↓ 大模型生成答案 ↓ 返回答案与引用来源从架构上看并不复杂但每一步都可能引入误差。例如文档解析阶段丢失了表格结构切分阶段把一个完整条款拆成两段向量召回阶段只适合语义相似但不适合编号、术语、金额、版本号等精确匹配top_k 设置太小关键片段没进上下文top_k 设置太大噪声片段干扰生成提示词没有约束“只能基于上下文回答”导致模型自由发挥没有评测集每次调参只能凭感觉。因此RAG 优化的第一原则是不要只盯着大模型要先确认检索链路是否把正确上下文送到了模型面前。2. 一个常见错误把“生成差”误判成“模型差”很多 RAG 问题表面看是模型回答不好实际原因是正确答案没有被召回召回内容排序太靠后上下文中存在互相冲突的信息提示词没有要求引用依据用户问题需要多跳推理但召回只覆盖了单个片段。如果模型没有拿到正确资料再强的模型也只能猜。所以排查时建议先问三个问题知识库里是否真的有答案检索结果里是否包含答案所在片段答案片段是否被放入最终提示词上下文只有这三步都成立才进入生成侧优化。二、最终效果预览本文不提供虚构实验数值但一个经过系统优化的 RAG 通常应该在以下方面变得更可控1. 可观察改进方向维度优化前常见现象优化后期望现象召回完整性知识库有答案但召不回关键片段更稳定进入候选集上下文质量片段噪声多、重复多上下文更集中冲突更少回答可信度无引用或引用不准答案能绑定明确来源问法鲁棒性换个问法结果变化大同义问法召回结果更稳定排查效率凭感觉调参可通过日志和评测集定位问题2. 建议评估方式你可以构建一个小型评测集每条样本至少包含{question:用户问题,expected_answer:参考答案要点,expected_source:答案所在文档或片段 ID,question_type:事实型 / 解释型 / 对比型 / 多跳型 / 规则型}每次调整切分策略、召回参数或提示词后记录正确片段是否被召回正确片段在召回结果中的排名最终上下文是否包含正确片段模型答案是否覆盖参考答案要点引用来源是否准确。这样才能判断优化是否真的有效而不是“某几个问题看起来变好了”。三、技术方案总览本文总结的 10 个优化点如下01. 文档解析先保证内容被正确抽取 02. 清洗规范去掉页眉页脚、目录噪声和重复文本 03. 切分粒度控制 chunk size 与 overlap 04. 语义边界按标题、段落、条款、表格进行结构化切分 05. 元数据设计保留标题层级、页码、文档版本和权限信息 06. Embedding 选择匹配语言、领域和查询形态 07. 混合召回向量检索 关键词检索互补 08. 召回参数合理设置 top_k、score 阈值和多路召回比例 09. 重排模型用 rerank 提升候选片段顺序 10. 生成约束通过提示词要求基于上下文、给出引用和不确定性一条更稳健的 RAG 工程链路可以设计为文档上传 ↓ 解析PDF / Word / Markdown / HTML / Excel ↓ 清洗去噪、去重、规范换行 ↓ 结构化切分标题层级 段落 表格 overlap ↓ 元数据补全source、page、section、version、permission ↓ 索引构建向量索引 关键词索引 ↓ 查询处理问题改写、术语扩展、意图识别 ↓ 混合召回dense sparse ↓ 重排reranker 对候选排序 ↓ 上下文压缩去重、截断、合并相邻片段 ↓ 大模型生成严格基于上下文回答 ↓ 日志与评测召回命中、答案质量、引用准确性四、环境准备以下示例以 Python 工程为例具体向量库和模型供应商可按团队技术栈替换。1. 推荐组件模块可选方案说明文档解析pypdf、pdfplumber、python-docx、BeautifulSoup根据文档类型选择文本清洗re、ftfy、自定义规则处理空白、页眉页脚、重复文本向量模型bge、text-embedding、领域 embedding优先选择中文效果稳定的模型向量库FAISS、Milvus、Qdrant、pgvector、Elasticsearch vector根据数据规模和运维条件选择关键词检索BM25、Elasticsearch、OpenSearch适合精确词、编号、术语重排模型bge-reranker、cross-encoder、商业 rerank API提升 top 结果排序质量评测自建 QA 集、RAGAS、DeepEval、自定义脚本重点是可重复评估2. 最小目录结构示例rag-demo/ data/ raw/ # 原始文档 processed/ # 清洗后的文本 indexes/ vector/ # 向量索引 keyword/ # 关键词索引 src/ parse.py clean.py chunk.py embed.py retrieve.py rerank.py generate.py evaluate.py configs/ rag.yaml eval/ qa_set.jsonl3. 配置文件示例chunk:chunk_size:800chunk_overlap:120split_by_heading:truepreserve_table:trueretrieval:dense_top_k:20sparse_top_k:20final_top_k:6min_score:0.25use_rerank:trueprompt:require_citation:trueallow_unknown:true注意这些参数不是通用最优值只是起点。实际项目中应结合文档类型、问题类型和模型上下文长度调参。五、核心实现思路优化点 1文档解析要先过质量关RAG 的输入不是“文件”而是“可被模型理解的文本与结构”。如果解析质量差后续切分和召回都会受影响。常见解析问题PDF 分栏文本顺序错乱表格被解析成无意义的换行文本页眉页脚混入正文扫描件没有 OCRWord 标题层级丢失图片中的关键信息没有抽取。建议做法defvalidate_parsed_document(doc):checks{has_text:len(doc.text.strip())0,has_reasonable_length:len(doc.text)100,has_metadata:bool(doc.metadata.get(source)),no_excessive_blank:\n\n\n\nnotindoc.text,}returnchecks上线前建议人工抽查几类典型文档长 PDF带表格的制度文件多级标题 Word扫描件中英混合文档。如果文档解析阶段已经错了不要急着调 top_k。优化点 2清洗时去噪但不要误删信息清洗的目标是降低噪声而不是让文本“看起来整洁”。适合清理的内容包括重复页眉页脚页码目录中的大量点线无意义空白重复版权声明HTML 标签残留。但要谨慎处理编号如3.1.2金额如1,000,000日期如2024-06-01产品型号如A-100 Pro法规条款如第十二条。清洗函数示例importredefclean_text(text:str)-str:texttext.replace(\u00a0, )textre.sub(r[ \t], ,text)textre.sub(r\n{3,},\n\n,text)textre.sub(r第\s*\d\s*页\s*/\s*共\s*\d\s*页,,text)returntext.strip()建议保留清洗前后的样本对比避免规则过猛导致关键信息丢失。优化点 3chunk size 不是越大越好也不是越小越好切分粒度是 RAG 效果的关键变量。chunk 太小的问题上下文不完整条款前提和结论被拆开表格标题和表格内容分离模型无法理解“这段话在讲什么”。chunk 太大的问题向量表示被多个主题稀释召回片段包含大量无关内容进入提示词后占用上下文窗口重排时粒度太粗难以精确排序。一个较稳妥的做法defsplit_by_length(text,chunk_size800,overlap120):chunks[]start0whilestartlen(text):endmin(startchunk_size,len(text))chunktext[start:end]chunks.append(chunk)ifendlen(text):breakstartmax(0,end-overlap)returnchunks但实际工程中不建议只按字符数硬切。更推荐结合语义边界。优化点 4优先按标题、段落、条款和表格切分更好的切分策略是先尊重文档结构再控制长度。例如一级标题 二级标题 段落 1 段落 2 表格 1可以切成{text:二级标题 段落 1 段落 2,metadata:{h1:一级标题,h2:二级标题,type:paragraph}}表格建议保留为 Markdown 表格或结构化 JSON而不是直接压平成一行。| 产品 | 免费额度 | 超出计费 | |---|---:|---:| | A 服务 | 1000 次/月 | 0.01 元/次 | | B 服务 | 500 次/月 | 0.03 元/次 |对于制度、合同、API 文档建议按以下边界切分标题层级条款编号小节代码块表格FAQ 问答对Markdown heading。核心原则一个 chunk 最好表达一个相对完整的语义单元。优化点 5元数据决定了后续过滤、引用和权限控制很多 RAG 系统一开始只存text和embedding后面才发现很难做筛选和引用。建议每个 chunk 至少保留{chunk_id:doc001-p12-c03,text:...,metadata:{source:员工手册.pdf,page:12,section:请假制度/年假规则,doc_type:policy,version:2024-01,created_at:2024-01-15,permission:internal}}这些信息可以用于按文档类型过滤按版本过滤返回引用来源权限控制召回结果去重问题定位和日志排查。如果用户问“最新版规则是什么”但元数据没有版本字段系统就很难稳定回答。优化点 6Embedding 模型要匹配语言和业务领域Embedding 不是“随便选一个就行”。选择时建议关注中文语义效果中英混合能力长文本截断策略对专业术语、缩写、代码、编号的表达能力向量维度和存储成本推理速度是否支持批量处理是否便于私有化部署。如果你的知识库包含大量法律条款金融术语医疗术语工业设备编号API 参数代码片段就需要重点验证 Embedding 对这些内容的召回能力。评估方式可以很简单为每类问题准备 1020 条样本观察正确 chunk 是否进入 top_k以及排序是否靠前。优化点 7不要只依赖向量召回混合召回更稳向量召回擅长语义相似但对精确匹配不一定稳定。例如“ERR_1024 是什么错误”“A-17 型号支持哪些接口”“第 3.2.1 条规定了什么”“订单状态码 5031 代表什么”这类问题往往更适合关键词检索或 BM25。推荐使用混合召回候选集 向量召回 top_k 关键词召回 top_k ↓ 去重 ↓ 重排 ↓ 取 final_top_k伪代码defhybrid_retrieve(query):dense_hitsdense_search(query,top_k20)sparse_hitsbm25_search(query,top_k20)mergeddeduplicate(dense_hitssparse_hits,keychunk_id)rerankedrerank(query,merged)returnreranked[:6]混合召回可以显著提升以下场景的稳定性编号查询专有名词查询短问题用户原话与文档表达差异较大文档中存在大量相似条款。这里的“显著”需要用你自己的评测集验证不建议直接套用他人的结论。优化点 8召回参数要结合问题类型调节常见参数包括参数含义风险dense_top_k向量召回数量太小漏召回太大噪声多sparse_top_k关键词召回数量太小漏精确匹配太大重复多final_top_k最终进入上下文数量太小信息不足太大干扰生成score_threshold相似度阈值太高无结果太低噪声多max_context_tokens上下文 token 上限太低截断太高成本增加建议不要只设一组固定参数而是根据问题类型动态调整。例如defchoose_retrieval_config(query_type):ifquery_typecode_or_id:return{dense_top_k:10,sparse_top_k:30,final_top_k:6}ifquery_typeconcept_explain:return{dense_top_k:30,sparse_top_k:10,final_top_k:8}ifquery_typemulti_hop:return{dense_top_k:40,sparse_top_k:20,final_top_k:10}return{dense_top_k:20,sparse_top_k:20,final_top_k:6}如果没有问题分类器也可以先从固定配置开始再通过日志观察哪些问题容易失败。优化点 9重排是提升 top 结果质量的重要手段向量召回通常是“先粗召回”重排模型负责“精排序”。典型流程query 40 个候选 chunk ↓ reranker 逐个打分 ↓ 按相关性排序 ↓ 取前 58 个进入上下文重排尤其适合文档相似度高chunk 数量多问题表达复杂需要提高首位命中率召回结果中相关和半相关片段混杂。需要注意rerank 会增加延迟候选数量越大成本越高reranker 也需要评估不是一定比原排序好对超长 chunkreranker 可能被噪声影响。工程上可以设置开关retrieval:use_rerank:truererank_top_n:40final_top_k:6并在日志中记录 rerank 前后的排序变化方便判断是否值得保留。优化点 10提示词要明确“基于上下文回答”即使召回正确如果提示词过于宽松模型仍可能自由发挥。建议提示词至少约束只能基于给定上下文回答找不到答案时明确说不知道给出引用来源多个来源冲突时说明冲突不要编造条款、数字、链接或出处。还可以要求模型先判断上下文是否足够再回答。例如请先判断上下文是否足以回答问题 - 如果足够给出答案和引用 - 如果不足说明缺少哪些信息 - 不要使用上下文之外的知识补全事实。这样可以减少“看起来很流畅但不可验证”的回答。六、提示词模板下面给出一个适合 RAG 问答的基础模板。1. 严格引用版你是一个严谨的知识库问答助手。请仅根据【上下文】回答【用户问题】。 要求 1. 如果上下文中没有足够信息请回答“根据已提供资料无法确定”并说明缺少的信息。 2. 不要编造事实、数字、条款、链接、文档名或页码。 3. 回答中涉及事实结论时必须标注引用来源。 4. 如果多个上下文片段存在冲突请指出冲突并分别列出来源。 5. 回答尽量简洁但关键步骤和限制条件不能省略。 【用户问题】 {question} 【上下文】 {context} 【回答格式】 结论 依据 引用 不确定或缺失信息2. 多片段综合版你需要基于多个检索片段综合回答问题。 请按以下步骤处理 1. 识别哪些片段与问题直接相关 2. 忽略无关片段 3. 合并多个片段中的互补信息 4. 如果片段之间有冲突优先使用版本更新、来源更权威的片段并说明原因 5. 最终答案必须附带来源编号。 用户问题{question} 检索片段 {context} 请输出 - 直接答案 - 关键依据 - 来源编号 - 需要人工确认的点3. 无答案兜底版请判断上下文是否包含回答问题所需的信息。 如果不包含请不要猜测按以下格式回答 “根据当前知识库资料无法确定该问题的答案。” 然后补充 1. 已检索到的相关信息是什么 2. 仍缺少什么信息 3. 建议用户补充什么文档或条件。 问题{question} 上下文{context}七、常见错误与排查错误 1只看最终回答不看检索结果排查方式记录每次请求的完整链路日志。建议日志字段{query:用户问题,query_rewrite:改写后的问题,dense_hits:[chunk_1,chunk_2],sparse_hits:[chunk_3],rerank_hits:[chunk_3,chunk_1],final_context:[chunk_3,chunk_1],answer:模型回答,citations:[chunk_3]}有了日志才能定位是没召回、没排前、没进上下文还是生成阶段没用上。错误 2chunk 没有稳定 ID如果 chunk ID 每次重建都变化会导致评测结果难以对比引用链接失效缓存不可复用增量更新困难。建议 ID 包含文档 ID、页码、块序号或内容哈希。importhashlibdefmake_chunk_id(doc_id,section,text):digesthashlib.md5(text.encode(utf-8)).hexdigest()[:8]returnf{doc_id}-{section}-{digest}错误 3overlap 过大导致大量重复片段overlap 可以避免上下文断裂但过大会造成索引膨胀召回结果重复final context 被相似片段占满模型答案啰嗦或引用重复。排查方法计算召回结果之间的文本相似度或检查同一文档相邻 chunk 是否大量重复进入 final_top_k。错误 4没有处理表格和代码块表格、代码、配置项经常是知识库中最关键的信息。建议表格转 Markdown 或 JSON保留表头表格标题和说明不要与表格分离代码块不要被普通切分器切碎API 参数表要保留字段名、类型、是否必填、说明。错误 5score 阈值直接照搬不同向量模型、不同距离度量、不同归一化方式下score 含义可能不同。例如cosine similarity 越大越相似distance 越小越相似有些向量库返回的是距离不是相似度不同 embedding 模型的分布不同。因此阈值必须基于真实样本观察不建议复制别人的数值。错误 6没有做权限过滤企业知识库中RAG 不只是“找得到”还要“该不该给”。检索前或检索后都需要做权限控制deffilter_by_permission(chunks,user):return[cforcinchunksifc.metadata.get(permission)inuser.allowed_permissions]不要把用户无权访问的内容放进上下文后再指望模型不泄露。错误 7评测集只包含简单事实问答真实业务问题往往包括对比型A 和 B 有什么区别归纳型这份制度对外包员工有什么影响多跳型满足条件 1 和条件 2 时应该如何处理时效型最新版规则是什么反问型这个说法是否正确评测集应覆盖主要问题类型否则调参会偏向简单问答。八、优化方向当基础链路稳定后可以继续考虑以下方向。1. 查询改写与多查询召回用户问题可能很短或者表达不规范。可以让模型生成多个检索 query原问题报销打车有什么限制 改写 1员工打车报销规则 改写 2交通费报销限制 改写 3出租车 网约车 报销 标准然后多路召回、合并、去重、重排。适合场景用户问题口语化文档表述正式术语不统一召回结果不稳定。2. 上下文压缩当召回片段较多时可以先进行上下文压缩删除与问题无关的句子合并相邻片段去掉重复内容保留引用 ID控制 token 数。但要注意压缩过程不能丢失关键限定条件。3. 层级检索对于长文档可以先召回章节再在章节内召回段落。问题 → 章节级召回 → 段落级召回 → 重排 → 生成适合制度文档产品手册法规文件技术文档站点。4. 增量索引更新文档经常变更时不建议每次全量重建。可以记录doc_idversioncontent_hashchunk_hashupdated_at。仅对变化文档重建索引。5. 面向业务的 RAG 评测看板建议建立可视化看板跟踪检索命中率正确片段平均排名无答案问题比例引用准确率用户点踩问题高失败率文档高失败率问题类型。RAG 优化最终应进入持续运营而不是一次性调参。九、资产复制区本节整理可直接复制到项目中的模板。1. RAG 排查清单# RAG 问题排查清单 ## 一、知识库内容 - [ ] 原始文档中是否存在答案 - [ ] 文档是否为最新版本 - [ ] 用户是否有权限访问该文档 ## 二、解析与清洗 - [ ] 文本是否完整抽取 - [ ] 表格是否保留结构 - [ ] 页眉页脚是否影响正文 - [ ] 清洗规则是否误删编号、金额、日期 ## 三、切分 - [ ] chunk 是否表达完整语义 - [ ] chunk size 是否过大或过小 - [ ] overlap 是否导致重复过多 - [ ] 标题层级是否写入 metadata ## 四、召回 - [ ] 正确 chunk 是否进入 dense top_k - [ ] 正确 chunk 是否进入 sparse top_k - [ ] 是否需要混合召回 - [ ] score 阈值是否过高 ## 五、重排与上下文 - [ ] rerank 后正确 chunk 是否靠前 - [ ] final context 是否包含正确 chunk - [ ] 是否存在大量重复或冲突片段 ## 六、生成 - [ ] 提示词是否要求仅基于上下文回答 - [ ] 是否要求引用来源 - [ ] 无答案时是否允许回答不知道2. Chunk 数据结构模板{chunk_id:doc001-p003-c002,text:这里是切分后的文本内容。,metadata:{doc_id:doc001,source:产品手册.pdf,page:3,section_path:[产品说明,功能限制],chunk_index:2,doc_version:2024-06,content_hash:xxxxxxxx,permission:internal}}3. 检索配置模板retrieval:mode:hybriddense:top_k:20score_threshold:nullsparse:top_k:20rerank:enabled:truecandidate_top_n:40final:top_k:6max_context_tokens:60004. 评测样本模板{id:qa_001,question:年假最多可以结转多久,expected_answer:根据公司制度年假结转规则为……,expected_source:[employee-handbook-p12-c03],question_type:policy_fact} {id:qa_002,question:A 服务和 B 服务的免费额度有什么区别,expected_answer:A 服务免费额度为……B 服务免费额度为……,expected_source:[pricing-doc-p03-c01],question_type:compare}5. 召回日志模板{request_id:req_xxx,user_id:user_xxx,query:用户原始问题,query_type:concept_explain,rewrite_queries:[改写问题 1,改写问题 2],dense_hits:[{chunk_id:c1,score:0.78}],sparse_hits:[{chunk_id:c2,score:12.4}],rerank_hits:[{chunk_id:c2,score:0.91},{chunk_id:c1,score:0.86}],final_context_ids:[c2,c1],answer_has_citation:true,latency_ms:1200}十、结尾互动RAG 优化不是“调一个参数就万事大吉”而是一套围绕数据、检索、生成和评测的系统工程。如果你的 RAG 效果不稳定可以先不要急着换大模型而是按这条路径排查文档是否正确解析 ↓ chunk 是否完整表达语义 ↓ 正确片段是否被召回 ↓ 正确片段是否排在前面 ↓ 最终上下文是否包含正确证据 ↓ 提示词是否限制模型基于证据回答 ↓ 是否有评测集持续验证欢迎在评论区分享你遇到的 RAG 问题是召回不到是召回到了但回答不对是引用不准确还是文档结构太复杂后续可以继续展开RAG 评测集怎么设计混合召回如何工程落地表格型知识库如何切分企业知识库权限控制怎么做如何搭建 RAG 质量监控看板。如果这篇文章对你有帮助欢迎点赞、收藏、评论。这里是AI 爪客我们下一篇技术实践见。