T
traeai
登录
返回首页
Towards Data Science

10 Common RAG Mistakes We Keep Seeing in Production

8.5Score

TL;DR · AI 摘要

RAG系统在生产环境中常见错误包括解析失败、忽略文档结构、固定窗口分块等,影响检索精度。

核心要点

  • 解析文档时应保留结构,避免将表格转换为字符串。
  • 固定大小的分块忽略文档结构,导致信息丢失。
  • 优先修复解析问题,再进行检索调优。

结构提纲

按章节快速跳转。

  1. 本文讨论了RAG系统在生产环境中常见的10个错误及其影响。

  2. 将文档视为纯文本而非结构化对象,导致信息丢失。

  3. 默认提取PDF为纯文本,导致表格信息丢失。

  4. 固定大小的分块忽略文档结构,影响检索精度。

  5. 分块方法未考虑文档结构,导致信息丢失。

  6. 应优先修复解析问题,再进行检索调优。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • RAG系统常见错误
    • 解析错误
      • 表格丢失
      • 忽略文档结构
    • 分块错误
      • 固定窗口分块
    • 修复建议
      • 优先修复解析

金句 / Highlights

值得收藏与分享的关键句。

  • A parser that flattened the table on page 47 produces noise no chunker can recover.

    第 3 段

    ⬇︎ 下载 PNG𝕏 分享到 X
  • The literature does not help. The most-cited vendor writeup on RAG techniques runs 194 pages on chunking and zero on parsing.

    第 4 段

    ⬇︎ 下载 PNG𝕏 分享到 X
  • Fix parsing first. Retrieval tuning is for pipelines whose parser already preserves structure.

    第 4 段

    ⬇︎ 下载 PNG𝕏 分享到 X
#RAG#AI#文档解析#企业应用
打开原文

我们在生产环境中反复看到的 10 个常见 RAG 错误 | Towards Data Science

大型语言模型

我们在生产环境中反复看到的 10 个常见 RAG 错误

企业文档智能 [Vol.1 #4bis] – 关于逐块分析的陷阱的合著者注释,解释为何在第二部分之前采用四块划分

Kezhan Shi

2026 年 6 月 9 日

28 分钟阅读

分享

照片由 Jens Lelie 提供,来源:Unsplash。

我是本系列文章的合著者之一,与 Angela Shi 共同撰写。这篇文章列举了我们在生产环境中反复看到的 RAG 系统失败模式,这些模式促使我们最初采用四块划分的结构。

我必须承认一件事。即使在撰写本系列文章时,我们也会将大文档直接输入 ChatGPT。一个 PDF,一个问题,发送,然后阅读答案。模型表现良好,供应商支付了令牌费用,对于一次性任务,这确实是最合适的做法。

本系列文章的目的是处理另一种情况。在企业工作中,问题几乎从不只涉及一个文档。理赔处理人员会将相同的问题应用于经纪人完整的后端目录。合规团队会扫描组合中的每一份合同。在这样的规模下,将文档直接输入 ChatGPT 的方法不再适用,而且成本会迅速上升。

接下来是我们在生产中反复看到的错误的回顾,逐块分析。解决方案将在第二部分中介绍。

十个陷阱,四个块,每张卡片一个解决方案 – 图片由作者提供

1. 解析:文档如何失去其结构

当团队将文档视为文本而非结构化对象时,解析会失败。有三种模式反复出现:丢弃表格和布局(陷阱 1)、将整个文档直接放入提示中(陷阱 2),以及将文档分割为忽略其结构的固定大小窗口(陷阱 3)。解决方案是使用结构化解析器,生成类型化的表格,而不是字符串或任意窗口。

这三种模式的元版本也贯穿整篇文章。团队跳过解析的正确性,花费数周时间调整块大小、重排序阈值、Top-K 和嵌入选择,却从未达到预期的精度。他们衡量的每个杠杆都建立在解析器的输出之上:一个将第 47 页表格展平的解析器会产生无法恢复的噪声,一个丢失列标题的解析器会产生无法排序的歧义。文献资料也帮不上忙。关于 RAG 技术的引用最多的供应商文档在分块上用了 194 页,而在解析上却用了零页。首先修复解析。检索调优适用于那些解析器已经保留结构的流水线。

同样的问题也影响多列布局(例如带有脚注侧边栏的合同页面)、页眉和页脚(每页的页码污染所有检索结果)以及扫描 PDF 的阅读顺序。每种情况都代表不同的失败模式,但它们有一个共同的根源:解析器丢弃了文档所携带的结构。

解决方法是使用一种关系型解析器,它生成带有类型信息的表格(如 line_df、page_df、toc_df 等),而不是简单的字符串。每条线都包含其边界框、所在页面、字体和所属部分。表格拥有自己的粒度。后续的模块读取结构,而不是大块的文本。

1.2 坑 2:每个问题都为 1200 页付费

解析方面的第二个错误是我们自己在小项目中常犯的:完全跳过解析,将整个 PDF 直接塞进聊天中。这种方法写起来很快,对单个文档有效,而且供应商在免费层级上会支付令牌费用。

在真实的数据集中,这种做法会在三个步骤中变得昂贵。首先,PDF 不再是 12 页,而是 1200 页。其次,问题不再是 1 个,而是每天 200 个。第三,团队会向聊天中添加另外 5 个文档以“为模型提供更多上下文”,导致每个问题的令牌数量线性增长。账单从几美分变成每月数千美元,而答案的质量却变差,因为模型面对的“干草堆”变大了,而“针”却没变。

解决方法是将技术与文档和问题相匹配:当答案只需三页时,发送三页,而不是 1200 页。同样的原则也适用于此处:解析一次,检索限定范围,生成基于最小上下文的答案。

在现实的企业场景中,账单如下:一份 1200 页的再保险合同,合规团队每天查询 200 次。两种方法,同一个问题。第一种方法从 PDF 中提取每一条文本,并将结果塞进每一个提示中。第二种方法是本系列构建的流程:解析生成结构化表格,检索返回三页相关页面,生成过程只读取这三页。

$131,000 每年用于数据倾倒方法,而限定流程只需 $330 – 图片由作者提供

在一份 1200 页的合同中,数据倾倒方法的费用大约是限定流程方法的 400 倍。数据倾倒方法的费用随着文档和问题数量的增加而增加;而流程方法的费用只随着答案的增加而增加。对于每份合同,每年 200 个问题,这之间的差距是每年花费 $131,000 与 $329。

提示缓存虽然改变了计算方式,但并没有彻底改变。Anthropic 的 90% 折扣缓存读取和 OpenAI 的 50% 折扣缓存输入都只在缓存命中时适用,缓存会在团队无法控制的 TTL(生存时间)后被清除,并且在未命中时仍会按全价计费。即使在 90% 的折扣率下,数据倾倒方法每年仍需花费 $13,140,是限定流程方法的 40 倍,且仍然随着文档大小而不是答案数量增长。

托管的 RAG(如 OpenAI 的 file_search、AWS 知识库、类似服务)则介于两者之间:比数据倾倒方法便宜,因为供应商会将文档切块并检索看似相关的内容;但又比流程方法更不透明,因为切块、嵌入模型和排序方式都不是你能够检查的。它是一个适合单个文档原型设计的便捷中间方案。但在企业规模上,审计和可重复性与账单一样重要,因此它很少是最终的答案。

对于一份合同,绝对数字是六位数。对于企业组合中的 10,000 份合同,同样的比例决定了年度成本是停留在数万美元还是跃升至数百万美元。

1.3 坑点 3:调整 chunk_size。PDF 具有结构。

第三个解析错误是假设 PDF 是一个字符串。团队导入 pdfplumber、pypdf 或 PyMuPDF,调用名为 extract_text 或 get_text 的函数,将结果传入 RecursiveCharacterTextSplitter,然后花下一个月的时间调整 chunk_size 和 chunk_overlap,以将检索精度提高两个点。PDF 本身具有结构,而便捷的 API 消除了这些结构。团队正在调整的旋钮位于信息丢失的下游。

一份 200 页的合同嵌入了一个可点击的书签目录(读者在侧边栏显示的目录),用不同字体大小呈现的章节标题(部分为 24pt,章节为 18pt,子章节为 14pt,与读者眼睛扫描时使用的提示相同),以及以单元格级边界框存储的表格,结构解析器可以重建这些表格。结构就在 PDF 对象模型中。extract_text() 跳过了这些结构,将未区分的流传递给分割器。分割器随后在 chunk_size=500 的位置进行切割,因为这是团队一直在调整的旋钮,而 PDF 自身的排版本来可以免费提供锚点。

固定大小的块会穿过表格;结构感知的块则保持表格完整 – 图像由作者提供

代价是精度。一个块在表格中间结束,只包含半行数据。一个块在章节中间开始,没有标题来锚定答案的上下文。LLM 从这些块生成的答案在技术上是基于语料库的,但其依据是被裁剪的片段,而不是有意义的单元。由于每个块看起来大致相同,检索没有可以过滤的内容。由于引用指向任意窗口,生成时也没有可以清晰引用的内容。审计链上显示类似“第 1142 块,共 10,000 块”这样的行,但没有可读的含义。

Markdown 感知和章节感知的分割器解决了症状,但保留了上游的问题。它们根据从平铺字符串中猜测的标题文本进行分割,但无法重建 extract_text() 已经丢弃的边界框、字体层次结构或表格网格。分割器在被裁剪的输入上进行战斗。

解决方案是文章其余部分一直指向的结构解析器。该解析器保留了 PDF 的排版(line_df 包含 bbox、字体、页面和章节路径),保留了表格网格(表格提取器生成的是类型化的单元格,而不是字符串),保留了目录(toc_df 来自 PDF 书签和字体大小检测)。下游模块读取结构。由于没有窗口,任何 500 字符的窗口都不会跨越章节边界。结构存在。

2. 问题解析:如何忽略用户

当团队将用户的自然语言问题视为查询时,问题解析就会失败。有两个反射行为反复出现:直接将原始字符串传递给检索(坑点 4),以及在问题包含答案形状、范围和格式约束时,只停留在关键词提取(坑点 5)。解决方案是一个带有所有信息的类型化 ParsedQuestion。

在任何 RAG 框架中,最便宜的连接方式是将用户输入的内容直接嵌入,然后发送到检索模块。用户输入问题,调用 API,完成。问题中包含了许多信息:一个范围、一个预期的答案形式、一个格式,有时是一个条件,有时是一个否定,有时是对之前对话的引用,通常是对用户心中所想文档的隐含约束。嵌入过程将所有这些信息压缩成一个向量,这个向量主要捕捉的是内容词。下游模块处理的是经过压缩后的内容,而这些内容通常并不是用户真正想表达的意思。

真实的问题可以有各种各样的形式。以下是一些在实际生产中出现的示例,以及每个问题为何会破坏简单的嵌入方式:

  • 一个简洁、结构化的提问。“Plan B 的取消期是几天?”只有五个词,但包含三个约束条件:一个范围过滤(Plan B)、一个答案类型(一个时间段)、一个格式(以天为单位)。嵌入过程将这三个条件压缩成一个向量,用于与语料库进行匹配。
  • 一个否定句。“这个政策不涵盖哪些内容?”嵌入过程几乎不会将“不”作为功能词进行编码,因此检索返回的是与句子其余部分最相似的片段,这些片段描述的是政策所涵盖的内容。生成过程则会生成与用户实际问题相反的解释。
  • 嵌套条件加上问题。“对于 Plan B,假设签订一年合同,如果在六个月后取消,取消期是多长?”条件属于检索范围,问题属于生成框架。嵌入过程将它们混合在一起,错误的模块处理了错误的字段。
  • 一个多部分比较问题。“排除条款或免赔额,哪一个更重要?”检索返回的是关于两者的片段;生成过程没有接收到用户想要比较而不是列出的信号。
  • 一个省略的引用。“那 Plan C 呢?”只有五个词,没有上下文。嵌入过程没有可以锚定的内容。

这个列表远未结束。每个新的语料库、每个新的受众、每个新的产品都会带来新的问题形式。有些问题简洁,有些则详细解释,有些包含操作符,有些依赖于之前的对话。问题解析器是处理这些多样性问题的模块,它将多样性转换为下游模块可以处理的类型化对象。没有它,每一个新的问题形式都会变成一个新的静默失败。

在每种情况下,捷径是相同的。问题本身包含结构(约束、操作符、范围、意图、引用)。嵌入过程将这些结构压缩成一个向量。检索模块处理压缩后的内容,生成模块读取检索模块找到的内容,用户得到的答案可能与他们的问题匹配,也可能不匹配。

字符串中隐藏的约束条件与解析器传递的类型化字段之间的对比 – 图片由作者提供

代价是管道无法检测到的矛盾。检索到的片段看起来相关,模型生成了一段流畅的段落。用户读到一个关于保障内容的自信回答,而他们实际询问的是排除条款,没有任何标志、警告或信号表明问题中的操作符在过程中丢失了。

常见的应对方法是插入一个小型 LLM 调用,返回一个 JSON 字典:intent、scope、keywords。这解决了没有结构的问题,但没有解决没有合同的问题。字典的键在不同提示之间漂移(一个调用返回 scope,下一个返回 scope_filter),检索读取一个键,生成读取另一个键,静默的遗漏最终到达用户。一个类型化的 ParsedQuestion Pydantic 模式将漂移转换为解析时的错误,审计日志可以捕获到这个错误。真正的胜利不是 JSON,而是验证。

它还会阻碍所有下游的改进。如果你不知道问题的类型,就无法将问题路由到专门的处理流程。如果你没有标记出模糊术语的模糊性,就无法向用户确认该术语的含义。如果你没有识别出问题包含多个部分,就无法对多部分问题进行分解。

解决方法是使用一个带有类型的 ParsedQuestion 对象:问题解析模块将原始字符串转换为一个结构化的对象,包含关键词、答案结构、范围过滤器和执行计划。字符串是输入;所有下游处理都使用这个类型化的对象。

2.2 坑点 5:“直接使用 HyDE。” 或者信任嵌入向量

第二个问题解析的错误是假设问题解析模块不需要存在。用户输入“计划 B 的取消期是多久?”,流程将字符串传递给嵌入模型,通过余弦相似度检索出前 K 个片段,然后交给生成模块处理。整个过程中没有问题解析模块。现代开发人员不信任手动提取关键词(有充分理由:脆弱的列表、漂移、语言特定的边界情况),而是倾向于使用嵌入向量,因为嵌入向量似乎可以自动吸收问题的含义。

一个向量表示所有内容,而每个模块根据类型化字段进行路由 – 图片由作者提供

嵌入向量确实吸收了一些信息。它们生成一个与问题类似段落的密集向量。但它们不会生成答案结构、范围、格式约束,或隐含的“在该文档中”这一条件。这些信息在没有被写入类型化字段之前,不会产生任何嵌入信号。

在这个时候,领域内常见的变通方法是使用 HyDE(Hypothetical Document Embeddings):LLM 生成一个假设的答案,流程对这个假设答案进行嵌入,然后用它来检索语料库中的片段,而不是用问题本身。这种方法在基准测试中有效,开发人员也因此将其视为从仅依赖嵌入向量的陷阱中聪明逃脱的方式。但很少有人明确说明这种方法有效的原因:假设的答案包含了真实答案中可能包含的关键词,而这些潜在的关键词正是嵌入向量所捕捉到的。HyDE 实际上是通过 LLM 进行关键词提取的一种伪装方式,每查询多生成一次,没有专家验证,也没有审计。当它表现不佳时,开发人员的本能反应是使用更强的模型。该洞察的确定性版本是向领域专家询问概念词汇,并将其存储一次。在企业环境中,值得发布的答案是领域专家验证过的答案,而不是更强大模型偶然想象出的答案。

“以天为单位”的格式约束是最明显的例子。将“天”这一信号编码进问题向量中,会使前 K 个结果偏向于“30 天内的响应时间”或“政策的第一天”等片段,而这些信息对于关于取消期的问题来说完全是噪音。这一约束应包含在生成指令中,而不是检索查询中。跳过问题解析的流程会将相同的编码向量发送给检索模块,同时将原始字符串发送给生成模块,导致错误的模块消费了错误的字段。

解决方案并不是更聪明的嵌入方式。解决方案是一个问题解析器,它生成一个带有答案形状、范围、格式和分解的类型化对象,每个字段分别路由到对应的处理模块。关键字的情况成为该对象的一个字段,通过与专家词典进行验证,使得术语“premium”映射到“prime”、“cotisation”、“price”等,而无需开发人员手动维护列表。第6章将详细阐述解析器以及从另一端输出的两个类型化摘要,一个用于检索,一个用于生成。

3. 检索:向量数据库的反射及其盲点

当“只需嵌入并按余弦相似度排序”成为唯一的工具时,检索就会失败。造成这一问题的三个习惯是:将RAG视为向量数据库的同义词(陷阱6),将块视为唯一粒度,而答案可能只是更大段落中的一句话(陷阱7),以及在文档中仅停留在对其他部分的引用(陷阱8)。解决方法包括混合检索、同时返回两种粒度,以及引用解析循环。

3.1 陷阱6:“只需使用向量数据库”

这是我们看到的最大错误,也是最难纠正的错误,因为它决定了整个基础设施堆栈。模式是固定的:对语料库进行分块,对每个块进行嵌入,对问题进行嵌入,然后根据余弦相似度返回前k个块。完成。

余弦相似度单独使用 vs 三个并行检测器加一个仲裁者 – 图片由作者提供

当关键词比向量更能帮助回答问题时,这种成本就会显现出来。首字母缩略词(保险中的“RC”,偿付能力中的“SCR”)、产品代码、数字范围、罕见名称、法律引用如第4.2(a)(iii)条。嵌入将这些内容压缩成一个密集向量,丢失了离散性。检索模块返回的是关于类似内容的段落,而不是包含该术语的段落。

更普遍地说,当问题是对等文本的改写时,嵌入是有效的。但当问题是一个标记时(如代码、数字、正则表达式形状的模式、精确的引用),嵌入就会遇到困难。一个让我印象深刻的例子。几个月前,我在一个文案工具中使用了一个聊天助手,用来在一个我粘贴进去的长文档中查找一个特定的短语。在某个时刻,助手尝试用正则表达式查找该短语。正则表达式返回了空结果。我去找原因:原始PDF中有一个排版字符(我认为是一个弯引号),而我的复制粘贴操作将其替换成了直引号。模型正确地尝试使用正则表达式。标记匹配是基本操作。围绕它的流程却无法处理一个字符的差异。

Anthropic的工具更进一步:当代理需要查找一个片段时,它会优先使用类似grep的原始工具,而不是嵌入。这是该领域缓慢发展的方向,因为对话由词语构成,而词语在标记上匹配最佳,而不是向量。

更深层次的问题是文化上的。“检索增强生成”(Retrieval Augmented Generation)这个名字并未提及向量。它说的是“检索”,这是一个已有五十年历史、包含许多技术的领域。然而,当我们与构建RAG系统的开发人员交谈时,几乎每次对话都是一样的:“是的,我们使用向量数据库进行检索。”它被视为默认选项,而不是众多选择中的一个。

Angela 和我甚至就我们所构建的东西是否应该使用一个不同的名称进行过争论。我们考虑使用 ROG,即 Retrieval Only Generation,因为在企业环境中,检索才是工作的核心,而生成只是围绕它的包装。传统的 RAG 定义方向相反:参数化模型生成内容,检索则对其进行补充。最终我们还是保留了“RAG”这个名称,因为这就是工作被搜索和认知的方式,我们不想仅仅为了表达一个观点而发明一个新的缩写。但需要明确指出的是:这一系列中没有任何地方使用纯粹的向量搜索。嵌入向量确实会出现,但只是作为后备方案。

解决方案是默认使用混合检索:关键字检测器(精确、自由、确定)与嵌入检测器并行运行,最后由一个 LLM 仲裁者对聚合后的候选结果进行排序并给出理由。流行的快捷方式“RAG 等于向量数据库”是我们看到的在大规模应用中造成昂贵失败的最主要原因。

3.2 第七种陷阱:块是正确的,但流程在此停止

第二个检索错误更为微妙,只有在你试图根据来源确认答案时才会显现。流程检索到一个块,将其交给 LLM,LLM 返回一个答案。答案在块中的哪个位置?没有人会去问,因为块是基本单位。

块中包含答案,但流程无法指出是哪一行 – 图片由作者提供

这破坏了所有依赖于知道答案位置的下游功能。在源 PDF 上的高亮显示。带有行号的引用。合规性追踪。系统可以说“取消期是 30 天”,但无法指出它读取的是哪一行。

直觉上,人们会尝试在事后通过字符串匹配 LLM 的引用与源文档来恢复位置。但一旦模型对引用进行了改写(这在引用超过几个标记时经常发生),引用就会指向一个几乎匹配的行。位置必须在输入时计算,而不是从输出中逆向推导。

块在修复的另一端也是错误的单位:LLM 所需的上下文文本量取决于问题。例如,针对一份 200 页的事件报告,问“事件的日期是什么?”关键词“事件的日期”匹配一行,该行包含日期。围绕它的两行就足以支撑答案。返回包含该行的块,甚至整个章节,都会让日期被噪声淹没,模型必须从中艰难地提取信息。关于合同取消政策的问题则需要不同的规模:一两段,因为政策是基于多个相互作用的条件构建的。同样的分块器,同样的文档,但需要保留的上下文文本量不同,正确答案也不同。

修复方法不是更好的分块器。修复方法是同时以两种粒度进行检索:一种足够精确,可以在源文档上进行高亮(关键词匹配的行),另一种大小根据问题需求而定(日期需要两行,政策需要一段)。第 7 章围绕这种拆分构建了检索砖块,并为这两个范围命名,这些名字是 Angela 和我争论了数周后才最终确定的。

3.3 第八种陷阱:“参见第 4.2 节”却从未查看

第三次检索错误出现在文档首次提及自身时。检索到的片段显示为“排除项列在第4.2节”,流程随即停止。检索找到了提及排除项的片段,但没有跟随指针。生成阶段获取到该片段,看到引用后,面临两个同样糟糕的选择:根据预训练先验知识编造第4.2节的内容,或者拒绝并声称“文档未作说明”。实际上文档确实做了说明。只是流程没有继续查找。

指针悬而未决与第二次查找引入第4.2节 – 图片由作者提供

代价是审计链的无声破裂。用户被告知系统基于语料库进行推理,但当引用无法解决时,答案就变成了基于先验知识的推理。这正是四块砖合同旨在防止的情况。更糟糕的是,这种失败从外部无法检测到:无论答案如何,读起来都流畅自然,引用的跨度覆盖了提及第4.2节的片段,而不是第4.2节本身。点击引用的审阅者看到的是一句“参见第4.2节”和一个自信的答案。证据链在一步之遥处戛然而止。

代理式RAG以代理式方式处理这种情况:当LLM看到引用时,会调用fetch_section工具。这确实有效,但团队往往看不到其代价。每个引用解析都成为非确定性循环,审计轨迹在每个代理步骤中分叉,每个问题的代价随着引用链的深度而增长。

确定性替代方案是带有类型触发器的两轮循环。第一轮生成一个结构化答案,标记待处理的引用,而不是围绕它编造内容。协调器跟随引用到被引用的章节,对正确的页面执行检索,第二轮则基于第4.2节本身进行返回。第11条开发了答案模式中的触发器字段、将引用映射到正确页面的解析器,以及将它们连接在一起的协调器流程。

4. 生成:审计链在此终结

当砖块被视为返回字符串的API调用时,生成会失败。两种模式反复出现:发送原始LLM字符串,没有标志、没有模式、没有审计(陷阱9),以及在没有外部缺失证明的情况下信任LLM的“未找到”声明(陷阱10)。解决方案是将类型化答案连接到模型无法访问的程序检查。

4.1 陷阱9:没有标志,没有模式,没有审计。只有文本。

检索到的段落进入提示,LLM返回一个字符串,系统将字符串传递给用户。每天都有生产RAG系统以这种方式运行。砖块就是API调用。

普通字符串与带有标志、跨度和审计字段的类型化对象 – 图片由作者提供

代价是你无法获得答案是否可靠的信号。无论段落是否包含答案,模型都会返回流畅的句子。没有answer_found标志,也没有支持跨度的引用。当模型编造一个数字时,系统在它到达用户之前没有信号来检测它。

结构化输出(如OpenAI的response_format、Anthropic的tool use、Pydantic AI)在一定程度上解决了这个问题。带有answer_found和quote字段的类型化响应说明了模型认为其基于的内容。但它们无法解决模型自我评价的问题。“confidence”: 0.95无论引用是真实的还是编造的,都以相同的信心出现。读取段落的同一块砖也负责对答案进行评分。

该验证是程序化检查,模型无法访问这些检查。一个正则表达式检查,确保引用的引文在引用的段落中逐字出现。对枚举答案进行集合覆盖率检查(问题要求四个排除项,模式返回四个,每个条目都映射到段落中的不同部分)。对答案值进行类型检查(答案应为 Duration 类型,模型返回了“大约一个月”)。每个检查都是调度器路由的判断依据。模型填充模式;验证器决定是否发送答案。

第二个成本由此产生:下游工具无法响应模型的状态。调度器无法触发重新获取,因为没有东西告诉它检索是不完整的。审计日志无法重建决策,因为原始文本没有出处。整个流程变成一次性:答案要么是好的,要么必须重新运行整个流程。

解决方案是使用类型化的答案模式,加上一个闭合循环的验证器。第八章将开发该模式、选择合适形状的调度器(根据答案类型),以及闭合循环的验证器。

4.2 坑 10:“不在块中”不等于“不在语料库中”

第二代错误是信任大语言模型(LLM)在说“未找到”时的判断。检索结果很少是空的:使用嵌入时,余弦相似度的 top-k 总是会返回一些内容,因此 LLM 会得到一些块,并决定答案是否在其中。当它说没有时,流程会将 answer_found=False 发送给用户。系统刚刚将验证任务委托给了读取块的同一模块。

LLM 的“未找到”与针对完整语料库的确定性缺失之间的对比 – 图片由作者提供

LLM 的“未找到”意味着“不在这些块中”。它并不意味着“不在这个语料库中”。模型看到的是 top-k 段落,而不是文档,也不是语料库的其余部分。在自信拒绝的背后隐藏着两种失败模式:答案在 top-k 中,但模型遗漏了(LLM 错误),或者答案在语料库的其他地方,但检索遗漏了(检索遗漏)。用户看到“文档未指定”,并假设语料库已被检查。实际上,它并未被检查。

解决方案是用确定性的缺失证明来支持“未找到”。专家关键词字典,每个术语和每个为问题概念精心挑选的同义词,作为字面子字符串搜索在完整语料库中运行,而不是在检索到的块中运行。零匹配时,系统会明确表示“不在这个语料库中”,并附有可辩护的审计追踪。至少有一个匹配,但 LLM 仍然说没有,说明检索遗漏,协调器会触发对关键词出现页面的第二次检索。关键词证明缺失;嵌入无法做到这一点。该解决方案使用了文章之前提到的模块:来自坑 5 的专家字典和来自坑 6 的关键词检索。

5. 你对第二部分应有的期待

以上十个错误中的每一个都是团队在早期做出的结构性选择,当时他们还没有一个命名模块的合同。使这些失败不可能发生的合同将在本系列的其余部分中开发:一个保持文档结构的关系解析器,一个将所有约束传递到下游的类型化问题,两个粒度的混合检索,一个引用解析循环,以及一个与模型无法访问的程序化检查连接的类型化答案。每一个都是四模块拆分所需的模块。

同样的向量反射问题在智能体系统中以不同的形式出现。当智能体需要从数百个工具中选择一个时,默认的反射是再次嵌入工具描述并按相似性排序。结果是一样的:在代码上不够精确,无法区分“读取”和“写入”,审计时也难以理解。解决方法的结构也是一样的:先处理文字,嵌入作为后备,每次选择都要进行审计。

如果你在阅读这篇文章时频频点头,因为你的流水线已经做了大部分这些内容,那么这就是最有用的点头方式。后续的文章将详细探讨这些解决方案。

6. 参考资料和进一步阅读

系列中的其他文章:

  • 文档智能系列简介,介绍了上述合同所实现的四块结构。
  • 基线企业 RAG,从 PDF 到高亮答案,一个完整的四块流水线,适用于单个 PDF。
  • 嵌入并非魔法:RAG 检索的可预测失败模式,对第 7 和第 8 个陷阱进行量化分析并提供示例。
  • 重排序器也并非魔法:交叉编码器层是否值得投入成本,讨论了元陷阱开启器标记为下游浪费时间的重排序器旋钮。
  • RAG 并不是机器学习,而机器学习工具包解决的是错误的问题,解释了为什么上述十个陷阱中没有一个是超参数搜索问题。
  • 从正则表达式到视觉模型:哪种 RAG 技术适合哪种问题,对解析块的三种解析陷阱进行技术对技术的映射。

外部参考资料:

  • Gao 等人,《无需相关性标签的精确零样本密集检索》,ACL 2023。原始 HyDE 论文。第 5 个陷阱解释了该技术为何有效(LLM 生成的假设包含真实答案中的关键词),并论证确定性等价于专家字典。
  • Anthropic,《引入上下文检索》,2024 年 9 月。LLM 生成的上下文前置方法,与第 3 个陷阱相邻。该系列通过结构化元数据而非 LLM 生成的摘要解决了去上下文化问题。
  • Anthropic,《使用 Claude 进行提示缓存》。成本杠杆,使第 2 个陷阱的数学计算在 90% 的缓存读取率下倾斜,但不会翻转。
  • Pinecone Learn,《LLM 应用的分块策略》。该领域的参考分块调查,包含第 3 个陷阱中提到的精度与丰富性矩阵,该矩阵位于 extract_text() 已经损坏的框架中。
  • LlamaIndex,《构建生产环境中的高性能 RAG 应用》。命名了用于检索与合成模式的解耦块,与第 7 个陷阱中提到的锚点与上下文相同。
  • Liu,《Instructor:LLM 的结构化输出》。Pydantic 类型输出库和将模式作为合同的论点。直接支持第 4 个陷阱中 Pydantic 与字典的反向推动,以及第 9 个陷阱中的类型化答案。
  • Zaharia、Khattab 等人,《从模型到复合 AI 系统的转变》,BAIR 2024。对本文所假设的四块架构的学术框架。

作者

查看 Kezhan Shi 的所有文章

LLM

,

LLM 应用

LLM 优化

RAG

检索

分享这篇文章

  • 在 Facebook 上分享
  • 在 LinkedIn 上分享
  • 在 X 上分享

Towards Data Science 是一个社区出版物。提交你的见解,以触达全球受众,并通过 TDS 作者支付计划获得报酬。

更新为你的实际提交 URL

为 TDS 写作

✦ 结束 CTA ✦

AI 可能会生成不准确的信息,请核实重要内容