5.2 Object / Link / Action:本体建模三件套
承前:5.1 节解决了"同一个会员在两套系统里是两条记录"的问题,
member_master已经落地。但主数据只是把"数据"理顺了——Agent 要在业务里干活,还需要知道"业务长什么样":有哪些东西、东西之间什么关系、能对东西做什么。第二章画的是低保真草图(四个对象、手绘关系),这一节把它硬化成正式本体:Object、Link、Action 三件套,并以青禾优选为例写成一份可落地的完整本体定义。
5.2.1 三件套是什么:一句话语法
本体建模的全部语法可以压成三个词:
- **Object(对象)= 名词。**业务里有独立身份和生命周期的东西:会员、订单、门店、优惠券。每个对象有属性(properties)和主键。
- Link(链接)= 名词与名词的关系。会员"下过"订单、订单"发生在"门店、会员"持有"优惠券。Link 是有类型的关系,不是数据库外键的简单翻译——它表达的是业务语义("归属"和"核销于"是两种完全不同的关系)。
- **Action(动作)= 动词,且必须改变状态。**发券、核销券、积分变动。"查询"不是 Action——它什么都不改。
这套三层结构的工业范本是 Palantir Foundry 的 Ontology:对象类型(Object Types)定义实体模式,链接类型(Link Types)定义关系,动作类型(Action Types)定义"对对象、属性、链接的受管控变更"——每个 Action 是一个事务,提交后反映到所有挂在本体上的应用。Palantir 官方文档明确把本体分为语义元素(对象、属性、链接)和动力元素(动作、函数、动态安全)两半:前者描述组织"是什么",后者描述组织"怎么动"。据 Palantir 官方文档。
为什么 Agent 项目特别需要这套东西?因为 LLM 的世界是语言,本体的世界也是语言。Agent 理解"给沉睡会员发张 20 元券"这句话,靠的正是:券是对象、持有是链接、发放是动作、沉睡是会员对象上的一个口径标签。没有本体,这句话里的每个名词动词都是概率性的猜测;有了本体,它们是可校验的结构。这就是"本体是 Agent 的语法书"的含义。
Object / Link / Action 三件套(青禾局部)
[门店 Store]◄─发生于─[订单 Order]─关联→[会员 Member]
│ │ 使用 │ 持有
│发放批次 ▼ ▼
└────────→ [优惠券 Coupon]←────────┘
Action(改变状态,带权限):
发券 : 门店/运营 → Coupon: 未使用
核销 : 收银触发 → Coupon: 已核销 + Order: 优惠金额
积分 : 交易触发 → Member: 积分余额5.2.2 Object:把草图对象硬化成正式定义
第二章草图的"会员"对象旁边插着两面旗,现在要把旗拔掉、把坑填上。Object 硬化要回答四个问题:主键是什么?属性有哪些(及口径)?身份判据是什么?安全等级是什么?
以会员为例,硬化的核心决策:
- 主键:不再用任何一端的系统 ID,统一用
member_master产出的master_id(5.1 节成果)。这是"主数据反哺本体"的直接体现——实体解析的产出,就是本体的身份层。 - 身份判据:什么事件会"创建"一个会员对象?新 master_id 诞生。什么事件会"合并"两个会员对象?主数据治理流程批准(合并本身是一个 Action,要走审批,不是 ETL 静默改)。
- 属性口径:
沉睡天数属性挂在对象上,取值规则引用 3.4 节注册的口径(90/180 天两档)——本体属性引用指标语义层,不复制公式。复制公式等于埋下第二处口径漂移点。 - 安全等级:手机号、实名信息标"敏感",Agent 默认不可见明文,只可见脱敏形态或聚合结果。
订单(Order):连接会员和门店的枢纽。复购率的计算最终落在"同一 member_id 在时间窗口内出现于多少笔 Order"。硬化时它带来两个决策:一是主键沿用 POS 的 order_id 但跨系统归一(老 POS 五位流水号、新 POS 十位,统一加门店前缀做命名空间);二是 order_type 枚举里把退货、员工测试、调拨显式列出——异常值不是脏数据,是业务事实,它们的处理走 5.3 节语义层的剔除规则,而不是 ETL 静默过滤。
优惠券(Coupon):北极星指标的主要干预手段。W2 草图上它的状态机是"未使用→已核销/过期",硬化时补上第四态"已冻结"——客诉场景下运营要能锁一张券。同时补一个新对象 CouponTemplate:青禾现实里"券"分模板和实例两层(一次活动建一个模板、发 N 张实例),草图阶段被简化掉了。这种"硬化时被迫拆分对象"的时刻,正是草图价值的证明:先简化,等真实需求出现再拆,比一开始就建十几个对象好得多。
5.2.3 Link:被低估的一半
实践中新手往往把 90% 精力花在 Object 上,Link 一笔带过——然后在实际用例上翻车。Link 设计的两个原则:
Link 有方向、有基数、有业务名字。"会员持有券"(1:N)和"券核销于订单"(N:1)在业务上完全不同:前者支撑"盘点会员手上的券"查询,后者支撑"核销归因分析"。基数标注错误会导致 Agent 生成错误的查询假设("每个会员只有一张券")。
**Link 是口径冲突的裁决现场。**第二章草图的未决冲突——"券是会员维度的还是门店维度的"——在 W3 已经裁决(发券决策按门店客群做,券的持有和使用按会员记账)。落到本体:Coupon 同时有两条 Link(Member 持有、Store 发放批次),两条 Link 的语义都不撒谎,撒谎的是只用其中一条回答所有问题的旧系统。这就是本体比 ER 图强的地方:它显式承载多个业务视角。
5.2.4 Action:把"能做什么"从 prompt 里拿出来
Agent 项目最常见的捷径是把业务规则写进系统提示词:"发券时检查会员等级≥金卡、券面额不超过客单价 30%、每月限发一次……"——短期有效,长期是灾难:规则改一次就要改 prompt,没有审计、没有权限、没有回滚。正确姿势是把这些约束做成 Action 的提交条件(Submission Criteria)。
Palantir 对 Action 的定义值得逐字读:"一个 Action 是一个事务,基于用户定义的逻辑变更一个或多个对象的属性"——参数(可以引用对象)、规则(创建/修改/删除对象、建立/解除链接)、提交条件(谁有权、什么前置条件满足)、副作用(通知、触发外部系统)。一个 Action 类型的全部行为在一个地方声明、一次提交原子生效、全程留痕。据 Palantir 官方文档。
青禾的"发券" Action 完整定义:
action: issue_coupon
display_name: 发放优惠券
description: 向指定会员发放一张指定模板的优惠券
parameters:
- name: member
type: ObjectRef[Member] # 参数本身是对象引用,不是裸 ID
- name: coupon_template
type: ObjectRef[CouponTemplate]
- name: channel
type: enum [APP, SMS, STORE]
submission_criteria:
permission: role in [运营经理, 店长(本店会员)]
preconditions:
- member.沉睡等级 != 深度沉睡黑名单
- member.月内已领同类券 < 1
- coupon_template.面额 <= member.近90天客单价 * 0.3
rules:
- create Coupon(template=coupon_template, status=未使用)
- create Link(member -持有-> 新Coupon)
- set Coupon.expires_at = now() + 14d
side_effects:
- notify: member(APP push) 延迟生效
- writeback: 旧CRM同步标记(遗留系统回写,5.4节)
audit: 全量记录操作者/时间/参数/规则快照对比一下"写进 prompt"版本:同样的约束,这里可测试(每条 precondition 是独立用例)、可审计(谁发了什么券全留痕)、可回滚(券有状态机)、可权限管控(店长只能发本店会员)。Agent 的角色也清晰了:它负责决定调用哪个 Action、填什么参数、向谁解释——而 Action 本身保证"不管 Agent 多聪明,都只能在围栏里动"。
5.2.5 青禾优选完整本体 YAML
把四个对象、链接、动作合成一份可读的本体定义(教学简化版,实际落在底座的实体/工具注册接口):
ontology: qinghe-retail
version: 1.0
objects:
Member:
primary_key: master_id # 来自5.1节实体解析主数据
properties:
phone: {type: masked-string, sensitive: true}
register_source: {type: enum}
sleep_tier: {type: enum [活跃, 半沉睡, 深度沉睡],
metric_ref: sleep_tier_definition@v2} # 引用指标语义层
points_balance: {type: int}
Store:
primary_key: store_id
properties:
trade_zone: {type: string, quality: low} # W2已知:客群标签质量差
Order:
primary_key: order_id
properties:
paid_amount: {type: decimal}
order_ts: {type: datetime}
order_type: {type: enum [SALE, RETURN, STAFF_TEST, TRANSFER]}
effective: {type: bool, metric_ref: valid_order_rule@v1}
Coupon:
primary_key: coupon_id
properties:
face_value: {type: decimal}
status: {type: enum [未使用, 已核销, 过期]}
expires_at: {type: datetime}
links:
member_places_order: {from: Member, to: Order, cardinality: 1:N, name: 下单}
order_at_store: {from: Order, to: Store, cardinality: N:1, name: 发生于}
member_holds_coupon: {from: Member, to: Coupon, cardinality: 1:N, name: 持有}
coupon_used_in_order: {from: Coupon, to: Order, cardinality: N:1, name: 核销于}
store_issues_batch: {from: Store, to: Coupon, cardinality: 1:N, name: 发放批次}
actions:
issue_coupon: {...} # 见5.2.4
redeem_coupon: {rules: [set Coupon.status=已核销,
set Order.discount_amount,
destroy Link(member-持有)→留存档]}
adjust_points: {submission_criteria: {permission: 系统触发},
rules: [set Member.points_balance += delta]}注意三个细节:Order.effective 直接引用指标语义层的"有效消费"口径——本体、指标、SQL 三层共用一份定义;Store.trade_zone 带着 quality: low 标注——数据质量显式写进本体,Agent 生成建议时知道这个字段不能尽信;redeem_coupon 的 Link 处理用"destroy + 留存档"而不是硬删——历史不消失,分析可回溯。
顺带回答一个常被问的问题:本体和知识图谱什么关系?知识图谱通常指"实体-关系"三元组的大规模集合,重心在推理和检索(多跳查询、图谱补全);本体是"模式层"(schema),重心在约束和治理(类型、权限、动作)。在 Agent 项目里你要建的是后者——先有受管控的模式层,图谱式的灵活推理才有安全的跑道。把两者混着谈的方案书,多半两样都没做扎实。
5.2.6 一个快速自测:你的本体能不能回答这些问题
本体画完别急着交付,用五个"面试题"自测,每题都在测一个建模决策:
- "张三把他老婆的卡也用了,算谁的消费?"——测身份判据:member_id 是卡维度还是人维度(青禾的答案是 master_id 自然人维度,卡是附属物)。
- "退掉一笔订单,优惠券要不要还回去?"——测状态机完整性:redeem_coupon 有没有逆向动作(青禾 v1 的答案是不还——人工例外处理,但本体里显式标注了这个缺口)。
- "加盟店的订单算不算试点门店群?"——测边界定义:试点门店集合是本体对象还是查询时的过滤器(答案应该是显式的 pilot_store 对象集合,可治理可变更)。
- "券过期是谁触发的?"——测 Action 的触发者枚举:系统定时?用户?如果只有"用户手动"一个答案,说明漏了 scheduled action。
- "删掉一个会员,他的历史订单还在吗?"——测 Link 的级联语义:会员对象删除是物理删除还是归档(正确答案几乎永远是归档——分析需要历史)。
这五题的共性是:它们都是"业务人员随口会问、而 ER 图不会替你回答"的问题。本体建模的成熟度,就看这些边角问题有没有显式答案。面试题答不上来的地方,就是 Agent 上线后第一次翻车的地方——因为 LLM 恰恰最喜欢在这种边角上自由发挥。
5.2.7 要点总结
- 三件套语法:Object=名词(有身份和生命周期的实体)、Link=关系(有方向、基数、业务名字,是口径冲突的裁决现场)、Action=改变状态的动词(查询不是 Action)。
- Action 的工业定义来自 Palantir Foundry:一个事务,含参数/规则/提交条件/副作用,原子生效全程留痕——把业务约束从 prompt 搬进 Action,换来可测试、可审计、可权限管控。
- Object 硬化的四个决策:主键(用主数据 master_id)、身份判据、属性口径(引用指标语义层,不复制公式)、安全分级。
- 本体属性带数据质量标注、状态变更带审计链——Agent 的能力边界由本体的围栏决定,而不是由提示词的措辞决定。
参考资料
- Palantir Foundry Ontology 官方文档:Overview — 语义元素(对象/属性/链接)与动力元素(动作/函数/动态安全)的权威划分。
- Palantir Foundry:Explore action types — Action 类型的规则种类(创建/修改/删除对象、管理链接、Webhook 回写)官方说明。
- Palantir Foundry Ontology 产品页 — 本体作为企业操作语义层、"数字孪生"定位的整体设计理念。