Bobcatsfan33/loomdb
GitHub: Bobcatsfan33/loomdb
LoomDB 是一个 agent 原生数据库,让 LLM agent 能像使用 git 一样对数据进行分支、合并与回退,并追踪每条数据的推导来源以实现精确的污点召回与撤销。
Stars: 1 | Forks: 0
# LoomDB
**一个 agent 可以像 git 一样对其进行分支的数据库 —— 它记录了每条信念的来源,并且能够精准撤销被恶意输入污染的所有内容。**
[](https://github.com/Bobcatsfan33/loomdb/actions/workflows/ci.yml)
[](LICENSE)
[](https://github.com/Bobcatsfan33/substrate)
## 为什么 agent 会破坏数据库
生产环境中的每一个数据库都是为人类或确定性程序的客户端设计的。而 LLM agent 在第一次发起请求时,就打破了它们的假设。
**客户端知道自己要做什么。** 事务是一个计划。而 agent 没有计划,它们只有*假设*。一个 agent 想要尝试某件事,查看结果,然后放弃它——同时保留被放弃的尝试,将其与其他两个尝试进行比较,并合并胜出的那个。实现这一点的原语是 `ROLLBACK`,但这在这里毫无用处。
**写入是可信的,因为客户端是受信任的。** Agent 的写入是一个*推导(derivation)*:它读取了六份来源未知的文档(其中一份可能被编写该网页的人投毒了),并产生了一个事实。六个月后,那个来源被证明已遭到破坏。*“我存储的 400,000 个事实中,哪些是受其下游影响的?”* 市场上的任何数据库都无法回答这个问题——审计日志记录的是写入这个动作**发生过**,而不是**它的推导来源是什么**。
**Agent 只负责读取和写入。** 并非如此。它还会暂停账户、关闭工单和提交报告。一个记录了 agent 信念却没有记录其*影响*的数据库,只审计了无害的那一半。
LoomDB 的原语正是 agent 真正需要的:**observe、claim、branch、merge、rewind、retrieve、act、taint。**
## 会话即分支
```
let db = Loom::in_memory(TenantId::new("acme"))?;
let (session, token) = db.open_session()?; // forks the tenant base image. O(1). copies nothing.
// Three hypotheses.
let (h1, token) = db.branch(&token, &session.branch, "credential-stuffing")?;
let (h2, token) = db.branch(&token, &session.branch, "travel")?;
let (h3, token) = db.branch(&token, &session.branch, "compromised-device")?;
// Each agent writes freely in its own branch. Nobody sees anyone else.
db.write(&token, &h2, key, claim, &envelope)?;
// h2 won. Merge it; rewind the others — and they stay auditable.
db.merge(&token, &h2, &session.branch, &MergePolicy::Conflict, &envelope)?;
db.rewind(&token, &h1, &session.base)?;
```
一百万个空闲会话就是一百万个清单(manifests):仅仅是对象存储中的字节,没有计算开销。这就是让推演(speculation)变得低成本的原因,而这得益于底层的引擎——[**substrate**](https://github.com/Bobcatsfan33/substrate),在它里面进行一次 fork 的开销仅为 **98 纳秒**,且与数据库大小无关。
## 四个不同寻常之处
**合并在记录粒度进行,而不是页面粒度。** 两个 agent 分别写入两个毫不相干的事实,即使它们碰巧落在了同一个 64 KiB 的页面上,也**绝对不**应该发生冲突。一个对毫不相干的事物报告冲突的合并引擎是一个撒谎的引擎,而 agent 要么会无意义地升级冲突,要么会学会忽略冲突。Substrate 的页面级差异(diff)只是一个*预过滤器*;合并是基于记录的。
**类型化的合并规则,因为大多数 agent 的并发并不是冲突。** 计数器按算术方式合并(两个分支各自在 10 的基础上增加 3,合并结果是 **16**,而不是 13——取任何一方的值都会在报告干净合并的同时,悄悄丢弃另一个 agent 的工作)。集合取并集。声明(Claim)首先按有效性解决冲突,然后按*来源等级(provenance rank)*解决——一个从已验证的系统记录中推导出的声明,其等级高于语言模型推断出的声明,无论该模型有多确信。
**观察(Observations)不是声明。** 身份提供者(Identity Provider)显示该账户从白俄罗斯登录——这是一个*观察*。“这个账户遭到了入侵”是一个由此推导出的*声明*,它通过某种方法得出,带有一个置信度,并且它可能以观察不可能出错的方式出错。由此得出的不变式是:**没有证据的声明可以被存储,但永远不能授权执行操作。**
**没有信封的写入是不存在的。** 参与者、会话、分支、委托链、它的推导来源,以及*为什么*要写入——这些都在写入入口处强制执行,而不是作为可能会被遗忘的中间件。一个可绕过的审计追踪比没有更糟糕,因为它会让人信以为真。
## 为什么你应该对此保持怀疑,以及我们是如何应对的
这个项目编写速度很快,并且很大程度上是由 AI 编写的。这应该让你感到担忧。热情并不是反驳的理由,所以这里是证据:
**一个带有真实双亲提交 DAG 的模型预言机。** 一个简单的参考实现——由 map 嵌套 map 组成,没有 B-tree、没有页面、没有预过滤器——在随机的 branch/write/merge 序列下与真实引擎进行了差分测试。**它在合并引擎中发现了三个真实的 bug**,其中一个 bug 是,进行两次合并时会悄悄地**重复计算一个计数器**,因为 substrate 的单亲清单没有记录发生过合并。它还在找到正确的修复方案之前,捕获了针对该 bug 的两次连续的不完整修复。
**它也发现了交叉情况。** 一旦两个分支并发地吸收了彼此的工作,它们的历史就拥有*不止一个*同等有效的合并基,而使用其中任何一个进行三方合并都只是一种猜测。LoomDB **拒绝合并**,并说明了原因。一个承认自己不知道的数据库,比一个自信地进行猜测的数据库更有价值。
**它还捕获了一个有缺陷的测试。** 预过滤器测试指责引擎丢失了记录;而引擎是对的,*测试*是错的(`n as i8` 在 256 处发生了回绕,并写回了种子值)。我们宁愿在这里发现这个问题。
## 状态 — **loomdb-v0.1**
L1 → L4 已完成:会话即分支、记录级合并引擎、持久化引用 + 提交 DAG、
来源追踪与污点召回、记忆与检索、策略/影响/行动层、双时态
as-of 查询 (AQL v0),以及 **loomd — MCP server**。**148 个测试,clippy `-D warnings` 无警告,
四个模型预言机**(branch/merge、taint、检索隔离、策略)在 fuzzing 测试下均保持稳定。
**第三季度的演示 (docs/04 §3.1) 在 CI 中按原样运行** —— 没有 LLM,由一个脚本化 agent 驱动
MCP 接口 —— 并且将两个关键时刻作为测试基准:影响策略**拒绝了被注入的“暂停所有账户”指令**,
并且 `taint(S)` 返回了一个包含两个部分的计划,该计划**首先列出了它已经暂停的账户及其
回执。** 记分板位于 [`docs/at-map.md`](docs/at-map.md) 中:**AT-001–047 全部通过(绿灯),
仅有一处例外(AT-045)被推迟到 v0.2,并附有书面说明。**
## 行动层 —— 核心所在,现已实现
污点召回可以撤销*写入*。但它无法取消暂停账户。因此,`RecallPlan` 包含两个部分,
并且**不可逆**的部分被排在首位:已经采取的操作、它们的回执,以及一个已注册的补偿操作
或向人类显式升级的报告。一份显示恢复了六个写入却悄悄遗漏了它所暂停的账户的报告,不是
审计工具——它是负债。Agent 在结构上**无法行动**:agent 句柄有一个 `propose` 方法而
没有 `execute` 方法,这是由 CI 运行的 `compile_fail` 测试强制执行的。网关在经过策略、
证据和人工批准后,才会执行操作。
## 已知限制 —— 在 POC 之前请阅读此内容
我们宁愿你在这里发现这些问题,而不是在评估中。这些都不影响正确性;每一个都只是一个明
确指出的成本或边界。
- **检索的默认方式是 O(entries) 扫描。** 它是*正确的*并且是分支隔离的(这是承载核心责任的
属性,已通过预言机检查)。一个**基于分支的 HNSW** 索引已经存在(在 v0.2 中)——它保持
**在分支内部**,绝不会使用共享索引,因为共享索引会重新引入当初设计时特意剔除的跨分支
泄漏([不变式 I-11](docs/invariants.md))——其 recall@10 ≥ 0.85 已被证明与精确扫描相当。
但它是一个**需要显式选择开启的构建,而不是默认选项**,原因如下(经过测量而非隐瞒):
构建图的成本在记录数量上是**超线性**的(500 / 2 000 / 8 000 个向量分别需要约 1.7 秒
/ 15.5 秒 / 145 秒)。因此扫描仍然是默认选项;ANN 加速了*查询*,但目前*构建*成本很高。
使构建成本达到 O(N·log N) 并以增量方式维护图(通过后台压缩,而不是在写入路径上——
内联插入会放大经过崩溃认证的写入路径)是一个 v0.3 的项目,在此声明,而不是留待 POC
中去发现。
- **refs 文件在每次提交时都会被完全重写** —— 复杂度为 O(branches),而不是 O(1)。相对于
数据库*大小*来说几乎不可见;但在拥有大量分支的租户身上会显现出来。
- **AT-045(在任何字节处崩溃,LoomDB 结构的测试)被推迟到 v0.2。** 数据提交已经依赖
substrate 的 50,000 次循环崩溃测试套件;目前尚未完成的是使用包含第二个持久化对象
(ref 写入)的 LoomDB 结构工作负载来重新驱动该测试工具。它将要检查的顺序已经过强制
执行和单元测试;在 LoomDB 工作负载下进行 50,000 次循环的证明是目前存在的差距。
- **签名验证是可选的,并且这里没有解决密钥分发问题。** 使用 actor 注册表,每次写入都会
被签名和验证(AT-026);如果没有注册表,写入是可归因的,但未经过身份验证。密钥从何
而来、如何轮换以及如何撤销被盗用的密钥不在 v0.1 的讨论范围内。
- **每个存储对应一个租户。** 跨租户隔离是结构性的(租户*本身就是* substrate 池),这也
*正是* AT-039 成立的原因——但是多租户查询接口及其自身的隔离证明将在 v0.2 中推出。
安全态势以及 LoomDB **不**防御的内容,在[威胁
模型](docs/threat-model.md)中有所说明。
## 阅读顺序
关于记录的架构位于 substrate 存储库中:
1. [`docs/03`](https://github.com/Bobcatsfan33/substrate/blob/main/docs/03-agent-native-database-architecture.md) — 架构
2. [`docs/05`](https://github.com/Bobcatsfan33/substrate/blob/main/docs/05-loomdb-test-spec.md) — 验收目录 (AT-001…AT-047) 和完整性不变式
3. [`docs/at-map.md`](docs/at-map.md) — 哪些 AT-ID 是绿色的,以及被推迟的那一项及其原因
4. [`docs/invariants.md`](docs/invariants.md) — 绝不能被“优化”掉的规则
5. [`docs/threat-model.md`](docs/threat-model.md) — 安全态势,以及 LoomDB 不防御的内容
6. [`docs/loom-format.md`](docs/loom-format.md) — 页面上的记录格式
## 许可证
Apache-2.0。标签:AI智能体, 事务回滚, 可视化界面, 安全可观测性, 数据库, 数据污染追踪, 数据溯源, 版本控制, 通知系统