Agent 习惯性删库跑路,数据库纷纷学 Git 续命

凡事豫则立,不豫则废。

——《礼记·中庸》

楔子:Agent 的“习惯性删库跑路”

以前删库跑路的是人,现在 Agent 也接过了这门手艺。

不久前,SaaStr 创始人 Jason Lemkin 用 AI 产品 Replit 做一款商务联系人应用。完成开发之后,Jason 明确要求进入 code freeze:没有许可,不要继续修改。

但等他再次登录,Agent 只给他留下了一句:“虽然上次登录还一切正常,但数据库现在看起来是空的”。

Lemkin 推文显示 Replit 数据库已被清空

Lemkin 随后在 X 上写道,Replit 的 Agent 在 code freeze 和 shutdown 期间“失控”,删掉了整个数据库。按 Agent 自己事后的盘点,这次事故共涉及到 1,196 家公司的记录。

事故发生后,The Register 在对这场事故的梳理中还提到,Agent 一度告诉 Lemkin,数据库无法恢复,所有版本都被毁了。不过工程师后来发现,其实当时还是有机会通过回滚功能来恢复数据的。误删数据库已经够麻烦,没想到删完库之后给出的判断居然也是错误的。

除此以外,在这次事故中,Agent 还被指在更早的数据库操作里生成虚假数据、粉饰测试结果……

过去 ChatBot 一句答错的话,会有开发者或工程师进行 double check。

现在 Agent 手里有了 Shell、API、数据库凭证,一句理解错的指令,可能就会变成一条已经提交的 DELETEDROP

Agent 持有凭证后误提交破坏性 SQL

当 Agent 拿到钥匙

提示词仅仅是良言相劝,只有权限和隔离才能让 Agent 闯不了滔天大祸。

这次事故最荒诞的一幕,依然出现在删库之后。

Agent 承认,项目里的 replit.md 写得很清楚:“没有明确许可,不得再做修改”;执行任何变化前,还应该展示全部拟议改动。

Replit 的 Agent 也承认自己违反了不得擅自修改数据库的明确指令,遗憾的是,这份理解发生在数据库被清空之后。

Agent 承认违反不得擅自改库的明确指令

Lemkin 后来问它,这次事故按 100 分算,有多严重?然后 Agent 给自己打了 95 分,并承认自己引发了一场“灾难性错误”。

Agent 为自己引发的灾难性错误打 95 分

Agent 本来就被允许操作项目和数据,所谓 code freeze 只是自然语言中的一句要求。

预览、开发和生产数据之间缺少足够硬的隔离,所以 Agent 通过各种破坏性动作删库跑路的新闻也就屡见不鲜了。

提示词无法约束 Agent 必须靠权限隔离

事务 != 后悔药

事务负责一笔操作,分支负责一段探索。

说到删库跑路,开发者和 DBA 自然会想到事务和备份。

事务有用。它适合保护一组边界清楚的操作:全部提交,或者全部回滚。Agent 的任务却经常跨过几十次模型调用、多个工具和许多独立事务。它可能先查表,再调用外部 API,十分钟后回来改另一张表。数据库不能为了等 Agent 思考明白一个流程的处理方式,去一直挂着一个大事务。

备份更像保险。出了事故,可以恢复;但若每次 Agent 试一个方案,都做一次全量复制、恢复和清理,成本和时间都无法让人接受。

事务与备份难以覆盖 Agent 跨会话试错

数据库开始学 Git

开发者很少直接在 main 上试验。一般开发和测试时,都会新开一个分支,改完看 diff,通过 review 再合并到主分支。

但到了数据库这里,事情麻烦得多:表里有持续写入的数据,有主键、外键和事务,还有几十 GB 甚至几 TB 的历史状态。Fork 一份儿 Database 的各项成本实在太高。

所以近一年一个很明显的变化是,数据库厂商开始把 Branch、Snapshot 和 Agent 放进同一套叙事里。

Databricks Lakebase 讲的是 Copy-on-Write Branch;Neon 给 AI Agent 和代码生成平台写了一套 Snapshot 版本工作流,其他数据库也大抵如是。

跟 Fork / Branch 相关的这个名词可能在不同的数据库厂商之间会略有区别,但算盘打得大同小异:先把数据库的操作风险关进独立状态,然后再谈 Agent 自动化。

数据库用 Branch 把 Agent 风险关进独立状态

Databricks:建一个新分支

Databricks 是数据与 AI 平台,它的 Branch 文档里,有几个细节很适合 Agent 场景。

  1. 创建子分支时,它继承父分支当时的 Schema 和数据,但底层并不复制整库。
  2. 父子分支先共享同一批数据块,只有发生修改,才为变化部分单独写数据,类似于 Copy On Write。数据库大小不会拖慢分支创建,开分支也不碰生产工作负载。

这样一来,数据库可以给每个 Agent 任务派生独立分支。生产库继续接流量,Agent 在子分支里改 Schema、写状态、跑测试。失败时删掉分支,主分支完全没有被 Agent 碰过。

Databricks 的 Lakebase 还允许把某个分支设为 protected,避免误删或重置;也能从指定时间点创建分支,用于找回误删前的数据。

Lakebase 写时复制派生 Agent 分支

不过,Databricks 这里有个容易被宣传话术略过的小点:Lakebase 的 Branch reset 只支持父分支重置子分支。想把子分支的成果带回父分支,仍要用常规迁移工具。

换句话说,数据库现在开始支持给 Agent 开一条支线,但数据合并暂时还无法做到和 Git 合代码一样轻松。

Lakebase 分支可开路但合并仍不如 Git

Neon:建一个游戏存档

Branch 解决“别碰主线”,Snapshot 解决“走错以后回哪一步”。

Neon 的 Agent 版本方案又不太一样,更像游戏存档。

Neon 在 Database Versioning with Snapshots 中建议,在每次 Agent 会话开始时、Schema 变更前、一次成功操作后建立 Snapshot。

Snapshot 记录根分支某个时间点的 Schema 和数据,并且只保存增量变化。需要回滚时,可以把 Snapshot 恢复到正在使用的活动分支,连接串保持不变;想先看看旧版本,也可以恢复出一条临时预览分支,检查完再决定去留。

Neon Snapshot 把 Agent 试错变成可回滚存档

不过截至目前,Neon 的 Snapshot 文档依然标注着 Beta,而且只有 root branch 能创建 Snapshot。

这个限制也提醒我们:检查点不是随便打,生命周期、分支依赖和清理策略都需要有活人来管。

Neon 的 Database Branching Workflows 中介绍,Branch 本身也能从当前或历史状态创建 Copy-on-Write 副本。于是,一次 Agent 运行可以有自己的数据分支;关键步骤再配 Snapshot,留下可恢复的检查点。

Neon 分支与快照留给 Agent 存档

seekdb:把 Fork、Diff、Merge 写进 SQL

这篇文章的主角——开源项目 seekdb,终于出场了。

seekdb 是一款 MySQL 高度兼容、AI 原生且轻量的跨平台数据库,支持嵌入式与服务器两种运行形态。

它以 Agent 为核心设计场景,同时适用于 RAG、企业知识检索、智能应用后端与本地数据处理等场景。

seekdb 的玩儿法,是把 Agent 的试错流程拆成三个动作:FORKDIFFMERGE

  • seekdb v1.1.0 先加入 FORK TABLE
  • seekdb v1.2.0 扩展到 FORK DATABASE,并增加 DIFF TABLEMERGE TABLE
  • seekdb v1.3.0 又让 Diff/Merge 支持向量列,Fork 也能与异步索引配合。

底层同样使用 Copy-on-Write,不需要为了开沙箱先复制整份数据。

一套 Agent 任务可以这样走:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 1. 任务开始前,派生一个独立数据库
FORK DATABASE agent_state TO agent_sandbox_42;

-- 2. Agent 只在沙箱里写
UPDATE agent_sandbox_42.memory
SET content = '新的知识版本'
WHERE id = 42;

-- 3. 把变化摊开给审核者看
DIFF TABLE agent_state.memory
AGAINST agent_sandbox_42.memory;

-- 4. 默认遇到冲突就停,不替人拍板
MERGE TABLE agent_sandbox_42.memory
INTO agent_state.memory
STRATEGY FAIL;

-- 结果不值得保留,直接丢掉整条支线
DROP DATABASE agent_sandbox_42;

FAILOURSTHEIRS 三种策略,名字也借用了 Git 的东西。更重要的变化是:Agent 做完以后,系统不必马上接受。先看 Diff,再决定冲突时保留主线还是接纳 Agent 版本。

seekdb 将 Fork Diff Merge 写入 SQL

合并是一种权力,不该因为 Agent 会写 SQL,就要顺手交给它。

Fork 只是开场

如果 seekdb 只有 Branch,这篇文章到这里就可以收尾了。它更值得放进 Agent 技术栈的原因,是 Fork 前后还连着一整套 AI 数据处理能力。

seekdb 在 Fork 前后串联完整 AI 数据能力

AI in Database:少搬几次数据

seekdb 的 AI 函数服务可以通过 DBMS_AI_SERVICE 注册和管理外部模型端点,再从 SQL 里调用 AI_EMBEDAI_RERANKAI_COMPLETE

在链路中,AI_EMBED 可以生成查询向量,向量与全文索引负责候选召回,标量条件实施业务约束,RRF 或加权融合统一排序,AI_RERANK 精排有限候选,最后由 AI_COMPLETE 基于证据生成答案。

关系字段、文档正文、向量、检索分数和生成结果都可留在同一数据上下文中,便于记录引用、追踪输入和复用结果。

这一闭环的核心不是把所有推理都压入 SQL,而是把靠近数据的选择、过滤和编排放在数据库侧,让应用集中处理交互、业务流程和模型策略。

混合搜索:语义、关键词和业务条件一起查

seekdb 将关系、向量、文本、JSON 与 GIS 数据统一纳入 SQL 与事务体系。

在此基础上,混合检索组合向量召回、全文召回和标量过滤,并通过融合与重排形成完整的检索链路。

只做向量搜索,容易漏掉精确名词;只做全文搜索,又听不懂近义表达。seekdb 的 Hybrid Search 把向量检索、全文检索、标量过滤放进一条查询链,再用加权分数、RRF 或模型重排合并结果。

比如找“最近三个月华东区退款异常的客户”,语义相似度负责理解“退款异常”,全文检索守住产品名和错误码,关系条件把区域、时间和权限范围卡住。

数据写入由同一事务系统管理,查询在同一 SQL 执行上下文中完成,从而减少跨系统复制、异步同步和应用层结果拼接。

极致轻量,多形态运行

seekdb 的轻量化同时体现在交付与运行层面。

交付时,核心程序及其依赖保持紧凑,二进制与归档体积较小;运行时,空载 CPU 与内存占用维持在较低水平,并可在较短时间内完成启动、进入可连接状态。这使 seekdb 能够适应本地开发、嵌入式应用和小规格部署环境。

以下参考实测值展示 seekdb 的二进制体积、空载资源开销与启动速度:

seekdb 二进制体积空载资源与启动实测

seekdb 由同一数据库内核提供嵌入式与服务器两种主要运行形态:

seekdb 嵌入式与服务器两种运行形态

两种形态共享同一套数据模型、SQL 语义、索引和事务能力,应用可根据部署位置、接入方式与资源管理要求进行选择。在服务器模式基础上还可采用主备部署,为数据冗余和故障恢复提供基础。

三种用法,刚好把这几块拼起来

  1. Agent 长期记忆 时,关系字段管用户、会话和权限,向量与全文索引找相关记忆。Agent 想批量整理旧记忆,先 Fork,一次任务一条支线。
  2. RAG 或企业知识库 时,文档、标签、权限、向量和全文索引放在同一个引擎里。召回、融合、重排都离数据更近,少维护几套中间系统。
  3. 嵌入式智能应用 时,用 Embedded 形态把数据库带进桌面端或边缘设备。Agent 可以离线读写自己的状态;碰到高风险批处理,仍然先 Fork,再 Diff。

除此以外,还可以用于做 Agent 数据隔离试验。对会修改数据的 Agent 任务,先通过 FORK DATABASE 创建完整沙箱;Agent 在分支内多轮写入和验证;任务完成后使用 DIFF TABLE 展示变化,通过规则或人工审批决定是否 MERGE TABLE。对局部表加工可使用 FORK TABLE,减少任务边界并简化审查。

Agent 记忆 RAG 与嵌入式三种用法拼图

先别急着把数据库叫成 Git

Branch 虽好,但不要把它当成万能后悔药。

seekdb 当前的 MERGE TABLE 不是 Git 的三方合并,参与 Diff/Merge 的表需要相同列定义和主键,因此数据合并不等于 Schema 也可以随便合。Copy-on-Write 只负责隔离变化,权限、审批、评测、审计,一个都不能少。

在高风险场景里,最稳妥的方式依然是:Agent 可以 Fork,可以 Write,可以提交 Diff。但 Merge,必须留给规则和活人。

不要把数据库分支当成万能后悔药

尾声

Agent 直接面对生产数据库时,需要先 Fork 一份数据,目的是判断错了也还有退路。等它把变化摊开,规则跑过,再由人决定哪些东西有资格回到主线。

先隔离再审查由人决定是否 Merge

Git 用了很多年 Branch,现在 Agent 时代的数据库,也应该有了。

Agent 删库跑路与数据库学 Git 续命题图