1. 从一个让人抓狂的提问说起你有没有遇到过这种场景花了大半天把一堆产品文档、客服话术、内部规范切好块、打好向量、灌进向量库兴冲冲地打开对话框问一句为什么我们的退款政策要设置成七天结果它给你返回三段原文——每段都写着退款政策为七天但就是没有一句解释为什么。你换个问法这个政策是怎么来的它还是把同样的三段话原封不动端上来。你再追问那如果客户情况特殊怎么办它开始一本正经地拼接不相关的段落看起来像在回答实际上什么都没回答。这不是你的向量库坏了也不是模型太笨。这是向量检索这套机制从娘胎里带出来的一个结构性缺陷它能找到是什么但几乎找不到为什么。我前后在三个不同规模的项目里踩过这个坑。第一次以为是 embedding 模型选得不好换了两个模型没用。第二次以为是 chunk 切得不对重新切了三遍还是没用。第三次才想明白——问题根本不在工程细节上而在于我们用了一种相似度匹配的工具去干一件因果推理的活。这篇内容就是把这三次踩坑的经验完整拆开讲清楚向量库为什么回答不了为什么以及在工程上我们到底能做什么、不能做什么、哪些是花架子、哪些是真管用。适合正在做 RAG 应用、知识库问答、智能客服的同行参考也适合刚接触向量检索、还没被它坑过的朋友提前打个预防针。2. 向量检索到底在干什么先把机制说透2.1 一句话讲清向量检索的本质向量检索的本质是把文本映射到一个高维空间里的点然后找离你问题最近的那些点。就这么简单。你问一句话这句话也被映射成一个点然后系统在这个高维空间里画一个圈把圈里所有的点捞出来按距离远近排序取前 K 个返回。这里的关键词是距离近。什么叫距离近就是两段文本在语义上说的是一回事。注意是说的是一回事不是一个解释了另一个。我常用一个类比来解释这件事向量库就像一个巨大的图书馆但它没有目录、没有索引、没有作者信息只有一个管理员。这个管理员的工作方式是——你把你想找的内容描述一遍他凭直觉去书架上抽几本感觉最像的书给你。他抽书的标准是封面和内容看起来像而不是这本书里有没有你真正需要的答案。所以当你问为什么退款政策是七天管理员看到退款政策七天这几个词就去抽了几本标题里带这些词的书。至于书里有没有写为什么他不管也管不了。2.2 相似度不等于因果性这是整件事的核心。向量空间里的近衡量的是语义相似度不是逻辑关联度。退款政策为七天和为什么退款政策是七天这两句话在向量空间里确实很近——它们共享了退款政策七天这两个核心概念embedding 模型会把它们映射到相邻的区域。但近不代表后者能从前者的文本里被推导出来。打个比方你问为什么天会下雨向量库给你返回十本写着下雨是一种降水现象的书。这些书和你的问题高度相关但它们回答的是下雨是什么不是为什么会下雨。你要的因果链条——水汽蒸发、遇冷凝结、重力下落——在这些书里可能一个字都没有。向量检索能做的是相关内容的召回。它不能做的是因果关系的推理。这两件事在信息检索领域里是两个完全不同的任务只是在大模型时代被混在一起了。2.3 为什么大家会误以为它能回答为什么因为在大模型出现之前我们习惯了搜索引擎的行为模式。你在搜索框里输入为什么XXX搜索引擎会返回一堆网页其中有些网页确实在讨论原因。你点进去看就得到了答案。但这里有个关键区别搜索引擎返回的是可能包含答案的网页最终回答你的是网页的作者不是搜索引擎。搜索引擎只负责找不负责答。向量库在 RAG 架构里扮演的角色和搜索引擎一模一样——它只负责找不负责答。真正回答你的是后面那个大模型。如果向量库找回来的内容里没有为什么的答案大模型再强也变不出来。它只能基于你给它的材料说话材料里没有的东西它要么编要么说我不知道。所以问题的根源是你的知识库里可能根本就没有为什么这个信息或者有但向量检索没能把它找出来。3. 拆解为什么类问题的三种失败模式3.1 第一种知识库里压根没有原因这是最常见也最容易被忽略的情况。很多团队在构建知识库的时候收集的都是结论性文档——产品说明书、操作手册、政策条款、FAQ 标准答案。这些文档的特点是只写是什么和怎么做不写为什么。比如一份退款政策文档它可能写着用户可在购买后七天内申请无理由退款。退款将在三个工作日内原路返回。它不会写我们设置七天这个期限是因为行业惯例是七天同时考虑到物流往返周期平均为五天留出两天缓冲。三个工作日是基于支付通道的结算周期决定的。后面这段为什么的信息通常存在于产品评审会的会议纪要里、产品经理的脑子里、或者某个已经离职的同事的聊天记录里。它从来没有被写进正式文档自然也就不在向量库里。这种情况下你问为什么向量库只能返回那段是什么的原文。它不是找不到原因是原因根本不存在于它的数据里。3.2 第二种原因存在但被切碎了假设你的知识库里确实有一份文档写了原因比如一份产品设计说明退款期限设置为七天。这个期限的设定参考了行业通行做法同时结合了我们自身的物流时效数据。物流往返平均需要五天因此七天可以覆盖大部分场景。支付通道的结算周期为 T1 到 T3所以退款到账时间承诺为三个工作日。如果你按固定长度切块比如每 500 字切一刀很可能退款期限设置为七天和物流往返平均需要五天被切到了两个不同的块里。你问为什么是七天向量检索命中的是第一个块返回的是退款期限设置为七天而解释原因的那句话在第二个块里没被召回。这就是切块策略带来的信息割裂。向量检索是按块召回的一个块就是一个独立的检索单元。如果因果关系跨越了块的边界检索就会断链。3.3 第三种原因存在且完整但语义距离太远还有一种更隐蔽的情况。原因和结论都在同一个块里但它们的表述方式差异太大导致 embedding 模型认为它们不相关。举个例子。你的文档里写着该功能上线后用户投诉率下降了 40%因此我们决定将其保留并推广到其他模块。你问的是为什么这个功能要保留。这句话的 embedding 和用户投诉率下降 40%的 embedding 之间的距离可能比你想的要远。因为保留和投诉率下降在语义上不是近义词embedding 模型不一定能把它们关联起来。向量检索擅长的是同义替换——你问退款它能找到退货返还你问七天它能找到一周7日。但它不擅长逻辑关联——你问为什么保留它很难联想到因为投诉率下降。3.4 三种失败模式的对照失败模式根本原因典型表现工程上能否缓解知识库无原因数据缺失返回的全是是什么不能只能补数据原因被切碎切块策略不当返回结论不返回原因能调整切块语义距离远embedding 局限返回不相关内容部分能混合检索这张表是我自己在排查问题时总结的每次遇到向量库答不了为什么先对照这张表定位属于哪种情况再决定怎么处理。大部分时候是第一种和第二种混在一起。4. 工程上能做的补救五种实操方案4.1 方案一在数据源头补为什么这是最根本的解法也是最费力的。你得回到知识库的构建环节把那些为什么的信息补进去。具体怎么做我的做法是在整理文档的时候强制要求每一条结论性描述后面跟一段背景说明。格式可以是这样结论退款期限为七天。 背景行业通行做法为七天物流往返平均五天七天可覆盖大部分场景支付通道结算周期 T1 到 T3。这段背景就是为什么的答案。它和结论放在同一个块里向量检索命中结论的时候背景也一起被召回了。这个方案的关键在于你得有人去写这些背景。如果原始文档里没有就得找产品经理、找业务方、找老员工去问。这是个体力活但效果最扎实。提示补背景信息的时候尽量用因为由于考虑到基于这类因果连接词。这些词能帮助 embedding 模型更好地捕捉因果关系提高召回率。4.2 方案二调整切块策略保住因果链如果你没法改数据那就改切块方式。核心原则是不要让因果关系被切断。我试过几种切块策略效果最好的是语义切块 重叠窗口的组合。语义切块的意思是不按固定字数切而是按段落、按小节切。一个完整的段落通常表达一个完整的意思因果关系一般不会跨段落。如果跨了那说明作者写得有问题你可以手动合并。重叠窗口的意思是相邻两个块之间保留一定的重叠内容。比如每个块 500 字相邻块重叠 100 字。这样即使因果关系刚好在边界上也有机会被其中一个块完整包含。具体参数怎么定我的经验是块大小300 到 800 字之间根据文档密度调整。技术文档可以小一点叙述性文档可以大一点。重叠比例10% 到 20%。太低起不到保护作用太高会引入大量冗余影响检索精度。切块边界优先在段落结束、标题切换、列表项结束处切避免在句子中间切。4.3 方案三混合检索用关键词兜底纯向量检索的一个大问题是它对低频但关键的词不敏感。比如为什么这个词本身在 embedding 空间里可能没有很强的区分度。但如果你用关键词检索BM25 之类的为什么就能直接命中那些包含原因说明的文档。混合检索的思路是同时跑向量检索和关键词检索然后把两边的结果合并、去重、重排序。我实测下来混合检索对为什么类问题的召回率提升很明显。纯向量检索可能只能召回 40% 的相关内容加上关键词检索之后能到 70% 以上。实现上可以用现成的混合检索框架也可以自己写一个简单的合并逻辑def hybrid_search(query, vector_store, keyword_index, top_k10): # 向量检索 vector_results vector_store.search(query, top_ktop_k) # 关键词检索 keyword_results keyword_index.search(query, top_ktop_k) # 合并去重 seen set() merged [] for doc in vector_results keyword_results: if doc.id not in seen: seen.add(doc.id) merged.append(doc) # 重排序可以用交叉编码器也可以用简单的分数融合 reranked rerank(query, merged) return reranked[:top_k]重排序这一步很关键。向量检索和关键词检索的分数不在同一个量纲上不能直接比较。要么用交叉编码器重新打分要么做归一化之后加权融合。4.4 方案四查询改写把为什么翻译成是什么这个方案有点绕但有时候很管用。思路是用户问的是为什么但知识库里存的是是什么。那就在检索之前先把为什么改写成几个是什么的子查询。比如用户问为什么退款政策是七天你可以改写成退款政策 七天 原因退款期限 设定 依据退款 七天 背景然后用这几个改写后的查询分别去检索把结果合并。这样做的逻辑是虽然知识库里没有直接写为什么但可能写了设定依据背景说明这类内容用这些词去检索命中率会更高。查询改写可以用大模型来做也可以预设一些模板。模板的好处是稳定、可控坏处是覆盖不全。用大模型的好处是灵活坏处是有时候会改得偏离原意。我的做法是先用模板做一轮如果召回结果不理想再用大模型做一轮。两轮结果合并。4.5 方案五在生成环节让模型承认不知道前面四个方案都是在检索端做文章。但有时候即使你做了所有能做的检索回来的内容里还是没有为什么。这时候最后一道防线是生成端的大模型。你需要通过 prompt 告诉模型如果材料里没有解释原因就直接说现有资料中没有说明原因不要自己编。这个 prompt 大概长这样你是一个知识库问答助手。请基于以下材料回答用户问题。 规则 1. 如果材料中明确包含问题的答案直接回答。 2. 如果材料中只有结论没有原因而用户问的是原因请回答现有资料中未说明原因并列出材料中与问题相关的结论性内容。 3. 不要基于材料之外的知识进行推测或编造。 材料 {context} 用户问题{question}这个方案不能让你回答为什么但能让你不错误地回答为什么。在很多场景下说我不知道比编一个答案要好得多。5. 一个完整的排查流程从提问到定位5.1 第一步确认知识库里有没有为什么这是排查的起点。不要一上来就调参数、换模型先确认数据里到底有没有你要的信息。具体做法把你认为可能包含原因的那份原始文档找出来用肉眼读一遍看里面有没有因为由于考虑到背景是这类表述。如果没有那就是数据缺失后面所有工程手段都是白搭。我踩过这个坑。有一次花了整整两天调检索参数最后发现原始文档里压根没写原因。那两天纯属浪费。5.2 第二步检查切块是否切断了因果链如果数据里有原因那就检查切块。把检索命中的块和它前后的块都打印出来看看原因是不是被切到了相邻的块里。这个检查很简单写个脚本把每个块的边界和内容打出来人工扫一遍。重点看那些包含因为由于的句子看它们和前面的结论句是不是在同一个块里。5.3 第三步测试不同查询的召回效果用不同的问法去测同一个问题看召回结果有什么变化。比如为什么退款政策是七天退款政策七天的原因退款期限设定依据退款 七天 背景如果某些问法能召回原因某些不能那说明是查询表述的问题可以通过查询改写来缓解。如果所有问法都召回不了那可能是 embedding 模型的问题或者数据本身的问题。5.4 第四步对比纯向量和混合检索的效果把纯向量检索和混合检索的结果并排对比看关键词检索能不能补上向量检索漏掉的内容。这个对比最好用一批测试问题来做不要只看一两个案例。我一般会准备 20 到 30 个为什么类问题跑一遍两种检索方式统计召回率。5.5 第五步检查生成端的 prompt最后检查大模型的 prompt看它有没有被指示不要编造。如果 prompt 里没有这条规则模型很可能会在材料不足的情况下自己编一个看起来合理的答案。这种答案比我不知道更危险因为它会误导用户。5.6 排查流程速查表步骤检查内容判断标准对应措施1原始文档是否有原因有因为/由于/背景等表述无则补数据2切块是否切断因果结论和原因在同一块否则调切块策略3不同问法召回对比至少一种问法能召回否则做查询改写4纯向量 vs 混合检索混合检索召回率更高否则上混合检索5生成端 prompt有不要编造规则无则加规则这张表我贴在工位上每次遇到为什么类问题答不好就按这个顺序过一遍。大部分问题在前三步就能定位。6. 几个容易踩的坑和我的实操心得6.1 不要指望换 embedding 模型能解决因果问题我见过不少团队遇到检索效果不好就换 embedding 模型。从某个通用模型换到另一个再换到某个领域微调模型。换了一圈发现为什么类问题还是答不好。原因很简单embedding 模型的训练目标是语义相似度不是因果关联度。再好的 embedding 模型也不会把为什么和因为映射到同一个区域除非训练数据里大量出现了这种配对。所以换模型可以提升同义替换的召回率但解决不了因果推理的问题。这是两个不同维度的事。6.2 切块大小不是越小越好很多人觉得块切得越小检索越精准。这话对是什么类问题成立对为什么类问题恰恰相反。块越小因果关系被切断的概率越大。一个完整的因果链条可能需要 200 字才能说清楚你切成 100 字的块必然断链。我的经验是对于包含解释性内容的文档块大小不要低于 300 字。如果文档本身写得很紧凑可以适当放宽到 500 字。6.3 关键词检索不是万能的但没有它万万不能混合检索里关键词检索的角色是兜底。它不追求高精度追求的是不漏。向量检索漏掉的内容关键词检索有机会补上。尤其是那些包含因为由于原因背景的文档关键词检索能直接命中。但关键词检索也有它的局限它不理解语义只能做字面匹配。用户问为啥它可能匹配不到为什么。所以查询改写还是要做。6.4 让模型说不知道比让它编答案更重要这一点我在多个项目里反复强调。用户问为什么如果知识库里没有答案模型编一个看起来合理的解释用户信了然后按照这个错误的解释去做决策——这个后果比我不知道严重得多。所以在 prompt 里一定要加不要编造的规则。如果业务场景允许还可以在回答里加上建议咨询相关业务方之类的引导。6.5 因果类问题的评测集要单独建大部分团队的 RAG 评测集都是是什么类问题比如退款政策是什么怎么申请退款。这些问题的评测指标不能反映为什么类问题的表现。你需要单独建一个为什么类问题的评测集。每个问题都要标注知识库里有没有答案、答案在哪个文档里、正确的召回结果应该是什么。然后单独统计这类问题的召回率和准确率。这个评测集不用很大20 到 50 个问题就够用了。关键是覆盖不同类型的为什么——有的是数据缺失有的是切块问题有的是语义距离问题。7. 向量库的边界在哪里7.1 它擅长什么不擅长什么把向量库的能力边界画清楚对做 RAG 应用很重要。它擅长的语义相似的内容召回。你问退款它能找到退货返还。模糊匹配。你记不清具体措辞它能凭大概意思找到相关内容。跨语言检索。中英文混合的知识库它能跨语言召回。它不擅长的因果推理。它找不到为什么除非为什么的答案以相似度高的形式存在于某个块里。多跳推理。A 导致 BB 导致 C你问 A 和 C 的关系它很难把中间那跳补上。否定和排除。你问除了七天还有哪些期限它可能把七天也召回回来。数值比较。你问哪个方案成本最低它不理解最低是什么意思。7.2 什么时候该用向量库什么时候不该用向量库适合找相关内容的场景不适合做推理的场景。如果你的需求是帮我在一堆文档里找到和这个问题相关的段落向量库是合适的。如果你的需求是帮我分析这些文档之间的逻辑关系向量库就不够了。你需要的是知识图谱、规则引擎、或者大模型的推理能力。很多团队把这两个需求混在一起用向量库去做推理结果就是为什么类问题答不好。7.3 一个务实的定位我的建议是把向量库定位成召回层不要指望它做推理层。召回层负责找到可能相关的内容推理层负责从这些内容里推导出答案。推理层可以是大模型可以是规则引擎可以是人工。但不管是什么它和召回层是两回事。把这两层分开设计、分开优化、分开评测很多问题会变得清晰。你不会再纠结为什么向量库答不了为什么因为你知道那不是它的活。8. 如果重新做一次我会怎么设计8.1 数据层结论和原因成对存储如果让我重新设计一个知识库我会要求所有结论性内容都必须配一段原因说明。这两部分存在同一个块里检索的时候一起召回。格式上可以用结构化的方式{ conclusion: 退款期限为七天, reason: 行业通行做法为七天物流往返平均五天支付通道结算周期 T1 到 T3, source: 产品设计文档 v2.3, owner: 产品部 }这样存储的好处是检索的时候可以针对 conclusion 和 reason 分别建索引也可以合并建索引。查询为什么的时候优先匹配 reason 字段。8.2 检索层向量 关键词 查询改写三件套检索层不要只用向量。向量 关键词的混合检索是标配查询改写是加分项。查询改写的逻辑可以做成一个 pipeline判断问题类型。如果是为什么类问题触发改写。用模板生成多个子查询。分别检索合并结果。重排序取 top K。这个 pipeline 不需要很复杂但要有。没有它为什么类问题的召回率会明显偏低。8.3 生成层严格的不编造约束生成层的 prompt 要明确告诉模型只基于给定材料回答材料里没有的就说不确定。同时可以在生成结果里加上信息来源的标注。用户看到答案的同时也能看到这个答案是从哪份文档里来的。这样即使答案不完整用户也知道去哪里找更多信息。8.4 评测层分类型评测评测集要按问题类型分开建。是什么类问题一套为什么类问题一套怎么做类问题一套。每套单独统计指标。这样才能准确反映系统在不同类型问题上的表现也才能有针对性地优化。9. 最后分享几个小技巧第一个技巧在文档里显式地写为什么。很多写文档的人觉得原因不言自明就不写了。但对检索系统来说不写就等于不存在。所以我在团队里推了一个规矩任何结论性描述后面必须跟一句原因/背景/依据。这句话可能看起来冗余但对检索效果提升很大。第二个技巧用问答对的形式补充数据。如果原始文档里没有原因可以人工整理一批为什么的问答对直接灌进知识库。这些问答对的 embedding 和用户的提问方式更接近召回率更高。第三个技巧给为什么类问题单独设一个检索策略。比如提高关键词检索的权重或者强制触发查询改写。不要和是什么类问题用同一套策略。第四个技巧定期 review 检索日志。看看用户都在问哪些为什么类问题哪些答得好哪些答不好。答不好的回到数据层去看是不是缺内容。这个 review 我一般每个月做一次每次都能发现几个可以补的数据缺口。这几个技巧都不复杂但坚持做下来效果是实打实的。向量库答不了为什么这件事短期内不会有根本性的改变但通过数据、检索、生成、评测四个层面的配合我们可以让它答不了的时候说答不了而不是答不了还硬答。这个区别在实际业务里就是靠谱和不靠谱的区别。