DC娱乐网

你的 RAG 从一开始就做错了

原文:https://x.com/akshay_pachaar/status/2052743644411765230有一

原文:https://x.com/akshay_pachaar/status/2052743644411765230

有一种新方案能做到:

语料库体积缩小 40 倍。每次查询的 token 消耗减少 3 倍。向量检索相关性提升 2.3 倍。

而且它不改你的检索算法、不换你的重排序器、不动你的嵌入模型。它修的是一个几乎没人审视过的上游问题。

每个 RAG(检索增强生成)管线都始于同一个假设:一段文本块(chunk)就是该被嵌入的知识单元。

这个假设几乎从未被质疑过。

而它恰恰是人们在系统下游拼命修补的大多数检索失败的根源。

文本块为什么是个糟糕的单元

一个文本块在结构上是中性的容器。它对自己一无所知:

不知道它所承载的观点从哪开始、到哪结束不知道它来自文档的哪个版本不知道谁有权看到它

因为没有语义边界,切割器只管按 token 数量切到哪就算哪。于是你检索到的可能只是半张表格,或是一条没有论证的结论,或是一个被剥离了使其成立的上下文的断言。模型根本不知道缺了什么。

版本问题同样糟糕。大多数企业语料库中,同一份文档在 SharePoint、Confluence 和 Git 里有十几个几乎相同的版本。Top-K 检索能给你返回五份同一条段落的拷贝,当前版本和废弃版本混在一起。大模型把它们糅合成一个答案——说得头头是道,但全是错的。

文本块也不带任何元数据,所以访问控制无处可挂。角色过滤、版本状态、安全等级——这些全变成了硬挂在编排层上的逻辑,跟它们本该治理的内容完全脱节。

LangChain、LlamaIndex 和 Haystack 都坐在这套东西的上层。它们编排检索,不管你往向量库里塞了什么。而在文档解析器和向量库之间,大多数技术栈什么都没放。那个空白地带,正是三种问题叠加恶化的地方。

更好的单元:问答数据包

文本块失败是因为它在结构上无关。修复之道是让知识单元在结构上变得显式。

不要嵌入一段散文窗口,去嵌入一个断言:一个问题、它的经过验证的答案,以及作为类型化模式的治理字段。每个单元只承载一个事实,仅此而已。

你的查询本来就是问题。当索引里存的是问题的答案时,匹配就变成了结构层面的对齐,而不只是语义层面的相似。你不再指望"正确的那段文字浮到最上面",你是在把问题和它的答案直接对上。

Iternal Technologies 推出的预处理层 Blockify,用了一种叫 IdeaBlock 的结构来实现这个理念:一个问题、它的经过验证的答案,以及类型化的治理字段——安全等级、版本状态、来源——全部放在同一个对象上。它精确地镜像了用户实际查询 RAG 系统的方式:以问题的形式。

关键洞见:当你嵌入的是一个问答数据包而非一段文本窗口,你的嵌入向量代表的是单一原子化的断言,而不是碰巧包含了那个断言的一段叙事。

这在向量几何上有可量化的后果。在 Blockify 对 17 份文档、298 页内容的内部基准测试中,查询到最佳匹配块的平均余弦距离,IdeaBlock 为 0.1585,而传统文本块为 0.3624。检索距离缩小了 2.29 倍。

反直觉的发现:更少的数据,更高的准确率

大多数人会本能地认为缩小语料库会损害检索质量。

语义蒸馏的结果恰恰相反。

在 Blockify 的内部基准测试中,管线从源文档中产生了 2,042 个原始 IdeaBlock。经过 80-85% 相似度阈值下 3-5 轮的迭代去重:

2,042 个块坍缩为 1,200 个规范化 IdeaBlock总字数从 88,877 降到 44,537蒸馏后的数据集在向量准确率上比未蒸馏的高出 13.55%

冗余副本有害而无益的原因很简单:同一条段落的十五个近似副本会在嵌入空间的同一区域产生十五个互相竞争的向量。检索权重被分散到全部十五个上面,从而拉低规范化版本的匹配分数。把它们坍缩成一个规范化块,信号就变清晰了。

你的向量索引不是一块你该拼命塞满的硬盘——它是一块检索表面,冗余只会让它变差。

管线:从文档到 IdeaBlock

解决之道是一条在数据触达向量库之前就运行的预处理管线。Blockify 的处理分为七个阶段,每个阶段都有明确的输入和输出,因此失败可以被定位和复现。

阶段 1:范围界定——在任何文档被解析之前,先定义索引层级:组织 > 业务单元 > 产品 > 角色。这决定了哪些块会被标记到哪个访问层级,也塑造了后续去重的执行方式。

阶段 2:文档摄取——文档以 DOCX、PDF、PPT、PNG/JPG、Markdown 或 HTML 格式进入。解析器将内容交给大模型层,将原始文本块转化为草稿 IdeaBlock:一个关键问题、一段 2-3 句的经过验证的答案,以及类型化的治理字段。输入大小限定在 1,000 到 4,000 字符之间,实践中 2,000 是最佳平衡点。

阶段 3:分块与提取——上下文感知的切分意味着大模型在将文本块转化为草稿 IdeaBlock,而不是单纯按 token 数量切割。每个块的输出是一个问答对,而非一段散文窗口。

阶段 4:语义去重——检索表面在这一步被清理干净。块以 80-85% 的余弦相似度阈值聚类,经过 3 到 5 轮迭代。近似副本通过第二个专门调优的大模型合并为一个规范化块。最终得到的是一份每个索引向量都代表一个独特断言的数据集,而不是十五个几乎相同的副本在抢同一个检索位。

阶段 5:自动标记——每个块被赋予类型化元数据:安全等级(公开、内部、机密、绝密)、版本状态(当前、已废弃、草稿、已批准)、产品线、出口管制标志和隐私标签。由管线自动打标,不依赖文档作者。

阶段 6:人工验证——一份 2,000 到 3,000 个 IdeaBlock 的产品语料库分给 5 到 10 位领域专家(SME,Subject Matter Expert),每人每季度只需花 1-2 小时审阅自己那一部分。审的是附有源引用的结构化断言,而非原始文档。

阶段 7:导出——验证完成的块通过 API 推送到向量数据库,或以 JSON-L 格式导出。支持的向量库:Azure AI Search、Pinecone、Milvus、Vertex Matching Engine。支持的嵌入模型:OpenAI、Bedrock、Mistral、Jina,以及开源模型。无论选用哪种组合,这条管线都位于文档解析器和向量库之间。

应用层会发生什么变化

你嵌入什么单元,决定了应用层能做什么。

查询构造变得更简单:你的查询本来就是问题。当索引里存的是问题的答案,匹配就成了结构性的,而非概率性的。你不再需要调相似度阈值来弥补查询形态和文档形态之间的语义错位。

治理下沉到数据层:基于角色的访问控制、版本状态、安全等级——这些是每个块上的类型化字段,而非硬挂在编排层上的逻辑。销售工程师和法律审查员查询同一个索引,得到不同数据集——不是因为检索层做了过滤,而是块本身就携带了访问边界。

更新从单条记录传播:当一份规格说明变更时,你只需要更新一个 IdeaBlock。所有查询这个块的应用在下一次请求时就能拿到修正后的答案。而用传统文本块方式,同一个事实藏在数十份文档的数十个近似段落中。更新它意味着把它们全部找出来——在企业级规模下,这根本不是一个可解的问题。

这套架构改变的,不是你怎么查询。它改变的是你能对答案有几分信任。

底层原理

文本块是解析的便利工具,却成了检索的惯性假设。

它没有语义边界、没有版本上下文、没有访问状态。检索技术栈花了好几年修补这些错位:重排序器、混合搜索、阈值调优、提示工程——全都在真正的问题下游打转。

解决之道不是更好的检索算法,而是一个更好的“单元”。

RAG 技术栈正开始在解析和向量化之间长出一层蒸馏层,就像当年的 Web 技术栈在源站和浏览器之间长出了一层 CDN。你可以自己用聚类、大模型摘要和模式约束来搭建,也可以用 Blockify 这样专门为此构建的工具。

无论选哪条路,核心结论不变:"文本块即单元"这个假设才是 bug,在数据层修好它,收益远超任何检索调优。