在分支上写定义

第 3 课 · 共 6 课 约 8 分钟

定义文件写在目录里;先试跑再写入再校验;在分支上自测;认出破坏性变更。

本课目标

读完这一课,你将能够

  • 建分支,把新Object Type和关系写成目录里的定义文件
  • 用 --dry-run 试跑、validate 校验,读懂结果并修正
  • 按命名规则写定义,认出会变成破坏性变更的改动

建分支,写定义文件

智能体确认了缺口,第一步是开分支。分支从 main 当前的语义版本分出来,记下这个基线。在它上面怎么改,都不影响正在用的本体(Ontology)。

export AIDC_AGENT_ID=procurement-agent
aidc semantic branch create delivery-promise --description "让「哪些供应商在哪些物料上晚了」能回答"

分支名用小写字母、数字和 . _ -。定义写在一个目录里:每个 .json 文件放一个词条,也可以一个文件放一个数组。这次有两个词条:新的Object Type DeliveryPromise,和把它挂到采购订单上的关系。动笔前,先把说明书里相关的类型读一遍,确认名字不会撞。先写Object Type,放在 ontology-change/DeliveryPromise.json:

{
  "kind": "objectType",
  "apiName": "DeliveryPromise",
  "title": "交期承诺",
  "description": "供应商对某张采购订单承诺的到货日期,以及实际到货日期。一张订单可以有多次承诺。",
  "schema": {
    "pluralDisplayName": "交期承诺",
    "synonyms": ["供应商承诺", "到货承诺", "交期"],
    "titleColumn": "promiseId",
    "columns": [
      { "name": "promiseId", "type": "string", "primaryKey": true, "title": "承诺编号" },
      { "name": "poNo", "type": "string", "title": "采购订单", "references": { "entity": "purchaseOrder", "column": "poNo" } },
      { "name": "promisedDate", "type": "date", "title": "承诺到货日", "synonyms": ["交期", "承诺日期"] },
      { "name": "receivedDate", "type": "date", "title": "实际到货日", "comment": "还没到货时为空" }
    ]
  }
}

关系放在 ontology-change/purchaseOrderPromises.json:一张采购订单对应多个承诺,外键在承诺这一端。

{
  "kind": "linkType",
  "apiName": "purchaseOrderPromises",
  "title": "采购订单的交期承诺",
  "schema": {
    "from": "purchaseOrder",
    "to": "DeliveryPromise",
    "cardinality": "ONE_TO_MANY",
    "apiNameAtoB": "promises",
    "apiNameBtoA": "purchaseOrder",
    "foreignKey": { "side": "to", "property": "poNo" }
  }
}

写对定义

  • 名字:Object Type 与 Interface 用 PascalCase(DeliveryPromise);属性和 Link Type 两端用 camelCase(promisedDate);Action 用 kebab-case。
  • 主键:新 Object Type 只用一个主键属性(promiseId)。新建或修改主键时,复合主键会被拒绝。仅原样保留的存量复合主键会收到警告。
  • 属性要有业务含义:源系统里的技术列,例如同步时间、批次号,不要搬进来。
  • 写说明和同义词:人和智能体都靠它们找到属性;description 是空的,校验会提醒。
  • 引用要闭合:references 指向的 purchaseOrder.poNo 必须存在,否则校验失败,合并会被挡住。

这份定义里有一个刻意的选择:DeliveryPromise 是独立的对象,不是 purchaseOrder 上的一个属性。因为供应商可以反复改口,一张订单有好几次承诺,是一对多,属性装不下。

同一批里的定义可以互相引用。校验看的是整批改完之后的全体定义:关系引用了同一批里的 DeliveryPromise,不用分先后。分支上的改动以资源为单位记录,新建、修改、删除各算一项,将来审核时也是一个资源一项。

试跑、写入、校验

  1. 先试跑

    --dry-run 只校验、不写入,返回每一项检查的结果。试跑不通过时,命令的退出码是 2。

  2. 再写入

    写入时带上 --expected-version。分支已经被别人或另一个智能体改过,就返回 409,不会互相覆盖。

  3. 最后校验

    validate 跑合并前的检查:引用是否闭合、主键规则、和 main 有没有冲突,另外还有说明是否为空这类设计提醒。失败的项会挡住合并,警告只是提醒。

aidc semantic branch modify delivery-promise ./ontology-change --dry-run
aidc semantic branch modify delivery-promise ./ontology-change --expected-version 0
aidc semantic branch validate delivery-promise

试跑可以反复做:它不写入,读报错、改文件、再试就行。检查结果分通过、警告、失败三档,常见的警告是说明为空、属性太多、名字像源系统的字段名,读一眼就知道怎么改。一个分支 35 天没有动静会转为不活跃,再过 7 天,分支上的改动会被删除,所以别把没用的分支留着。

在分支上自测,认出破坏性变更

按命令明确列出的参数使用 --branch。以下命令支持分支预览;Ontology SQL 的命令行选项没有 --branch。

aidc semantic object-types DeliveryPromise --branch delivery-promise
aidc semantic links purchaseOrder <采购订单主键> promises --branch delivery-promise

有一点要说清楚:分支只改定义,对象数据只有 main 一份。新类型在分支上能验证的是形状:名字、属性、关系走得通;它的对象要等合并、并把数据流绑进来之后才有。自测要如实写下验证了什么、没验证什么,下一节会用到。

加 Object Type、属性和同义词是加法。删类型、删属性、改属性类型、改主键,以及 Action 删参数或新增必填参数,是破坏性变更,审核时要确认。先加替代属性,迁移下游。旧属性是 active 时,先把它改为 DEPRECATED 或 EXPERIMENTAL,并落地这次状态改动,再另开提案删除。

要点

  • 先开分支:它从 main 当前的语义版本分出来,怎么改都不影响正在用的本体。
  • 定义是目录里的 JSON 文件。引用必须闭合。新 Object Type 和 Interface 用 PascalCase;新 Object Type 只用一个主键。
  • 顺序是试跑、写入、校验:--dry-run 不写入,--expected-version 防覆盖,validate 跑合并前的检查。
  • 分支只改定义,数据只有 main 一份:自测要写清验证了什么、没验证什么。
  • 删和改类型是破坏性变更;默认先加后删,35 天不动的分支会转为不活跃。

练一练

在分支上写一次定义

用你自己的业务,或者示例工厂,需要一个有 AIDC_AGENT_ID 的终端。

照 DeliveryPromise 的写法,为你上一节找到的缺口写一个Object Type和一条关系,放进一个目录。

小测

选一个答案,马上看解析。

Q1在分支上新建了 DeliveryPromise,objects DeliveryPromise --branch … 却一个对象也没有。最可能的原因是?

Q2下面哪一个改动是破坏性变更?

Q3写入时带 --expected-version 0 是为了什么?

延伸阅读