进阶 RAG:检索不是拼向量那么简单
很多人以为 RAG 的难点在生成给模型塞点文档它就能答对。现实恰恰相反检索质量才是生死线。你丢一个 query 进去embedding 算完相似度top-3 一取拼给大模型结果它要么答非所问要么自信地编。问题几乎从不出在 LLM而出在你喂给它的那几段文本本身就不对。向量相似度只是众多信号里的一种而且是最容易被语义漂移带偏的一种。这篇把 RAG 检索这层讲透从 naive 向量检索的坑到 chunk 切分、混合检索、rerank、query 改写最后串成一条召回→粗排→精排→压缩的管道。一、三个坑为什么只丢一个 embedding 检索就会答非所问RAG 入门教程告诉你把文档切成块、各自向量化、存进向量库来问题时算个相似度取 top-k。这套流程跑通 demo 很快一到真实文档就露馅。根子上的问题有三个。语义漂移向量检索衡量的是语义场的接近不是答案的命中。用户问怎么重置密码语义最接近的可能是一段讲密码强度要求的内容——词义高度相关但答非所问。embedding 模型把重置和强度都归到了相近的语义簇里相似度虚高。长文档切分丢上下文一篇万字技术手册切成 500 字一块后单块可能只讲了半句话的前提。模型拿到这块缺了上下的限定条件很容易过度泛化把局部表述当成全局结论。top-k 截断丢关键信息即便真答案排在第三top-k2 也会把它直接截掉。更糟的是排在前面的往往是语义相关但无关的干扰块模型被它们带偏反而离答案更远。这三件事叠加就是 RAG 答非所问和幻觉的主要来源。下面这个反模式我在不少生产代码里见过。反模式文档随便切 单向量检索 top-3 直喂# 一段过时的__STR0__代码问题全在检索端 from langchain.text_splitter import CharacterTextSplitter from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings, ChatOpenAI texts CharacterTextSplitter(chunk_size500, chunk_overlap0).split_text(doc) vs FAISS.from_texts(texts, OpenAIEmbeddings()) # 只用一个向量检索top-3 直接拼给模型 ctx \n.join(d.page_content for d in vs.similarity_search(query, k3)) answer ChatOpenAI().invoke(f问题{query}\n上下文{ctx}) # 结果答非所问 幻觉锅却在 LLM 头上注意这段代码里 LLM 一点没做错。它只是忠实地基于你给的几段文本作答。错在检索端硬切、单向量、top-3 截断——三道关卡每一道都在漏掉真答案。chunk 切分策略对比固定长度硬切斩断句子 vs 标题结构语义切分overlap二、Chunk 切分检索质量真正的地基很多人把 chunk 当成 preprocessing 里顺手做的事殊不知切分方式直接决定了检索的上限。向量再准块里没有完整语义召回的永远是碎片。固定长度硬切的代价按字符数一刀切句子、段落、章节边界全被无视。一个完整的注意事项可能被劈成两半前半句在块 A、后半句在块 B检索时两块都只命中一半语义残缺。按标题 / 语义切分用文档自身结构当刀Markdown 的#/##、HTML的标签、\n\n段落符都是天然的语义边界。LangChain 的MarkdownHeaderTextSplitter会按标题层级先劈再在块内细切每块都带着属于哪一章哪一节的元信息。overlap 防边界断裂相邻块重叠一小段如 64 字保证跨边界的语义不被腰斩。代价是存储翻倍但换回的是边界处不再丢上下文这笔账划算。真实切分代码长这样from langchain.text_splitter import ( MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter, ) # 第一刀按标题结构切保留层级元信息 headers [(#, h1), (##, h2), (###, h3)] header_splitter MarkdownHeaderTextSplitter(headers_to_split_notallowheaders) structured header_splitter.split_text(md_doc) # 第二刀按语义长度细切带 overlap 防止边界断裂 splitter RecursiveCharacterTextSplitter( chunk_size512, chunk_overlap64, separators[\n\n, \n, 。, , ], ) chunks splitter.split_documents(structured)先把结构捋清楚再在结构内细切最后用 overlap 兜底——这才是一套对检索友好的切分。三、混合检索BM25 关键词 向量的双保险向量检索有个绕不开的短板它弱在精确词。专有名词、错误码、版本号、人名这些低频但决定性的 token在稠密向量里被稀释了。用户搜ORA-00600向量可能觉得它和数据库报错更像却召回不了一段精确讲 ORA-00600 的文档。BM25 补精确匹配BM25 是经典稀疏检索靠词频和逆文档频率算分。它对精确词、稀有词极度敏感正好补上向量的盲区。它不理解语义但ORA-00600出现在哪段它一秒命中。两套信号并行同一句 query一路走 BM25 拿关键词命中一路走向量拿语义命中。两者召回的候选集合天然互补向量找得到意思接近的BM25 找得到字面命中的。RRF 融合去重排序用 Reciprocal Rank Fusion 把两套排名融成一个每个文档的最终得分是它在各路结果里排名的倒数之和score Σ 1/(krank)不依赖各自的分数量纲直接按名次合并。LangChain 里这件事已经是标准化组件from langchain.retrievers import EnsembleRetriever from langchain.retrievers.bm25 import BM25Retriever from langchain_community.vectorstores import FAISS bm25 BM25Retriever.from_documents(chunks) bm25.k 10 vec FAISS.from_documents(chunks, embeddings).as_retriever( search_kwargs{k: 10} ) # BM25 与向量各占一半权重召回阶段就双路并行 hybrid EnsembleRetriever( retrievers[bm25, vec], weights[0.5, 0.5], ) hits hybrid.get_relevant_documents(如何重置 ORA-00600 相关密码)到这里召回阶段已经从单向量升级成关键词 语义双路融合漏召回的概率大幅下降。四、Rerank 与 query 改写精排阶段的两板斧召回解决别漏精排解决排对。混合检索后你手里可能有上百个候选但 LLM 上下文窗口有限、且对塞进来的噪声极其敏感。精排要做的是把真正相关的那几个顶到最前面。交叉编码器重排双塔向量是先编码再比query 和文档从没见过彼此。交叉编码器cross-encoder则是把 query 和文档拼在一起联合编码让两者 token 级互相注意相关度判断准得多。代价是慢所以只用在召回后的少量候选上做精排不在全库上跑。query 改写 / 扩展用户的问题常口语、缺上下文、带指代。让 LLM 把原 query 改写成更利于检索的版本或扩展成多个子问题并发检索MultiQuery能显著改善召回。这一步改的是问法不动文档。from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_community.cross_encoders import HuggingFaceCrossEncoder from langchain.retrievers.multi_query import MultiQueryRetriever from langchain_openai import ChatOpenAI # 交叉编码器精排只留 top_n5 rerank_model HuggingFaceCrossEncoder(model_nameBAAI/bge-reranker-v2-m3) reranker CrossEncoderReranker(modelrerank_model, top_n5) # 套在混合检索外面召回结果先被精排一遍 ranked ContextualCompressionRetriever( base_compressorreranker, base_retrieverhybrid, ) # 再叠加 query 改写让 LLM 把原问题扩写成多个检索视角 expanded MultiQueryRetriever.from_llm( retrieverhybrid, llmChatOpenAI(modelgpt-4o-mini), )精排不是可选项。实践证明在召回候选上过一道 cross-encodertop-1 命中率能比纯向量检索高十几个百分点——而这恰恰是决定答案对不对的那一步。召回→粗排→精排→压缩 检索管道候选数从千级逐层收敛到几段精华五、串成一条管道召回→粗排→精排→压缩拆开看是四件武器合起来是一条逐层收敛的检索管道。每一层都在帮上一层纠错、瘦身最终留给 LLM 的是又准又干净的上下文。召回Recall混合检索并行出手目标是别漏宁可多召一些噪声。这一步的候选可以是上千个。粗排Coarse RankEnsembleRetriever 融合两路结果按融合分砍到百级先把明显无关的请出去。精排Rerank交叉编码器在百级候选上逐对打分只留 top-5~10。这一步决定谁排第一是答案命中的关键。压缩Compress即便 top-10 都对每块里也夹杂着无关句子。上下文压缩只抽取和 query 相关的片段把整块噪声压成几句精华喂给 LLM 的 token 数骤降幻觉也跟着降。最后这道压缩用 LangChain 的LLMChainExtractor或EmbeddingsFilter都能做把整条管道收口from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor from langchain_openai import ChatOpenAI # 压缩器只抽取每个 chunk 里和 query 相关的句子 compressor LLMChainExtractor.from_llm(ChatOpenAI(modelgpt-4o-mini)) # 完整管道混合召回 → 交叉编码器精排 → 上下文压缩 pipe ContextualCompressionRetriever( base_compressorcompressor, base_retrieverranked, # ranked 已在第四节叠了 reranker ) final_docs pipe.get_relevant_documents(如何重置密码) # final_docs 里每一段都是和__STR0__直接相关的精华句子上下文压缩前后对比检索到的原始 chunk 含冗余噪声压缩后只留相关句子把这条管道讲完RAG 的检索不是拼向量这句话才算落地。向量检索是入口不是终点chunk 决定上限混合检索补盲区rerank 定名次压缩去噪声。四层各管一段合起来才把检索质量这件事真正做扎实。下次再有人跟你说RAG 不就是向量库查一下你可以把这条管道甩给他差的就是这四道工序。资料展示下面是我整理的AI大模型 学习资料和工具包预览适合收藏后按主题逐步学习

相关新闻

测试管理工具评测:Gitee Test如何打通研发协同链路

测试管理工具评测:Gitee Test如何打通研发协同链路

项目管理圈里关于“测试管理工具”的讨论,这几年越来越热闹。尤其是研发协同链路拉长之后,测试早已不是“提Bug、写用例”这么简单,用例怎么关联需求、缺陷怎么回溯代码提交、质量数据怎么反哺迭代决策,这些原本被忽略的问题&…

2026/10/14 6:55:31 阅读更多 →
Claude Code接入Google搜索的MCP实战方案

Claude Code接入Google搜索的MCP实战方案

1. 项目概述:这不是“给AI装上网卡”,而是重构本地代码助手的工作流最近在某跨平台开发实验室做工具链优化时,我反复被同一个问题卡住:写一段数据清洗脚本,需要查 pandas 的groupby().apply()在空组下的默认行为&#…

2026/10/14 6:55:31 阅读更多 →
GNN工业落地实战:超大规模图谱的构建、扩展与部署

GNN工业落地实战:超大规模图谱的构建、扩展与部署

1. 项目概述:这不是又一本“GNN入门书”,而是一份能让你在真实业务中跑通整条链路的实操手记“Python 图神经网络实战:构建、扩展与部署 GNN 应用的完整实践者指南(二十三)”——这个标题里藏着三个被绝大多数教程刻意…

2026/10/14 6:55:31 阅读更多 →

最新新闻

Claude 看板与动画工具实战:对话式办公套件搭建指南

Claude 看板与动画工具实战:对话式办公套件搭建指南

1. 从单点对话到办公套件:这次更新到底改了什么Claude 最早给人的印象就是一个“会聊天的模型”,你问它答,顶多帮你改改文案、写写代码。但最近这一波更新,方向明显变了——它开始往“办公套件”的路子上走。所谓看板工具和动画工…

2026/10/14 9:47:26 阅读更多 →
小白程序员转行AI必看:Agent开发与大模型开发的区别与学习路径

小白程序员转行AI必看:Agent开发与大模型开发的区别与学习路径

本文对比了大模型算法工程师和Agent开发工程师的薪资、工作内容、学习门槛及发展前景。指出大模型算法岗薪资高但门槛极高,适合深入研究AI模型;而Agent开发岗入门门槛低,适合将AI应用于实际业务场景,是当前及未来的热门方向。建议…

2026/10/14 9:47:26 阅读更多 →
Claude 办公套件更新:看板与动画工具实战指南

Claude 办公套件更新:看板与动画工具实战指南

1. 从单点对话到办公套件:这次更新到底改了什么Claude 最早给人的印象就是一个"能聊天的模型",你问它答,偶尔帮你改改文案、写写代码。但最近这一波更新,方向明显变了——它不再满足于做一个对话框里的助手,…

2026/10/14 9:47:26 阅读更多 →
菲律宾客服外包不是四选一:把服务模式做成阶段性组合

菲律宾客服外包不是四选一:把服务模式做成阶段性组合

菲律宾客服模式怎么选?一个直接的判断可以作为起点:工单少、波动大且问题标准化时,先评估共享坐席;业务稳定并需要持续沉淀产品知识时,倾向专属团队;智能硬件等复杂产品需要菲律宾英语一线与中国技术团队形…

2026/10/14 9:47:26 阅读更多 →
CSS阴影效果深度解析与性能优化指南

CSS阴影效果深度解析与性能优化指南

1. 为什么CSS阴影效果总是让人头疼?作为前端开发中最常用的视觉增强手段之一,CSS阴影效果看似简单,实则暗藏玄机。新手常遇到的典型问题包括:阴影效果不自然、性能消耗大、浏览器兼容性差、多层阴影叠加混乱等。这些问题的根源在于…

2026/10/14 9:47:26 阅读更多 →
模型量化:4bit、8bit量化原理与实践

模型量化:4bit、8bit量化原理与实践

📝 本章学习目标:通过本章学习,你将全面掌握"模型量化:4bit、8bit量化原理与实践"这一核心主题,建立系统性认知。一、引言:为什么这个话题如此重要 在人工智能快速发展的今天,模型量化…

2026/10/14 9:46:26 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →