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. 规则与上限
- 一个 Object Type 恰好一个主键属性;Object Type 名在本体里不分大小写唯一,
ontology、object、link这类保留字不能用。 - 名字:Object Type 与 Interface 用 PascalCase;Property、Link 两端、Value Type、Shared Property 用 camelCase;Action Type 用 kebab-case。
- 派生属性最多沿关系 3 跳;派生属性不进 Semantic SQL。
- 定义里写了不认识的字段直接被拒,错误信息给出路径。
- 数据流的字段白名单里,证件号、银行卡、密码这类字段直接拒收(见连接)。
- 数据源永远只读;要真的改源系统,给 Action 配 writeback webhook(源系统先接受,语义层才记一笔)。
- 敏感数据用 Markings、对象安全策略(行)与属性安全策略(列)管住,见 Semantic「数据安全」。
6. 常见问题
对象和数据库表一一对应吗? 不一定。一个对象类型可以汇集几张表(订单头 + 订单行),一张表也可能拆成几个对象类型。按业务里的一件东西建,不按表建。
已经有 ERP 报表了,为什么还要建对象? 报表回答「表里有什么」,对象回答「这件东西现在是什么状态、归谁、和谁有关、下一步谁来做」。报表不能被受控地修改,也不会在变化时触发下一步。
建多少个对象类型合适? 从支撑决策的核心对象起步(常见是 8 个左右),按真实需要扩展。在用的不完美本体好过设计中的完美本体。
对象的数据多久更新一次? 由数据源决定:数据流的发布端有变化就推;Data Connection 在有人读、数据比 freshness.maxAgeSeconds 旧的时候同步,?fresh=true 最多等 20 秒,到时还没同步完就先返回现有的数据。
本页由 developer/docs/objects.md 生成 · Markdown 原文 · llms.txt