- 引言 — 客户的模型大多在人的头脑里
- 最廉价的第一步 — 通用语言
- 限界上下文 — 当同一个词指向不同的东西
- 为名词建模 — 实体、值对象、聚合
- 把三个词分清楚 — 分类法 vs 本体 vs 知识图谱
- 形式栈 — RDF、RDFS、OWL,以及 schema.org
- 运营栈 — 属性图与 Palantir 的本体
- 形式本体到底什么时候值回票价
- 失败模式 — 一份美丽却无人使用的本体
- 结语 — 从问题出发,而非概念
- 参考资料
引言 — 客户的模型大多在人的头脑里
Forward Deployed Engineer 初次抵达客户现场时,最有价值的资产往往不在任何数据库里。它藏在那位守了二十年现场的维修班长的脑子里,藏在理赔核算员指尖的直觉里,藏在调度员说出「不,这事儿一向不这么干」时的那份隐性知识里。
FDE 的核心工作之一,就是把这些散落的隐性知识,变成整个团队都能共享、机器也能读懂的显式模型。本文自下而上地攀爬这道阶梯 — 从最廉价、最有价值的第一步「通用语言」出发,经过形式本体(RDF/OWL),一直到运营本体(属性图与 Palantir 式的对象+链接)。FDE 这个角色本身,我已在 FDE 匠艺深潜与 Forward Deployed Engineer 备战里谈过,所以这里只专注于「把领域变成模型」这一项技艺。
而且我要从一开始就说句诚实话。本体是最容易被过度设计的产物,而最常见的失败,是一份美丽却无人使用的本体。本文有一半,讲的都是如何绕开这个陷阱。
最廉价的第一步 — 通用语言
在你动手画形式图之前很久,有一件几乎免费、却高杠杆的活儿。它就是 Eric Evans 的 Domain-Driven Design 所说的通用语言(ubiquitous language)。Martin Fowler 把它概括为「在开发者与用户之间,建立一门共通而严谨的语言的实践」。
承重的那个词是严谨。因为正如 Evans 所说,「软件不善于应对模糊(software doesn't cope well with ambiguity)」。如果在现场,「设备」「装备」「资产」「机组」对不同的人指的是不同的东西,而没有人点明这个差别,那么这份混乱就会原封不动地流进代码里。
所以 FDE 的第一份产物不是图,而是一份术语表。你和领域专家面对面坐下,把同样的词反复用给他们听,在任何一处别扭或不准确的地方彼此指出、加以打磨。正如 Fowler 指出的,领域专家会对含义含糊的术语提出异议,而开发者会揪出那些会拖垮设计的模糊。这场对话本身就在验证模型。而且这门语言,必须在会议里、在代码里、在界面标签里、在数据库列名里,被一模一样地使用。翻译层一旦出现,bug 就诞生了。
限界上下文 — 当同一个词指向不同的东西
在大型组织里,想把通用语言统一成唯一一门的尝试,往往会失败。Fowler 把原因说得很明白 — 「在大型系统中,把领域模型完全统一起来,既不可行,也不划算」。
他举的例子很精准。在某家电力公司,「meter(电表)」这个词,「对组织的不同部分意味着微妙不同的东西」 — 对某个团队是电网的接入点,对另一个团队是与客户的合同关系,对再一个团队则是墙上那个物理装置。这在对话里可以含糊带过,但「在精密的计算机世界里」不行。
DDD 的答案不是强行统一,而是限界上下文(bounded context)。在语言发生变化的边界处把模型切开(「语言一变,就需要另一个模型」),上下文之间则用显式的映射连接起来。对 FDE 来说,这不是奢侈,而是生存技能。如果你在早期没能抓住「维修部门的『工单』和财务部门的『工单』指的是不同东西」这个事实,几个月后就会在集成阶段为它付出代价。
为名词建模 — 实体、值对象、聚合
语言定下来之后,就给名词赋予结构。DDD 给了你三件工具。
- 实体(Entity)。 拥有一个贯穿时间的身份标识的东西。泵 A17 即便换了零件、挪了位置,仍然是同一台泵。哪怕两样东西属性完全相同却依旧彼此有别,那它就是实体。
- 值对象(Value Object)。 不带身份标识、纯粹由属性来定义的东西。就像
金额(100, CNY)或一个 GPS 坐标 — 可以互换,而且通常不可变。值相等,就当作同一个东西来对待。 - 聚合(Aggregate)。 用 Fowler 的定义,是「可以作为单个单元来处理的一簇领域对象」。订单与其订单项就是典型例子。每一簇都有一个聚合根,「来自聚合外部的引用只能指向聚合根」,而且「事务不应跨越聚合的边界」。
聚合之所以重要,是因为它本身就是你的一致性边界。把工单、维修项、所用零件归入同一个聚合,什么必须原子性地一起变、什么可以独立地变,就一目了然地浮现出来。这个决定,会原封不动地一路延续到你之后的图 schema 与事务设计里。
把三个词分清楚 — 分类法 vs 本体 vs 知识图谱
人们最常在这里把它们混为一谈。这三个词,指的是不同的东西。
- 分类法(taxonomy) 是一种层级分类。它用一条 is-a 关系把概念挂到一棵树上。图书分类或商品类目就是这样。
- 本体(ontology),用 Ontotext 的定义,是「对某一领域内的一组概念、以及它们之间成立的关系所做的形式化描述」。与分类法不同,它「表达关系,并让人以多种方式把多个概念彼此连接起来」。也就是说,它不是单一的层级,而是一张多方向的关系网。
- 知识图谱(knowledge graph),是把那个本体应用到真实事实上的结果。正如 Ontotext 所说,「把本体数据模型应用到一组个体事实上,就造出了一张知识图谱」。本体是 schema,知识图谱则是用这套 schema 填充出来的真实数据。
分类法 (分类): 单一层级。只有 is-a。
Asset
+- Pump
+- Motor
本体 (schema): 概念 + 关系的「种类」。多方向。
Asset --hasPart--> Part
WorkOrder --services--> Asset
Technician --qualifiedFor--> AssetType
知识图谱 (事实): 把本体应用到真实数据上。
Pump#A17 --hasPart--> Seal#9 (单个事实 = 节点 + 边)
WO#4421 --services--> Pump#A17
一句话概括:本体「定义好结构,为知识图谱盛放数据搭好舞台」(Ontotext)。所以「我们来建本体」和「我们来建知识图谱」是两项不同的工作 — 前者是 schema 设计,后者是数据管线。
形式栈 — RDF、RDFS、OWL,以及 schema.org
要把形式本体做好,你得懂语义网标准的三个层级。
- RDF 是三元组模型。每一条事实都写成(主语、谓语、宾语),每个元素都用 URI 做全局标识。它的要点是「把数据从它的 schema 中剥离出来」,从而让多个来源合而为一。
- RDFS(RDF Schema) 在此之上叠加类、属性,以及像
subClassOf这样的层级。也就是分类法层级的 schema。 - OWL 加上了逻辑。类、对象属性(个体-个体)、数据类型属性(个体-值),以及个体(individual)。在这之上,还附带逻辑构造子(用 and/or/not,比如「Mother = Woman AND Parent」)、基数(「hasChild max 4」)、逆(inverse)、传递性(transitive)、函数性(functional)、对称性(symmetric)与不相交(disjoint)声明。而最关键的是,推理机(reasoner) 能从这些规则中「自动计算出推论」,发现人本会漏掉的事实。
想看具体的、活生生的实例?去看 schema.org。这套词汇表由 Bing、Google、Yahoo 于 2011 年 6 月发起,同年 11 月 Yandex 加入,如今收录了 823 个类型、1,529 个属性,是在网页里嵌入结构化数据的事实标准。它证明了:本体不是学术玩具,而是每天驱动着搜索运转的基础设施。
运营栈 — 属性图与 Palantir 的本体
FDE 实际部署的,通常不是一个 OWL 文件,而是一张属性图。Neo4j 式的带标签属性图(LPG)由四个部件构成 — 节点(领域中的离散对象)、标签(对节点属于哪一类进行分类)、关系(带方向、带类型的边),以及同时挂在节点和关系上的属性(键-值)。这正是它与 RDF 三元组的决定性差别。在 RDF 里,要给一条边挂上属性,需要一种变通(reification);而在 LPG 里,关系直接就带着属性。对运营查询和图遍历来说,这要方便得多。
这套运营本体的工业级巅峰,是 Palantir Foundry 的 Ontology。Palantir 的文档把它称作「组织的运营层」,也是「组织的数字孪生」,并将其分为两类。
- 语义要素 — 对象类型(object type)、属性、链接类型(link type)。通过「把既有数据源映射为本体中的对象、属性与链接」,来定义组织的含义。
- 动能要素 — 操作类型(action type)、函数(function)、动态安全。这里,正是它与普通图数据库分道扬镳之处。操作类型不只读取图,还会接收操作者的决定,去改变图。
同一个事实: 「工单 4421 对泵 A17 进行维修」
RDF 三元组: wo:4421 ex:services asset:A17 .
属性图: (:WorkOrder {id:4421})-[:SERVICES]->(:Asset {id:'A17'})
Palantir 对象: WorkOrder(4421) --link:services--> Asset(A17)
+ ActionType: 「标记为维修完成」会改变状态
核心的差别在于动词。形式本体描述世界是怎样的;运营本体则在此之上,还容纳了你能在上面做什么。
形式本体到底什么时候值回票价
那么,什么时候该搬出笨重的 OWL,什么时候一张务实的属性图就够了?诚实的判断标准是这样的。
当形式本体(RDF/OWL)值回票价时:
- 当多个组织或系统必须共享同一套含义时 — 标准词汇、监管报送、跨机构数据交换,凡是以互操作性为本质的场合。这正是 schema.org 能在整个网络上通行的原因。
- 当推理能创造出真实价值时 — 当「从这些规则里逻辑上能推出什么?」正是业务要问的问题时。规则经常变动的监管与合规领域里、基于规则的分类,就是典型。
- 当含义必须长久存活、并在多个团队之间共享数年时。
当一张务实的属性图就够了时:
- 当你在单个组织内部,目标是对既定问题快速作答、并对图进行遍历与运营时。绝大多数 FDE 项目都属于这一类。
- 当关系带着大量属性(何时、何人、多少),而且最重要的是速度至关重要时。
一条粗略的规则:如果让推理机跑一整晚,也发现不了什么真正的新事实,那么 OWL 那套逻辑机器,多半只是成本。这种情况下,一张带 schema 的属性图,才是更诚实的选择。
失败模式 — 一份美丽却无人使用的本体
现在到了本文最重要的一节。本体的常见失败,不是一个错误的模型,而是一个完美却没人用的模型。
症状总是相似。一个为期半年的本体委员会召开,一套囊括了世间一切概念的优雅类层级被画了出来,演示赢得掌声 — 然后,再没有人去查询它。要么是数据从未接上,要么是它与业务问题毫不相干,要么两者皆是。它是为了完整性而建模的,而不是为了使用。
处方是把方向反过来。从问题出发,而不是从概念出发。 不是从白板上的类图出发,而是从业务今天答不上来、正为之焦躁的那份问题清单出发。比如 — 「下个季度故障概率最高的设备是哪些?」「眼下有哪位有资质的维修工能接手这张工单?」「仓库 X 里该囤哪些零件、囤多少?」只对回答这些问题所需的个体与关系建模,其余的,一律推迟到有问题来要求它时再说。
另一条纪律:本体应当始终连着真实数据地生长。碰不到数据的本体,只是一个未经验证的假设。就像通用语言一样,本体也应当是一个不断演化的、活的产物。
结语 — 从问题出发,而非概念
归纳起来是这样。FDE 把领域变成模型,这件事是一道阶梯。底层是几乎免费的通用语言 — 与领域专家一起,建立一套严谨的共享词汇。在它之上,用限界上下文处理同一个单词的冲突,用实体·值对象·聚合为名词赋予结构与一致性边界。再往上,是形式栈(RDF/RDFS/OWL)与运营栈(属性图,Palantir 式的对象+链接+操作),两者选哪一个,取决于你是否真的需要互操作性与推理。
还有一条最诚实的建议。别急着冲向阶梯的高层。大部分价值,都出自最底下的两级 — 共享语言与清晰边界。每当建造本体的诱惑袭来,就这样问自己:「这个模型,回答的是业务实际提出的哪一个问题?」 如果没有答案,那就是一个信号:你此刻该建的,并不是本体。
一张做得好的知识图谱,本身并不是终点,而是下一阶段的地基。把这张图用作 LLM 检索之基础的 Graph RAG,以及知识图谱的构建,将在生产级 RAG 模式里继续展开。
参考资料
현재 단락 (1/70)
Forward Deployed Engineer 初次抵达客户现场时,最有价值的资产往往不在任何数据库里。它藏在那位守了二十年现场的维修班长的脑子里,藏在理赔核算员指尖的直觉里,藏在调度员说出「不,...