RAG 文档权限变更如何同步到索引?
简化版
权限变更必须作为独立于内容更新的事件同步到索引,并以源系统 ACL 为权威。每个 chunk 继承 document_id、tenant 和权限版本;查询先做服务端授权过滤,返回前再用实时权限复查。撤权优先 fail closed,立即使相关缓存和旧索引版本失效,不能等待下一次全文重建。
详细版
通过 CDC/Webhook/消息队列接收 grant/revoke/move/delete,使用 document_id+acl_version 幂等更新权限元数据。若向量库过滤能力不足,可先按租户/安全域物理分区,再对候选做 ACL post-filter,但绝不能只在生成后遮掉答案。索引双写迁移时,新旧索引都必须应用同一权限事件流。
监控权限事件 lag、失败队列、源—索引版本差、撤权后可检索时间和跨租户命中。对高风险 revoke,先写 deny/tombstone 再异步清理向量;缓存 key 带权限指纹,并按 document tag 主动失效。定期从源系统全量对账修复漏事件。
源ACL变更 -> 版本化事件 -> deny/tombstone先行 -> 索引元数据更新 -> 缓存失效 -> 对账
用户查询 -> 身份解析 -> pre-filter -> ANN -> 实时ACL复查 -> 组装上下文
完整版教学
一、内容与权限有两个生命周期
文档正文没变,成员离职或文件夹移动也会改变可见范围。如果索引刷新只监听内容哈希,权限变化不会触发,旧向量继续被召回。权限因此必须拥有独立版本和事件。
源系统是唯一授权事实,向量库里的 ACL 只是加速副本。出现分歧时应拒绝或回源验证,不能让搜索索引反过来决定权限。
二、权限元数据怎样建模
每个 chunk 至少带 tenant_id、document_id、acl_version 和安全域;具体 ACL 可存允许的用户/组,或存 policy ID 由授权服务解释。大量用户列表直接复制到每个 chunk 会膨胀,组/属性策略更紧凑但查询复杂。
| 模型 | 优点 | 风险 |
|---|---|---|
| chunk 内 allowed_ids | 过滤直接 | 列表大、更新放大 |
| tenant/空间分区 | 隔离强 | 分区多、运维复杂 |
| policy_id 回源 | 规则集中 | 每次授权延迟 |
| 安全域+候选复查 | 折中 | 必须保证 post-check |
不论模型,document_id 必须稳定,才能撤销全部派生 chunk 和缓存。
记忆钩子:向量是文档的派生物,权限也是;派生数据可以延迟,授权决定不能比源系统更宽。
三、事件如何做到幂等有序
权限事件包含 document_id、acl_version、operation、timestamp 和来源。消费者只应用比当前版本新的事件,重复消息无害,乱序旧消息不会把已撤权限重新加回。删除用 tombstone 保留版本,防迟到的 grant 复活文档。
if event.version > stored.version:
apply(event)
else:
ignore_as_duplicate_or_stale
跨区域系统无法保证绝对顺序时,可用单文档单调版本或 source sequence。死信队列必须告警并可重放。
四、为什么撤权要优先
授权延迟会让用户暂时看不到新文档,影响可用性;撤权延迟会泄露数据,影响安全。两者 SLA 应不同。收到 revoke 后立即写 deny overlay 或 tombstone,即使向量物理删除尚未完成,查询路径也先阻断。
例如索引更新通常 5 分钟,安全要求撤权 30 秒内生效。deny store 可在秒级更新,ANN 候选返回后先查 deny;后台慢慢删除 chunk。监控 revocation_exposure_window,严重超时触发搜索关闭或回源强校验。
五、Pre-filter 与 Post-filter 如何配合
Pre-filter 在 ANN 前缩小可见集合,效率和安全都好,但有些索引在复杂 ACL 下召回下降。Post-filter 先召回再删除无权结果,若 Top-k 都被删会返回空,扩大候选又增加成本。生产通常粗粒度 pre-filter 加实时精确 post-check。
任何无权候选都不能进入 LLM 上下文、日志或缓存。即使最终回答没复述,发送给外部模型本身也可能构成泄露。若过滤后不足 k,应在已授权空间继续检索,而不是补无权文档。
六、缓存和双索引迁移
检索、上下文、语义答案缓存都可能保存旧权限结果。缓存项记录 document tags 和 permission fingerprint,事件到来主动失效;命中后仍复查 ACL。TTL 只是兜底,不能满足紧急撤权。
更换 embedding 时常并行维护旧新索引。权限事件必须双写,且切流量前比较两边 acl_version。若新索引内容回填需要数天,不能只在创建时复制 ACL 后停止更新,否则切换时权限已过期。
七、如何验证与对账
定期抽取源文档与索引对比权限版本,扫描孤儿 chunk、已删除文档和跨租户元数据。构造 canary 文档,执行 grant→可检索、revoke→不可检索的端到端测试,并测传播延迟分位数。
假设 10 万次权限事件中 P99 lag 20秒、最大 3分钟,但安全 SLA 30秒,最大值仍是事故。报告失败事件数、死信年龄和 revoke 超时,而不只报平均延迟。红队还应尝试缓存命中、旧会话和引用链接绕过。
八、常见误区与追问
- 误区:内容不变就不需要更新索引。 ACL 有独立生命周期,必须单独同步。
- 误区:在答案展示前隐藏无权限引用就安全。 文档可能已进入模型上下文,泄露已经发生。
- 误区:设置短 TTL 足以处理撤权。 紧急撤权需要事件驱动 deny 和主动失效。
- 追问:过滤后召回不足怎么办? 在授权集合扩大 ANN 候选或重查,不得补无权内容。
- 追问:事件丢失如何发现? 周期全量对账、版本差监控和 canary 授权测试。
- 追问:权限列表太大怎么办? 用组/属性策略、租户分区或 policy_id,并保留实时复查。
九、加强记忆
权限同步可记成“源权威、独立事件、撤权先挡、查询双检、缓存同失效、周期做对账”。正文不变也要更新 ACL,事件用单调版本幂等消费;粗粒度预过滤后再实时授权,任何无权文本都不进上下文。把撤权窗口当安全 SLO,才能防止索引最终一致性变成数据泄露。