一个 1,000 万行的商品目录,20 个 Codex Agent,各自修复数据,最后只让符合规则的修改进入主表。首页那段 60 秒视频,就是从这次真实 MatrixOne 实测中生成的。
最终,4,552,565 行改动成功合并,33,248 行提案保留待审,14 条主动注入的价格违规被排除。本文把镜头拉长:从构造数据,到 Agent 交付 SQL、审批分支的组装、最终 MERGE,再到视频生成,逐步还原整个过程。
数据是我们生成的合成商品目录,运行在独立的实验数据库中。20 个 Codex 任务真实生成了修复方案,SQL 在真实 MatrixOne 实例上执行。视频是根据完成后的结果制作的压缩回放,界面用来解释脚本工作流;其中 DATA PULL REQUEST #42 是演示编号。
1. 先定义允许修改什么
这个场景从一句话开始:“You have a 10-million-row product catalog.” 但要让它成为可执行任务,必须把“清理商品数据”拆成明确的修改范围。
我们允许修复品牌、类目、属性、描述和 SKU;价格保持不变。每条商品使用稳定的 id 主键,并保留 source_brand、source_category、source_attributes、source_description 等来源字段。Agent 可以据此修复目标字段,避免凭空补出产品事实。
来源字段和置信度也都是合成数据。source_confidence 在大多数行上为 0.99,每逢 id % 97 = 0 时为 0.60;协调脚本将低于 0.80 的提案保留待审。这个设置让我们能够精确检验“哪些改动应当进入主表”。
2. 故意造出一个有问题的千万行目录
| 问题 | 初始数量 | 构造方式 |
|---|---|---|
| 缺失属性 | 2,000,000(20%) | 属性设为 NULL |
| 错误类目 | 500,000(5%) | 类目统一改成 Misc |
| 品牌不一致 | 1,000,000(10%) | 小写并附加空格 |
| 描述质量差 | 1,000,000(10%) | 替换为相同的空泛描述 |
| 重复 SKU | 100,000(1%) | 借用前一个商品的 SKU |
五类问题刻意分布在不同的主键集合中,因此这一轮可以分别观察每个任务的效果。SKU 修复保留所有商品行:稳定的商品 ID 代表不同商品,任务是恢复正确 SKU。
造数脚本每批写入 200,000 行,共 50 批。50 次插入的客户端计时合计为 36.865 秒,这个数字不包含建表、初始质量检查和快照。随后,我们记录各类问题数量,并建立基线快照。
CREATE SNAPSHOT run_base FOR TABLE catalog main;
DATA BRANCH CREATE TABLE catalog.agent_brand_0
FROM catalog.main{snapshot='run_base'};
文中的 SQL 用较短的库名和快照名展示实际语法;完整运行标识与原始语句保留在实验日志里。CREATE SNAPSHOT ... FOR TABLE 中,数据库名和表名以空格分开。
3. 让 20 个 Codex 任务各写一份修复方案
我们将五种修复任务各拆成四个 ID 分片,每片覆盖 2,500,000 个 ID,于是得到 5 × 4 = 20 个独立 Codex 任务。每个任务绑定一个分支。分支的逻辑基线仍是完整的千万行目录,任务的写入范围则被限定在自己的 ID 区间。
main · 10,000,000 rows
│
├── attributes-0 … attributes-3
├── brand-0 … brand-3
├── category-0 … category-3
├── description-0 … description-3
└── dedup-0 … dedup-3
20 task branches → approved branch → main
给每个 Codex 任务的输入包含表结构、问题生成规则、可改字段、ID 范围和输出格式。任务交付一个 JSON 文件,其中包括修复理由、一条 UPDATE、一条验证查询和待审说明。任务没有收到数据库凭据;协调者检查这些方案后,再由执行脚本访问数据库。
例如,品牌任务的第一个分片生成了以下修复逻辑:
UPDATE {{branch}}
SET brand = source_brand
WHERE id BETWEEN 1 AND 2500000
AND MOD(id, 10) = 2
AND source_confidence >= 0.80
AND NOT (brand <=> source_brand);
这里的 <=> 用于 NULL 安全比较;{{branch}} 由协调脚本替换为该任务的完整表名。每条 SQL 批量处理符合条件的数据,因此模型主要承担“把任务约束转换成可执行方案”的工作。
20 个 Codex 任务按波次调度,最多 3 个同时运行。数据库执行同样限制为 3 条并发工作连接。视频中显示的 20 个分支对应实际创建的分支,并不表示 20 个任务同时占用数据库。
4. 在分支里执行,让完整差异接受校验
执行器检查任务类型、分片范围和 SQL 的基本形状,然后在各自分支上运行修复和验证查询。这些检查面向经过协调者审查的实验输入;生产场景仍需要数据库权限或受控执行服务限制访问范围。
每个分支完成后,我们同时记录 SQL 统计结果与 MatrixOne 原生差异:
DATA BRANCH DIFF catalog.agent_brand_0
AGAINST catalog.main{snapshot='run_base'}
OUTPUT SUMMARY;
DATA BRANCH DIFF catalog.agent_brand_0
AGAINST catalog.main{snapshot='run_base'}
OUTPUT LIMIT 3;
OUTPUT SUMMARY 用于核对整体变化数量,小样本用于展示具体改动。每个分支的原生差异计数都与相应 SQL 计数对齐。我们还再次检查了主表:问题数量与初始记录一致,主表相对于基线快照的 INSERTED、DELETED、UPDATED 均为 0。
到这里,Agent 分支已经产生了数百万行提案,主表仍然保持原样。
5. 主动注入 14 条价格违规
为了验证拒绝路径,协调脚本在品牌任务执行结束后,选择其中 14 行,把价格改为 99。视频里展示的商品原价为 100.20,分支中的价格变成了 99.00。
这是有意设计的故障注入。 它检验的是:当一个分支中同时存在合理的品牌修复和禁止的价格改动时,筛选逻辑能否把违规行排除。
这里有一个容易漏掉的细节:PICK 选择的是行,行上的其他改动也必须通过校验。 如果某行的品牌修复正确、价格修改违规,我们就把整行排除;只批准“brand 字段”并不能让未经处理的那条源行变得安全。
6. 得到一份可以追溯的 Data Pull Request
所有分支结果汇总后,实际数字如下:
| 测量项 | 行数 |
|---|---|
| 产生提案的唯一行 | 4,585,827 |
| 通过校验并最终合并 | 4,552,565 |
| 保留待审的提案行 | 33,248 |
| 被排除的价格违规行 | 14 |
后三类互斥,合计正好是 4,585,827 行。价格违规发生在已经有品牌提案的行上,因此逐字段的改动数会有重叠。
初始目录共包含 4,600,000 行问题数据。部分 Codex 任务在生成方案时,已经主动跳过低置信度行,因此有 14,173 行没有形成提案。它们继续保留原状,也不会被计入 33,248 行的提案待审队列。把“没有提出修改”和“提出后需要审核”分开,才能准确解释结果。
7. 先组装审批分支,再做一次真实 MERGE
我们从同一个基线创建 approved 分支,然后依次从 20 个任务分支 PICK 符合条件的键:属于该任务的 ID 范围和问题集合、目标字段确实发生变化、置信度不低于 0.80,而且价格与主表一致。
DATA BRANCH PICK catalog.agent_brand_0
INTO catalog.approved
KEYS (
SELECT b.id
FROM catalog.agent_brand_0 b
JOIN catalog.main m ON b.id = m.id
WHERE b.id BETWEEN 1 AND 2500000
AND MOD(b.id, 10) = 2
AND NOT (b.brand <=> m.brand)
AND b.source_confidence >= 0.80
AND b.price = m.price
)
WHEN CONFLICT FAIL;
这段价格比较适用于本实验中已知非空的价格数据。适配真实业务时,应处理 NULL,并验证所有受保护字段、插入/删除、主键范围以及跨行约束。
本次“审批”由协调脚本按照预先设定的规则完成,整个实测与合并在用户授权范围内执行。视频中的 Approve 按钮是这个阶段的可视化表达;要接入业务团队,还需要独立的授权记录和审批入口。
审批分支组装完成后,执行最终操作:
DATA BRANCH MERGE catalog.approved
INTO catalog.main
WHEN CONFLICT FAIL;
20 次顺序 PICK 共耗时 643.391 秒。最终 MERGE 耗时 840.808 秒,约 14 分钟。执行脚本从开始创建分支,到完成最终验证,共约 28 分 31 秒;这个阶段不包含此前的造数和 Codex 方案生成。
我们在收到结果后继续核验,而没有用“语句返回成功”代替数据检查:
- 主表仍为 10,000,000 行。
- 原生最终 DIFF 记录 4,552,565 行 UPDATED,INSERTED 和 DELETED 均为 0。
- 主表价格改动为 0。
- 本实验定义的低置信度、未经审查的修复进入主表的数量为 0。
这些耗时来自一次共享实例运行,记录的是客户端观察到的查询时间。它们适合用来理解这次实验的过程。
8. 过程里遇到的三个问题
聚合语法需要按实际版本调整
第一次基线统计使用了布尔表达式的 SUM,实例返回类型错误。此时千万行数据已经写入。我们保留已有数据,改成 SUM(CASE WHEN ... THEN 1 ELSE 0 END) 后继续统计,再建立快照。
PICK 之后重新 MERGE 原分支会遇到冲突
单独的小规模探针验证了安全行选择和冲突拒绝,也观察到:PICK 之后再次合并原始来源分支,在这个实例上可能出现冲突。因此,本次采用“汇总到一个审批分支,然后只合并这个分支一次”的路径。遇到超时或断线时,应先查操作状态与目标差异,再决定是否重试。
数据隔离之外,还要考虑实例负载
实测结束后的 Playground 检查曾收到“计算节点繁忙,可能存在过多活跃事务”的错误,直连也出现相同响应。这条记录没有确定错误的根因,但提醒我们:分支共享实例时,计算资源仍可能相互影响。生产部署应另外规划资源预算、并发限制,必要时使用独立的工作实例。
9. 最后,才把完成后的结果做成 60 秒视频
视频生成器读取真实的 execution.json。正式渲染前,它要求运行状态已经是 merged;画面中的提案数、待审数、违规数和成功合并数都来自这份结果。
| 视频时间 | 对应过程 |
|---|---|
| 0–8 秒 | 展示目录规模与五类数据问题 |
| 8–22 秒 | 展示 20 个任务分支 |
| 22–34 秒 | 汇总 Data Pull Request 与字段改动 |
| 34–44 秒 | 展示价格违规的行级差异 |
| 44–53 秒 | PICK 到审批分支,再 MERGE |
| 53–60 秒 | 展示经过验证的主表结果 |
我们用 Pillow 绘制画面,再用 FFmpeg 编码成 1280 × 720、20 fps 的 H.264 视频,并配上英文字幕。耗时较长的数据库操作被压缩成几个镜头;“Synthetic catalog”和“Edited replay”标识保留在画面里,方便观众理解演示与实际运行的关系。
把这套流程带到自己的项目
这次实验说明,可以让 Agent 先在分支里完成大规模数据修复,再把差异、校验和批准的范围明确表达出来,最后用数据库原生操作应用这些改动。
要把它用于生产,还需要进一步落实:提案 Agent 的受限权限、独立保管的合并凭据、与冻结版本绑定的审批、完整的业务校验,以及资源和恢复策略。本次合成数据实验验证了数据库工作流,生产权限隔离需要在各自部署里单独建立。
我们已将这些流程和边界整理为开源 Skill。你可以让 Codex 使用现有 MySQL 客户端先在少量数据上实践,再逐步接入自己的业务规则。
下载 Skill,复用这套工作流
开源指令、SQL 指南、生产边界与审核报告模板。连接你自己的 MatrixOne。
源代码与原始证据
下列链接固定到实验完成时的提交,便于对照本文。公开 Playground 是独立的 124 行 SQL 教程。