跳转到内容

RAG 元数据过滤,先排除不适用的资料

用技术文档问答说明产品、版本、语言和权限怎样限制检索范围,减少错误资料,同时避免把正确答案一起过滤掉。

RAG 会先找资料,再让模型据此回答。元数据是资料附带的产品、版本、语言和权限等字段。把已知条件加入检索请求,先限定这次问题适用的资料范围,再在范围内比较相关性,可以减少旧版文档、其他产品和草稿混进答案。

过滤条件越多不一定越好。标签写错、版本不明,都可能把正确资料排除。权限由服务端强制限制;产品和版本采用已确认的信息;模型猜出的主题标签,优先用于排序,不直接拿来删候选。

搜“自动重连”,为什么会拿到错误资料

Section titled “搜“自动重连”,为什么会拿到错误资料”

假设一个设备接入平台的知识库同时保存多个 SDK 的文档。用户问:

网关产品的 Java SDK 3.x,断线后怎样自动重连?

下面六段资料都在这个用户有权访问的范围内。顺序是教学示意,不是某个模型的实测排名。

候选资料这次该怎样处理
① Java SDK 2.x 重连说明版本不同,排除
② Python SDK 3.x 重连说明SDK 语言不同,排除
③ Java SDK 3.x 重连功能草稿尚未发布,排除
④ Java SDK 3.x 正式重连说明保留,直接回答问题
⑤ 同产品通用的重试协议说明保留,补充适用原则
⑥ Java SDK 3.x 日志配置范围符合,再判断与问题是否相关

它们都可能出现“连接”“断线”“重试”。仅凭文字或向量相似度,不能强制保证版本和适用对象正确。向量检索负责寻找含义接近的内容,元数据过滤负责表达这些明确条件。

过滤掉前三段以后,④⑤⑥仍要继续比较相关性。必要时再做重排,也就是对候选资料重新排序,优先把能够回答问题的④⑤交给模型。范围符合,只说明资料可以参与检索,不说明它已经回答了问题。

条件从哪里来,哪些不能让模型猜

Section titled “条件从哪里来,哪些不能让模型猜”

元数据在文档入库时就要准备好。切成多个片段后,每个片段都要继承适用范围和权限信息,不能只给原文件打标签。

以④的一个片段为例,字段可以这样组织。名称是本文的设计示例。

{
"chunk_id": "java3-reconnect-01",
"product_id": "gateway",
"sdk_language": "java",
"major_versions": [3],
"all_versions": false,
"status": "published",
"topic_tags": ["连接管理"]
}

这里的 [3] 表示资料适用于所声明的 3.x 范围。只适用于 3.2 以后的内容,需要更细的版本规则,不能也粗略标成所有 3.x。文档更新时间同样不能代替软件适用版本。

本次问题已经给出产品、语言和主版本,可以据此过滤。用户只说“断线怎么办”,就不能擅自补成 Java 3.x;先从项目配置确认,仍不明确时再问用户,或暂不加这项业务条件。

权限则从认证和授权结果取得。客户端或模型传来的租户 ID、知识库列表,都不能直接决定访问范围。授权范围为空,就返回没有可访问资料,不能改成全库搜索。服务端授权原则

主题标签要更谨慎。④写的是“连接管理”,问题用的是“自动重连”。如果模型生成 topic_tags = 自动重连 并强制过滤,正确文档反而会消失。这类不确定标签可以帮助排序,别轻易变成排除条件。

⑤这样的通用资料也要明确标记。例如同产品通用协议可以用 sdk_language = allall_versions = true。这里的“通用”必须经过确认;缺字段代表不知道,不能自动当成通用。

这种做法沿用了 Christopher Manning 等人在《信息检索导论》中介绍的字段检索思路,用可明确表达的字段限定范围,再检索文本。应用到 RAG 时,还要把适用范围和权限一并考虑。字段检索

先看一个容易漏资料的写法。

从可访问资料中取最相似的 3 段
→ 得到①②③
→ 再按语言、版本和发布状态过滤
→ 一段都没剩下

正确资料④⑤在后面,但这次已经没有参与后续处理。应用如果直接回答“知识库里没有”,就把“没有进入前 3”误当成了“没有资料”。

应当把过滤条件交给检索引擎,让它查找符合条件的候选,尽量返回所需数量;不要只取全局前几条再删。随后按相关性选出依据,保留文档版本和出处,交给模型回答。增加候选数量能缓解后过滤的遗漏,但不能保证把遗漏全部补回来。过滤时机的影响

采用关键词与向量混合检索时,两路都要带上相同的权限约束。不能一条路过滤,另一条路把越权资料重新带回来。重新排序、补读上下文和获取引用原文,也不能绕开授权。混合检索的过滤规则

用 PostgreSQL 和 pgvector 表达这些条件

下面是查询示意,假设片段表已有对应字段,并以知识库为授权单位。有文档级权限时,还要加入那一层约束。

SELECT chunk_id, content
FROM chunks
WHERE tenant_id = $1
AND kb_id = ANY($2::uuid[])
AND product_id = $3
AND (sdk_language = $4 OR sdk_language = 'all')
AND (all_versions = true OR $5::int = ANY(major_versions))
AND status = 'published'
ORDER BY embedding <=> $6::vector
LIMIT $7;

$1$2 由服务端认证和授权结果生成。$3$5 是已确认的产品、SDK 语言和主版本;$6 是问题向量,$7 是候选数量。参数通过数据库驱动绑定,不拼接模型生成的 SQL。

<=> 计算余弦距离,越小越接近。问题向量和文档向量必须使用兼容的编码配置。这段查询只处理同产品内的通用资料,不包含跨产品共享文档。

SQL 中写了 WHERE,不代表数据库必然先过滤再扫描向量索引。 pgvector 使用近似索引时,可能先扫描候选,再应用条件,因此仍可能返回不足数量。近似检索是用部分召回换取速度,不能从 SQL 的书写顺序推断实际执行过程。

过滤后资料很少时,可以先评估普通过滤索引加精确检索。需要近似索引时,再评估扩大搜索范围或使用迭代扫描。迭代扫描会继续寻找候选,但有扫描上限;返回结果按距离排序,也不代表找全了最相关资料。pgvector 过滤与迭代扫描

结果变少以后,怎么判断有没有变好

Section titled “结果变少以后,怎么判断有没有变好”

不能只数删掉了几段。假设这个问题确实需要④的操作说明和⑤的通用规则,过滤后只剩④,虽然每段都相关,依据仍然少了一半。

信息检索通常同时看两个指标。查准率是返回内容里有多少相关,召回率是需要的相关内容找回了多少。过滤的目标是减少不适用资料,同时尽量保住真正需要的依据。查准率与召回率

固定一组问题和人工确认的正确资料,用相同检索模型、候选数量,对比增加业务过滤前后。两组始终保留相同权限检查,再看错误版本是否减少、正确资料是否漏掉,以及最终回答引用了什么。

评测集要故意放进几个难例:用户没说版本、资料缺字段、通用资料没有具体版本标签,还有正确文档的主题名称与问题用词不同。这些案例能发现过度过滤,单测条件完整的简单问题不够。

查不到时,先查是哪项条件排除了资料。模型推断的主题条件可以撤掉重试;用户已明确的版本不能悄悄换成别的版本,权限始终不能放宽。仍然缺依据,就说明缺少什么,不能拿旧版内容拼一个看似完整的答案。

如果过滤后仍给模型同样数量、同样长度的片段,token 费用未必下降;向量索引带过滤也未必更快。先验证错误资料确实减少,再用实际请求测费用和延迟。

入库和更新时还要守住什么

产品、版本和发布状态优先来自文档管理系统或经确认的配置。模型可以辅助提取,但不能把未经核实的猜测直接当成访问权限或适用事实。

同一文件内有多个版本的对照章节时,要按章节继承或覆盖字段,不能把整个文件统一贴成最新版。版本范围按项目采用的版本规则比较,避免把字符串的字典序当成版本先后。

权限调整、文档撤回和新旧版本切换,要同步影响所有片段。检索结果缓存也必须考虑授权范围、过滤条件和资料版本,不能在条件变化后继续复用旧候选。拿到候选后如果还要补读父文档,补读也要检查适用范围与权限。

更完整的入库过程见企业内部知识库的 RAG 落地记录。这篇的范围只到用元数据减少不适用资料,不要求为此更换整个 RAG 框架。