第10课

实用技巧 — 知道这些就能指挥AI

代码结构化了(第8课),系统结构化了(第9课)。剩下的是数据。数据最危险。代码错了测试抓。系统错了/health抓。数据错了没人知道。3个月后在季度报表中才发现。

对智能体说:“不用JSONB,用显式的列和约束来做模式。amount必须大于0,status只接受指定的值。”

往JSONB里随便塞,6个月后100种格式的数据混在一起。显式的列和约束就是数据的法律。违反约束DB立刻拒绝。

对智能体说:“把这个Excel导入DB。必须遵守DDL的约束。违反约束的行单独放文件里报告。”

智能体执行转换,DB的约束来验证。被拒绝的行连同原因一起报告。你只需检查被拒绝的数据。

对智能体说:“修改DDL并通过yongol validate。生成迁移文件,失败就回滚。”

模式变更时棘轮也起作用。通过就下一步,失败就回退。

不懂DB的你要做的只是决定"存什么"。“电话号码必须010开头”、“邮箱必须唯一”——把这些决策用自然语言说出来,智能体就翻译成DDL。


快速体验

不需要DB也能体验。在Claude Code中:

“创建CSV数据:10个客户的姓名、邮箱、电话、注册日期。故意混入问题:2个邮箱格式错误、1个电话为空、1个注册日期在未来。”

CSV生成后:

“找出这个CSV中有问题的行。”

看AI能找出几个。大多数找不全——有些行会被"看起来没问题"带过。

现在:

“先定义这个CSV的模式。邮箱必须包含@,电话NOT NULL,注册日期在今天之前。用那个模式重新验证。”

先声明模式,AI就能机械地全部抓住。问意见就漏,给规则就抓——第7课学的原理在数据上同样适用。


为什么要这样指挥

引言:数据比代码先腐烂

代码结构化了(第8课)。系统结构化了(第9课)。剩下的是数据。

但数据跟代码和系统有根本不同。

代码错了测试抓。跑一下go test,1秒内"这里坏了"就出来了。系统错了监控抓。/health返回500立刻响警报。

数据错了没人知道。

客户电话应该010开头但有人输了02开头的。订单金额是负数。配送状态是"配送中"但配送日期是null。这些错误测试抓不到。监控也抓不到。3个月后季度报表中"为什么营收是负的?“时才发现。

假设用氛围编程做应用。“做个订单管理应用"代码很快就出来。用户输入数据。智能体添加功能时数据格式变了。迁移没做好旧数据和新数据混在一起。代码没问题但数据腐烂了。

代码的漂移看得见。数据的漂移看不见。

这就是数据比代码更危险的原因。


数据腐烂的三种类型

氛围编程中常见的数据腐烂分为三种。

1. 没有模式 — 没有合同的交易

只对智能体说"做个订单表"就会变成这样:

-- 如果这样做
CREATE TABLE orders (
    id SERIAL,
    data JSONB    -- 什么都能塞进去
);

这三行就是6个月后100种格式数据混在一起的元凶。

JSONB列什么都能放。一开始很方便。6个月后100种不同格式的数据混在一起。有些订单有amount,有些有price。有些是数字有些是字符串。智能体要处理这些数据就得猜100种格式。

2. 迁移失败 — 过去和现在的冲突

让智能体"给用户表加个email字段”。智能体修改DDL并执行迁移。新用户有email。现有10万用户email为null。代码假设email一定存在。现有用户登录500错误。

3. 业务规则违反 — 不该被允许的数据

“折扣率必须在0-50%之间。“这个规则如果只在代码里,智能体重构时可能消失。DB里没有CHECK (discount >= 0 AND discount <= 50)约束的话,200%折扣进去了也没人知道。3个月后结算时才发现。


Agent Operable Data的4个条件

智能体要安全地处理数据需要四个条件。


条件1. 模式已声明 — DDL是数据的合同

下面是数据库蓝图(DDL)。看起来像编程语言,但每行就是一条规则。读不懂没关系。紧接着逐行解释。

CREATE TABLE orders (
    id          BIGINT       GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    customer_id BIGINT       NOT NULL REFERENCES customers(id),
    amount      DECIMAL(12,2) NOT NULL CHECK (amount > 0),
    status      TEXT         NOT NULL DEFAULT 'pending'
                             CHECK (status IN ('pending', 'confirmed', 'shipped', 'delivered', 'cancelled')),
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT now(),
    shipped_at  TIMESTAMPTZ,
    CONSTRAINT  shipped_requires_date 
                CHECK (status != 'shipped' OR shipped_at IS NOT NULL)
);

来读这个DDL。不是智能体也能读:

  • amount必须大于0 — 不可能有负数订单
  • status只能是5种之一 — “processing"之类的随意值被拒绝
  • shipped_at在status为’shipped’时必须存在 — 防止"配送中但没有配送日期”
  • customer_id必须存在于customers表中 — 防止幽灵客户

DDL是数据的合同。 类型、约束、关系都是显式的。智能体不需要猜测解读方式。规则声明在DB中。

跟往JSONB里塞什么都行做个比较:

JSONB(无模式)DDL(有模式)
有效性验证无——什么都能进DB自动验证
智能体理解需要猜测读声明就行
数据质量时间越长越腐烂约束强制质量
迁移不知道该改什么diff明确

有模式,智能体读DDL就能完美理解数据结构。没模式,智能体读数据样本猜结构。猜测会错。


条件2. 转换可验证

数据会被转换。从CSV到DB。从一个表到另一个表。从原始数据到报表。

转换规则必须是声明式的,结果必须能机械确认。

# 不可验证的转换
对智能体说:"把这个Excel放进DB"
→ 智能体自己映射列
→ 10万行中3000行映射错误
→ 没人知道

# 可验证的转换
对智能体说:"把这个Excel放进DB。
映射规则参考transform.yaml。
放完后比较行数,
确认amount合计跟原始一致。"

把转换规则写在声明文件中,验证转换前后的不变式。这就是可验证的转换。

yongol的DDL → sqlc链式反应就是这个原理的例子。在DDL中声明模式后,sqlc生成类型安全的Go代码。DDL和sqlc之间发生漂移,yongol validate就抓到。从模式到代码无断裂地验证。


条件3. 来源和时间点被追踪

“这个数据是什么时候、从哪里、为什么创建的?”

这个问题机器必须能回答。

CREATE TABLE orders (
    ...
    source      TEXT         NOT NULL,   -- 'web', 'api', 'import', 'migration'
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT now(),
    created_by  BIGINT       REFERENCES members(id),
    updated_at  TIMESTAMPTZ,
    updated_by  BIGINT       REFERENCES members(id)
);

来源(source)、时间点(created_at, updated_at)、行为者(created_by, updated_by)记录在DB中。智能体追查"这个订单为什么是负数?“时:

SELECT source, created_at, created_by FROM orders WHERE amount < 0;
-- source: 'import', created_at: 2026-02-15, created_by: NULL

“2月15日通过import进来的数据产生了负数,且未记录行为者。“有这个信息就能追踪原因。没有就是迷宫。

正如whyso追踪代码的"为什么”,数据的"为什么"也需要追踪。代码有whyso。数据有来源/时间点列来承担这个角色。


条件4. 数据变更也适用棘轮

第6课学的Ratchet Pattern不只适用于代码。也适用于数据。

迁移棘轮:

模式变更请求
→ DDL修改
→ yongol validate(交叉验证通过)
→ 迁移文件自动生成(up + down)
→ 应用到staging DB
→ 验证现有数据完整性
→ 通过 → 应用到production(审批门控)
→ 失败 → 用down文件回滚

yongol已经实现了这个。yongol generate检测DDL变更并自动生成迁移文件。up文件和down文件成对出现。没有不可逆的迁移。

specs/db/
└── users.sql                         # SSOT — 在这里编辑

artifacts/db/
├── .latest_schema.sql                # 基线快照
└── migrations/
    ├── 0001_initial.up.sql
    ├── 0001_initial.down.sql
    ├── 0002_add_users_email.up.sql
    └── 0002_add_users_email.down.sql

模糊的变更(重命名列、类型转换、NOT NULL回填)用DDL注释提示(-- @rename-- @cast-- @backfill)向智能体明确意图。

棘轮保证的是: 迁移成功就进入下一步。失败就回退到之前的状态。不会停在中间。跟代码的棘轮原理相同。


模式就是我制定的法律

这里出现了贯穿整个课程的哲学。

第5课我们学了"约束就是契约”。法治的三个条件——可验证、违规有定义、可强制执行——同样适用于代码。

在数据中这个原理更直接地体现。

数据库有模式。

模式**定义(定義)**什么是有效数据什么不是。NOT NULL、FOREIGN KEY、CHECK——数据必须通过这些约束才能存储。不管谁放数据。人放、程序放、AI放——满足模式就进去,否则拒绝。1970年以来一直有效的模式。

模式就是法律。我制定的法律。

再代入法治原理:

法治数据模式
可验证CHECK (amount > 0) — DB自动验证
违规有定义NOT NULL违规、FOREIGN KEY违规 — 离散的
可强制执行违规时INSERT被拒绝

这与作者的世界观相连:

法不是正义(正義)而是定义(定義)。

法不保证正义。模式也不保证数据的"真相”。但法保证定义。模式保证有效性。

这个最小保证——事前可知、机械可验证、违规被拒绝——是人类花了数千年用血换来的,也是数据库用50年证明了的。

没有模式的数据是没有法律的社会。谁都能放任何数据。错了也不知道。3个月后发现。

有模式的数据是法治社会。违反规则立刻被拒绝。理由明确。修正后可以重试。


从非结构化到结构化

现实中的数据大部分是非结构化的。

  • Excel文件 — 每个工作表格式不同
  • 通话录音 — 音频文件
  • 会议记录 — 自由格式文本
  • PDF文档 — 半结构化
  • 邮件 — 自然语言

智能体要处理这些数据,结构化必须先行

非结构化数据 → 决定模式 → 转换 → DB

Excel       →  声明DDL   → import  → PostgreSQL
通话录音     →  STT       → 结构化  → 摘要DB
会议记录     →  解析      → 提取行动项 → 任务DB
PDF         →  OCR+解析  → 提取字段  → 文档DB

注意关键:人决定模式(结构),智能体执行转换。

第5课学的"决策与实现分离"在这里同样适用。

  • 决策(人): “客户信息需要姓名、电话、邮箱、注册日期。电话必须01开头。”
  • 实现(智能体): 读Excel,映射列,把符合约束的数据放进DB。

智能体执行转换,DB的约束来验证。违反约束的数据被拒绝并报告原因。人检查被拒绝的数据并决定:修正后重新放入还是丢弃。


数据验证模式:三层防线

数据验证不是一层而是三层防线。

第一道防线 — DB约束(最强力)

NOT NULL           -- 防止空值
UNIQUE             -- 防止重复
CHECK (amount > 0) -- 范围限制
FOREIGN KEY        -- 参照完整性
DEFAULT            -- 保证默认值

DB约束无法绕过。不管写什么代码、用什么智能体,违反约束的数据进不去。这就是第一道防线的原因。最重要的规则必须声明为DB约束。

第二道防线 — 业务规则(Rego)

有些规则DB约束表达不了。“折扣超过30%需要经理审批”、“同一客户一天3次以上订单是可疑交易”。这些规则用Rego声明。

这个也不需要会读。用自然语言说规则智能体就翻译成Rego:

# 订单验证规则
deny[msg] {
    input.order.discount > 30
    not input.approver.role == "manager"
    msg := "超过30%的折扣需要经理审批"
}

warn[msg] {
    count(input.customer.orders_today) >= 3
    msg := "同一客户今日3次以上订单 — 需确认可疑交易"
}

用自然语言说就是:

  • “折扣超30%但经理没批准就拒绝” = 第一条规则
  • “同一客户今天下单3次以上就警告” = 第二条规则

Rego规则是yongol的SSOT之一。第4课学的交叉验证在这里也起作用:SSaC声明了@auth就必须在Rego中有对应规则。没有的话yongol validate就抓到。

第三道防线 — 迁移棘轮

模式变更时验证现有数据是否与新模式兼容。

三道防线的分工:

防线负责违反时
第一道:DB约束数据完整性INSERT/UPDATE被拒绝
第二道:Rego规则业务逻辑警告或阻断
第三道:迁移棘轮模式演进回滚或回填

相同模式,不同领域

看到第8、9、10课横跨的共同模式了吗?

第8课:代码第9课:系统第10课:数据
可读吗?filefunc(1文件1概念)/health(结构化JSON)DDL(声明式模式)
可验证吗?go test + tsmaCI/CD + 健康检查DB约束 + Rego
可回退吗?git revert之前的镜像回滚migration down
进度持久化吗?session.json(tsma)Terraform statemigration历史
决策与实现分离吗?SSOT → 代码生成声明式配置 → 执行模式 → 数据

全部是同样的结构:

声明、验证、锁定、持久化。

在代码中有效的原理在系统中、在数据中同样有效。不是新发明。是把同一原理应用到新领域。

第5课学的"约束就是契约"的原理贯穿三个领域:

  • 代码的契约: filefunc 22条规则、yongol 287条交叉验证规则
  • 系统的契约: Docker Compose、Terraform、CI/CD流水线
  • 数据的契约: DDL约束、Rego规则、迁移棘轮

合理的约束可验证、违规有定义、可强制执行——任何领域都收敛。


DDL → sqlc → 代码:无断裂的链式反应

来看yongol中数据的链式反应具体怎么运作。

这些代码也不需要会读。理解从DDL开始到代码自动串联的流程就够了。

1. 在DDL中声明模式
   CREATE TABLE orders (
       id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
       customer_id BIGINT NOT NULL REFERENCES customers(id),
       amount DECIMAL(12,2) NOT NULL CHECK (amount > 0),
       status TEXT NOT NULL CHECK (status IN ('pending','confirmed','shipped'))
   );

2. 在sqlc中声明查询
   -- name: GetOrder :one
   SELECT * FROM orders WHERE id = $1;
   
   -- name: ListOrdersByCustomer :many
   SELECT * FROM orders WHERE customer_id = $1 ORDER BY created_at DESC;

3. yongol validate交叉验证
   - DDL的列和sqlc的查询参数一致吗?
   - OpenAPI的响应字段和DDL的列一致吗?
   - SSaC中用@get引用的查询在sqlc中存在吗?

4. yongol generate生成类型安全的Go代码
   - sqlc → Go struct + query函数
   - 编译时捕获类型不匹配

从模式(DDL)开始到代码生成,中间没有人的解读介入的余地。DDL变了sqlc就变,sqlc变了生成代码就变,代码变了测试就抓。

这就是数据驱动开发中不发生漂移的结构。


氛围编程者的数据实践

“我不懂DB怎么办?”

不懂就对了。智能体来写DDL。你要做的只是决定存什么

对智能体说:"做个客户管理表。
- 需要姓名、电话、邮箱、注册日期
- 电话必须010开头
- 邮箱必须唯一
- 注册日期自动填入
用DDL做,加上约束。"

智能体写DDL。yongol validate跟其他SSOT交叉验证。通过就生成迁移。即使你读不懂DDL,决策(“电话必须010开头”)你能做。

决策用自然语言做。决策的实现用DDL做。DDL的验证由机器做。


真相以光速消逝

这里取出本课程最后一个哲学。

物理学告诉我们一个冷酷的事实。事件发生的瞬间,那个真相以光速消逝。1秒前的月亮是1.3秒前的月亮。100亿光年外的星系是100亿年前的样子。

真相在物理上消逝。留下的只有真相的碎片——声明。

“我看到了这个。““这个测量值是这样的。““这个来源这样说。"——全是声明。有来源、有时间点、有可信度的声明。

数据也一样。DB里的"订单金额50,000元"不是真相。是声明。是某人在某个时间点通过某个路径放入的声明。所以来源和时间点很重要。没有来源的数据是没有证据的声明。没有时间点的数据是没有日期的报纸。

模式做的事就是给声明赋予结构。“这个声明必须是这种形态,必须满足这些约束,必须来自这个来源。“这个结构就是数据的法律。

没有来源的不是我的数据。 没有时间点的不是我的记录。 没有模式的不是我的系统的数据。


实操(DB)

准备物: 如果第9课用Docker启动了PostgreSQL就直接用。还没有就对智能体说:“用Docker启动PostgreSQL”

目标: 把非结构化数据转换为Agent Operable Data。

步骤1 — 模式设计

对智能体说:"分析这个CSV文件并生成DDL。
- 推断每列的类型
- 添加NOT NULL、UNIQUE等适当约束
- 用CHECK约束也加入业务规则
  (例如:amount > 0, status IN ('active','inactive'))
- 添加来源和时间点追踪列
  (source, created_at, created_by)"

步骤2 — 数据导入

对智能体说:"按生成的DDL把这个CSV放进DB。
- 违反约束的行放到rejected.csv并报告
- 告诉我总行数和成功/失败数
- 确认金额合计跟原始CSV一致"

步骤3 — 约束违反检测

  • 确认rejected.csv中的行:为什么被拒绝?
  • 确认DB约束是否真的过滤了错误数据
  • 故意INSERT错误数据 → 确认被拒绝

步骤4 — Rego业务规则(可选)

对智能体说:“安装OPA并运行这个Rego规则”

对智能体说:"给订单数据做Rego验证规则。
- 折扣率超50%就deny
- 同一客户一天3次以上订单就warn
- 配送状态是'shipped'但没有配送日期就deny"
  • 确认Rego规则是否在实际数据中找到违规
  • 让智能体"找出这些数据中的规则违反”,机械地确认结果

步骤5 — 迁移棘轮

如果没装yongol就对智能体说:“帮我装yongol”

对智能体说:"给orders表添加delivery_estimate列。
- 修改DDL
- 通过yongol validate
- 生成迁移文件(up + down)
- 应用到现有数据
- 出问题就用down文件回滚"

确认事项:

  • DDL约束是否真的拒绝了错误数据?
  • Rego规则是否检测到了业务违反?
  • 迁移失败时回滚有效吗?
  • 整个过程中智能体有"猜测"的时刻吗,还是全部基于"声明”?

第10课愿景:说了就做 — 代码、系统、数据

第1课我们从这里开始。

“做个待办应用。”

代码出来了。3个功能还行。5个就崩了。

第10课结束时我们站在这里:

第1课的世界:
  "做个应用" → 代码出来 → 5个功能就崩

第10课的世界:
  "做个订单管理SaaS"
  → 决策:定义模式、声明功能、声明规则
  → 代码:yongol从SSOT生成(第8课)
  → 系统:CI/CD自动化构建-部署-监控(第9课)
  → 数据:DDL强制模式,Rego验证规则(第10课)
  → 审批:人只按"批准"按钮
  → 200个端点也不崩
课程学了什么结果
第1课氛围编程的现状“说了代码就出来”
第2课为什么会崩漂移、上下文消失、谄媚偏差
第3课怎么防Hurl、Git、CI/CD
第4课200端点之墙yongol — 声明式SSOT
第5课有缰绳的AIReins Engineering三支柱
第6课锁定前进Ratchet Pattern — 单向棘轮
第7课反向利用谄媚IFEval — 反馈创造收敛
第8课结构化代码filefunc + tsma — Agent Operable Codebase
第9课结构化系统4个条件 — Agent Operable System
第10课结构化数据模式就是法 — Agent Operable Data

代码 → 系统 → 数据。 三个领域中同一原理起作用。

声明、验证、锁定、持久化。 决策由人,实现和验证由机器。 不是人治而是法治。

从第1课的"做个待办应用"到第10课的"做个订单管理SaaS”——变的不是模型大小。是结构。给智能体套了缰绳、铺了铁轨、立了法。

说了就做。 不只是代码,还有系统和数据。 要做到这一点,必须有缰绳、有铁轨、有法律。 设计那个缰绳、铁轨和法律的,就是Reins Engineering。


你在一行代码都读不懂的状态下开始了这个课程。完成第10课的现在,变的不是你能读代码了。你知道了该让智能体做什么、为什么这样做、怎么验证智能体的报告。这就是决策者的能力。


相关文章


Reins Engineering 全部课程

课程标题
第1课如何指挥AI
第2课如何不信任AI
第3课不会崩溃的应用
第4课将决策移出代码
第5课有缰绳的AI
第6课通过就锁定
第7课翻转谄媚
第8课智能体的工厂
第9课代码之外的自动化
第10课数据的法则

参考资料来源

  • Stanford,“Lost in the Middle: How Language Models Use Long Contexts”(2024)— 相关信息埋在上下文中间时30%+性能下降(从第8课再引用)
  • Amazon,“Context Length Alone Hurts LLM Performance”(2025)— 不相关token即使是空格也导致13.9-85%性能下降(从第8课再引用)
  • E.F. Codd,“A Relational Model of Data for Large Shared Data Banks”(1970)— 关系数据库模型,基于模式的数据完整性理论基础
  • OPA(Open Policy Agent)/ Rego — 声明式策略语言,在代码外部验证业务规则
  • yongol DDL → sqlc链式反应 — 从模式到类型安全代码的无断裂交叉验证结构
  • 法治(Rule of Law)原理 — 可验证性、违规定义、可强制执行的三个条件同样适用于代码/系统/数据
  • “法不是正义(正義)而是定义(定義)” — 数字法治哲学,把模式呈现为法律的类比
  • “真相以光速消逝” — 从物理观测的局限出发的数据来源/时间点追踪的依据