Ontology(本体论)数据结构
本文主要为了搞清楚"Palantir Ontology 本体论的本质",它的结构是怎样的,具体怎么用,与ER/OOP/DDD等有什么区别?"。
Palantir官方指出Ontology 主要用于连接数据与现实世界的业务对象、关系和决策流程。它包含 数据 (Data) ,逻辑 (Logic) ,操作 (Action) 三大要素,同时 安全 (Security) 贯穿其中。
- 数据 (Data):事实与状态定义。真实世界的事物映射。
- 逻辑 (Logic):规则、模型、外部回调。把基本逻辑与约束定义在其中。
- 操作 (Action):执行、回写,权限与审计约束。基于数据和逻辑进行决策执行。
一、Ontology 数据结构的七类基本构件
本体(Ontology)不是一种数据库,而是一套逻辑语义层。这套语义层建立在七种基本构件之上,它们相互配合、缺一不可。:
语义类型 + 约束]) -.约束.-> P P([Property
属性]) --> OT([ObjectType
对象类型]) OT -->|关系| LT([LinkType
链接类型]) OT -->|被作用| AT([ActionType
行为契约]) LT -->|被作用| AT AT -->|调用| FN([Function
无副作用计算]) AT -->|触发| SE([Side Effect
Webhook / Notification]) SEC[Permission / Security / Audit
安全横切面] -.约束.-> OT SEC -.约束.-> LT SEC -.约束.-> AT style VT fill:#e0f2fe,stroke:#0369a1,color:#000 style P fill:#dbeafe,stroke:#1d4ed8,color:#000 style OT fill:#dcfce7,stroke:#15803d,color:#000 style LT fill:#bfdbfe,stroke:#1d4ed8,color:#000 style AT fill:#ede9fe,stroke:#6d28d9,color:#000 style FN fill:#fef3c7,stroke:#b45309,color:#000 style SE fill:#fce7f3,stroke:#be185d,color:#000 style SEC fill:#f1f1f1f1,stroke:#999,color:#000
| 构件 | 表达什么 | 一句话定位 | |
|---|---|---|---|
| 1 | Property | 对象的属性(字段) | 给对象贴标签 |
| 2 | ValueType | 属性的语义类型 + 约束(邮箱、ICAO、密级、币种等) | 让 "string" 变成 "Email" |
| 3 | ObjectType | 一类业务实体(车辆、订单、目标、患者) | 业务世界里的"是谁" |
| 4 | LinkType | 对象之间的具名关系(ORDERED_BY、ENGAGING) |
"谁跟谁有什么关系" |
| 5 | ActionType | 一次合法变更的声明式契约(参数 / 校验 / 规则 / 权限 / 审计) | "能做什么 + 做了会怎样" |
| 6 | Function | 无副作用的计算(评分、风险、推荐、规则判断) | "怎么算出来" |
| 7 | Side Effect(Webhook / Notification) | Action 落库以外的事:通知、写回外部系统、触发管道 | "改完之后还要做什么" |
横切面上的是 Permission / Security / Audit:基于 Role / Classification / Purpose 的三维授权 + Audit Log,作用于每一个构件。
1.1 Property(属性)
最基础的构件是对象的字段。Property 需要:
- 一个唯一 API 名(程序访问用);
- 一个类型 :基础类型(
string/integer/boolean/timestamp/geoPoint/ ...)或 ValueType; - 可选:是否可空、是否可搜索、显示名、单位、密级。
注:根据官方对支撑数据源的约束,Property 的物理列不能是
MapType或StructType。嵌套结构需要拆成独立 ObjectType,或用 ValueType 的 struct 约束表达。
1.2 ValueType(语义类型)
ValueType是字段的值类型,语义化表达便于数据按统一规范处理,也便于AI理解。Palantir 官方原文:"Value types are semantic wrappers around a field type that include metadata and constraints"------把基础类型"语义化",并集中表达校验。
示例:
json
{
"ValueType": "Email",
"baseType": "string",
"constraint": { "type": "regex", "pattern": "^[^@]+@[^@]+\\.[^@]+$" }
}
{
"ValueType": "CountryCode",
"baseType": "string",
"constraint": { "type": "length", "min": 3, "max": 3 }
}
{
"ValueType": "USDAmount",
"baseType": "decimal",
"constraint": { "type": "range", "min": 0 },
"metadata": { "currency": "USD", "scale": 2 }
}
约束:
- ValueType 与 Space 绑定,默认 Ontology 不支持------必须在用户自建 Space 内;
- 可作用于数组元素(
array<Email>的每一项都被校验)和 struct 字段; - 常见用法:邮箱 / URL / UUID / 枚举 / 单位化数值 / 国家代码 / ICAO 机场代码 / NSN 国军标编号等。
工程价值在于:校验逻辑只写一次,所有用到该 ValueType 的 Property、Function 参数、Action 参数、管道字段都能自动生效。
1.3 ObjectType(对象类型)
ObjectType是数据对象本身的类型,每个对象都有自己的业务类型。数据拥有业务类型使得数据具有特定的业务含义,成为了一个业务对象,这样便于统一逻辑处理以及AI理解。
示例:
json
{
"ObjectType": "Order",
"primaryKey": "orderId",
"titleKey": "orderNumber",
"properties": {
"orderId": { "type": "string" },
"orderNumber": { "type": "string", "searchable": true },
"customerEmail": { "valueType": "Email" },
"totalAmount": { "valueType": "USDAmount" },
"status": { "type": "enum", "values": ["DRAFT","PLACED","SHIPPED","DELIVERED","CANCELLED"] },
"placedAt": { "type": "timestamp" }
},
"indexes": [["status"], ["customerEmail", "placedAt"]],
"security": { "classification": "INTERNAL" }
}
关键字段:
primaryKey:必须确定(Palantir 官方明确警告:非确定性主键会导致用户编辑丢失、链接失效);titleKey:界面显示用的"对象名"------例如订单卡片标题用orderNumber,而不是orderId;indexes:影响 OSS 查询性能的提示(OSv2 内部根据后端索引能力做映射);security:默认密级;可被字段级security:进一步细化。
1.4 LinkType(链接类型)
LInkType是带有数据库思路的数据结构设计,Ontology把对象之间的关系直接体现在数据结构中。Ontology把关系作为一等公民:它不是简单外键,而是带 cardinality、属性与密级的独立类型:
json
{
"LinkType": "ORDERED_BY",
"from": "Order",
"to": "Customer",
"cardinality": "many-to-one",
"properties": {
"placedVia": { "type": "enum", "values": ["WEB","APP","CALL_CENTER","B2B_API"] }
}
}
支持三种 cardinality关系:one-to-one / one-to-many / many-to-many。OSDK 据此自动生成 SingleLink(用 .get() 取)或 MultiLink(用 .all() 取)字段。多对多还可以挂连接表(join table)属性------例如"某员工被某项目分配的角色与起止时间"。
1.5 ActionType(行为契约)
ActionType 是一种声明式的行为契约,它是整个 Ontology 流程的"声明式总指挥。它与面向对象代码中"绑定在具体实例上的 Service 方法"类似,但又有着核心差异。ActionType可脱离对象存在,Action 不是某个对象上的方法。Action是横跨对象的,可以被 UI / OSDK / Agent 调用的标准化行为。
json
{
"ActionType": "PlaceOrder",
"parameters": {
"customer": { "type": "ObjectReference", "objectType": "Customer" },
"lineItems": { "type": "array", "items": { "type": "ObjectReference", "objectType": "Product" } }
},
"submissionCriteria": [
{ "rule": "${inventorySufficient(lineItems)}", "failureMessage": "存在库存不足的商品" },
{ "rule": "$customer.status == ACTIVE", "failureMessage": "客户账号已停用" }
],
"rules": [
{ "type": "createObject", "objectType": "Order", "values": { ... }, "bind": "order" },
{ "type": "createLink", "linkType": "ORDERED_BY", "from": "order", "to": "customer" },
{ "type": "modifyObject", "object": "lineItems[*]", "edits": { "reservedQty": "+1" } }
],
"permissions": { "roles": ["Customer","CSRAgent"], "purpose": "order-fulfillment" },
"sideEffects": [
{ "type": "notification", "recipients": ["$customer"], "template": "ORDER_CONFIRMATION" },
{ "type": "webhook", "kind": "side-effect", "target": "warehouse-system" }
],
"audit": true
}
ActionType由五块构成(每块都是声明式):
| 块 | 表达什么 | 不通过会怎样 |
|---|---|---|
parameters |
入参类型、是否必填、关联对象类型 | UI 不让提交,API 返 400 |
submissionCriteria(前称 Validations) |
业务规则、用户上下文、参数约束 | 返回 INVALID + 可读 failure message |
rules(也称 Action Logic) |
实际变更:createObject / modifyObject / createLink / modifyLink / deleteLink / invokeFunction / emitEvent |
原子执行,任一失败整体回滚 |
permissions |
Role / Group / Purpose | 返回 403 |
sideEffects |
Webhook / Notification | 见 1.7 |
1.6 Function(逻辑外挂与计算中心)
复杂规则校验、算法编排以及外部第三方数据查询,通常会封装在 Function on Objects 中,而不是直接塞进 ActionType 的声明式契约里。这是 Foundry 实现"动静分离"的核心架构设计:ActionType 负责定义变更的边界与意图,而 Function 则作为 OSDK 中的代码扩展点(通常用 TypeScript 实现),负责执行逻辑的计算与推演。
typescript
import { Function, Objects } from "@foundry/functions-api";
import { Product } from "@foundry/ontology-api";
export class InventoryFunctions {
@Function()
public inventorySufficient(items: Product[]): boolean {
return items.every(p => p.stockOnHand > 0);
}
@Function()
public computeShipping(weightKg: number, destZip: string): number {
// 调外部费率表或内部规则
return base + perKg * weightKg;
}
}
在 Foundry 架构中,Function 主要承担以下三类职责:
-
行为准入(Submission Criteria) :在 Action 触发前进行引用(例如:
${inventorySufficient(...)}),作为高阶业务规则的准入卡关。 -
派生计算(OSDK Query):在数据消费端承担动态派生计算,为前端视图和复杂报表提供实时渲染支持。
-
智能调度(AIP Agent Tool):作为原子能力注册至 Logic / Agent Studio,供大模型 Agent 在运行时自主理解并编排调用。
Function vs Action 的边界:"判断该不该改"是 Function,"实际去改"是 Action。Function 只负责"读数据、逻辑计算、给结论";而 Action 才是真正"改数据、留审计底单、触发后续一连串反应"的执行者。
1.7 Side Effect(副作用):Webhook 与 Notification
当 Action 触发对象状态变更后,通常还需要做两类事:通知团队(Notification) 或 同步外部系统(Webhook) 。Palantir 官方将这些"状态持久化之外的联动操作"统一抽象为 Side Effect(副作用)。
Webhook 的两种模式
在配置 Webhook 时,必须严格遵循官方区分的两种时序模式。它们的时机、事务边界和报错行为截然不同:
| 模式 | 时机 | 失败行为 | 允许数量 | 典型场景 |
|---|---|---|---|---|
| Side Effect(默认) | 对象完成修改 之后 异步执行 | 失败不影响已落库的对象状态 | 多个,执行顺序不保证 | 订单已落库,发送通知到 IM 或将订单状态通知给外部 ERP。 |
| Writeback(回写模式) | 对象发生修改 之前 同步执行 | 失败则整笔 Action 熔断,终止修改对象 | 严格限制只能配一个 | 先请求外部供应链 API 获取全局唯一物流追踪号,成功拿号后才允许落库。 |
官方原文:
"if the webhook execution fails, no other changes will be made... Using a writeback webhook guarantees that if the request to the external system fails, no changes will be applied to the Foundry Ontology."
但官方也明确:Writeback 并非完美的分布式事务(ACID)。外部请求可能成功、对象修改可能失败,两者无法做到完全原子。
Notification
通知用户:平台内推送 + 邮件(用户偏好可调)。官方注意点:
"Notifications are sent once all action edits have been applied, however the content of the notification will be generated based on the state of the Ontology before edits are applied."
------通知内容用的是改之前的快照,避免"邮件里看到的状态和数据库里的不一致"。
分支隔离(Branch Isolation 隔离栅栏)
在 Foundry Branch(沙盒分支)上跑 Action 时,Webhook 与 Notification 默认不执行------避免测试环境意外打到生产外部系统。可在 Ontology Manager 显式开启。
1.8 横切:Permission / Security / Audit
三维授权(Role / Classification / Purpose)+ Audit Log,已在《技术原理介绍.md》4.3 和《ontology存储与读写方式.md》5 详述。本文下面的所有实例都默认这三维生效,不再重复说明。
二、Ontology 与 ER / OOP / DDD 的对比
2.1 ER(实体-关系模型)
物理层通过"实体表(Object Types)与关系表(Link Types)"对现实世界进行图网络拓扑和持久化建模:
| 维度 | 表现 |
|---|---|
| 数据结构 | 表 + 字段 + 外键 |
| 关系语义 | 无业务语义 ------vehicle_part 只是连接表,不告诉系统"必装/可选" |
| 行为 | 不在模型内------靠应用层代码或存储过程体现 |
| 权限 | 外挂------DB 角色 + 应用层 RBAC 各管一段 |
| 一致性 | 事务 + 约束(强项) |
| AI 友好度 | 较低------LLM 看到的是表名 + 列名,并不能理解其含义,还需要补充额外的文档 |
| 成熟生态 | 极广------SQL、BI、备份、复制都很成熟 |
适合场景:
- 财务、订单、客户主数据等强一致 + 报表为主的 OLTP/OLAP;
- 业务规则相对稳定、SQL 团队能撑住的领域;
- 不需要 AI Agent 直接参与决策的系统。
相对不适合:跨系统决策协同、AI/Agent 驱动的业务闭环、密级/合规细到字段级的场景。
2.2 OOP(面向对象编程)
应用层通过"数据属性(Properties)与行为(Actions / Functions)"的统一封装实现业务逻辑的多端复用与逻辑内聚:
| 维度 | 表现 |
|---|---|
| 数据结构 | Class + Field |
| 关系语义 | 引用 / 集合,仅在内存中 |
| 行为 | 类的方法(vehicle.startProduction()) |
| 权限 | 外挂------AOP / 装饰器 / 中间件 |
| 一致性 | 取决于 ORM 与底层 DB |
| AI 友好度 | 较低------LLM 不容易看到方法的前置条件,也难以稳定调用 |
| 成熟生态 | 极广------所有主流语言 |
适合场景:
- 单进程内的复杂状态机、规则演算、仿真引擎、游戏;
- 领域模型只服务于一个应用、不被多端共享;
- 团队全栈控制代码 + 数据库。
相对不适合:行为需要被多个应用/Agent 共用,并且要求"一处定义、全平台生效"的场景。OOP 很难跨进程共享方法语义,LLM 也难以稳定调用某个 Java/Python 类的方法。
2.3 DDD(领域驱动设计)
DDD 是 OOP 体系中更成熟的一套方法论,包括聚合根(Aggregate Root)、限界上下文(Bounded Context)、领域服务(Domain Service)及仓储模式(Repository)等核心概念。其核心思想与 Ontology 异曲同工,但在技术栈的表达上存在不同。
| 维度 | 表现 |
|---|---|
| 数据结构 | 聚合根 + 实体 + 值对象 |
| 关系语义 | 聚合内强约束,聚合间用 ID 引用 + 领域事件 |
| 行为 | 富血模型(行为在聚合上)/ 应用服务(命令入口) |
| 权限 | 仍然外挂------靠 ABAC / OPA / OAuth Scope 等组件 |
| 一致性 | 聚合内事务,聚合间最终一致(领域事件) |
| AI 友好度 | 中------若有清晰的应用服务边界,可被 LLM 当工具调用,但不自动 |
| 成熟生态 | 较广------文献、模式语言、社区都较成熟 |
适合场景:
- 业务规则复杂、需要清晰的领域语言(Ubiquitous Language);
- 团队同时拥有代码 + 领域知识;
- 主要消费者是程序员写的应用,AI 不是主要参与者。
DDD 与 Ontology 的对比:
| DDD 概念 | Ontology 对应 | 关键差别 |
|---|---|---|
| 聚合根 / 实体 (Aggregate Root / Entity) | ObjectType |
DDD 靠实体类代码约束边界;Ontology 的 ObjectType 是全局声明式 ,可被平台跨应用调用。DDD的实体往往是充血模型,单一实体或边界内**的数据+行为高内聚在一起。ObjectType 则是贫血模型,只是纯粹的、静态的数据节点。 |
| 值对象 (Value Object) | ValueType |
DDD 的值对象内嵌在代码域中;Ontology 的 ValueType 具备全局唯一标识且整个 Space 复用,自带独立校验规则。 |
| 领域服务 (Domain Service) | Function |
DDD 的领域服务纯为代码层逻辑处理服务;Ontology 的 Function 则是多端共用的计算微服务(Action/AIP Agent/报表均可直接调用)。 |
| 应用服务(命令) (Application Service / Command) | ActionType |
DDD 通过 Application Controller/Handler 转发流程;ActionType 则用完全声明式的模型表达参数 / 校验 / 权限 / 副作用 / 审计流。 |
| 领域事件 (Domain Event) | Action emitEvent rule + 外部订阅 |
DDD 依赖事件总线(Event Bus)做事件驱动;Ontology 将事件抛出,抽象为 Action 的标准副作用(Side Effect)。 |
| 限界上下文 (Bounded Context) | Ontology Space + 项目边界 | DDD 依靠限界上下文划分微服务边界;Ontology 依靠 Space 进行边界隔离与平台行/列级安全限权(ACL)的强绑定。 |
| 仓储 (Repository) | OSS(读)+ Funnel(写) | DDD 开发者需要手写大量的 DAO/Repository 胶水代码;Ontology 的读写引擎由平台原生托管,开发人员零感知、零编码。 |
简单概括 :DDD 把模型表达成代码 ;Ontology 把模型表达成平台声明,必要时再补充代码(Function)。
2.4 综合对照
| 能力 | ER | OOP | DDD | Ontology |
|---|---|---|---|---|
| 关系是否一等 | ✗(外键) | △(引用) | ✓(聚合内) | ✓(独立 LinkType) |
| 行为是否声明式 | ✗ | ✗ | △ | ✓(ActionType) |
| 权限是否内建 | ✗ | ✗ | ✗ | ✓(Role/Class/Purpose) |
| 审计是否自动 | ✗ | ✗ | ✗ | ✓(Action 默认) |
| 副作用是否结构化 | ✗ | ✗ | △(事件) | ✓(Webhook/Notification) |
| AI 直接消费 | ✗ | ✗ | △ | ✓(OSDK + AIP) |
| 跨应用共享语义 | △ | ✗ | △ | ✓ |
| 单进程性能 | 高 | 极高 | 高 | 中(平台开销) |
| 自建成本 | 极低 | 极低 | 低 | 极高(需平台) |
| 生态成熟度 | 极高 | 极高 | 高 | 单厂商主导 |
2.5 选型指南
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 单体业务系统 团队全栈开发、AI 不是核心参与者 | DDD + ER(应用服务 + 关系型数据库) | 结构简单、开发效率高、事务一致性成熟 |
| 大规模数据分析 报表与 BI 分析、规则稳定、强一致事务要求 | ER + 数仓 | 适合结构化分析、OLAP 与传统企业数据体系 |
| 高频核心计算 内存型仿真、游戏逻辑、状态机、单进程复杂行为系统 | OOP(甚至无需 DDD) | 对象行为集中,领域边界无需跨系统抽象 |
| 知识图谱 知识检索、语义搜索、推理分析为主,不需要回写现实业务系统 | Knowledge Graph | 强关系推理与语义查询,但不强调业务执行闭环 |
| 复杂数智化协同 跨系统协同、AI Agent 参与决策、强审计与密级合规 | Ontology(Foundry 或同类架构) | 统一对象语义、关系网络、权限治理与 AI 上下文 |
| 未知领域探索 技术方向不确定,需要快速验证 | DDD 起步 | 先构建领域模型,后续在 AI 集成、跨系统共享、审计合规等复杂度上升时,再评估是否升级为 Ontology 架构 |
三、实例模拟
下面给出2个示例加以说明。给出完整的 ObjectType / LinkType / ActionType / Function / Webhook 五件套。
实例 1(简单 · 商业):电商订单管理
场景:一家小型 DTC 品牌,订单从下单到发货。 对象 3 个、链接 2 条、Action 2 个。
ObjectType
json
[
{
"ObjectType": "Customer",
"primaryKey": "customerId",
"titleKey": "displayName",
"properties": {
"customerId": { "type": "string" },
"displayName": { "type": "string", "searchable": true },
"email": { "valueType": "Email" },
"tier": { "type": "enum", "values": ["BRONZE","SILVER","GOLD"] },
"status": { "type": "enum", "values": ["ACTIVE","SUSPENDED"] }
}
},
{
"ObjectType": "Product",
"primaryKey": "sku",
"titleKey": "name",
"properties": {
"sku": { "type": "string" },
"name": { "type": "string", "searchable": true },
"price": { "valueType": "USDAmount" },
"stockOnHand": { "type": "integer" }
}
},
{
"ObjectType": "Order",
"primaryKey": "orderId",
"titleKey": "orderNumber",
"properties": {
"orderId": { "type": "string" },
"orderNumber": { "type": "string", "searchable": true },
"status": { "type": "enum", "values": ["PLACED","PAID","SHIPPED","DELIVERED","CANCELLED"] },
"totalAmount": { "valueType": "USDAmount" },
"placedAt": { "type": "timestamp" }
}
}
]
LinkType
json
[
{ "LinkType": "ORDERED_BY", "from": "Order", "to": "Customer", "cardinality": "many-to-one" },
{ "LinkType": "CONTAINS", "from": "Order", "to": "Product",
"cardinality": "many-to-many",
"properties": { "quantity": { "type": "integer" }, "unitPrice": { "valueType": "USDAmount" } } }
]
ActionType
json
{
"ActionType": "PlaceOrder",
"parameters": {
"customer": { "type": "ObjectReference", "objectType": "Customer" },
"lineItems": { "type": "array", "items": { "type": "object",
"properties": { "product": { "type": "ObjectReference", "objectType": "Product" }, "qty": { "type": "integer" } } } }
},
"submissionCriteria": [
{ "rule": "$customer.status == ACTIVE", "failureMessage": "客户账号已停用" },
{ "rule": "${inventorySufficient(lineItems)}", "failureMessage": "存在库存不足的商品" }
],
"rules": [
{ "type": "createObject", "objectType": "Order",
"values": { "orderId": "${newId('ORD')}", "status": "PLACED",
"totalAmount": "${computeTotal(lineItems)}", "placedAt": "$now" },
"bind": "order" },
{ "type": "createLink", "linkType": "ORDERED_BY", "from": "order", "to": "customer" },
{ "type": "createLink", "linkType": "CONTAINS", "from": "order", "to": "$lineItems[*].product",
"properties": { "quantity": "$lineItems[*].qty", "unitPrice": "$lineItems[*].product.price" } },
{ "type": "modifyObject", "object": "$lineItems[*].product",
"edits": { "stockOnHand": "${$.stockOnHand - $lineItems[*].qty}" } }
],
"permissions": { "roles": ["Customer","CSRAgent"], "purpose": "order-fulfillment" },
"sideEffects": [
{ "type": "notification", "recipients": ["$customer"], "template": "ORDER_CONFIRMATION" },
{ "type": "webhook", "kind": "side-effect", "target": "warehouse-pick-system",
"payload": { "orderId": "$order.orderId", "items": "$lineItems" } }
],
"audit": true
}
Function
typescript
@Function()
public inventorySufficient(lineItems: LineItem[]): boolean {
return lineItems.every(li => li.product.stockOnHand >= li.qty);
}
@Function()
public computeTotal(lineItems: LineItem[]): number {
return lineItems.reduce((sum, li) => sum + li.product.price * li.qty, 0);
}
Webhook(Side Effect)
入仓系统挑货:
yaml
webhook:
target: warehouse-pick-system
method: POST
url: https://warehouse.internal/api/picks
kind: side-effect # 在对象修改之后异步执行
authentication: oauth2 # 由 Foundry 代为获取 token
payload:
orderId: "{{ $order.orderId }}"
items: "{{ $lineItems }}"
这是最朴素的形态:用 side-effect Webhook,而不是 writeback,因为入仓失败不应阻塞下单(订单已确认在先,缺货另走异常流程)。
实例 2(中等 · 商业):汽车厂改派
场景 :物料短缺触发订单从 Line-A 改派到 Line-B。重点展示多对象、多链接、多副作用 Action。
ObjectType(节选)
json
[
{ "ObjectType": "Vehicle",
"primaryKey": "vin",
"properties": {
"vin": { "type": "string" },
"model": { "type": "string" },
"status": { "type": "enum", "values": ["PLANNED","IN_PRODUCTION","ON_HOLD","DONE"] },
"plant": { "type": "string" }
} },
{ "ObjectType": "ProductionLine",
"primaryKey": "lineId",
"properties": {
"lineId": { "type": "string" },
"plant": { "type": "string" },
"status": { "type": "enum", "values": ["IDLE","BUSY","DOWN"] },
"capacityPerHour": { "type": "integer" }
} },
{ "ObjectType": "Material",
"primaryKey": "materialId",
"properties": {
"materialId": { "type": "string" },
"stockOnHand": { "type": "integer" },
"safetyStock": { "type": "integer" }
} },
{ "ObjectType": "Order",
"primaryKey": "orderId",
"properties": {
"orderId": { "type": "string" },
"customerTier": { "type": "enum", "values": ["A","B","STRATEGIC"] },
"dueDate": { "type": "timestamp" },
"assignedLineId": { "type": "string", "nullable": true }
} }
]
LinkType
json
[
{ "LinkType": "ASSIGNED_TO", "from": "Vehicle", "to": "ProductionLine", "cardinality": "many-to-one",
"properties": { "assignedAt": { "type": "timestamp" }, "detachedAt": { "type": "timestamp", "nullable": true } } },
{ "LinkType": "REQUIRES_PART", "from": "Vehicle", "to": "Material",
"cardinality": "many-to-many",
"properties": { "qty": { "type": "integer" }, "mandatory": { "type": "boolean" } } },
{ "LinkType": "FULFILLS", "from": "Vehicle", "to": "Order", "cardinality": "many-to-one" },
{ "LinkType": "SCHEDULED_ON", "from": "Order", "to": "ProductionLine", "cardinality": "many-to-many",
"properties": { "scheduledAt": { "type": "timestamp" }, "endedAt": { "type": "timestamp", "nullable": true } } }
]
ActionType
json
{
"ActionType": "ReassignProduction",
"parameters": {
"order": { "type": "ObjectReference", "objectType": "Order" },
"targetLine": { "type": "ObjectReference", "objectType": "ProductionLine" },
"reason": { "type": "enum", "values": ["MATERIAL_SHORTAGE","QUALITY_HOLD","CUSTOMER_REQ","EMERGENCY"] }
},
"submissionCriteria": [
{ "rule": "$targetLine.status == IDLE", "failureMessage": "目标产线非 IDLE" },
{ "rule": "$targetLine.plant == $order.currentPlant", "failureMessage": "跨厂改派需另走 CrossPlantTransfer Action" },
{ "rule": "${requiredMaterialsAvailable(order, targetLine)}", "failureMessage": "目标产线物料未齐套" },
{ "rule": "${operatorHasLineRole(currentUser, targetLine)}", "failureMessage": "无该产线调度权限" }
],
"rules": [
{ "type": "modifyLink", "linkType": "SCHEDULED_ON",
"filter": "$order.assignedLineId is not null",
"edits": { "endedAt": "$now" } },
{ "type": "createLink", "linkType": "SCHEDULED_ON",
"from": "order", "to": "targetLine",
"properties": { "scheduledAt": "$now" } },
{ "type": "modifyObject", "object": "order",
"edits": { "assignedLineId": "$targetLine.lineId" } },
{ "type": "invokeFunction", "function": "computeETA",
"args": ["$order", "$targetLine"], "bind": "eta" },
{ "type": "emitEvent", "eventType": "ORDER_REASSIGNED",
"payload": { "orderId": "$order.orderId", "from": "$order.assignedLineId", "to": "$targetLine.lineId", "eta": "$eta", "reason": "$reason" } }
],
"permissions": { "roles": ["LineSupervisor","PlantScheduler"], "purpose": "production-scheduling" },
"sideEffects": [
{ "type": "notification", "recipients": ["$order.assignedSupervisor", "$targetLine.supervisor"], "template": "REASSIGNMENT_BRIEF" },
{ "type": "webhook", "kind": "side-effect", "target": "mes-system",
"payload": { "orderId": "$order.orderId", "newLineId": "$targetLine.lineId", "scheduledAt": "$now" } },
{ "type": "webhook", "kind": "side-effect", "target": "customer-portal",
"condition": "$order.customerTier in [STRATEGIC, A]",
"payload": { "orderId": "$order.orderId", "newETA": "$eta" } }
],
"audit": true
}
要点:
modifyLinkwith filter :先把旧的SCHEDULED_ON链路盖上endedAt,再建新的,保留历史而不是删除;- 条件副作用 :
customer-portalWebhook 只在 A 类/战略客户时触发------通过condition表达; - 两条 Side-Effect Webhook 顺序不保证(官方明确):若依赖顺序,需要拆成多个 Action 串行触发。
Function
typescript
@Function()
public requiredMaterialsAvailable(order: Order, line: ProductionLine): boolean {
const vehicles = order.vehicles.all();
const requiredByMaterial: Record<string, number> = {};
for (const v of vehicles) {
const links = v.requiresPart.all();
for (const link of links) {
if (!link.mandatory) continue;
requiredByMaterial[link.material.materialId] = (requiredByMaterial[link.material.materialId] || 0) + link.qty;
}
}
return Object.entries(requiredByMaterial).every(([mid, qty]) => {
const m = Objects.material().filter(x => x.materialId.eq(mid)).take(1).get();
return m && m.stockOnHand >= qty;
});
}
@Function()
public computeETA(order: Order, line: ProductionLine): Date {
const vehicleCount = order.vehicles.all().length;
const hours = vehicleCount / line.capacityPerHour;
return new Date(Date.now() + hours * 3600 * 1000);
}
Webhook(两个 Side Effect)
yaml
webhook:
- target: mes-system
method: POST
url: https://mes.factory.internal/api/schedule
kind: side-effect
payload: { orderId: "{{ $order.orderId }}", newLineId: "{{ $targetLine.lineId }}", scheduledAt: "{{ $now }}" }
- target: customer-portal
method: POST
url: https://portal.example.com/api/order/{{ $order.orderId }}/eta
kind: side-effect
condition: $order.customerTier in [STRATEGIC, A]
payload: { newETA: "{{ $eta }}" }
五、总结
结合前面的结构对比与实例,可以将 Ontology 数据结构的核心特征概括为:
- 关系是一等公民:LinkType 独立存在,具备 cardinality、属性、权限与密级;它不是 ER 的连接表,也不是 OOP 的对象引用,更不属于 DDD 聚合内部字段。
- 行为是声明式契约:ActionType 将参数、校验、规则、权限、副作用与审计统一放入同一份声明中。相比之下,ER 很难表达行为,OOP 往往分散在代码中,DDD 则通常落在应用服务层。
- 计算与修改分离:Function 负责计算,Action 负责状态变更;两者都可以被 UI、OSDK、Agent、报表等复用。
- 通过副作用连接外部系统:Webhook 的 writeback 与 side-effect 两种模式,分别对应:"外部权威源"与"外部下游消费者"两类场景。
- 横切治理内建于模型: Permission、Classification、Purpose、Audit 并非外围注解,而是每个构件天然携带的元数据,并随数据与行为一起流动。
当系统同时具备以下三类需求中的两类以上时,Ontology(或类似方案)会明显优于传统架构:
- 多个应用、Agent 需要共享统一业务语义;
- 权限、审计、密级需要随数据流转;
- AI 需要直接参与业务决策与执行闭环。
否则,采用 DDD + 关系库 + 应用层治理,通常仍是更简单、更经济的选择。
六、参考资料
- Palantir 官方:Object and link types · Types reference
- Palantir 官方:Value types · Overview、Value type constraints
- Palantir 官方:Create an object type
- Palantir 官方:Action types · Overview
- Palantir 官方:Action types · Rules
- Palantir 官方:Submission criteria
- Palantir 官方:Side effects · Overview
- Palantir 官方:Side effects · Webhooks、Set up a webhook
- Palantir 官方:Functions on objects · Objects and links
- Palantir 官方:Foundry Branching · Managing side effects on branches