编程语言描述过程,无法描述世界。
编程语言是人类最伟大的发明之一
编程语言是无歧义的。
x = 3 + 4 无论何时何地运行,结果都是7。
没有解释的余地。
编程语言是可验证的。 语法错误在编译前就能捕获。 类型错误在运行前就能捕获。 测试一旦运行,结果要么通过,要么失败。
编程语言是图灵完备的。 一切可计算的事物都能表达。 只要有足够的时间和内存,任何过程都能描述。
这个系列中指出的自然语言的局限——歧义、不可验证、缺乏结构——编程语言全部解决了。
那么,用编程语言来表达AI的上下文不就行了吗?
不行。
编程语言描述的是过程
下面是有效的Python代码。
def calculate_revenue(units_sold, unit_price):
return units_sold * unit_price
这段代码清晰、可验证、可执行。 但它表达的是什么?
“将销售数量乘以单价,得到收入。”
这是一个过程。一种方法。是HOW。 它描述的是当输入到来时该做什么。
现在试着表达以下内容。
“三星电子2024年第三季度的收入为79万亿韩元。”
这不是过程。这是事实。是WHAT。 什么都不执行。它描述的是世界的状态。
用Python怎么表达这个?
samsung_revenue_2024_q3 = 79_000_000_000_000
把一个数字赋值给了一个变量。 能运行。但这不是描述,而是存储。 这段代码不知道:
- “三星电子"是什么类型的实体。
- “收入"是什么意思。是财务指标,还是物理量?
- “2024年第三季度"是时间、版本,还是标签?
- 79万亿韩元这个值的来源是什么。
- 这个值有多确定。
变量名 samsung_revenue_2024_q3 让人类可以猜测其含义。
对机器来说,它只是一个任意字符串。
改成 xyzzy_42,执行结果完全一样。
在编程语言中,变量名不承载含义。 含义存在于代码之外,存在于程序员的脑海中。
更精细的方式也无济于事
创建一个类不就行了吗?
class FinancialReport:
def __init__(self, company, metric, period, value, currency):
self.company = company
self.metric = metric
self.period = period
self.value = value
self.currency = currency
report = FinancialReport("삼성전자", "매출", "2024-Q3", 79_000_000_000_000, "KRW")
好了一些。有了结构。 但问题依然存在。
company 是字符串"삼성전자”(三星电子)。
“Samsung Electronics”、“SEC"和"005930"都指同一家公司。
代码知道吗?不知道。
它只能比较字符串是否相等。
metric 是字符串"매출”(收入)。
“매출”、“매출액"和"revenue"是同一个东西吗?
代码不知道。字符串不同,所以它们就是不同的。
定义一个schema不就行了吗? 用Enum管理公司列表,管理指标列表。 可以。能运行。
那现在试着表达以下内容。
“李舜臣是伟大的。”
opinion = Opinion("이순신", "was", "위대했다")
这是什么? 一个字符串"이순신”(李舜臣)和一个字符串"위대했다”(伟大的)绑在了一起。 这不是在表达"李舜臣是伟大的”, 而是在存储"이순신"和"위대했다"。
代码不知道"위대했다"(伟大的)是什么意思。 “위대했다”(伟大的)和"훌륭했다"(优秀的)是否相近, “비겁했다”(懦弱的)是否相反—— 代码无从知晓。
像财务数据这样的结构化事实还勉强可以应对。 评价、语境、关系、抽象描述——这些都超出了编程语言的表达范围。
代码不知道自己在做什么
def process(data):
result = []
for item in data:
if item["value"] > threshold:
result.append(transform(item))
return result
这段代码执行得完美无缺。 但这段代码是做什么的?
是在过滤收入数据? 是在筛选病历? 是在清洗传感器数据?
代码本身不知道。
data、value、threshold、transform——全是抽象名称。
这段代码属于财务系统还是医疗系统,
取决于代码之外的上下文。
可以写注释。 但注释是自然语言。机器无法理解。 注释和代码不一致,编译器也不会发现。 注释是给人看的,不是给机器看的。
当AI接收代码作为上下文时,这个问题直接显现。 因为代码没有自我身份, AI每次都必须通过推理来重建它的身份。 因为是推理,所以有成本,而且可能出错。
根本原因
编程语言无法描述世界,这不是设计缺陷。 是因为目的不同。
编程语言的目的是向机器下达过程指令。 “当这个输入到来时,执行这个操作。” 编程语言的语义学是执行的语义学。 每一条语句都被解释为"机器做什么"。
x = 3 是指令"在名为x的内存位置存储3"。
if x > 0 是指令"如果x大于0,则执行下一个代码块"。
return x 是指令"将x的值返回给调用者"。
全是动词。全是行为。全是过程。
“三星电子是韩国的企业"不是动词。 不是行为。不是过程。 它描述的是世界处于什么状态。
编程语言中没有为此留出位置。 可以存入变量,但那是存储,不是描述。 存储的值的含义不在代码的管辖范围内。
JSON、YAML、XML呢?
不是编程语言,那数据格式呢?
{
"company": "삼성전자",
"metric": "매출",
"period": "2024-Q3",
"value": 79000000000000,
"currency": "KRW"
}
有结构。字段是显式的。 但没有含义。
“company"是否表示企业,JSON不知道。 “삼성전자"和别处的"Samsung Electronics"是否是同一个东西,JSON不知道。 这个JSON对象和那个JSON对象是否描述同一个实体,JSON不知道。
JSON提供结构,但不提供含义。 它是键值对,不是实体-关系-属性。
定义schema会好一些。 JSON Schema、Protocol Buffers、GraphQL。 字段类型被定义了,必填性被定义了,引用被定义了。
但这些全是为特定系统设计的结构。 不是通用的知识表示。 财务数据schema无法表达对历史人物的评价。 医疗数据schema无法表达企业间的竞争关系。
每个领域一套独立的schema。 每套schema一套独立的工具。 schema之间没有互操作性。
这一局限在为什么MD/JSON/XML不够用中有更详细的讨论。
LISP呢?
读到这里的人中,有些可能已经想到了一个反例。
LISP。
(is 삼성전자 (company korea))
(revenue 삼성전자 2024-Q3 79000000000000)
(great 이순신)
S-expression是树结构, 代码即数据,数据即代码。 同像性(homoiconicity)。
事实上,早期AI全部基于LISP。 SHRDLU、CYC、专家系统。 用LISP表示知识,运行推理引擎。 似乎是对"编程语言无法描述世界"的历史性反证。
但这个反例因三个原因而不成立。
LISP知道的与程序员规定的
在 (is 삼성전자 company) 中,is 表示"是……“这种关系——
LISP并不知道这一点。
是程序员这么规定的。
把 is 换成 zzz,LISP毫不在意。
(zzz 삼성전자 company) 对LISP来说是完全有效的表达。
LISP提供的是结构——一种叫S-expression的树。 但结构中承载的含义不是语言赋予的,而是程序员赋予的。 这和JSON不知道其键的含义本质上是一样的。
提供结构与内嵌含义是两回事。
CYC的30年
最雄心勃勃地尝试这一点的项目是CYC。
始于1984年。 试图用LISP表示通用知识。 数百万条规则由人工输入。
30年证明的不是可行性,而是局限性。
每个领域都必须手工设计本体。 跨领域互操作行不通。 无法跟上自然语言的灵活性。 规模越大,维护一致性就越接近不可能。
用LISP"能做"知识表示,这没错。 “做得好”——这一点被30年的成果所否定。
如果不eval,就没有理由使用LISP
最根本的问题。
LISP的力量在于 eval。
因为代码即数据,所以数据可以被执行。
元编程、宏、运行时代码生成。
这才是让LISP成为LISP的东西。
但对 (is 삼성전자 company) 执行 eval 会发生什么?
它变成一个函数调用——将 삼성전자 和 company 作为参数传递给名为 is 的函数。
不是描述,而是执行。
要用于知识表示,就不能eval。 如果不eval,就没有使用LISP的语义。 只是借用了S-expression的语法而已。
这不是"用LISP描述世界”, 而是"用LISP的括号记法存储数据”。
LISP作为编程语言的语义——执行的语义—— 仍然是为描述过程而设计的。 借用语法并不会改变语义。
描述世界的语言需要什么条件
编程语言描述过程。 数据格式提供结构但没有含义。 即使LISP也只是借用语法,没有描述的语义。
那么,描述世界的语言需要什么?
实体的身份。 “三星电子"必须拥有唯一标识符。机器必须知道它和"Samsung Electronics"是同一个东西。不是字符串比较,而是身份的同一性。
关系的表达。 在"三星电子是韩国的企业"中,必须能够表达"韩国的企业"这一关系。不是变量赋值,而是关系的描述。
描述的自我描述。 这个描述是关于什么的、谁说的、以什么时间为准、有多确定——这些都必须包含在描述本身中。在代码之内,而非之外。
领域独立性。 财务数据、历史事实、主观评价、抽象关系——都必须能用同一种格式表达。不是每个领域一套独立的schema,而是一种通用结构。
编程语言不具备这四个特性。 因为编程语言不是为此而造的。 它们是为描述过程而造的。
自然语言能做到这四点。但充满歧义。 我们需要的,是将自然语言的表达范围与编程语言的精确性结合起来。
总结
编程语言无歧义、可验证、图灵完备。 但无法描述世界。
编程语言描述的是过程。 “当这个输入到来时,做这个。” 全是动词,全是行为。 “三星电子是韩国的企业"不是行为。 编程语言中没有为此留出位置。
代码不知道自己的身份。 它属于哪个领域、执行什么目的—— 代码中没有记录。
JSON、YAML等数据格式提供结构,但没有含义。 LISP可以借用语法,但没有描述的语义。 CYC用30年尝试基于LISP的知识表示,证明的是局限性。
描述世界需要实体的身份、关系的表达、描述的自我描述、领域独立性。 编程语言不是为此而造的。 自然语言能做到,但充满歧义。 我们需要的,在两者之间的某处。