Databricks推出的数据库版本控制工具,为AI Agent提供秒级隔离的生产数据副本,解决Agent在真实数据上工作的安全性问题。
每个 Agent 数据项目都会遇到一个尴尬的时刻。Agent 确实能工作:它写出的 SQL 还不错,能够推理 schema,还能提出一个看起来正确的迁移方案。然后,有人问出了那个没人愿意回答的问题:如果它在生产环境中做错了,会发生什么?
常见的答案都很糟糕。
让它使用只读凭据连接生产环境,确实限制了破坏范围,但也同时限制了 Agent 的能力——它只能诊断,永远无法修复。让它连接一个预先填充数据的 staging 数据库,你得到的则是一个对数据充满信心的 Agent,而那些数据早在四个月前就已经不再像生产环境了。每次运行都启动一个容器,则意味着你得永远维护 fixture 流水线,同时还要为两次运行之间一直闲置的计算资源付费。
你真正想要的是:在不到一秒的时间里获得生产环境的完整副本;这个副本存在期间几乎不花钱;Agent 完成任务后,它还能不留痕迹地消失。换句话说,你想对数据库执行 git checkout -b。
Databricks Lakebase 做到了这一点,而它背后的机制非常有意思,值得我们理解其原理,而不只是把功能打开了事。
Lakebase 就是 Postgres——真正的 Postgres,而不是兼容 Postgres wire protocol 的重新实现。Databricks 改变的是它的底层部分。

标准 Postgres 是一个计算与存储紧密耦合的单体系统。Databricks 将两者解耦,并把存储迁移到 lake storage 上,这立刻带来了一个问题:对象存储延迟高,而且不具备事务一致性,而 Postgres 的设计并没有考虑这两种情况。两个组件填补了这道鸿沟。Safekeeper 基于 Paxos 共识算法构建,负责低延迟的持久化写入;Page server 负责低延迟读取,并按需从 lake 中物化数据页。

一旦存储被解耦,并且能够按数据页寻址,创建分支几乎就不再需要成本。它采用 copy-on-write:数据仍然保存在 lake 中的同一个位置,Lakebase 只跟踪各个分支之间的增量差异。写入某个分支时,系统会独立记录变更,不会影响其他分支。
对于 Agent 工作负载而言,真正重要的数字是:创建一个分支大约需要 500 毫秒;启动一个新实例不到 500 毫秒;回滚到更早的快照同样很快;闲置分支可以缩容到零,因此在分支闲置期间,你只需要为廉价的 lake storage 付费。Databricks 表示,其生产环境每天会启动 1,200 万次数据库。与其说这是一个性能基准,不如说它表明:临时实例才是系统预期的使用模式,而不是什么边缘场景。
最后这个特性,正是它特别适合 Agent 的原因。正如 Ali Ghodsi 在 Summit 主题演讲的舞台上所说,Agent“可不想等上 10 分钟,等数据库启动”。如果 Agent 必须等待十分钟才能获得一个环境,人们最后只会让它直接使用生产环境,因为另一种选择会让整个迭代循环变得无法使用。亚秒级资源准备,才真正让安全的方案同时也成为方便的方案。
通常真正决定方案能否落地的是资源准备时间,因为它决定了开发者是否真的会使用隔离路径,还是会在截止日期的压力下绕过它。
下面是这套模式的完整端到端流程。Databricks 特有的部分被刻意隔离在一个很薄的封装层中;其余所有部分都是普通的 Postgres——因为它本来就是 Postgres。
第一步——获取一个实例。Lakebase 现在已经与 Genie Code、serverless GPU、Agent Bricks 和 LakeFlow Designer 一起包含在 Databricks Free Edition 中,因此你可以零成本跟着操作。
# lakebase_control.py — the only Databricks-specific layer in this design.
# Endpoint shapes for branch operations are still settling; check current
# docs before shipping. Everything downstream of get_dsn() is plain Postgres.
import os
import time
import requests
WORKSPACE = os.environ["DATABRICKS_HOST"].rstrip("/")
TOKEN = os.environ["DATABRICKS_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
def create_branch(parent: str, name: str) -> dict:
"""Branch an existing Lakebase instance. Copy-on-write; returns in ~500ms."""
started = time.perf_counter()
resp = requests.post(
f"{WORKSPACE}/api/2.0/database/instances/{parent}/branches",
headers=HEADERS,
json={"name": name},
timeout=30,
)
resp.raise_for_status()
elapsed_ms = (time.perf_counter() - started) * 1000
print(f"branch {name!r} ready in {elapsed_ms:.0f} ms")
return resp.json()
def delete_branch(parent: str, name: str) -> None:
requests.delete(
f"{WORKSPACE}/api/2.0/database/instances/{parent}/branches/{name}",
headers=HEADERS,
timeout=30,
).raise_for_status()
def get_dsn(instance: str, branch: str) -> str:
"""Standard Postgres connection string for a branch."""
resp = requests.get(
f"{WORKSPACE}/api/2.0/database/instances/{instance}/branches/{branch}",
headers=HEADERS,
timeout=30,
)
resp.raise_for_status()
host = resp.json()["read_write_dns"]
return f"postgresql://{os.environ['LAKEBASE_USER']}:{TOKEN}@{host}:5432/databricks_postgres?sslmode=require"
第二步——把它封装起来,让 Agent 从物理上就不可能看到生产环境。这是最重要的部分,而且它是刻意设计得如此乏味。Agent 永远不会收到 DSN;它拿到的是一个绑定到某个分支的 session。
# agent_sandbox.py
import contextlib
import psycopg
from lakebase_control import create_branch, delete_branch, get_dsn
PROD_INSTANCE = "orders-prod"
@contextlib.contextmanager
def branched_session(run_id: str, keep_on_failure: bool = True):
"""
Yield a Postgres connection on a throwaway branch of production.
On success the branch is discarded. On failure it is retained so you can
attach a psql session and see exactly what the agent did — which is the
single most useful debugging affordance in this whole design.
"""
branch = f"agent-{run_id}"
create_branch(PROD_INSTANCE, branch)
conn = None
try:
conn = psycopg.connect(get_dsn(PROD_INSTANCE, branch), autocommit=False)
yield conn
conn.commit()
delete_branch(PROD_INSTANCE, branch)
except Exception:
if conn:
conn.rollback()
if not keep_on_failure:
delete_branch(PROD_INSTANCE, branch)
else:
print(f"retained branch {branch!r} for inspection")
raise
finally:
if conn:
conn.close()
第三步——在相信任何结果之前,先进行验证。Agent 声称任务成功,并不能算作证据。在执行前后分别为你关心的不变量创建快照,然后比较差异。
# verify.py
from dataclasses import dataclass
INVARIANTS = {
"order_count": "SELECT count(*) FROM orders",
"open_order_total": "SELECT coalesce(sum(total_cents), 0) FROM orders WHERE status = 'open'",
"orphaned_items": "SELECT count(*) FROM order_items i "
"LEFT JOIN orders o ON o.id = i.order_id WHERE o.id IS NULL",
"negative_totals": "SELECT count(*) FROM orders WHERE total_cents < 0",
"duplicate_skus": "SELECT count(*) FROM ("
" SELECT sku FROM products GROUP BY sku HAVING count(*) > 1"
") d",
}
@dataclass
class Snapshot:
values: dict
@classmethod
def take(cls, conn) -> "Snapshot":
out = {}
with conn.cursor() as cur:
for name, sql in INVARIANTS.items():
cur.execute(sql)
out[name] = cur.fetchone()[0]
return cls(out)
def diff(self, other: "Snapshot") -> dict:
return {
k: (self.values[k], other.values[k])
for k in self.values
if self.values[k] != other.values[k]
}
def assert_no_corruption(before: Snapshot, after: Snapshot) -> None:
"""Hard invariants must not move regardless of what the task was."""
for guard in ("orphaned_items", "negative_totals", "duplicate_skus"):
if after.values[guard] != before.values[guard]:
raise AssertionError(
f"invariant {guard} violated: "
f"{before.values[guard]} -> {after.values[guard]}"
)
第四步——真正运行任务。
# run_agent_task.py
import uuid
from agent_sandbox import branched_session
from verify import Snapshot, assert_no_corruption
def run(task: str, agent) -> dict:
run_id = uuid.uuid4().hex[:8]
with branched_session(run_id) as conn:
before = Snapshot.take(conn)
# The agent gets a live connection to real-shaped data and full write
# access — to a branch that exists only for this run.
agent.execute(task, conn)
after = Snapshot.take(conn)
assert_no_corruption(before, after)
return {
"run_id": run_id,
"changed": after.diff(before),
}

这并不是我为了这篇文章临时发明的模式——Databricks 内部的自主流水线修复 Agent Genie ZeroOps,采用的就是这种架构。ZeroOps 会调查故障并拟定修复方案,然后使用与 Lakebase 相同的分支机制,为生产数据创建 shallow clone;接着把拟议的修复部署到 clone 上,验证行数,最后以 pull request 的形式提交结果。没有明确的人工批准,任何变更都不会进入生产环境。在 clone 上进行验证这一步,让自主修复变得合理可辩护,而不是鲁莽冒险;现在,你也可以把它作为一种基础能力使用。
这一点值得直说,因为它提供的隔离范围比直觉感受到的更窄。
副作用会逃出分支。 你的数据库被隔离了,但 Agent 调用的支付 API 没有被隔离。它发送的电子邮件、发布的 Slack 消息,以及写入的 Kafka topic 也都没有被隔离。分支只能保护你在 Postgres 中拥有的状态,除此之外什么也保护不了——每一个外部集成都需要自己的 sandboxing 机制。很多团队误以为分支已经覆盖了一切,最终就在这里吃了亏。
分支泛滥是真实存在的问题。 亚秒级创建能力加上失败后保留分支的策略,会产生大量分支。从第一天开始就应该强制设置 TTL,用创建分支的运行任务来标记分支,并安排一个定时运行的清理程序。Copy-on-write 存储很便宜,但并不是免费;当某个分支与其父分支产生大量差异后,它也就不再便宜了。
创建分支时是最新的,之后立刻开始过时。 长时间运行的 Agent 任务,是基于一个快照进行推理的。如果任务执行了二十分钟,而底层问题又具有时效性,那么等结果落地时,结论可能已经错了——并不是因为隔离失败,而是因为你拿一个时间点上的副本,去回答一个实时问题。
不变量检查只能捕获你事先想到并写下来的问题。 上面的 assert_no_corruption 函数只是最低保障,而不是能力上限。它能捕获结构性破坏,但无法发现这样的情况:Agent 正确地更新了错误客户的记录。因为从语义规则来看,这次更新有效;从数据结构来看,它也没有任何问题。对 diff 进行人工审查,仍然是整个流程不可替代的支柱。
这件事的意义并不局限于 Databricks,因为它补上了一个一直在暗中限制所有 Agent 数据项目的缺口:过去,Agent 要么安全,要么有用;想要同时兼得,就必须建设一套大多数团队根本不会去做的基础设施。
应用程序代码早在几十年前就解决了这个问题。没人会通过直接编辑生产环境的源代码来测试重构——你会创建分支,放手去破坏,再合并真正有效的改动。数据库一直没有获得这种便利,因为复制数据库的成本太高,以至于相应的易用体验始终没有出现。计算与存储的解耦,终于让副本变得足够便宜;而廉价的副本,才让实验变得安全。
给 Agent 一个分支。让它放手去破坏。然后查看 diff。
Lakebase 的分支 API 仍在演进之中;上面的控制平面调用在结构上是准确的,但在正式运行之前,请根据当前的 Databricks 文档核实 endpoint 和字段名称。DSN 之后的所有部分都是标准 Postgres,其行为会完全符合你的预期。
“Lakebase——Lakehouse 上的 Serverless Postgres。”Databricks。
“Introducing Genie ZeroOps。”Databricks Blog。
Marattha, P.,《Databricks Data + AI Summit 2026 回顾:Genie One、LTAP、Lakehouse//RT 及所有重要发布》。Flexera Blog,2026 年 6 月 30 日。
“Databricks 发布 LTAP:首个湖上事务与分析处理架构。”Databricks Newsroom。
“Databricks Sandbox——Serverless Compute 文档。”Databricks Docs。
“Introducing Genie One, Genie Ontology, and Genie Agents。”Databricks Blog。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。