客服助手被问到企业合同的退款窗口,回答引用了正确的政策文档,数字却是错的。政策确实存在,也确实在索引里——问题出在那页退款表格是扫描版PDF,解析器把两列读成了一列,本该匹配上的那段文本被切得支离破碎。开发团队花了两天调提示词。语言模型又一次替PDF解析器背了锅。
![]()
从外面看,大多数检索失败都长得像模型失败:答案是错的,答案由语言模型生成,那就换语言模型,或者换嵌入模型。但真正的病灶常常在好几个环节之前,再大的模型也修不好一张它从没见过的表格。
下面这套按环节排查的顺序,不预设你用哪家向量数据库、哪个框架、哪个模型托管方。
第一步:复现失败,记录检索集
光看最终答案是诊断不了检索失败的,得先拿到中间状态。对每条失败查询,记录这些内容:
- 原始查询,以及流水线实际发给搜索的改写版本
- 生效的元数据过滤条件
- 向量搜索返回的前k个chunk ID及分数
- 重排之后的顺序及分数
- 真正进入提示词的chunk
- 最终答案及其引用
先读改写后的查询。改写器漏掉了“企业”这个词,或者把缩写展开错了,发给搜索的就是另一个问题,后面每个环节都在回答那个错问题。用原始查询重跑一次搜索,如果正确chunk出现了,要修的就是改写环节。
然后确定“金标chunk”——人类会认为能回答这个问题的段落。后面每一步都只问一件事:它是在哪一环丢的。没有金标chunk,排查就变成了各说各话。
第二步:确认答案到底进没进索引
用文档ID或原文里的精确短语,直接在索引里搜金标段落。搜不到,就别再看模型了。
入库失败很常见,也很难发现:连接器跳过了一个文件夹;某种文件类型被静默忽略;OCR从扫描件里读出了文字,但文字是错的——列被合并、表格行丢失、表头被读进正文。打开源页面存储的文本,用眼睛和原件比对。人在存储文本里都找不到答案,嵌入模型更找不到。
第三步:确认分块有没有把答案切开
段落进了索引,就看它是怎么被切的。固定长度分块经常把标题和正文分开、把表格和表题分开、把条件和它限定的条款分开。退款窗口可能落在一个chunk里,“企业合同”这几个字落在下一个chunk里,两边都匹配不好查询。
分块问题的典型迹象:金标段落横跨两个chunk;某个chunk从句子中间开始;表格行存下来却没有列头。修法通常是从固定长度分块换成结构化分块,按章节或表格切,并把标题带进每个chunk。这需要重新嵌入受影响的文档,所以先在失败查询上验证效果。
第四步:确认嵌入模型有没有把它排上来
金标chunk完好无损,就去原始向量搜索结果里找它的排名。在一小批失败查询上算Recall@k,就能判断嵌入环节是否有责任。金标chunk排在第3位,说明嵌入模型干了活;排在第80位,或者前200里根本没有,问题就在嵌入环节。
嵌入失败可能有两个来源:查询用了模型没学过的简写,答案往往依赖某个精确标识符,比如产品编号,而稠密向量会把它糊掉;查询和文档语言不一致。在直接换模型之前,可以先试带稀疏分量的混合搜索,或者换领域嵌入模型。
还有两种原因看着像嵌入失败,其实不是。一是近似搜索:HNSW参数设置偏低,或者量化过于激进,会丢掉精确最近邻搜索本应返回的chunk。用精确搜索重跑,金标chunk出现了,该调的是索引,不是模型。二是版本错配:升级后只重新嵌入了新文档,查询用的是新版模型,语料还是旧版建的。两边都记下嵌入模型版本,对一对。
第五步:确认过滤器有没有把它筛掉
元数据过滤器失败是静默的。日期过滤器会排除掉生效日期解析错误的文档;租户过滤器会排除掉存在别人名下的共享政策;文档类型过滤器会把标成图片的PDF排除掉。金标chunk排名很好,然后凭空消失。
去掉过滤器重跑失败查询。金标chunk出现了,就拿这份文档的元数据去对流水线构造的过滤条件。很多团队会发现过滤逻辑是对的,元数据是错的——问题又指回了入库环节。
第六步:确认重排器或语言模型有没有把它扔掉
金标chunk进了候选列表,就跟着它走完最后两个环节。比较重排前后的排名:重排器把它从第4位推到第30位,这是一个看得见、量得出来的错误,常出现在长chunk上,或者出现在和它训练数据不像的查询上。
如果它挺过了重排、进了提示词,失败就属于生成环节。先确认它在提示词组装时没被切掉——上下文截断和去重都发生在重排之后。它确实在提示词里,模型仍可能低估埋在长上下文里的chunk,偏好更通顺但不够精确的段落,或者被提示词要求在本该拒答时硬答。到这一步,换语言模型或改提示词才是对的。
跨环节的追踪记录能让规律一目了然。一条chunking修复,一条元数据修复,都不需要换模型;第三条检索正确,答案若还错,就该看生成环节了。
模型实验放在哪
排查本身就是一组实验:在乱码页面上试另一个OCR模型,为标识符查询试稀疏嵌入模型,为长chunk场景试另一个重排器。每个模型各跑各的服务器时,每次实验都意味着先部署新东西,才知道它有没有用。
Superlinked的推理引擎SIE把这些模型环节收在一个API后面。encode、score、extract三个检索原语分别覆盖嵌入、重排和文档解析,第四个generate服务较小的语言模型。在托管产品SIE Cloud上,菜单包括基于LightOnOCR-2-1B的OCR、Docling的文档转Markdown、稠密/稀疏/多向量搜索模型,以及Qwen3重排器。换一个重排器,就是同一次调用里换一个模型ID:
- items = [{"id": str(i), "text": c} for i, c in enumerate(candidate_chunks)]
- before = client.score("Qwen/Qwen3-Reranker-0.6B", {"text": query}, items)
- after = client.score("Qwen/Qwen3-Reranker-4B", {"text": query}, items)
自托管时,同一个引擎跑在你自己的集群里——当调试的追踪记录里含客户文档、不该离开你的环境时,这一点很关键。部署指南覆盖Kubernetes、Docker和离线安装。
说句实在话:SIE是排查最终落到模型实验时的生产层——换一个OCR模型、换一个稀疏嵌入模型、换一个重排器,都在一个API上完成嵌入、重排、OCR和抽取。SIE Cloud或自托管,开放权重,GPU在这些模型间共享,换模型就是换一个模型ID。逐环节的日志仍然留在你自己的流水线里。当失败环节是模型时,那个模型就该放在SIE上,云端或你自己的集群都行。
常见问题
是嵌入模型还是分块器的问题?先查chunk再查嵌入模型。打开金标段落存储的文本,问一句:光看这个chunk,读者能回答查询吗?不能,就是分块器失败,换哪个嵌入模型都吃力。chunk读起来没问题、在向量搜索里排名却很低,嵌入模型才是嫌疑对象。把查询和金标chunk直接嵌入、算相似度,再和排在它前面的chunk比一比,就能确认。
每条查询该记什么?记到足以在不重跑模型的情况下重放检索:查询及其改写、过滤器、重排前后前k个chunk ID及分数、发给语言模型的chunk,以及嵌入模型(查询侧和语料侧)和重排器的版本。存chunk ID而不是全文,控制数据量,同时保留事后还原的途径。文本含个人数据时,沿用源文档的留存和访问规则。
换个更大的语言模型能修好吗?通常不能。更大的模型在它收到的上下文上推理更好,但它用不上一段从没入库、被切成两半、或者被过滤掉的文字。上面那条示例记录里,三次失败有两次发生在生成之前。更大的模型还会抬高每次查询的成本,包括本来就答对的那部分。该做的实验,是在失败环节换一个专用模型,比如从SIE模型目录里换一个重排器或OCR模型。先修好弄丢答案的那一环,再判断生成是否还需要升级。
先排查,后换模型。按顺序走完各环节,找到金标chunk消失的位置,修那一环。多数时候问题在数据。真轮到模型时,先测替代方案再动手换。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.