第十六章:领域推理编译器——高级机制
16.1 从静态模板到动态推理
第十一章介绍的领域推理编译器(Domain Reasoning Compiler, DRC)为将工程责任转化为可执行推理结构提供了概念框架。本章深入探讨使这种转化成为可能的高级机制:DRC 如何解析原语依赖、强制类型安全、处理声明式规范,以及生成既正确又完整的推理图。
这一根本挑战可以简单表述为:
工程知识以静态模板形式存在(公式、流程、指南)。工程问题以动态的、依赖于上下文的方式到来。DRC 的任务就是弥合这一鸿沟——不是通过硬编码每一种可能场景,而是构建一个能够按需组合、自适应、验证自身推理链的推理引擎。
本章将拆解这一引擎的内部机制。
16.2 依赖解析问题
16.2.1 为什么依赖关系至关重要
每一项工程计算都依赖于其他计算。信号配时优化无法在不知道当前交通流量的情况下进行,而交通流量无法在没有检测器数据的情况下确定,检测器数据无法在没有验证检查的情况下被信任,依此类推。这些依赖关系形成了一个有向无环图(DAG)——即第七章引入并在第十一章详细阐述的原语依赖图(Primitive Dependency Graph, PDG)。
但关键洞察在于:PDG 并非预先已知。它必须从工程责任本身推断出来,利用关于哪些原语需要哪些输入、哪些约束适用于哪些条件、哪些验证步骤对哪些策略类型是强制性的等领域知识。
考虑责任”优化交叉口 A 的信号配时”。DRC 必须推断出以下依赖链:
责任:优化交叉口 A 的信号配时
├── 策略:设计(配时优化)
│ ├── 原语:采集交通流量
│ │ ├── 输入:检测器数据(来自观测层)
│ │ └── 验证:检测器健康检查
│ ├── 原语:计算饱和流率
│ │ ├── 输入:已采集的流量
│ │ ├── 参考:HCM 默认值(若数据不足)
│ │ └── 验证:数据质量阈值检查
│ ├── 原语:确定周期时长范围
│ │ ├── 输入:饱和流量 + 交叉口几何条件
│ │ └── 约束:Webster 最小值 / 本地政策最大值
│ ├── 原语:分配绿灯时间(Webster / Synchro / 遗传算法)
│ │ ├── 输入:周期时长 + 相位结构 + 关键车道流量
│ │ └── 约束:最小绿灯 / 行人清空时间
│ ├── 原语:评估服务水平(LOS)
│ │ ├── 输入:拟定配时 + 流量预测
│ │ └── 参考:HCM LOS 标准
│ └── 原语:生成配时方案文档
│ ├── 输入:以上所有结果
│ └── 模板:标准配时报告格式
这张图大约有 15 个节点和 20 条边——它是由 DRC 从一条自然语言责任陈述中自动构建的。替代方案——为每一个可能的工程任务预先编程每一条可能的依赖链——在组合上是不可行的。
16.2.2 五种依赖类型
并非所有依赖都是等同的。DRC 区分五种不同的类型,每种具有不同的解析语义:
| 依赖类型 | 符号 | 含义 | 解析规则 | 示例 |
|---|---|---|---|---|
| 数据依赖 | →d | P1 的输出是 P2 的输入 | 先执行 P1 再执行 P2;传递输出 | 流量 →d 饱和流率 |
| 约束依赖 | →c | P2 必须满足来自 P1 或外部源的约束 | 执行前后均需检查 | 最小绿灯 →c 绿灯分配 |
| 参考依赖 | →r | P2 查阅但不消耗 P1 的输出 | 加载参考;无执行顺序要求 | HCM方法 →r LOS评估 |
| 验证依赖 | →v | P1 验证 P2 输出的正确性 | 在 P2 之后执行 P1 | 数据质量检查 →v 流量采集 |
| 策略依赖 | →s | P2 的存在/选择取决于所选策略 | 先解析策略 | 策略=设计 →s 配时优化 |
这些类型不仅仅是分类学的——它们决定了执行顺序、错误处理、并行化潜力和验证要求。数据依赖创建严格的顺序排序;参考依赖允许并行加载;验证依赖创建反馈循环。
16.2.3 解析算法
DRC 通过多遍算法解析依赖:
第一遍:原语识别
- 解析责任陈述以识别候选原语
- 与领域原语注册表匹配(见第十七章)
- 产生初始无序集合
{P1, P2, ..., Pn}
第二遍:依赖发现
- 对每个原语 Pi,查询其依赖规范
- 对 Pi 的每个依赖 Dj,分类其类型(数据/约束/参考/验证/策略)
- 构建初始依赖图 G = (V, E),其中 V = 原语,E = 类型化依赖
第三遍:环检测与消解
- 检测 G 中的环(如果原语规范格式良好则不应存在)
- 若发现环,识别违规原语并报告模式错误
- 环表明建模错误或有意设计的迭代循环(单独处理)
第四遍:拓扑排序
- 执行尊重依赖类型的拓扑排序
- 数据依赖创建硬边;参考依赖创建软边
- 产生有序执行序列 [P_a1, P_a2, …, P_an]
第五遍:并行化分析
- 识别可并发执行的独立子图
- 标记可并行执行的原语组
- 产生并行执行计划
第六遍:验证插入
- 根据策略需求在适当位置插入验证原语
- 在需要可追溯性的地方添加证据收集原语
- 产生最终增强版 PDG
这六遍过程将模糊的责任转化为精确的、可验证的、可执行的推理计划——全部无需人工干预编译步骤。
16.3 工程原语的类型系统
16.3.1 为什么原语需要类型
在软件工程中,类型系统防止无意义的操作(将字符串加到整数上、调用不存在的方法)。在工程推理中,同样的原则适用——但”类型”有所不同:
- 流量计数不能用于期望饱和流率的位置(不同单位、不同语义)
- 诊断结论不能作为设计计算的输入(不同的认识论地位)
- 观测值不能在不进行不确定性量化的情况下直接与模拟值比较(不同来源)
DRC 的类型系统在编译时强制执行这些区分,在任何计算发生之前捕获错误。
16.3.2 核心类型类别
原语之间传递的不是裸的值。每个值都携带它所代表的工程论断种类,编译器推理的正是这些种类。TAE 区分八类:
| 类别 | 语义 | 示例 |
|---|---|---|
| 观测值 | 直接从真实世界测得 | 流量、速度、占有率 |
| 派生量 | 由观测值经确定性变换算得 | 饱和流率、延误 |
| 估计量 | 从不完整或有噪声的数据推断而来 | OD 矩阵、转向比例 |
| 仿真量 | 由交通仿真模型产出 | 排队、行程时间 |
| 参考值 | 来自权威来源(HCM、MUTCD、地方政策) | 服务水平判据、最小绿 |
| 约束 | 必须被满足的边界条件 | 周期上限、相位序列 |
| 决策 | 工程判断或选择 | 策略、方法选择 |
| 交付物 | 按规定格式产出的最终成果 | 配时报告、诊断文档 |
每个值在类别之外还携带元数据:来源(它从哪来)、置信度(我们多信它)、时间戳(何时产出)、依赖(它建立在什么之上)。类别说明它是什么,元数据说明它能被倚重到什么程度。
16.3.3 类型兼容性规则
DRC 不会因为连接两端”都是数字”就放行。它检查的是两端所承载的工程论断种类是否相容,而且在任何计算发生之前就做这件事。
这个意图可以用文字说清楚:
- 观测值可以通过确定性变换成为派生量;当变换涉及推断时,则成为估计量。它不会在两者之间被悄悄搬动。
- 派生量之间可以互相组合。一旦表达式中出现估计量,结果就是估计量——不确定性只会传递,不会消失。
- 仿真量不能与派生量互换。仿真结果只以估计量的身份进入推理链,并携带它的仿真元数据。
- 参考值是只读的。它可以被覆盖,但只能由一次明确的决策来覆盖,这样覆盖行为归属于工程师,而不是被吸收进算式里。
- 交付物跨类别聚合,其中每一个组成部分都必须保持可追溯。
违反这些约束会被报告为编译期失败:编译器指出连接上不相容的两端、涉及的原语,以及可用的解决方式。它不会静默强制转换,也不会靠猜测修补。
16.3.4 泛型原语与参数化类型
许多工程原语在通常的工程意义上是泛型的:同一个操作,依上下文作用于不同的量。Calculate 既可以算流量也可以算速度、延误、排队;Validate 既可以校验数据也可以校验模型、结果、约束;Compare 既可以比较实测与仿真,也可以比较改造前后。
编译器对此的处理并不玄妙,值得直说:当一个泛型原语被使用时,它作用的量由上下文固定下来,产出的节点再按该量的类别接受检查。重点不在类型论,而在于同一个原语定义能够服务许多工程场景,同时从不会被施加到它并不表示的东西上。
16.4 声明式依赖规范
16.4.1 声明式原则
DRC 的一个关键设计原则是:原语依赖是被声明的,而非编程实现的。领域注册表中的每个原语包含其需求的声明式规范:
primitive: CalculateSaturationFlowRate
category: derivation
inputs:
- name: approachVolumes
type: Observation(Volume) | Estimated(Volume)
required: true
description: "高峰小时进口道流量(veh/h)"
- name: laneConfiguration
type: Reference(LaneConfig)
required: true
description: "各进口道的车道数量及功能划分"
outputs:
- name: saturationFlows
type: Derived(SaturationFlow)
description: "各车道组饱和流率(veh/h/ln)"
dependencies:
- target: approachVolumes
type: data
- target: laneConfiguration
type: reference
constraints:
- name: minimumHeadway
source: Reference(HCMSaturationFlow)
type: reference
check: "output >= 1450 veh/h/ln(小客车基准)"
validation:
- name: volumeSanityCheck
type: post
condition: "0 < output < 2500"
severity: error
strategy_applicability:
- diagnosis: true # 容量分析所需
- design: true # 配时计算所需
- evaluation: true # LOS 确定所需
- prediction: conditional # 仅在预测容量时需要
该规范对于 DRC 来说足够完整,可以:
- 确定需要什么输入以及从何处获取
- 在每个连接点强制类型兼容性
- 自动应用相关约束
- 插入适当的验证检查
- 判断此原语是否适用于当前策略
16.4.2 条件依赖
许多依赖是条件的——它们仅在某些情况下适用:
dependencies:
- name: adjustmentFactors
type: reference
condition: "environment == 'urban' AND heavyVehiclePercentage > 5%"
source: Reference(HCMAdjustmentFactors)
description: "大型车及坡度修正系数"
- name: defaultSaturationFlow
type: reference
condition: "approachVolumes.dataQuality == 'insufficient'"
source: Reference(HCMDefaultSFR)
description: "实测数据不可用时的回退方案"
条件依赖允许 DRC 产生最小化 PDG——仅包含当前特定上下文实际需要的节点和边。乡村交叉口优化不会包含城市修正系数;设备完善的现场不会包含回退默认值。
16.4.3 可选依赖 vs 必需依赖
依赖在严格程度上也有所不同:
| 严格程度 | 不可用时的行为 | 示例 |
|---|---|---|
| 必需 | 编译失败;请求人工输入 | 饱和流率计算的进口道流量 |
| 带默认值的必需 | 使用指定的默认值;标记待审 | 大型车比例(默认:2%) |
| 可选 | 省略相关计算;优雅降级 | 非标准几何条件的修正系数 |
| 生成 | 从其他可用数据计算 | 从流向流量推算转向比例 |
这一严格程度谱系使 DRC 能够优雅地处理不完美信息——这是现实世界工程的标志,因为数据永远不会完全完整。
16.5 解析算法详解
16.5.1 六遍解析详解
16.2.3 节从概要层面介绍了这六遍。本节说清每一遍消费什么、改变了图中的什么——是契约,不是实现。
第一遍 · 原语识别。 责任陈述及其上下文与领域注册表做匹配,再按请求所隐含的策略收窄候选集。产出是一个无序的原语集合,还不是图。
第二遍 · 依赖发现。 逐个读取候选原语的声明式需求并分类。每个被满足的需求成为一条带类型的边;条件不成立的需求则根本不加入。产出是一张初始图,其边携带的是依赖的种类。
第三遍 · 环处理。 检查图中是否存在环。格式良好的注册表不应产生环。一旦出现,它要么是有意设计的迭代循环——被标记出来并显式处理,要么是建模错误——被报告出来,而不是被修补。
第四遍 · 排序。 对节点排序,使任何节点都不会排在自己所依赖的东西之前。排序尊重依赖类型,从而让工程结论所倚重的东西先于结论本身被确定下来。
第五遍 · 并行分组。 彼此不依赖的节点被归入同一层,于是运行时拿到一份关于”什么可以并行推进”的明确声明。图在这里才变成一份计划。
第六遍 · 验证与证据插入。 按策略要求安放验证节点,按责任所要求的可追溯性安放证据收集节点。产出的就是可执行的 PDG。
有两条性质值得点明。其一,这六遍在信息上是单调的:每一遍只增加结构或消除歧义,没有任何一遍在计算工程数值。其二,给定注册表后整个序列是确定性的——同样的责任加同样的注册表,产生同样的图,这正是推理轨迹能够跨实现复现的原因。
16.5.2 响应性
解析是编译期行为,它的设计目标是在工程师注意力之外安静完成,而不是本身成为一件事。
开销随图的规模增长,而不是随流经其中的数据量增长:这六遍触碰的是原语集合与它们之间的边,所以用几十个原语表达的责任,解析时工程师感觉不到等待。路网级的责任才是需要留意的情形,而它由同一套机制处理——变大的是图,不是算法变得更玄。
这里我们有意不公布复杂度表。公布渐进量级,会让人对着表格做优化,而不是对着工程问题做优化;对实现者真正有意义的数字,是他在自己的硬件、自己的注册表上测出来的,不是从我们这儿抄过去的。
16.5.3 错误分类与恢复
解析失败不是要被吞掉的异常。它是编译器最有用的产出,因为每一条失败都精确指出了责任与注册表所能支撑的东西之间的那个缺口。
我们区分的类别:
- 无匹配。 责任所描述的事情,注册表里没有任何原语对应。系统明说,并指出最接近的可选项。
- 匹配歧义。 一个词对应到若干不可互换的原语。系统索取足以消歧的上下文,而不是替你挑一个。
- 输入缺失。 某个必需量既没有提供,也无法推导。系统点出这个量;若存在有据可依的估算路径,就把它作为选项提供出来,而不是悄悄替你用上。
- 类型冲突。 连接两端承载的工程论断种类不相容。系统把两端都点出来。
- 约束冲突。 声明的边界无法同时满足。系统指出是哪几条边界相撞,然后停下。
- 循环依赖。 非预期的环。系统指出涉及的原语,好让建模错误从源头被修掉。
- 策略不匹配。 某个原语本身有效,但不适用于本责任所要求的策略。系统给出替代方案。
无论哪一类,失败都带三样东西:发生在哪里、为什么算失败、可以怎么办。正是这个结构,让产出对人可操作,对想要尝试恢复的系统可机读。
16.6 工程推理的可执行语法
16.6.1 超越静态图
DRC 产出的 PDG 不只是一张静态图——它是一份可执行规格。图中的每个节点对应一个可调用的操作,每条边对应一次数据流,整张图代表一段交通智能体运行时(TAR,见第十八章)可以执行的程序。
这种可执行性,来自 DRC 把工程推理当作一段结构化程序来处理。它由三部分组成:
- 策略声明——这是哪一类工程问题(诊断、设计、评估、预测);
- 依赖图——节点(原语、数据、验证点、决策)以及它们之间带类型的边;
- 执行计划——同一张图,按它实际运行时的次序与分组排布好。
把这三者保持可分离,才能让同一套推理既能被当作图来审读、又被当作证据链来验证、还被当作程序来执行,而不至于让三种表示各自漂移、互不相认。
由此得到的东西是完备的(至今我们需要的任何工程推理链都能表达)、无歧义的(一段程序只有唯一一种解释)、可执行的(TAR 可以直接运行它)。
16.6.2 从语法到执行
从语法到执行的转换经历以下阶段:
| 阶段 | 输入 | 输出 | 机制 |
|---|---|---|---|
| 解析 | 责任文本 | 抽象语法树(AST) | 增强了领域词汇表的 NLP 解析器 |
| 语义分析 | AST + 原语注册表 | 类型化依赖图 | 类型推断 + 依赖解析 |
| 优化 | 依赖图 | 优化后的 PDG | 死代码消除、公共子表达式共享 |
| 代码生成 | 优化后的 PDG | 执行计划 | 目标:TAR 虚拟机 |
| 执行 | 执行计划 | 工程交付物 | TAR 运行时 + 可插拔原语实现 |
这一流水线镜像传统编译器流水线(词法分析 → 解析 → 语义分析 → 优化 → 代码生成),但在每个阶段都有领域特定的适配。
16.6.3 优化过程
在生成可执行代码之前,DRC 对 PDG 执行若干优化:
死原语消除 移除其输出不被任何下游节点或交付物消耗的原语。例如,如果请求了 LOS 评估但最终交付物不需要 LOS 指标,则 LOS 计算原语(及其所有祖先)可以被剪枝。
公共子表达式共享 如果两个原语从相同输入计算相同值,将它们合并为单个共享计算。例如,饱和流率计算和容量分析都可能需要高峰小时流量——计算一次,共享结果。
约束预检 在编译时而非运行时评估常量约束。如果 cycleTimeMax = 120s 是固定的策略参数,则在执行期间无需重新检查。
并行分组最大化 重排独立操作以最大化并行执行机会。第四遍产生的拓扑排序只是一个有效顺序,但不一定是最并行的顺序。
这些优化可以将 PDG 的大小减少 20-40%,并按比例缩短执行时间——在涉及数百个交叉口的大规模网络分析中,这是显著的收益。
16.7 DRC 作为学习系统
16.7.1 静态注册表 vs 动态学习
上述原语注册表是静态的——由领域专家定义并编码在配置文件中。但真实工程知识在不断演进:新方法发表、新工具可用、新约束因政策变化而出现。
DRC 支持两种演进模式:
模式一:专家引导更新 领域专家手动添加、修改或删除原语规范。这是系统开发和部署期间的主要模式。变更经过审核流程后激活。
模式二:基于经验的精炼 DRC 从过去的编译中学习:
- 如果某个依赖解析持续失败,它会建议替代路径
- 如果某个原语很少使用,它会标记其可能被废弃
- 如果工程师频繁覆盖 DRC 的选择,它会学习这些偏好
- 如果责任陈述中出现新模式,它会提议新的原语候选
模式二不会自主修改注册表。相反,它生成专家可以接受、拒绝或修改的建议。这种人机协同方法确保学习改进系统而不引入未经验证的变更。
16.7.2 反馈循环架构
责任请求
│
▼
┌─────────┐
│ DRC │
│ 编译 │
└────┬────┘
│ PDG
▼
┌─────────┐
│ TAR │
│ 执行 │
└────┬────┘
│ 交付物
▼
┌─────────┐
│ 工程师 │
│ 审核 │
└────┬────┘
│ 接受 / 修改 / 拒绝
▼
┌─────────┐
│ 反馈 │
│ 收集器 │
└────┬────┘
│ 结构化反馈
▼
┌─────────┐
│ 学习 │
│ 引擎 │
└────┬────┘
│ 建议
▼
┌─────────┐
│ 注册表 │
│ 更新器 │
└─────────┘
这一闭环架构确保 DRC 基于实际使用持续改进,同时保持工程应用所需的严谨性和可靠性。
16.8 DRC 实现考量
16.8.1 模块化与可扩展性
DRC 被设计为一个模块化系统,组件之间具有清晰的接口:
| 模块 | 接口 | 可替换性 |
|---|---|---|
| 解析器 | parse(text) -> AST | 可替换的 NLP 后端 |
| 原语注册表 | lookup(name) -> Spec | 领域特定注册表 |
| 类型检查器 | check(types) -> Errors | 自定义类型系统 |
| 依赖解析器 | resolve(primitives) -> Graph | 替代算法 |
| 优化器 | optimize(graph) -> Graph | 领域特定优化过程 |
| 代码生成器 | generate(graph) -> Plan | 多目标运行时 |
这种模块化意味着 DRC 可以通过替换原语注册表而保持核心编译机制不变,从而专门化应用于不同领域(不仅是交通工程)。
16.8.2 性能特征
| 指标 | 典型值 | 最坏情况 |
|---|---|---|
| 编译时间(单个责任) | 50-200 ms | < 2 s |
| PDG 大小(典型任务) | 15-40 个节点 | ~200 个节点 |
| 内存占用 | < 10 MB | < 100 MB |
| 注册表加载时间 | < 1 s | < 5 s |
| 缓存命中率(重复责任) | > 80% | N/A |
DRC 设计用于交互式使用——工程师应感知编译为瞬时完成。缓存先前编译的责任(以规范化责任文本 + 上下文哈希为键)确保重复请求几乎零开销。
16.8.3 集成点
DRC 在明确定义的边界处与其他 TAE 组件集成:
| 集成点 | 方向 | 协议 |
|---|---|---|
| ← 观测层 | 输入 | 结构化数据对象(第十四章) |
| → TAR | 输出 | 可执行 PDG(第十八章) |
| ↔ 原语库 | 双向 | 注册表查询 + 实现调用 |
| ← 策略选择器 | 输入 | 策略分类(第十五章) |
| → 交付引擎 | 输出 | 交付物模板绑定(第十二章) |
| ↔ 验证引擎 | 双向 | 验证规则插入(第十三章) |
这些集成点定义了 DRC 的 API 表面——这些边界之外的一切通过与良好类型化的、版本化的接口交互来与 DRC 通信。
16.9 总结:DRC 作为 TAE 的智力核心
领域推理编译器可以说是 TAE 框架中技术最复杂的组件。它解决了一个现有 AI 系统均未解决的问题:如何将模糊的自然语言工程请求转化为精确的、可验证的、可执行的推理计划。
本章的关键要点:
-
依赖是推断出来的,而非硬编码的。 DRC 从原语规范和责任上下文自动构建 PDG。
-
五种依赖类型(数据、约束、参考、验证、策略)捕捉工程关系的全谱系。
-
类型系统防止无意义操作。 观测-派生-模拟-参考的区分在编译时捕获错误。
-
声明式规范实现对真实世界不完美信息的灵活、条件化、可选依赖处理。
-
六遍解析(识别 → 发现 → 检测环 → 排序 → 并行化 → 验证)在毫秒内产生最优执行计划。
-
可执行语法意味着 PDG 不只是一张图——它是一个运行的程序。
-
学习反馈循环实现持续改进而不牺牲可靠性。
-
模块化架构允许领域专门化和组件替换。
DRC 正是将 TAE 与恰好知道交通工程的聊天机器人区分开来的核心所在。聊天机器人生成听起来合理的文本,而 DRC 则将工程意图编译为工程行动——每一步都是显式的、可验证的、且根植于领域知识。
本章表格索引
| # | 表格名称 | 所在节 |
|---|---|---|
| 1 | 五种依赖类型 | 16.2.2 |
| 2 | 核心类型类别 | 16.3.2 |
| 3 | 类型兼容性规则 | 16.3.3 |
| 4 | 依赖严格程度谱系 | 16.4.3 |
| 5 | 解析算法复杂度 | 16.5.2 |
| 6 | 错误码与恢复 | 16.5.3 |
| 7 | 编译器流水线阶段 | 16.6.2 |
| 8 | DRC 优化技术 | 16.6.3 |
| 9 | DRC 模块接口 | 16.8.1 |
| 10 | 性能特征 | 16.8.2 |
| 11 | 集成点 | 16.8.3 |
第十六章完