生产数据不敢给 Agent 试错?Penca 用零拷贝分支把 OLTP、分析与审计放在同一份列式数据上
让 Agent 根据实时业务数据调整策略,通常会立刻碰到一个矛盾:它需要读到刚写入的事务数据,最好还能做分析;但我们又不希望一个实验策略直接污染生产库。常见补救是复制一套测试库、搭 CDC 把数据同步到数仓,或者给每个实验准备独立环境。它们能降低风险,却也带来了副本滞后、管道维护和高昂的存储成本。
Penca 是一个仍处在早期阶段的开源项目,试图把这个问题收敛为数据层的能力:同一份存放在对象存储中的开放列式数据,同时承载事务型和分析型访问;分支可以用于隔离试验,版本历史则提供审计和按时间点读取。它不是“给 Agent 的数据库”,但这种架构很适合作为 Agent 策略试验的底座。
本文关注它已经明确实现的边界,以及怎样把它放进一个可控的实验流程,而不是把 README 中的路线图当成现成功能。
为什么“给每个 Agent 复制一份库”很快变得笨重
假设推荐 Agent 要比较三种排序策略。最直白的方案是给每种策略复制一套生产数据,让它们分别写入、统计结果,再人工挑一个合并。这有三个问题。
第一,副本往往不是实时的。若分析侧来自 ETL 或 CDC,下游看到的状态可能落后于刚刚提交的交易,Agent 的判断就会建立在过期数据上。第二,副本和同步链路本身也需要运维;一旦 schema 演进或重放失败,实验结果很难解释。第三,传统数据库克隆要么消耗实际存储,要么需要提前设计复杂的快照机制,实验数量一多,清理工作也会变成负担。
Penca 的目标不是让所有查询都变快,而是把“单份数据、可分支、可审计”的组合放在一个系统里。它把持久数据放到对象存储中的 Lance 文件(也支持 Parquet);写入先进入内部的 Postgres 热层,在 ACID 事务中完成,后台生命周期流程再执行持久化、快照和清理。读取会合并热层与列式层,因此已提交的写入可被读取,无须先等待另一条同步管道。
这套取舍也必须说清:单行事务的成本比本地磁盘行存数据库高;项目文档明确称其更接近概念验证而非成熟产品。在小规模分析上,Postgres 仍可能更快。因此它更像验证“可版本化 lakebase”这一方向的工程原型,不是可以不经评估就替换线上 Postgres 的产品。
分支的关键不是 Git 式命名,而是隔离的读写视图
Penca 的一个分支是可读写的视图,但创建分支不会复制数据行。它记录与父分支的位置关系,并从父分支读取已有的冷数据文件;每次变更会进入带作者和时间戳的不可变日志。这样,实验策略能在自己的分支上写入结果、查询自己的已提交数据,而主分支保持不变。
这正好对应 Agent 试验的最小闭环:从 main 分出实验环境,写入候选决策,读取指标,检查结果,最后丢弃分支。官方 examples/sandbox_demo.py 演示了三个策略从 main 分别派生分支、消费同一份确定性访问流、比较转换结果后删除分支的过程;它的目的在于说明隔离机制,并不构成通用性能基准。
不过,分支并非免费。创建分支前,父分支中尚未持久化的写入会被刷新到冷层,所以分支创建时间会受到缓冲写入量影响。此外,当前能力只允许从 main 分叉;合并只支持 fast-forward,目标分支若在分叉点之后已有提交,合并会被拒绝。没有冲突解决、diff、revert,后两者仍在路线图中。
对 Agent 工作流而言,这意味着要把“可自动丢弃的实验”作为默认动作,而不是假定系统已经具备 Git 那样的任意合并体验。需要由人或更高层编排器确认的变更,仍应经过明确的审批与写回步骤。
先跑通最小实例:SQL 数据面与实验分支要分开理解
Penca 提供 Arrow Flight SQL 网关,官方 Quick Start 使用 ADBC Python 驱动建立表和读写数据。启动前需要 Docker、uv 和 just;默认端口仅绑定回环地址。下面的命令与示例来自项目 README:
just penca-up
uv run python - <<'PY'
from adbc_driver_flightsql.dbapi import connect
with connect("grpc://localhost:50060", autocommit=True) as conn, conn.cursor() as cur:
cur.executescript(
"CREATE TABLE greetings (id BIGINT PRIMARY KEY, note VARCHAR)"
)
cur.executescript(
"INSERT INTO greetings (id, note) VALUES (1, 'hello'), (2, 'world')"
)
cur.execute("SELECT * FROM greetings ORDER BY id")
print(cur.fetch_arrow_table())
PY
这里最容易漏掉的是 autocommit=True。README 指出 DB-API 默认不是自动提交;连接关闭前未提交的写入会被丢弃。SQL 客户端可以使用 JDBC、ODBC、ADBC 或 SQLAlchemy 等接入方式,但分支、审计和时间旅行相关调用走的是项目提供的 Python/gRPC 表面,不应凭 SQL 语法猜测存在 CREATE BRANCH 或分支 DDL。
启动后还可以运行官方沙箱示例:
set -a && source docker/.client.env uv run python examples/sandbox_demo.py
它比手写一段伪造的“Agent 自动合并”代码更有价值:示例展示了分支隔离和清理的真实入口,读者可以先观察主分支未被触碰这一不变量,再决定是否把自己的策略执行器接到该流程上。
一个更稳妥的 Agent 实验编排方式
将 Penca 放进 Agent 系统时,我会把职责切成四层。
- 数据准备层:只允许受控服务写入主分支;对象存储是持久数据的归宿。不要因为系统提供 Flight SQL 就把它暴露给不受信任的网络。
- 实验层:调度器为每次策略候选创建分支,并赋予单独的实验标识、预算和超时。Agent 的写操作只针对该分支。
- 评估层:在实验分支内读取已提交状态,计算业务指标,并把输入版本、策略参数、结果摘要与执行者保存为可追溯记录。Penca 的行级历史与
as_of读取可以辅助复盘,但不能替代业务层的评价规则。 - 晋级层:先由外部规则验证实验结果;若要写回主线,必须处理当前 fast-forward 限制。发生并发主线更新时,不要悄悄覆盖,而是重新基于最新主线创建实验,或转为人工处理。
这种设计的要点是:Agent 拥有的是“可验证实验的写入权”,不是“生产数据的最终决定权”。分支降低了试错半径,版本历史让结果可追问;审批、身份、网络隔离和业务约束仍属于系统外围必须补齐的控制面。
必须正视的安全与工程边界
Penca 当前最重要的限制不是性能,而是安全成熟度。项目文档明确说明:目前没有认证、授权或 TLS,Flight SQL 握手也尚未实现;端口因此绑定在 loopback。任何能触达端口的实体都拥有完整访问权。结论很直接:不要把它公开到不受控网络,也不要把它当作已具备多租户权限边界的服务。
另外,项目尚无 pgwire 网关,传统 Postgres 客户端不能原样接入;没有全文检索或向量索引,二级索引仅用于等值查找;列式数据虽是开放格式,但尚未提供 Iceberg 导出。若你的 Agent 依赖语义检索、复杂权限模型或现有 PostgreSQL 生态,这些缺口会直接影响选型。
适合优先尝试 Penca 的,是内部开发环境或受控原型:你希望让多个策略在同一份业务数据视图上做隔离试验,又希望保留事务可见性、分析查询、版本和审计线索。不适合它的,是需要公网暴露、强合规权限、稳定高吞吐 OLTP 或成熟分支合并体验的生产核心路径。
结语
Penca 提供的价值不在于把 Agent 变得更自主,而在于让试验更容易被隔离、复现和解释:热层保证已提交数据可读,Lance/Parquet 让持久层保持开放,零拷贝分支降低实验复制成本,历史记录则为复盘保留证据。
但它仍是早期项目。正确的实践顺序应是先用官方示例验证数据路径和分支行为,再把 Agent 限制在可丢弃实验中,最后才评估是否值得扩展到真实业务。把边界写进架构,而不是交给模型“自行判断”,才是让 Agent 接近生产数据时更可靠的起点。