第七章 原语依赖:工程推理的语法
7.1 图本身并非创新
图(Graph)已成为现代 AI 系统中最流行的抽象形式之一。知识图谱连接实体,场景图连接视觉对象,计算图连接数学运算,工作流图连接可执行任务,LangGraph 连接智能体状态。
尽管形态各异,这些图共享一个共同特征:图本身并非智能的来源。真正的智能蕴藏在边的语义之中。缺乏有意义的依赖关系,图不过是一堆离散节点的集合——没有意义的数据结构,没有血肉的骨架。
这一观察绝非空谈哲思。它对工程智能系统的设计具有深刻的实践指导意义。当一位交通工程师面对信号配时优化或拥堵诊断这类问题时,他不会一上来就在白板上画框画箭头。他会先问:什么依赖于什么? 哪些工程概念必须先行确立,其他概念才能被评估?哪些证据必须先收集到位,结论才能得出?
因此,交通智能体工程(TAE)的出发点并不是:”原语应该如何连接?”而是:为什么两个原语必须连接? 只有回答了这个问题,一个合法的推理图才能浮现出来。图是工程推理的结果,而非其起点。这种倒置——从”图即设计”到”图即后果”——正是 TAE 与传统工作流式 AI 系统的根本分野之一。
根本性倒置:工作流系统问”应该执行哪些步骤?”TAE 问”什么知识依赖于什么?”前者产出执行计划;后者产出推理结构。二者是服务于不同目的的根本性产物。
不妨以自然语言作类比。句子的语法结构(即其语法树)产生于词语之间的语义关系,而非某种预先设计的模板。同理,原语依赖图(Primitive Dependency Graph)产生于工程概念之间的语义关系。工程推理的语法,如同语言的语法一样,是被发现的,而非被发明的。
7.2 依赖即工程知识
假设一位工程师希望评估某个信号交叉口的服务水平(Level of Service, LOS)。LOS 能否直接从原始传感器数据计算得出?不能。在 LOS 能够确立之前,若干前置工程概念必须已知且可量化:
到达流量 → 通行能力 → 饱和度 → 控制延误 → LOS
这条链中的每一支箭头都代表一种不可逆依赖:不知道到达流量和通行能力,就无法计算饱和度;不知道饱和度,就无法计算控制延误;不知道控制延误,就无法评估 LOS。这些关系并非可以随意 rearrange 的实现细节。它们是工程知识——关于交通工程概念之间如何关联的不变真理。
关键洞察在于,这些依赖关系在所有可能的算法实现中始终成立。无论延误是用 HCM 解析公式计算的、从浮动车轨迹数据估算的、由神经算子预测的、还是通过仿真近似得到的,LOS 仍然依赖于延误。这种依赖属于工程语义范畴,而非计算实现范畴。它是领域的属性,而非任何特定算法的属性。
这种算法无关性正是依赖图作为抽象层如此强大的原因所在。算法在演进——这是必然的——而依赖结构保持稳定。Webster 方法让位于 HCM,HCM 又正在让位于数据驱动和神经网络方法。然而,从流量到通行能力、到饱和度、到延误、再到 LOS 这一概念依赖链,半个多世纪以来从未改变。TAE 将这些关系称为工程依赖(Engineering Dependencies),并将它们提升为工程智能架构中的一等公民。
| 依赖类型 | 示例链路 | 算法无关? | 稳定性 |
|---|---|---|---|
| 语义定义 | 流量 → 通行能力 → 饱和度 | 是 | 数十年 |
| 证据需求 | 检测器数据 → 排队长度 | 部分 | 高 |
| 约束验证 | 信号方案 → 最小绿灯 | 是 | 永久 |
7.3 依赖不等于执行顺序
一个常见且后果严重的误解是将图的边解读为规定了执行顺序。将依赖与调度混为一谈,会导致架构既僵化又不正确。以下面交叉口运营中的一个例子为例:
排队 → 延误 → LOS
工作流设计者可能会这样解读:”先执行排队计算,再执行延误计算,最后计算 LOS。”这种解读是错误的。图并没有声明排队必须在延误之前执行。它所声明的是:除非存在充分的排队相关证据,否则延误无法确立。边代表的是语义依赖,而非运行时调度。
这一区分之所以至关重要,原因有三:
第一,独立性带来并行性。 如果两个原语之间不存在依赖关系,它们就可以并发执行。不同进口道流向的排队计算彼此独立,完全可以并行运行。强制顺序执行的工作流系统会无谓地浪费这种并行性。
第二,依赖并不意味着即时性。 延误依赖于排队,并不等于每次需要延误时都必须重新计算排队。排队证据可能早已计算完毕、缓存起来且仍然有效。依赖规定的是一种知识需求,而非计算触发。
第三,执行仍然是运行时调度器的职责。 原语依赖图只定义什么知识依赖于什么其他知识。如何以及何时获取这些知识是另一个问题,交给后续章节介绍的领域编译器(Domain Compiler)和运行时调度器来处理。推理结构(图)与执行策略(调度器)之间的这种关注点分离,对于构建灵活、自适应的工程智能系统至关重要。
架构原则:依赖图回答”什么必须在什么之前知道?”运行时调度器回答”何时以及应该如何计算?”混淆这两个问题,产出的系统要么过于僵化(工作流引擎),要么过于混乱(无结构的智能体循环)。
7.4 五类工程依赖
工程依赖并非同质化的。TAE 将它们划分为五个截然不同的类别,每类捕捉一种不同的工程关系模式。理解这些类别对于构建正确的推理图和实现高效的依赖解析算法都必不可少。
(一)语义依赖
一个工程概念依据另一个概念来定义。被依赖的概念若脱离其前驱,便不具备独立含义。
示例:延误 → LOS。根据 HCM 定义并在全球采用的服务水平(LOS),是一种明确基于控制延误的分类方案。没有底层的延误值,LOS 评估就毫无意义。这种依赖是定义性的、绝对的。
语义依赖构成工程本体论的骨架。它们建立领域概念的层级结构,违反它们等同于重新定义概念本身。它们是最基本、最不可变的一类依赖。
(二)证据依赖
一个工程结论需要另一个原语产生的证据支持。被依赖的原语若无其前驱的输入,就无法产出可靠输出。
示例:通行能力 → 需求过载。”需求超过通行能力”这一结论的得出,必须先确立通行能力的实际值究竟是多少。通行能力提供了用以比较需求的证据基准线。
证据依赖与语义依赖的区别在于,它们是经验性的而非定义性的。它们描述的是得出结论需要什么信息,而非一个概念意味着什么。它们有时可以被放宽或近似处理(例如,当实时通行能力估计不可用时使用历史平均通行能力),而语义依赖则不能。
(三)约束依赖
一个原语需要另一个原语来验证工程约束。该约束必须满足,被依赖原语的输出方可视为有效。
示例:信号配时 → 最小绿灯 → 行人安全。一个拟议的信号配时方案依赖于最小绿灯时间满足行人过街要求的验证。信号方案受行人安全考量约束,而非相反。
约束依赖为推理引入方向性:它们定义哪些工程关切优先于哪些。它们对于确保工程输出满足法规、安全和运营要求至关重要。
(四)时间依赖
工程推理往往跨越时间维度。当前结论依赖于过往观测,未来预测依赖于当前状态。
示例:Queue(t) → Queue(t+1) → 排队增长。分析排队随时间的增长需要多个时间步的时间观测。不理解时刻 t 的排队状态,就无法理解 t+1 时刻的排队状态。这种依赖存在于时间维度之上。
时间依赖在交通工程中尤为重要,因为交通状况本质上是动态的。高峰小时积聚、溢出传播和拥堵消散都是需要时间序列推理的时间现象。依赖图必须显式捕捉这些时间关系。
(五)空间依赖
交通工程本质上具有空间属性。某一位置的状况通过物理传播效应影响其他位置的状况。
示例:上游排队 → 下游溢出 → 死锁风险。上游交叉口形成的排队可能导致溢出到下游交叉口,进而增加整个网络的死锁风险。这种依赖存在于空间维度上,而非单个交叉口内部。
空间依赖正是网络级交通工程区别于孤立交叉口分析的关键所在。它们使得关于走廊级现象、全网拥堵传播和区域交通动态的推理成为可能。正确捕捉空间依赖,对于任何超越单交叉口范围的工程智能系统都不可或缺。
| 依赖类别 | 方向性 | 违反后果 | 示例 |
|---|---|---|---|
| 语义 | 定义性 | 概念失去意义 | 延误 → LOS |
| 证据 | 经验性 | 结论不可靠 | 通行能力 → 过载 |
| 约束 | 法规性 | 输出无效/不安全 | 配时 → 最小绿灯 |
| 时间 | 时序性 | 动态分析不可能 | Queue(t) → Queue(t+1) |
| 空间 | 拓扑性 | 网络推理失败 | 上游Q → 溢出 |
7.5 依赖驱动图的生长
与工作流系统中流程图由人工工程师手动设计不同,TAE 中的原语依赖图通过一种称为依赖展开(dependency expansion)的过程自动涌现。这种自动构建不仅仅是为了方便——它是应对真实世界工程问题复杂性的根本要求。
考虑这样一个责任:优化孤立交叉口的信号配时。推理规划器(第五章介绍)识别出此责任需要原语 Signal Timing。随即,系统查询 Signal Timing 的依赖规格,发现其直接依赖:
Signal Timing
├── Queue
├── Delay
├── Capacity
├── Offset
规划器随后递归展开每个依赖。Capacity 展开为:
Capacity
├── Arrival Flow
├── Lane Geometry
├── Saturation Flow Rate
├── Green Split
Delay 展开为:
Delay
├── Queue
├── Signal Timing (cycle length, splits)
├── Arrival Pattern
推理图就这样逐层生长,如同一棵从根部不断扩展的树。每一步展开都应用编码在原语依赖声明中的工程知识。当所有叶节点均为依赖已被可用证据完全满足的原语(例如原始检测器数据、几何属性或缓存的计算结果)时,过程终止。
这一递归展开过程具有几项重要性质:
它是确定性的。 给定相同的责任和相同的依赖规格,总是生成相同的图。不涉及随机性或启发式搜索。
它是完备的。 展开过程保证所有前置知识都被发现。没有任何依赖会被遗漏,因为过程系统地探索整棵依赖树。
它是高效的。 通过记忆化(memoization)和去重,同一个原语不会被展开两次。如果 Queue 同时作为 Signal Timing 和 Delay 的依赖出现,它只会被解析一次并共享。
它是可解释的。 最终的图精确记录了每个原语为何被包含其中——它的存在是因为有别的原语依赖于它,且可追溯回原始责任。
因此,图的构建是一个工程推理过程,而非工作流设计练习。图编码了工程师的逻辑,使其显式化且可机器执行。
7.6 依赖构成工程类型系统
这或许是原语依赖图最重要的性质,也是 TAE 与通用基于图的 AI 系统最鲜明的区别之处:并非任意两个原语都可以相互连接。连接必须满足工程语义。
考虑下面两条依赖链:
- 合法:排队 → 延误(排队长度是延误估计的必要输入)
- 非法:排队 → 天气 → 周期时长(天气或许与交通模式相关联,但天气与周期时长之间不存在直接的工程依赖)
第一条链代表交通工程理论认可的合法工程关系。第二条链虽然可能存在统计相关性,但并不代表有效的工程依赖。连接这些原语会产生荒谬的推理——这正是那些纯粹基于统计关联或无约束图连通性构建的系统所饱受困扰的那类错误。
TAE 因此引入了一套工程类型系统(Engineering Type System)来规范原语的连接方式。每个原语暴露四个类型签名:
| 类型签名 | 描述 | 示例(traffic.queue.length) |
|---|---|---|
| 语义类型 | 该原语代表什么工程概念? | QueueMeasurement |
| 输入类型 | 它消费何种证据类型? | DetectorCounts, TimeInterval |
| 输出类型 | 它产生何种证据类型? | QueueLengthEstimate |
| 证据类型 | 其输出的认识论地位是什么? | EmpiricalMeasurement |
从原语 A 到原语 B 的依赖仅在 B 的输出类型与 A 所需输入类型之一兼容(或可转换)时才是类型安全(type-safe)的。如果 traffic.delay.control 要求类型为 QueueLengthEstimate 的输入,而 traffic.queue.length 产生的输出类型恰好是 QueueLengthEstimate,则该依赖有效。反之,若 traffic.weather.condition 产生的输出类型是 WeatherObservation,且该类型不可转换为 QueueLengthEstimate,则类型系统会拒绝此依赖。
于是,推理图变成了类型安全的。类型系统阻止荒谬的连接,在图构建阶段(而非运行时产出垃圾输出时)捕获错误,同时充当嵌入系统中的工程语义文档。本质上,它在工程推理过程中扮演着与编程语言中类型系统相同的角色——只不过这里接受”类型检查”的”程序”是工程推理过程本身。
类型安全 = 工程正确性:正如编译器的类型检查器能防止程序将字符串与整数相加,TAE 的工程类型系统能防止推理图将天气观测输入延误计算。两者都在错误造成损害之前捕获了范畴错误。
7.7 依赖是声明式的
工程依赖绝不应硬编码在算法内部。那样做会将工程知识嵌入实现代码中,使其变得不可见、不可测试、不可演进。取而代之的做法是,依赖应作为工程知识来声明——独立于任何算法或执行引擎存在的一等公民artifact。
一个原语依赖的声明式形式大致如下:
Primitive: traffic.delay.control
DependsOn:
- traffic.queue.length # 证据依赖
- traffic.signal.cycle # 证据依赖
- traffic.signal.split # 证据依赖
Produces: delay_evidence # 输出类型
EvidenceType: DerivedMeasurement # 认识论地位
---
Primitive: traffic.los.assessment
DependsOn:
- traffic.delay.control # 语义依赖(定义性)
Produces: los_assessment
EvidenceType: Classification
正因为依赖是声明式的,编译器可以对推理图执行强大的自动化操作:
- 分析:检测不可达原语、孤立的子图和缺失的依赖。
- 优化:消除冗余计算、合并重复路径、按效率重排序。
- 验证:证明所有依赖均已满足、所有约束均被尊重、不存在循环引用。
- 演进:当工程知识发生变化(例如发现新的依赖)时,更新声明,让编译器自动将变更传播至所有受影响的推理图。
这种声明式方法映照了软件工程中基础设施即代码(Infrastructure as Code)的演进。正如 Terraform 和 Kubernetes 清单声明期望的基础设施状态(将”如何实现”留给编排器),TAE 依赖声明声明期望的推理结构(将执行交给领域编译器)。收益是同样的:意图与机制的分离。
7.8 依赖解析
一旦依赖变得显式且声明化,编译器便可自动解析之。这个过程——依赖解析(dependency resolution)——是从初始目标原语构造完整原语依赖图的机械程序。
给定一个目标原语(例如 traffic.los.assessment),解析算法执行以下步骤:
- 递归发现:从目标出发,沿声明的依赖链接递归发现所有前置原语。
- 去重:移除重复项——若同一原语可通过多条路径到达,仅保留一个实例。
- 循环检测:验证依赖图中不含环。(环意味着建模错误——工程依赖应形成有向无环图。)
- 语义兼容性检查:验证所有依赖边均满足工程类型系统。拒绝非法连接。
- 叶节点识别:识别叶节点(无未解决依赖的原语),这些节点可直接从可用证据源获得满足。
- 图组装:产出完整的、经过验证的原语依赖图。
这个过程类似于现代包管理器(npm、pip、cargo)中的依赖解析——只不过被解析的对象是工程概念,而非软件库。解析器确保所有前置条件均已具备、版本兼容、冲突被检测到。但 TAE 解析器不从注册表下载包,而是从工程知识出发构建推理结构。
依赖解析的输出是一张完整的、经过验证的、类型安全的原语依赖图——随时可交付给领域编译器,转化为可执行形式。
| 解析步骤 | 包管理中的类比 | TAE 对应物 |
|---|---|---|
| 递归发现 | 传递性依赖扫描 | 沿 DependsOn 链接追踪 |
| 去重 | 去重(deduplication) | 共享公共原语 |
| 循环检测 | 循环依赖错误 | 建模错误检测 |
| 类型兼容性 | 版本约束检查 | 工程类型系统 |
| 叶节点识别 | 无依赖的包 | 证据源原语 |
| 图组装 | 锁文件生成 | 完整 PDG 输出 |
7.9 原语依赖图是可执行的语法
编程语言定义语法。编译器将源代码解析为抽象语法树(AST)。AST 代表程序的语法结构——表达式、语句和声明的层级组织赋予程序以意义。
依赖图在工程推理中扮演着类似角色。正如 AST 代表源代码的语法结构,原语依赖图代表工程推理的语法结构。节点是”词汇”(原语),边是”语法规则”(依赖),整个图则是”句子”(推理过程)。
但有一个关键差异。编程语言的 AST 是被动数据结构——它描述但不执行。原语依赖图则不然,它是可执行的语法(executable grammar)。它不仅是对推理结构的描述,更是可以被编译为可执行代码的规格。图同时承担三种角色:
- 文档:以人和机器均可读的形式解释工程推理过程。
- 规格:精确定义需要执行哪些计算以及依赖顺序为何。
- 编译目标:充当中间表示,供领域编译器转换为优化后的执行代码。
这种三位一体——文档、规格、编译目标——正是原语依赖图成为 TAE 核心组织结构的原因。图中之前的一切(责任、IR、规划器、原语)服务于其构建。图中之后的一切(编译器、验证、交付)操作于其输出之上。
PDG 的核心地位:若将 TAE 比作编译器,原语依赖图便是其中间表示(IR)——所有前端阶段产出、所有后端阶段消费的规范性中间表示。它是整个架构中结构性清晰度的最高点。
7.10 从依赖图到推理编译器
走到 TAE 架构的这一步,工程推理已经完全声明化了。让我们追溯一下整个转化链条:
- 责任已被分解为可回答的工程问题。
- 问题已被提炼为可检验的假设。
- 假设已被锚定于具体的证据需求。
- 证据需求已被映射到工程原语。
- 原语已被组织成带类型的依赖图。
抽象链条已然完整:从模糊的工程意图到精确的、机器可读的推理结构。然而,图本身仍是一项工程规格。它描述了什么必须计算以及为何存在依赖,但它尚未成为一个可执行程序。它声明了推理结构,但其自身并不进行推理。
下一章将介绍领域推理编译器(Domain Reasoning Compiler)——负责将原语依赖图转换为适合运行时调度的优化执行图的组件。依赖图回答”什么依赖于什么?”,编译器回答”应该如何执行?”这种从声明式规格到命令式执行的转化,完成了工程思维与工程行动之间的桥梁。
| TAE 组件 | 回答的问题 | 输出形式 |
|---|---|---|
| 责任模型 | 工程目标是什么? | 结构化目标 |
| 工程中间表示 | 解决方案需要什么? | 声明式规格 |
| 推理规划器 | 应该如何推理? | 假设-证据对 |
| 工程原语 | 推理的最小单元是什么? | 带类型的、有文档的原语 |
| 依赖图 | 什么依赖于什么? | 带类型的无环图 |
| 领域编译器(下一章) | 应该如何执行? | 优化后的执行计划 |