查看 Markdown

Object:从表到对象

Object(对象)是 Semantic 里企业数据的基本单位:一张订单、一种物料、一个工单、一位客户。它不是数据库里的一行,而是一件业务里的东西——有名字、有属性、和别的对象有关系、能被受控地修改、变了之后会自动触发下一步。人、应用和智能体读的是同一份对象。

这一页讲清四件事:对象是什么、一个对象由哪几部分组成、怎样从零构建一个对象、和原来直接查表的做法比有什么不同。每一部分的完整写法在 Semantic,数据怎么进来见 数据管道,一整套交付方法见 Academy 的 FDE 课程。示例取自虚构的 Demo Company(一家生产工业泵与阀门的制造企业,数据全部虚构)。

ERP / MES / OA(只读)──▶ 数据管道 ──▶ Object Type(属性 · 关系)──▶ Action · Function · Automation
                                              │
                                              └──▶ 应用 · 智能体 · SQL · 订阅(读同一份对象)

1. 对象是什么

数据库里的一行 Semantic 里的一个对象
名字 表名、列名照源系统(dbo.SalesOrderLine、dueDt) 业务名字:SalesOrderLine 订单行、dueDate 交期,带单位与同义词
口径 写在每个查询、每个脚本里,各写一遍 写在对象类型的定义里,一处定义、处处一致
关系 靠 join,谁写查询谁知道 Link Type 两端都有名字:order.lines、line.order
改数据 回写源系统,或者另建一张表 成员和智能体只走 Action:参数校验、提交条件、谁能做、留痕
权限 按库、按表开账号 按对象类型、按行、按列,读的人是谁就按谁判定
变了做什么 定时任务每隔几分钟查一遍 Automation 在对象变化时触发
谁来读 会写 SQL 的人 人(Object Explorer、应用)、智能体(MCP、CLI)、程序(SDK、SQL)

对象由 Object Type(对象类型) 定义:一类对象的主键、标题和属性。对象的值从数据源同步进来(按设计不由动作改),人和智能体的改动经 Action 落在语义层,两层叠起来就是对象的当前值。

2. 一个对象的组成

一个对象 = 名词(它是什么、和谁有关)+ 动力(它能做什么、变了之后做什么)。以 Demo Company 的订单行 SalesOrderLine 为例:

部分 构件 回答什么问题 SalesOrderLine 的例子
名词 Object Type 这一类东西是什么、用什么认出它 主键 lineId(<订单号>-<行号>),标题 productCode
Property 它有哪些特征,各是什么类型 qty(integer)、dueDate(date)、status(值类型 orderStatus)、promisedDate(只在语义层维护)
Link Type 它和谁有关 order(所属订单)、workOrders(生产它的工单)
Interface 它和别的类型有什么共同的形状 实现 Schedulable(dueDate、status),和工单、采购订单一起查「今天到期」
Shared Property / Value Type 哪些字段跨类型复用、取值有什么约束 dueDate 是共享属性;orderStatus 只能是 已下单 / 已排产 / 生产中 / 待发货 / 已发货 / 已关闭
动力 Derived Property 哪些值读的时候算 沿关系数出工单数(最多 3 跳;派生属性不带筛选条件,「未完成的工单数」这种要筛选的写成函数)
Action 谁能怎样改它 confirm-delivery-date:确认交期,只能由人执行,开 Action Log
Function 复杂的逻辑怎么算 deliveryRisk:按距交期天数、未关闭缺料、工单进度算风险
Automation 它变了、或者到点了,自动做什么 overdue-lines-daily:工作日 08:30,交期已到而未发货的订单行给营业负责人发邮件

动力部分让对象「会做事」:只能看的对象只是报表;能被受控地改、能自动触发下一步,对象才进入业务流程。

3. 从零构建一个对象

下面八步在 Demo Company 上建出 SalesOrderLine。命令要登录(aidc login)并有本组织的开发者权限;智能体作者走第 8 步的分支与提案。

① 从决策出发

先列出业务每周要做的决策,再倒推需要的对象:营业员每天要「确认交期」,计划员每天要「决定先做哪张工单」——于是需要订单行、工单、物料、缺料四个对象,以及确认交期、登记缺料两个 Action。不要从 ERP 的表清单出发:表是系统的形状,对象是业务的形状。

② 定义对象类型与属性

一个 Object Type 恰好一个主键属性(复合键在数据源里拼成一列);名字照规范:Object Type 用 PascalCase,属性用 camelCase。

{
  "kind": "objectType",
  "apiName": "SalesOrderLine",
  "title": "订单行",
  "schema": {
    "titleColumn": "productCode",
    "columns": [
      { "name": "lineId", "type": "string", "primaryKey": true, "title": "订单行" },
      { "name": "orderNo", "type": "string", "title": "订单号" },
      { "name": "productCode", "type": "string", "title": "产品" },
      { "name": "qty", "type": "integer", "title": "数量", "unit": "台" },
      { "name": "dueDate", "type": "date", "title": "交期", "synonyms": ["交货日期", "要货日期"] },
      { "name": "status", "type": "string", "title": "状态" },
      { "name": "promisedDate", "type": "date", "title": "承诺交期", "writeback": true }
    ]
  }
}

writeback: true 的属性只在语义层维护:它不从数据源来,由 Action 写。其余属性来自数据源,Action 的规则不要写它们:平台会给设计警告;写了,这笔改动会一直盖住之后同步来的值。

③ 接上数据

数据源写在对象类型定义的 datasources 里——改数据源就是改本体。Demo Company 的 ERP 在工厂内网,用 Data Connection 读:每张表一个 TableImport,结果写成 Dataset,再映射到属性(完整做法见 数据管道):

"datasources": [
  { "type": "dataset", "dataset": "sales-order-lines",
    "propertyMapping": { "lineId": "line_id", "orderNo": "order_no", "productCode": "product_code", "qty": "qty", "dueDate": "due_date", "status": "status" } }
]

主键必须映射;只在语义层维护的属性(promisedDate)和派生属性不映射。定义落了之后平台先全量同步一次,之后只把变了的行写成对象。

④ 建关系

一对多用外键,两端各起名字:

{
  "kind": "linkType",
  "apiName": "salesOrderLines",
  "title": "订单的订单行",
  "schema": {
    "from": "SalesOrder", "to": "SalesOrderLine", "cardinality": "ONE_TO_MANY",
    "apiNameAtoB": "lines", "apiNameBtoA": "order",
    "foreignKey": { "side": "to", "property": "orderNo" }
  }
}

多对多每条链接单独存;关系如果带状态、要审批、有两个以上属性,升级成中间对象(例:缺料不是物料和工单之间的关系,而是有负责人、有状态的 Shortage 对象)。

⑤ 定义 Action

成员和智能体改数据只走 Action:参数、规则、提交条件、留痕都写在定义里(直接写数据的接口只给开发者)。

{
  "kind": "actionType",
  "apiName": "confirm-delivery-date",
  "title": "确认交期",
  "schema": {
    "parameters": [
      { "name": "line", "type": "object", "objectType": "SalesOrderLine", "required": true },
      { "name": "promisedDate", "type": "date", "required": true }
    ],
    "rules": [{ "type": "modifyObject", "objectType": "SalesOrderLine", "object": "$line", "values": { "promisedDate": "$promisedDate" } }],
    "submissionCriteria": [{
      "condition": { "type": "comparison", "left": { "user": "authorKind" }, "operator": "is", "right": { "literal": "human" } },
      "failureMessage": "交期是对客户的承诺,只能由人确认"
    }],
    "actionLog": true
  }
}

提交条件里比较的值可以是参数(对象参数可带 property)、常量,或执行人({ "user": "id" | "name" | "role" | "authorKind" })。「只有人能确认」用肯定的写法 authorKind is human;用了 authorKind 的 Action 暂时只能经 v1 接口、CLI 或 Semantic 界面执行。先只校验再执行:client.action("confirm-delivery-date").applyAction({…}, { $validateOnly: true })。

⑥ 写函数(需要时)

派生属性够用就不写函数(沿关系 count / sum / avg / min / max,最多 3 跳)。更复杂的逻辑写成函数:一个 ES module,默认导出 (params, ctx) => 结果,ctx 读对象;语义化版本,发布后不能改。

aidc semantic functions publish delivery-risk.js --api-name deliveryRisk --version 1.0.0 \
  --output '{"type":"integer"}' --parameters '{"lineId":{"dataType":{"type":"string"}}}'

⑦ 加自动化

数据一变或者到点就做事,不写定时任务。「交期已到而未发货」是因为时间过去才进集合的,用 Run on all objects;新出现的缺料用 Objects added。效果可以是 Action、Function 或邮件通知,详见 Semantic「自动化」。

aidc semantic automations upsert --ontology cell-demo --file overdue-lines-daily.json --dry-run
aidc semantic automations upsert --ontology cell-demo --file overdue-lines-daily.json

⑧ 提交、验证、发版本

aidc semantic define ontology/ --dry-run       # 整批校验:引用是否闭合、相对上一个语义版本有哪些破坏性变更
aidc semantic define ontology/
aidc semantic publish --notes "订单行、确认交期"
aidc semantic objects SalesOrderLine --where '{"status":"生产中"}' --json

智能体不能直接改 main:它在分支上改、开提案,人在网页上逐项批准后合并(aidc semantic branch create / modify / propose,见 智能体接入)。

4. 和原来的做法比

原来:直接查表、定时刷新 现在:Semantic 对象
数据怎么来 每个看板、每个脚本各查一遍 ERP 源头只读一次,只把变了的行写成对象
什么时候算 定时任务每 5–10 分钟跑一次,没人看也跑 有人读、数据过期才同步(自动化引用的类型按评估频率);应用没人打开不计算
看板怎么发 生成整页 HTML 再上传;内网页面靠临时隧道从外网打开 Applications 只存定义,打开时按看的人的权限现算
口径 散在各个脚本里,改一处漏一处 写在对象类型里,人、应用、智能体同一份
改数据 回写源系统,或者在看板旁边另记一张表 Action:校验、权限、Action Log,同步不会冲掉人的改动
数据是不是新的 脚本断了也没人知道,看板静默变旧 类型带同步状态(最近同步时间),应用有数据新鲜度组件;数据健康检查失败会告警
智能体 拿库的账号自己写 SQL,读到不该读的 读对象集、改数据只走 Action,权限按对象判定

一个每 10 分钟刷新一次的看板,一天跑 144 次(1440 ÷ 10);24 个这样的脚本一天 3,456 次(示例)。按需之后,同一类数据一小时最多同步 3600 ÷ 300 = 12 次(freshness.maxAgeSeconds 缺省 300 秒),而且只在有人读的时段发生。账怎么算、怎么迁,见 数据管道。

5. 规则与上限

6. 常见问题

对象和数据库表一一对应吗? 不一定。一个对象类型可以汇集几张表(订单头 + 订单行),一张表也可能拆成几个对象类型。按业务里的一件东西建,不按表建。

已经有 ERP 报表了,为什么还要建对象? 报表回答「表里有什么」,对象回答「这件东西现在是什么状态、归谁、和谁有关、下一步谁来做」。报表不能被受控地修改,也不会在变化时触发下一步。

建多少个对象类型合适? 从支撑决策的核心对象起步(常见是 8 个左右),按真实需要扩展。在用的不完美本体好过设计中的完美本体。

对象的数据多久更新一次? 由数据源决定:数据流的发布端有变化就推;Data Connection 在有人读、数据比 freshness.maxAgeSeconds 旧的时候同步,?fresh=true 最多等 20 秒,到时还没同步完就先返回现有的数据。

本页由 developer/docs/objects.md 生成 · Markdown 原文 · llms.txt