Grounded Access
面向企业知识库的权限感知检索参考系统。它把授权编译进检索查询本身,并要求每一次检索改动都在公开评测上拿出证据。
录自 ./scripts/demo-queries 和 ./scripts/benchmark:查询输出来自一次本地运行,表格来自仓库里已发布的报告。以 # 开头的行是注释。
它想解决什么
多数 RAG 项目的做法是:先从索引取回 top-k,再在应用代码里把用户无权查看的结果剔除。这带来两个问题:
- 泄漏面:未授权的内容已经进入应用内存,接下来会流向重排模型、prompt、日志和 trace,任何一处疏忽都是一次泄漏;
- 召回损失:被剔除的正是索引已经选中的 top-k,用户看到的结果因此变少,而且这种损失在指标上不可见。
Grounded Access 的主张是:授权属于检索环节。身份被编译成一个 SQL 谓词,关键词通道与向量通道都带着它,未授权的行从不离开数据库。
第二个主张是:检索质量要被测量。项目自带一份带人工标注的虚构语料,每次运行都记录数据集版本、commit、策略与运行平台,报告由提交在仓库中的结果文件生成。
当前能做什么
项目按 M0–M4 推进,M0–M4 都已完成,v0.1.0 已发布。它是参考系统,没有按生产环境加固;v0.1 不做的和做得不好的地方,见仓库的 docs/project/known-limitations.md。下表区分「现在已实现」与「计划中」,避免把设计当成现状。
| 能力 | 当前已实现 | 计划中 |
|---|---|---|
| 检索内授权 | 租户、密级、部门、项目四条规则,每次请求编译一次,写进每条通道的 SQL;用基于属性的测试对照参考实现验证 | 回答链路上一致的「无法回答」(M3) |
| 检索 | sparse-only(PostgreSQL FTS)、dense-only(pgvector 精确检索)、hybrid-rrf(RRF 融合并去除重叠 chunk)、hybrid-rrf-rerank(cross-encoder 重排,失败时退回融合顺序),响应带检索配置哈希 | – |
| 入库 | Markdown 与纯文本切分(长段落按句拆分、小节内重叠);异步任务(202 + 轮询)、SKIP LOCKED worker、有限重试与断点续跑;内容哈希幂等、原子版本切换、并发安全 ;停用与删除对下一次查询生效,后台清理不可达版本 | 带重叠的 chunker v1(M1b) |
| 评测 | 35 篇带标签的文档、128 条用例(含 31 条授权负例、13 条适用范围用例、6 条提示注入用例)、BM25 参考行、bootstrap 置信区间与配对比较;CI 安全门禁逐个检查返回的 chunk 和每个身份的完整可见列表 | 回答指标:引用有效性、拒答(M3) |
| 回答 | /api/v1/query:每句话都引用已授权证据的回答;证据不足时拒答;没有对话模型时只返回证据。回答指标来自注明模型的本地运行 | 检查每句话是否被引用的段落支持(v0.1 之后) |
| 可观测性 | 每个请求一条 OpenTelemetry trace,span 属性白名单在导出时强制执行;带 trace id、不含异常消息的 JSON 日志;一条命令启动的 Grafana dashboard;审计事件与查询执行记录同步写入,写失败则拒绝请求;Docker Compose、每个 PR 的 CI | – |
生成的回答目前还不可靠,这个项目把这一点测了出来:在 llama3:8b 的本地运行里没有任何泄漏,但 24 个应当拒答的问题有 10 个被回答了,5 条植入的指令有 2 条生效。详见评测方法末尾。
技术选型
| 部分 | 选择 | 理由 |
|---|---|---|
| 控制面 | Java 21、Spring Boot 4.1 | 身份、授权、事务、入库与 API 编排;作者的后端工程能力所在 |
| 数据存储 | PostgreSQL 17 + pgvector | 关键词与向量检索共用同一套事务和过滤面,授权谓词只需写一次 |
| 模型服务 | Python 3.12、FastAPI、ONNX Runtime | embedding 与后续重排;不引入 PyTorch,CPU 可跑,权重打进镜像 |
| 评测 | Python CLI ga-eval | 通过公开 API 以不同身份调用,不读数据库 |
| 数据访问 | Spring JdbcClient + Flyway | 授权谓词需要手写、可审查的 SQL,不引入 ORM |
Java 与 Python 之间只通过带版本的 OpenAPI 契约通信,模型服务不持有任何身份信息,也不访问数据库。
文档地图
| 文档 | 内容 |
|---|---|
| 快速开始 | 三条命令跑起来,看到同一问题在不同身份下的不同结果 |
| 检索时授权 | 三条不变量、决策表、编译后的 SQL、当前验证到哪一级 |
| 检索通道 | FTS 的 OR 改写、pgvector 精确检索、排序确定性、为什么不叫 BM25 |
| 评测方法 | 用例格式、span 标注、指标与安全门禁、可复现规则 |
| 架构与边界 | 组件划分、数据模型、版本切换、并发入库、故障行为 |
| 路线图与非目标 | M0–M4、明确不做的事、待决问题 |
与 Domain Driven Kit 的关系
Domain Driven Kit 解决的是「Java 项目如何把 DDD 分层与架构约束沉淀成可复用代码」;Grounded Access 解决的是「AI 检索系统如何把权限与质量证据做进工程」。两者没有代码依赖,共享的是同一套工程习惯:架构规则写成测试、空安全由编译器保证、CI 门禁不可绕过。
状态与许可证
- 仓库:https://github.com/poppycoderr/grounded-access,代码 Apache-2.0,虚构语料 CC BY 4.0
- 当前阶段:M0 与 M1a 完成并在 CI 中运行;尚未发布 release
- demo 身份体系使用公开的本地签名密钥,按设计就是不安全的,仅用于本地演示