数据截至 (上游 commit ceade4cbe9f2)
存储、分发注册表与非旁路的多租户/ABAC 隔离
30 秒导读: 前几章讲的是"请求怎么进来、怎么路由、怎么编排"(见 请求生命周期、路由与适配、Responses 编排)。这一章讲数据落在哪、重启后怎么活下来、多个租户/用户的数据凭什么不会互相看见。核心精华只有一句:OGX 把"权限"做进了每一条 SQL 的 WHERE 子句——不是"应用层记得检查就检查",而是结构上绕不过去。
本章讲支撑全局的持久化底座和安全隔离底座。它不产生业务逻辑,但所有业务逻辑的数据都经过它。
1. 这是什么(零基础也能懂)
一句话定义: 这是 OGX 的"磁盘层"——两套存储抽象(键值 KVStore、关系 SqlStore),外加一层强制隔离的安全包装(AuthorizedSqlStore)。
解决什么问题: 一个 API 服务器要记住很多东西——
- 控制面数据: 你注册过哪些模型、向量库、工具组?(注册表,重启后要还在)
- 业务面数据: 每次推理的日志、对话历史、prompt、连接器配置。
- 谁能看谁的数据: 如果 OGX 被多个团队/公司共用,A 公司的对话历史绝不能被 B 公司读到。
给谁用: 平台方(自己搭 OGX 给内部多团队用)、SaaS 提供方(一套 OGX 服务多个客户)。单机自用的人可以完全无视隔离层——它默认关闭。
一句话直觉/类比: 把 KVStore 当一个持久化的字典(存"注册表"这种键值元数据),把 SqlStore 当一张张带列的表(存日志这种结构化记录)。而 AuthorizedSqlStore 就像给每张表焊了一道闸门:任何人进来查数据,系统都会自动、悄悄地在他的查询后面加上"…… AND 这行属于你这个租户 AND 你有权看它",他改不掉、绕不过。
用起来什么样: 业务代码从不直接碰数据库。它只声明"我要一个受权限保护的 SQL 存储",然后照常读写:
# 示意,非源码 —— 展示业务层视角有多"无感"
store = await authorized_sqlstore(reference, policy) # 拿到受保护的表
await store.create_table("inference_store", {"id": ColumnType.STRING, ...})
await store.insert("inference_store", {"id": "req-1", "model": "gpt-4o"}) # 自动盖上 owner + tenant
rows = await store.fetch_all("inference_store") # 自动只返回"我"能看的行
业务层完全不写 WHERE tenant_id = ...——隔离是存储层替它做的。这正是"非旁路(non-bypassable)"的含义。