今天面了一家叫爱化身的公司,做企业级 Agent 操作系统,产品叫 Agentrix。面试官问我:你了解本体吗?

我不知道。这个词我见过,大概知道和知识图谱沾边,但让我正经回答,答不上来。

但是我结束马上就开始了解这个概念了。这场面试我觉得面试官的表达十分的清晰!我问他岗位的定义,问他当前业务的难点,问题其实问的都很简单,但他每次都能扩展得非常清晰:业务和市场的部分讲得透,技术细节也稍微提一下,我觉得很强。

面完我做的第一件事就是补课:搜了一些资料,也跟 LLM 聊了几轮,顺带把一项关键基准测试的原论文读了。产出分两篇。这一篇是概念篇,默认你没接触过本体,从零把它讲清楚:它是什么、为什么 Agent 时代它突然变得重要,以及我最开始最怀疑的一点,概念一多,上下文不也会装不下吗。另一篇是落地篇,拿 AWS 开源的 Context Ontology Accelerator,看一张表怎么一步步变成本体。

一、本体是什么:把共识写成机器能读的说明书

从例子开始最直观。假设你在做一个企业问答助手,用户问「苹果去年的营收是多少」,系统得先知道这个苹果是一家公司,不是一种水果。再看数据:CRM 里的客户是签过合同的公司,计费系统里的客户是一个付款账户,客服系统里的客户是打过电话的个人。三张表都叫 customer,含义各不相同。

人可以靠上下文分辨,机器做不到。要让机器知道一个东西是什么、和什么有关、要遵守哪些规则,就需要一份明确的、形式化的语义模型,这就是本体。

学术定义有两个版本。1993 年 Tom Gruber 给出「本体是对概念化的明确规范说明」,1998 年 Studer 等人补成「对共享概念化的形式化的明确规范说明」。拆开是四个关键词。概念化:对某个领域做抽象,挑出重要的概念和关系。明确:概念、关系和约束都要写出来,而不是藏在代码里或者某个人的脑子里。形式化:用机器能读、也能据此推理的语言来描述。共享:它代表一个团队、一个行业乃至一个社区的共识,不是某个系统的私有约定。

补课之前,我以为本体写的是「客观世界真实的规则」。看完才反应过来,它写的是一群人对世界如何运转达成的共识。儿科是看孩子病的科室,心内科负责循环系统疾病,这些不是自然规律,是行业约定;但正是这些约定,让两个素不相识的医生能接上话。机器缺的就是这份约定。

具体长什么样?用一个公司组织的例子过一遍,一共四个要素。类:经理是一种员工,员工是一种人。个体:张三是一个经理,研发部是一个部门。属性:连接个体和个体,比如张三管理研发部;或者连接个体和值,比如李四的入职日期是 2021 年 3 月 1 日。公理,也就是约束和规则:「管理」这个属性的定义域是经理、值域是部门,每个员工至少就职于一个部门,人和部门互不相交。

前三个要素确实像 ER 图,熟悉数据库的话可以先这么理解。真正把两者拉开的是公理。比如只声明「管理」的定义域和值域,再加一条事实:

1
2
3
4
5
6
7
@prefix : <http://example.com/company#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

:管理 rdfs:domain :经理 ;
rdfs:range :部门 .

:王五 :管理 :市场部 .

第一次看 Turtle 这种语法没关系,逐段翻译一下。头两行是前缀声明:本体里每个概念的真实身份是一串网址,rdfs:domain 展开其实是 http://www.w3.org/2000/01/rdf-schema#domain,前缀声明的作用就是给这些网址起短名,后面才能写「:管理」这样的缩写。中间那段是公理:「管理」这个属性,主语必须是经理,宾语必须是部门。最后一段是事实:王五管理市场部。推理机读进去之后,能推出两条没有写过的事实:王五是经理,市场部是部门。

二、它像 ER 图,但每条声明都带逻辑语义

推理能走的路不止定义域这一条。沿类层次推:张三是经理,经理是员工的子类,所以张三是员工也是人。沿属性特征推:「下属于」声明为传递属性,测试组下属于研发部,研发部下属于技术中心,那么测试组也下属于技术中心。

推理机还能检查一致性:如果有人把研发部同时标成「人」,而本体声明人和部门互不相交,它就会报矛盾。这相当于在逻辑层面给数据跑了一遍单元测试。

这里有个工程师容易踩的坑。OWL(W3C 制定的 Web 本体语言)采用开放世界假设:没写出来的事实只是未知,不等于假。在数据库里查不到一条记录,你会认为它不成立;OWL 推理机却认为它可能存在,只是暂时不知道是哪个。所以「每个员工必须就职于一个部门」这类完整性检查,不能指望 OWL,要交给按封闭世界方式做数据校验的 SHACL,它是 W3C 的另一套标准,专管约束检查。两个工具解决的问题不同,混着用会得到反直觉的结果。

反例同样能帮我们看清这个概念。新手常犯的错误里有两个特别典型:把发动机建模成汽车的子类,错,发动机是汽车的组成部分;把研发部建模成部门的子类,也错,研发部是部门的一个实例。同样一种「X 和 Y 摆在一起」的关系,是子类、实例还是部分,是业务判断,不是语法问题。这一步写错,后面所有推理都会跟着错。

本体和知识图谱的关系,一个好用的比方是规划图和城市:本体是城市规划图,规定有哪些类、什么关系、遵守什么规则;知识图谱是按图建起来的城市,装着海量具体的事实。DBpedia 两者都有:从维基百科抽取的海量实体是知识图谱,那套定义了人物、地点、作品的模式层是本体。

补完这一段,我对面试路上那个模模糊糊的直觉有了更准的表述:过去做系统,我们设计的是数据存储模型,目的是告诉数据库,这个数据应该怎么存、怎么高效取。现在面向 Agent 开发,需要的是本体模型,一个带语义和推理规则的业务世界模型,目的是告诉大模型,这个世界如何运转、规则如何推导。

三、Agent 为什么会算错:口径不在 schema 里

把镜头拉近到日常工作。把一个数据分析 Agent 接到 CRM、计费、客服三个系统上,业务同事问它:「上季度华东区的活跃客户贡献了多少净收入?」Agent 很快给出一个数字,看起来很专业。但它可能用错了客户表,把退款算进了收入,「活跃客户」的口径是它自己猜的。更麻烦的是,没人说得清这个数字是怎么算出来的。

今天常见的做法是把表结构放进提示词,让大模型直接写 SQL。表名、列名、外键它都看得到,看不到的是字段背后的业务含义。拆一下这个问题里藏着多少隐含知识:客户该用哪张表?「活跃」是指最近 90 天下过单,还是有一份有效合同?净收入要不要扣掉退款?华东区包括哪几个省?这些都不在 schema 里。它们散落在指标文档、wiki 和数据团队的脑子里,有的根本没写下来。模型只能猜。

口径这个东西,我其实接触过。做 token 计费的时候:什么叫一次调用?重试算不算一次?缓存命中了怎么计价?系统提示词的输入 token 算谁的?每一个问题都有不止一种合理答案,口径不同,对出来的账就不一样。运营侧同理,一个「活跃用户」的定义换了,整条周报的趋势都会变。以前这些口径靠人和人对齐,开会吵清楚就完了。现在多了一个不会开口的读者,模型不会来问你要口径,它只会猜。

危险的地方在于:SQL 照样能跑,结果照样能返回。这类错误不报错,它看起来是对的。

那把所有文档塞进提示词行不行?两个问题。上下文窗口再大,也装不下几百个系统的说明;同一个词的几种定义放在一起,模型反而更容易混淆。

2023 年 Sequeda、Allemang 和 Jacob 发表过一项基准测试:在一个真实的保险业企业数据库上,GPT-4 零样本直接把问题翻译成 SQL 执行,准确率只有 16%;先用本体和映射把数据库表示成知识图谱再查询,准确率提到 54%。三倍多的提升来自哪里值得看清楚:模型没有换,变的是它要推理的对象。本体把「几百张含义不明的表」压缩成了「一小套有名字、有关系、有规则的业务概念」,模型不用再从列名里猜世界。

四、语义层:指标、本体、文档,各管一块

解法的位置在数据和 Agent 之间:加一层语义层,把业务含义写成机器能读的形式,Agent 先问语义层,再去碰数据。

语义层这个词并不新,BI 工具里早就有,主要管指标口径和维度,让每张报表里的收入都按同一种方法计算。所以「带语义层的建模就叫本体」这个说法,我觉得方向反了:语义层不等于本体,本体是让语义层拥有推理能力的那块。一个完整的 Agent 语义层通常包含三种上下文:

上下文 装什么 什么情况用
指标 受治理的固定计算逻辑,比如净收入的官方定义 一个计算需要每次算得一模一样
本体 概念、关系、约束,以及概念对应哪些表和字段 描述业务世界里有什么、什么和什么有关
文档 政策、流程、例外情况 规则背后的原因,回答需要引用出处

三者互相补充,不是互相替代。本体在这里还有一项容易被忽略的能力:执行前拦截。比如本体规定「拥有账户」这个关系的主语必须是签约客户,如果生成的查询把客服系统里的个人当成签约客户去关联,就违反了约束,可以在执行之前拦下来。

这让我修正了面完试时的一个说法。当时我的理解是:模型可能并没有变聪明,而是本体为它补齐了自然语言到物理数据之间缺失的那张业务常识地图。补完课我觉得这个说法只对了一半。地图负责找到正确的表和口径,但地图不会拦人,本体还有闸门的那一面:定义域、互斥、基数这些约束,会在查询执行前检查一遍。只有地图没有闸门,拦不住模型「顺手猜一个合理口径」的本能。

这套分工背后的思路叫神经符号(neuro-symbolic):大模型负责理解自然语言、提出候选,本体和推理机负责用确定的逻辑去约束、校验和解释。一个灵活,一个可靠。

五、上下文会不会爆炸

我最开始最怀疑的是这一点:本体建好了,一个企业几百个概念、几十条规则,总不能全塞进上下文吧?塞进去,不就是把「文档太多装不下」换成了「本体太多装不下」?

答案是:把解析分成三层,原则是先用最确定的方法,不够再往下走。

第一层是指标。问题正好是一个已定义的指标,比如「总净收入是多少」,就直接执行预先写好的 SQL,结果是确定的,谁来问得到的都是同一个数。但「上季度华东区活跃客户」多了几个限定,指标的 SQL 是固定的,加不进这些条件,于是交给第二层。

第二层基于本体生成结构化查询。这一层并不丢掉指标:净收入的官方定义会一起交给模型,模型在这个定义之上加时间、地区和活跃客户的条件,再通过虚拟知识图谱改写成 SQL。口径来自指标,概念来自本体,模型面对的是「签约客户、拥有账户」这样的业务语言,不是几百张缩写表名。

第三层是知识检索。用户问「为什么退款要从收入里扣除」,答案不在任何一张表里,而在收入确认政策这样的文档里,系统做语义检索,大模型综合出答案并附上出处。

越往下越灵活,确定性越低,耗时也越长。不管走哪一层,生成的 SQL 执行前都要过一道防火墙:只允许只读查询,按当前用户的权限限制能访问的表和列。Agent 是代表某个用户在做事,每次调用都按这个用户的身份判断权限,agent 自己的身份只用来记审计日志。换句话说,agent 能看到的不会超过它背后的人。

另一个减压的设计是「先探索再执行」。语义层把自己的能力包装成一组工具:列出指标、描述本体、执行查询。agent 先调用描述本体的工具,弄清楚签约客户和付款账户的区别,再发起查询,就像新员工入职先读业务词典再去查数据。本体不需要常驻上下文,它是按需查询的服务。

所以对我自己的疑问,答案是:本体不是上下文的负担,它恰恰是把「必须塞进提示词的隐性知识」变成「可以按需拉取的显性服务」的手段。上下文问题的解不在窗口大小,在知识治理。

六、回到那道面试题

如果现在再被问一次「什么是本体」,我会这样答:

本体是把一个团队对业务世界的共识,有哪些概念、概念之间是什么关系、要遵守什么规则,写成机器能读、能推理的说明书。它像 ER 图,但每条声明都带逻辑语义,能推出没写出来的结论,也能在执行前拦下违反约束的查询。在 Agent 系统里,它和指标、文档一起组成语义层,让模型用业务语言而不是表名来思考。

至于「要从哪里开始建」,我现在的答案是:从口径开始。从你的系统里挑一个最容易吵起来的字段,回答三个问题:它的口径是什么?它和哪个概念有什么关系?谁能看它?把答案写成一条机器能读的声明。这三个问题里只要有任何一个要靠「问一下老王」才能答出来,它就是你第一个该写进本体的东西。

参考