有一种故障模式几乎出现在每一个企业 RAG 部署中:一名员工向内部 AI 助手询问薪酬政策相关问题。系统返回了正确、具体的信息——却是从一份该员工本无权查看的 HR 文档中提取的。由于没有人监控检索层,这件事不会立刻让任何人丢掉工作。但那份机密文档已被索引,用户的查询在语义上命中了它,模型忠实地报告了它所找到的内容。
这个错误并不罕见,它是将公共网络 RAG 模式原封不动地应用于私有组织知识却不做架构适配的默认结果。公共网络 RAG 没有访问控制层,因为公共网络内容本身就没有访问限制。而企业数据有——这一约束从根本上改变了整个系统的设计。
打破一切的核心假设
公共网络 RAG 基于一个简单模型构建:索引所有内容,按语义相似度检索,根据所找到的内容生成回答。这之所以有效,是因为检索语料库是同质的(来自开放网络的文本),且不受访问限制——任何人都可以读取任何人检索到的内容。
企业知识打破了这两个前提。一个典型组织拥有 Confluence 文档、Slack 消息历史、Google Drive 文件、CRM 记录、组织架构图、会议纪要和工程 Wiki——每一类都有其自身的权限模型。支持工程师可以查看客户工单,但无法查看薪资等级。初级分析师可以查看公开看板,但无法查看董事会材料。这些数据分散在数十个系统中,访问策略相互交叉且并不一致。
当你将所有这些数据索引到向量存储中并按相似度查询时,你就将这些权限模型合并到了一个统一的检索界面上。若没有额外的控制手段,检索层就会成为权限提升的攻击面:一个访问权限有限的用户可以通过查询触发对其无权直接打开的文档的检索,而模型将把这些文档合成进回答,仿佛它们完全合规。
最直接的修复方案——"在应用代码中过滤结果再展示给用户"——是大多数团队止步的地方。这远远不够。模型已经处理过该文档了。如果应用层只在生成之后才捕获检索结果,损害已经造成。而且应用层过滤器很容易通过提示注入、API 误用或简单的代码漏洞绕过。
真正的修复是在管道的更早阶段——向量层本身——执行访问控制。
为什么 BM25 在内部实体查询上胜过嵌入
在深入探讨安全问题之前,有一个在安全问题之前就会出现的检索质量问题:企业知识包含的查询类型与公共网络 RAG 的优化目标截然不同。
网络查询往往是概念性的:"退款政策是什么?"或"如何配置 SSL 证书?"这类查询受益于语义嵌入,因为用户的词汇可能与文档的词汇不同,而嵌入模型会将两者压缩到共同的语义空间中。
内部查询则高度依赖实体:"找 EMEA 团队的 Q3 销售管道评审"、"支付服务的值班轮换是什么"、"查一下 Sarah Kim 上季度的项目分配情况"。这些本质上是对专有名词、标识符和精确短语的查找,而在公共互联网文本上训练的嵌入模型对此处理能力很差。内部术语、产品代码、团队名称和组织专属缩写在公共训练数据中统计上极为罕见,在嵌入空间中代表性不足。
对于这类查询,BM25(最佳匹配 25)——一种基于词频和逆文档频率的词法排序算法——持续优于密集嵌入。原因很简单:BM25 奖励精确词项匹配,而当有人按名称查找特定文档时,专有名词的精确匹配几乎总是正确答案。
生产级企业搜索系统使用混合检索:BM25 和密集嵌入并行运行,其排序结果列表通过倒数排名融合或加权评分进行合并。这既能处理实体查询(BM25 占优),也能处理概念性查询(嵌入占优)。仅依赖语义搜索来处理内部知识库的团队,放弃了只需添加一个 BM25 列就能免费获得的大量检索质量提升。
访问控制必须在检索层执行
RAG 管道中有两个可以执行访问控制的位置。大多数团队选择应用层,因为那里已有现成的授权逻辑。但安全边界应该是向量层。
应用层方式大致如下:无过滤地查询向量存储,检索出 top-k 文档,再丢弃用户无权查看的结果。这有两个失效模式:其一,效率低下——你在获取文档后才丢弃它们,既浪费计算资源,又消耗上下文窗口空间;其二,防御薄弱——如果应用逻辑存在漏洞、做出错误假设或被提示注入攻击绕过,文档依然会流通。
向量层方式将访问控制下推至检索查询本身。索引时,每个文档块被打上代表其访问策略的元数据标签——哪些用户或角色可以检索它。查询时,检索查询包含一个过滤条件,将结果限制在发请求用户有权查看的块上。向量存储从一开始就不返回未授权文档;模型从不处理它。
现代向量数据库以不同方式支持此模式。PostgreSQL 配合 pgvector 原生实现行级安全(RLS)——你会应用于 SQL 表的 RLS 策略同样透明地应用于向量相似度查询。最近的一项性能改进(pgvector 0.8.0,针对过滤查询的迭代索引扫描)在权限约束检索上实现了高达 9 倍的速度提升。Weaviate、Pinecone 和 Qdrant 均支持基于命名空间的隔离和按集合的 RBAC。Milvus 通过位图索引添加了行级 RBAC。
对于希望在一处兼顾 ACID 事务、现有认证基础设施和向量搜索的团队,pgvector 配合 RLS 现在已是中等规模部署(最多数亿向量)的可信生产方案。对于更大规模或多租户 SaaS 模式,具有命名空间分区的专用向量存储仍然是更简洁的架构。
一个行之有效的实现模式:将每个用户可访问的文档列表作为其会话上下文的一部分存储(在登录时从身份提供商派生),将该列表作为过滤谓词传递给每次向量查询,绝不依赖检索后过滤作为主要安全控制。
数据新鲜度问题比表面看起来更难 企业知识是活的。员工离职,组织架构更新。政策变更。客户数据演进。一个按月索引组织知识的 RAG 系统会定期提供基于过时上下文的答案——轻则略有偏差(过时的项目状态),重则主动有害(已废弃的安全策略、已离职员工的访问权限仍出现在检索中)。
简单的解决方案是每晚重新索引所有内容。这比什么都不做要好,但引入了 24 小时的时效性窗口,且对大型组织难以扩展。生产解决方案是基于事件驱动更新的增量索引。
当源系统支持时,基于 Webhook 的同步是正确方案:发送了一条 Slack 消息、编辑了一个 Confluence 页面、更新了一个 Google 文档——这些事件触发受影响文档的近实时重新索引。问题在于,并非每个源系统都能可靠地支持 Webhook。传统 Wiki、内部工具和本地部署软件通常需要基于轮询的同步——后台任务定期检查变更。大多数生产系统将两者结合:Webhook 保证主要新鲜度,轮询作为回退。
删除和编辑的文档是微妙的边界情况。当文档在源头被删除时,向量存储中对应的向量应被标记为非活跃或移除。在同步周期内,软删除优于硬删除,因为正在进行的查询可能引用了源头已不存在的向量。重要的是,被删除的文档应快速停止被检索——一份因包含错误信息而被删除的文档不应无限期地留在检索池中。
数据新鲜度问题使企业 RAG 更像是一个数据工程挑战,而非 AI 挑战。嵌入模型和语言模型在很大程度上是现成的。真正困难的工作是构建一个可靠的同步管道,保持检索语料库的新鲜,正确处理文档生命周期,并在用户访问级别变更时执行权限更新。
生产架构长什么样 将以上内容整合起来,一个生产级企业知识系统有几个大多数团队低估了的独立组件:
富元数据索引 :每个文档块除内容之外还携带元数据——来源系统、文档类型、作者、部门、创建和修改时间戳、敏感度分类,以及访问策略(可检索它的用户 ID 或角色列表)。这些元数据既支持权限过滤,也支持时间过滤。
带权限感知过滤的混合搜索 :每次查询同时运行 BM25 和密集嵌入搜索。两条路径应用相同的访问控制过滤。结果经过融合,仅返回"语义相关"与"用户有权查看"的交集。
事件驱动的同步层 :一个监听源系统(Slack、Confluence、Google Drive、Jira、CRM)变更并增量更新向量索引的管道,包括处理文档删除和权限变更。
针对真实内部查询的评测套件 :RAGAS 等公开基准不衡量内部知识的关键指标:权限边界是否得到执行?当按名称查询特定员工或项目时,BM25 能否找到正确结果?一个专用的内部评测集——包含 150 个以上标注查询,涵盖实体查询和概念性查询——对于在索引变更时捕获回退至关重要。
构建企业 RAG 时的诱惑是聚焦于 LLM,将检索视为已解决的问题。事实并非如此。检索层是安全、新鲜度和质量故障的共同起源。做好检索意味着将向量存储视为具备一流安全属性的数据库——而不是一个你查询后祈祷结果正确的搜索引擎。
更深层的转变 大多数企业 AI 项目以这个问题开始:"我们能用内部知识做什么?"更好的问题是:"谁被允许知道什么,我们如何确保检索系统遵守这一点?"先回答第二个问题,会迫使你从一开始就将权限感知设计进架构中,而不是在机密文档泄露后再亡羊补牢。
做好企业知识注入是缓慢、谨慎且不光鲜的。索引必须保持新鲜。访问策略必须在检索层执行。必须根据查询类型选择正确的搜索算法,而不是默认假设语义相似度搜索就是答案。这些都不会出现在演示中。但它们共同决定了这个系统是否能安全地在生产环境中运行。
会员专享
余下内容仅对会员开放。 会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
— 完整文章,包含未公开存档的部分 — 可落地的工作框架,附带权衡与决策依据 — 新文章抢先看,先于公开发布 登录以继续阅读→ 随时取消 · 一次订阅,畅读全部