RAG权限过滤:为什么要先于top-k

黎 浩然/ 16 10 月, 2026/ 大语言模型/LARGELANGUAGEMODEL/LLM, 教程/TUTORIAL, 计算机/COMPUTER/ 0 comments

RAG权限过滤不只是把最终答案里的几段文字删掉。若先在全部文档上取top-k,再删除无权访问的结果,可能只剩很少的可用证据;若先让模型看到受限内容,再要求它不引用,更没有形成可靠的数据边界。授权决定候选集合,相关性排序应在这个集合内进行。

文章目录
  1. 先定义用户有权检索的集合
  2. 先取前三名,会漏掉两个可用结果
  3. 用可执行的小例子核对语义
  4. 多取一些候选,不能保证解决问题
  5. 过滤条件必须来自可信路径
  6. 多租户与组权限,别把AND写成OR
  7. 结果缓存也要遵守当前权限
  8. 过滤前置,不代表可以省掉交付检查
  9. 检索评测的分母,也要按授权口径定义
  10. 来源与补发说明

下面用六条虚构记录解释过滤顺序,并把检索质量与访问控制分别验收。Read in English。

先定义用户有权检索的集合

设全集为D,可信授权逻辑对当前请求给出可检索集合A。目标不是top-k(D)再与A取交集,而是对A里的文档按相关性取top-k。前一种写法会被无权文档占用候选名额,两者一般不相等。

这是一个集合与排序的区别,不依赖某种向量数据库。Qdrant过滤文档提供基于payload条件组合的过滤机制;它能表达检索条件,却不能替应用判断当前请求真实拥有什么权限。

1
可信请求身份
确定当前可访问范围
2
授权条件
约束候选集合
3
集合内检索与排序
取所需top-k
4
结果复核后
交付生成与引用
原创权限检索流程:条件来自可信授权路径,不由问题文本或模型自由指定。

先取前三名,会漏掉两个可用结果

构造排名:无权记录x1得100分、x2得99分,可访问的a、b、c分别98、97、96分。先取全局前三名后过滤,只剩a;在授权集合中取前三名,则得到a、b、c。两种结果都没有把x1、x2交给用户,但证据覆盖明显不同。

这些分数只是虚构排序值,不是余弦相似度或真实检索成绩。例子说明过滤与截断顺序不交换,不能由“最后已经删掉越权结果”推断检索质量也与前置约束相同。

1
全局top-3
x1、x2、a
2
之后过滤
只剩a
3
先约束授权集合
a、b、c
4
集合内top-3
保留a、b、c
原创顺序对照:后过滤可减少返回数量;它不等价于在授权候选中取top-k。

用可执行的小例子核对语义

下列程序只在内存中处理虚构ID、范围标签与分数。trusted_scopes被明确假设为可信授权模块的输出,它不是身份认证实现,也不能直接用客户端传入的字段替代。

# Synthetic IDs and scores; no database, private text, or real ACL is used.
rows = [dict(id="x1",score=100,scope="other"),
        dict(id="x2",score=99,scope="other"),
        dict(id="a",score=98,scope="team-a"),
        dict(id="b",score=97,scope="team-a"),
        dict(id="c",score=96,scope="team-a"),
        dict(id="unknown",score=95)]

def topk(items,k):
    if not isinstance(k,int) or isinstance(k,bool) or k<0:
        raise ValueError("invalid k")
    return sorted(items,key=lambda x:(-x["score"],x["id"]))[:k]

def allowed(row,trusted_scopes):
    return row.get("scope") in trusted_scopes

def retrieve(rows,k,trusted_scopes):
    return topk([r for r in rows if allowed(r,trusted_scopes)],k)

scopes = frozenset(["team-a"])  # Assumed output of trusted authorization.
post = [r for r in topk(rows,3) if allowed(r,scopes)]
pre = retrieve(rows,3,scopes)
assert [r["id"] for r in post] == ["a"]
assert [r["id"] for r in pre] == ["a","b","c"]
assert retrieve(rows,3,frozenset()) == []
assert not allowed(rows[-1],scopes)
assert retrieve(rows,0,scopes) == []
assert len(retrieve(rows,10,scopes)) == 3
# Simulated revocation: the current scope set becomes empty.
assert retrieve(rows,3,frozenset()) == []
print("post-filter returns1; authorized-set top-k returns3; deny cases passed")

代码已执行,验证后过滤只得到1条、授权集合检索得到3条,以及空权限、缺少范围、k=0、可用结果不足和模拟撤权。没有连接数据库,也没有读取受限文本。这里是精确排序参考,不保证近似向量索引一定返回完全相同的集合。

多取一些候选,不能保证解决问题

先取更多全局候选再过滤,可以在某些数据分布下得到更多允许结果,但没有一个固定候选倍数能普遍保证补足k。只要高分无权记录足够多,有限候选窗口仍可能把所有允许文档挡在外面。

过采样也不能代替权限边界。若系统为排序先读取所有候选正文,之后才删除无权内容,敏感数据可能已经进入不应到达的组件。应明确过滤发生在哪个阶段、该阶段是否只处理被允许的元数据,以及什么时候读取正文。

前置约束也不等于某个近似索引必然返回最优结果;不同索引与选择率会影响搜索路径。先用精确参考证明业务语义,再在同样授权条件下评测实际实现,才能知道数量不足来自过滤顺序、近似搜索还是语料本身。

过滤条件必须来自可信路径

用户可以描述想找什么,但不能因此指定自己属于哪个租户或组。服务端应从已核实的身份与授权状态派生访问条件,并防止普通查询参数或LLM工具参数把强制条件覆盖。

OWASP授权指南强调默认拒绝与逐请求检查。落实到RAG,就是没有明确授权时不能把“缺字段”解释为公开,授权查询失败时也不能退回无过滤检索。这是应用需要实现的策略,不是写了一个数据库filter就自动获得的保证。

内容相关性与授权条件应分开管理。模型可以帮助改写查询词,但不应通过改写改变强制访问范围;日志也应能在不暴露正文的前提下记录所用策略版本与结果计数,方便复查。

多租户与组权限,别把AND写成OR

若访问需要同时满足租户匹配与组成员资格,两者是交集关系。把它们写成“任意一个满足”,会扩大候选集合。数据库条件组合的语义要用最小记录集验证,不能仅凭配置看起来包含两个条件就认为正确。

对于数组或嵌套payload,还要确认条件是否要求同一对象同时匹配。这里不提供某个业务的通用ACL表达式,因为授权规则本身取决于产品需求。可以先写出明确的许可与拒绝案例,再对照数据库查询实际返回的ID。

结果缓存也要遵守当前权限

相同查询文本不代表相同可见文档。若不同身份共享一个只按查询字符串建立的结果缓存,可能把某次允许的证据复用到另一种访问范围。缓存键至少应覆盖使结果不同的授权语义,但怎样表达要结合现有策略。

还要考虑撤权与文档更新。仅把组名写进键,不一定覆盖组成员变化或策略变化;缓存失效、策略版本与交付前检查需要配合。本文没有修改任何真实缓存,也不把示例中的frozenset当作完整生产授权方案。

过滤前置,不代表可以省掉交付检查

检索与读取正文可能跨越时间边界。候选生成后文档被撤权,或payload中的权限信息滞后,都可能让早先判断失效。对高要求场景,应在把正文交付给生成模型或用户之前,按当前可信状态核对可访问性。

如果复核排除了候选,不要为了补足k悄悄放宽权限。可以继续在同一授权范围内补取,或准确报告证据不足。返回较少结果是允许出现的业务状态,越界补齐不是检索优化。

一旦无权内容已经进入生成上下文,最后不显示引用并不等于它没有参与答案形成。因此数据边界应覆盖正文读取、模型输入、缓存和日志,而不仅是前端展示。

检索评测的分母,也要按授权口径定义

对某个身份的请求,相关文档集合应先限定在该身份有权访问的范围。把无权相关文档也算进召回分母,会惩罚一个正确拒绝越权内容的系统,诱导错误优化方向。站内RAG召回率文章讨论了分母定义,这里补上权限条件。

质量验收与访问验收分开记录:前者看授权范围内是否找到所需证据,后者检查是否有任何不允许的文档进入后续链路。别用“总分更高”抵消一次越权结果,也别把没有越权视为召回一定足够。

可用虚构数据覆盖跨租户、空权限、缺少标签、策略失败、撤权、缓存复用和结果不足,再在获授权的测试环境做集成验证。若这个顺序反例与验收方法帮你发现一次过滤缺口,欢迎在文末自愿支持。

来源与补发说明

Qdrant过滤机制;OWASP授权指南。图、分数与代码为原创教学构造,无真实身份或文档数据。本文于10月16日补发,文章日期保留当天19:00。

支持

如果文中的解释或示例帮你解决了问题、节省了时间,欢迎自愿打赏支持本站。

微信支持二维码,点击查看大图
微信
支付宝支持二维码,点击查看大图
支付宝
Buy Me a Coffee,点击支持本站
Buy Me a Coffee

二维码可点击放大。更多支持方式见支持页面。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*