RAG 中元数据过滤有什么作用?
简化版
元数据过滤把时间、产品、语言、文档类型、租户和权限等硬约束从语义相似度中分离,避免“内容很像但版本/范围错误”的结果。过滤应由可信结构化字段驱动,并在 ANN 前尽量缩小候选;过滤太严会零召回,所以要区分不可放宽的权限条件与可回退的业务偏好。
详细版
查询先抽取 filter AST,例如 product=mobile AND valid_at<=today AND tenant=T1,枚举值与 schema 校验后传向量库。pre-filter 安全且减少搜索空间,但在高选择性下 ANN 图可能召回不足;post-filter 实现简单,却可能取回 Top-k 后全被删。可采用分区+pre-filter+扩大候选复查的组合。
元数据来自入库管线,要有类型、词表、版本与缺失语义;日期不能存模糊字符串,权限不能由模型自由生成。评测按过滤选择性测 Recall、零结果率、P95 和错误范围命中;记录最终 filter、来源和放宽步骤,权限/租户永不自动放宽。
自然查询 -> 语义query + filter AST -> 权限硬过滤 -> ANN -> 业务过滤/复查 -> Rerank
完整版教学
一、语义相关不代表业务可用
向量会把“iOS 退款政策”和“Android 退款政策”拉近,但用户只问 iOS;旧版与新版政策语义也几乎相同。仅靠 embedding 很难严格执行产品、时间和权限约束。元数据过滤提供确定性边界。
它还减少候选噪声,让 reranker 不必在大量错误版本中辨别。但字段错误会造成系统性漏召回,所以元数据质量与文本质量同等重要。
二、哪些字段适合过滤
适合过滤的是可枚举、可比较、对有效范围有明确含义的字段:tenant、ACL、安全域、语言、产品、地区、文档类型、valid_from/to、状态和版本。模糊主题更适合语义检索,不必全部变标签。
| 字段 | 类型 | 例子 |
|---|---|---|
| tenant_id | keyword | t_17 |
| product | enum | mobile |
| valid_from | datetime | 2026-01-01T00:00Z |
| language | enum | zh-CN |
| acl_groups | keyword[] | finance-read |
| authority | ordinal | official/internal/forum |
记忆钩子:Embedding 负责“像不像”,metadata 负责“是不是这个范围”;软相关与硬边界不要混为一个分数。
三、过滤表达式如何安全生成
可让模型抽取候选槽位,但只能从服务端 schema 的字段与枚举中选择,随后程序验证。权限 filter 完全由认证上下文产生,不接受用户或模型修改。使用 AST/参数化 API,不拼接查询字符串,避免注入。
{"and":[
{"eq":{"product":"mobile"}},
{"lte":{"valid_from":"2026-09-18"}},
{"eq":{"tenant_id":"t_17"}}
]}
相对日期“去年”先按用户时区解析成范围,并把解析结果记录在 trace 中。
四、Pre-filter 与 Post-filter 的差别
Pre-filter 先限定集合再做 ANN,理论上不会把预算浪费在无效文档;但某些 HNSW 实现过滤后图连接不足,Recall 降低。Post-filter 先取全局 Top-N 再筛,若满足条件文档稀少,可能一个不剩,还可能让无权内容进入中间层。
安全字段必须在上下文前严格过滤,可通过租户分区、filter-aware ANN 或大候选加最终 ACL 复查实现。业务偏好可 post-filter 或 rerank。选择性 0.1% 的过滤应专门压测,而非只测常见 50%。
五、过滤过严怎样回退
零结果可能是确实无文档,也可能是用户产品槽位解析错或元数据缺失。定义分级回退:先保留所有硬权限,放宽可选版本/语言偏好,再询问用户;绝不能去掉 tenant 或 ACL 以“提高召回”。每步放宽要在答案中可解释。
例如 product=mobile, region=上海, type=FAQ 零结果,可先去掉 type,仍保留 product/region;如果 region 是用户明确要求也不该自动删。字段分 hard/soft,回退策略由业务定义,不由模型临场决定。
六、元数据质量怎样治理
入库时从源系统读取权威字段,模型抽取的标签要标置信度和 extractor_version。使用枚举词表和类型校验,处理 null/unknown,不把缺失当任意值。文档分块后继承文档级字段,局部章节字段可覆盖但要有来源。
监控每字段缺失率、值分布和版本变化。若产品字段从 ios 改为 iOS 而查询只用旧枚举,会产生静默零召回。schema 迁移时双写兼容并回填历史索引。
七、如何评测过滤收益
构造同主题不同产品/时间/租户的 hard negatives,测范围准确与 evidence Recall。扫描过滤选择性,记录候选数、ANN visited nodes、P95 和零结果率。比较无过滤、pre-filter、post-filter 与混合方案。
假设无过滤 Recall@10 90% 但错误版本率 20%;加时间产品过滤后 Recall 88%、错误版本 1%,端到端答案更可靠。若 Recall 跌到 50%,要查索引过滤实现和字段缺失,而不是直接取消边界。
八、常见误区与追问
- 误区:向量模型能自动理解所有时间和权限条件。 语义相似不能提供严格集合约束。
- 误区:过滤越多越精准。 错误或缺失字段会造成严重漏召回。
- 误区:零结果时逐步移除所有过滤。 权限与租户硬约束永不放宽。
- 追问:模型能生成过滤表达式吗? 可抽取业务槽位,但字段/枚举受 schema 限制,权限由后端生成。
- 追问:Pre-filter 一定更快吗? 高选择性可能破坏 ANN 导航,需要按具体索引实现测试。
- 追问:文档没有元数据怎么办? 标记 unknown、补充治理或走受控回退,不应默认匹配所有范围。
九、加强记忆
元数据过滤可记成“语义找相似、字段守边界”。用可信 schema 表达产品、时间和权限,硬条件由后端产生并永不放宽;在 pre/post-filter 间按索引能力权衡,零结果按软条件有序回退。最后用同主题错版本 hard negatives 验证,才能既提高精确性又不把真证据过滤掉。
项目实战落地
项目里怎么做的
《AI Agentic RAG高级企业知识库平台》的向量表 knowledge_chunk_vector 里冗余了 kb_id、document_id、chunk_type 三列,过滤条件和向量排序写在同一条 SQL 里:
# 按知识库、文档、片段类型过滤,和 <=> 向量距离排序在同一条 SQL 里完成
conditions = ["kb_id = any(%(kb_ids)s)"]
if document_ids:
conditions.append("document_id = any(%(document_ids)s)")
if chunk_types:
# 父子分块的库只检索子块,父块留给命中之后回填上下文
conditions.append("chunk_type = any(%(chunk_types)s)")
BM25 那一路从 MySQL 读候选片段时,也用同样的条件先收窄。检索测试页上,知识库可以多选,选完后可以再限定只在某几篇文档里检索,比如库里同时有 2024 版和 2026 版制度时,把范围缩到新版那一篇。
为什么这样取舍
- 在向量库内部过滤。 先全表算距离、取完 TopK 再回 MySQL 筛,不仅慢,还可能让 TopK 被无关文档占满,筛完一条不剩。
- 冗余列换过滤能力。 向量表多存三列,换来过滤不用跨库联表。
面试官还会追问
- 查询向量作为
<=>运算符的操作数,为什么要包一层Vector再传?直接传 Python 列表会报什么错? - 知识库多选时,「限定文档」的下拉选项是怎么联动出来的?
学完《AI Agentic RAG高级企业知识库平台》,上面这些追问你都会迎刃而解。