ERDL 规范 v2.1
(Entity-Rule Definition Language · 实体规则定义语言)
权威源声明(2026-09-04 · 同步 v2.1):本文件为 ERDL 语言规范的副本,权威源为 erdl 仓库
erdl-language-spec-v2.1.md;规范性事实以权威源为准。
状态:v2.1 · 定稿 日期:2026-09-03 版本语义:本文档(ERDL 语言规范)版本为 v2.1;规则文件顶层
protocol: "erdl/v2"(协议标识,固定值)与version: "2.1.0"(规则格式版本)为独立版本标识,与本文档版本互不混同。 作者:唐启鑫 商标:ERDL™ 是深圳市秒镜科技有限公司的商标。 定位:ERDL(Entity-Rule Definition Language,实体规则定义语言)是一种以 YAML/JSON 承载的声明式规则定义格式,用于精确表达实体结构与行为规则。本规范独立且中立——仅定义格式本身,不依赖任何特定实现或上层框架;其确定性求值与规范化形式支持跨实现逐字节验证。在 ERDL 中,规则决定一切:规则既是语义的载体,也是执行的边界、审计的证据与治理的事实。 规范语言:本文档中 MUST / MUST NOT / SHOULD / SHOULD NOT / MAY 按 [RFC 2119] 解释。
1. Introduction
1.1 ERDL 是什么
ERDL(Entity-Rule Definition Language,实体规则定义语言)是一种以 YAML/JSON 承载的声明式规则定义格式,用于精确表达实体结构与行为规则。它包含两类声明:**Entity(实体)**定义数据结构;**Rule(规则)**定义 when → then 决策。Entity 界定对象的结构,Rule 规定条件的后果;二者共同构成可执行、可审查、可验证的规则表达。
1.2 设计哲学
ERDL 是多方语义层:它不仅是规则格式,更是人、LLM、系统与审计四方共享的语义约定层。
| 参与方 | ERDL 的角色 |
|---|---|
| 人(业务/领域专家) | 自然语言规则的精确翻译结果,可读、可审 |
| LLM(通用大模型) | 结构化输入,消除歧义,支持确定性求值 |
| 系统(规则引擎) | 标准化规则描述,并以 fn 委派控制调用边界 |
| 审计(监管/合规) | 比代码与自然语言更清晰的可追溯规则记录 |
在这一语义层中,规则决定一切:人表达意图,LLM 翻译语义,系统执行决策,审计复核证据。ERDL 的确定性语义层为 LLM 提供明确方向:使用者以自然语言描述规则,经 ERDL 精确翻译后,由 LLM 基于结构化语义确定性执行——对话界面即为统一入口。
1.3 ERDL 在 AI 治理层面的价值
AI 治理的核心难题,不是模型能否给出答案,而是概率性输出如何满足监管、审计与追责所要求的确定性边界。ERDL 以 YAML/JSON 承载声明式规则,将实体结构、行为约束和处置动作固化为可验证的确定性规则层,使 AI 的「应当如何行动」从训练假设转变为可检查对象。
从信任到验证:传统治理依赖对齐评估或事后解释,难以证明某次具体决策遵循了哪些约束。ERDL 将约束表达为 when → then 规则,每次求值绑定 canonical_tree 快照与结果哈希,可独立重算、逐字节跨实现复核。治理方不再只是相信模型「被正确训练」,而是可以验证某次行为是否命中规则、命中哪条规则、为何得到该结果。
可追责的证据链:ERDL 求值结果可哈希、可重算,可将规则版本、输入快照、决策输出与哈希锚定形成审计记录。审计不是复述日志,而是可重现的证明过程——为何放行、为何拦截、由谁批准,均可回溯查验,避免事后解释成为唯一依据。
机制化的人机权责边界:ERDL 可通过 REQUEST_HUMAN、ESCALATE 等决策类型,将高风险操作、敏感实体变更、不可逆动作等强制交由人工裁决。人的最终决策权不再依赖流程口号,而是被编码为可执行、可测试、可审计的规则路径。
合规即代码与跨组织互认:法规、平台政策和内部红线可转写为 ERDL 规则,并通过测试向量持续验证。不同组织、不同实现或第三方审计机构可基于同一规则与规范化树独立复算,形成「信任但验证」的治理范式,使合规从声明文档变为可运行、可检查、可互认的治理事实。
1.4 设计目标
- 确定性:同一规则、同一输入,任何兼容实现 MUST 产生逐字节一致的求值结果与哈希;
- 可读性:任何规则可回读为自然语言(gloss),人可秒懂、可审;
- 可审计性:规则的求值过程可独立重算、可追溯;
- 跨实现可验证:语义收敛到唯一内核,通过测试向量逐字节比对证明实现合规。
1.5 核心承诺
语义载体是内核,不是语法。 语义 = 树 = 哈希。 三者在规范化形式下合一。
任何以「运算符语法」为语义载体的方案,都会因新需求而被迫线性扩张运算符,成本永不收敛。因此本规范把语义收敛到唯一的内核(表达式树),而把多种书写形态作为内核的确定性投影——它们不是各自独立的语言,而是同一语义的不同视图。规则决定一切:规则的有效性不取决于书写入口或实现形态,而取决于唯一、可重算、可哈希、可逐字节验证的规范化语义。
2. 文档结构
2.1 顶层格式
一个 ERDL 文档(*.erdl.yaml)由四个顶层字段构成,字段顺序 MUST 固定:
protocol: "erdl/v2" # 协议标识,固定值
version: "2.1.0" # 规则格式版本
metadata: { ... } # 文档级元数据(见 §2.2)
rules: [ ... ] # 规则列表(见 §4)
| 顶层字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
protocol | string | MUST | 协议标识,固定值 "erdl/v2" |
version | string | MUST | 规则格式版本(语义化版本) |
metadata | object | MUST | 文档级元数据 |
rules | array | MUST | 规则列表,元素见 §4 |
2.2 metadata
metadata:
name: "my-first-rule-set"
description: "允许读文件操作"
category: coding
decision: ALLOW # 所有规则不匹配时的 fallback 决策
tags: [example]
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 规则集名称 |
description | string | 规则集描述 |
category | string | 规则集分类(coding/security/compliance…) |
decision | string | fallback 决策(所有规则不匹配时的默认裁决,见 §6) |
tags | array | 标签 |
2.3 格式约定
- 字符串值 MUST 双引号,枚举关键字 / 数字 / 布尔值裸词书写;
- 缩进 MUST 2 空格;
- 文件 MUST 以
protocol: "erdl/v2"开头; - 版本兼容:同一
protocol大版本内,新增约束对存量规则 SHOULD Non-breaking(加载时 Warning 而非 Error);跨大版本(如 erdl/v1 → erdl/v2)为 breaking change,不适用向后兼容承诺。例外:when:"true"+ 拦截性then在任何版本均拒绝加载(Error)。
2.4 解析与求值概览
一个 ERDL 文档从文件到决策结果,走固定的五步管线。理解这条管线,即可理解 ERDL 如何被「解析」与「求值」:
| 步骤 | 动作 | 输入 → 输出 | 依据 |
|---|---|---|---|
| ① 加载 | 读入规则文档 | *.erdl.yaml → 结构化对象 | §2.1–§2.3 |
| ② 校验 | 加载时类型检查 | 结构化对象 → 合法文档(拒绝非法) | E5 |
| ③ 编译 | 三种书写形态归一化 | 合法文档 → 表达式树(canonical_tree) | E7、§8.2 |
| ④ 求值 | 树对输入事实逐节点判定 | 表达式树 + fact → 决策 | §7 |
| ⑤ 输出 | 生成求值证据 | 决策 → 可哈希、可重算的求值结果 | E6、§8 |
- ① 加载:读入
*.erdl.yaml,按 §2.3 格式约定解析(YAML 与 JSON 等价,无损互转)。 - ② 校验:加载时类型检查——字段顺序、必填、枚举值、
when/expr互斥等;违规拒绝加载。 - ③ 编译:Simple / Expression / 决策表 MUST 编译到同一表达式树(E7),生成规范化树(§8.2)。
- ④ 求值:表达式树对输入事实逐节点判定(§7)。树是纯函数(E1);
within/rate的状态由temporal_state受控注入。 - ⑤ 输出:产出决策结果,绑定 canonical_tree 快照与结果哈希(E6),可独立重算、逐字节验证。
输入事实(fact)与求值结果(output)的完整契约见 §7.0。
3. Entity 定义
Entity 是规则作用的主体(经 context 传递)。ERDL 预置以下 Entity 类型:
| Entity 类型 | 说明 |
|---|---|
agent | 单个 Agent 实例 |
tool | Agent 调用的工具 |
task | Agent 执行的任务 |
workflow | 多 Agent 编排流程 |
human | 人类审批者 |
guardian | 监管者(supervisor) |
规则中的字段引用(如 tool.name、context.amount)以 Entity 为语义命名空间。字段路径承重:字段名一旦发布即冻结 [FREEZE-1],别名 MUST 先行归一化为规范名。
4. Rule 定义
Rule 是 ERDL 的核心单元:Rule = Metadata + When(条件)+ Then(动作)+ Audit(审计)。When 编译为表达式树(§5),Then 为决策类型(§6),Audit 记录规则求值的审计信息。
4.1 字段定义
rules[] 子字段顺序 MUST 固定为:name → description → category → priority → override → ring → enabled → when → then → message → instruction → correction → unless → explanation → alternative → legal_basis → source_text。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | MUST | 规则唯一标识,格式 [CAT]-[NNN]-描述 |
description | string | MUST | 人读描述 |
category | string | MAY | 规则级分类;缺省继承 metadata.category(见 §2.2),允许同一文档内混合分类 |
priority | integer | MUST | 数字越小越优先(见 §7.1) |
override | string | SHOULD | 覆盖级别:critical > high > normal > low(默认 normal) |
ring | integer | SHOULD | 执行环:0 内核 / 1 恢复 / 2 审批 / 3 建议 |
enabled | boolean | MAY | 规则启用标志(默认 true);false 时求值跳过该规则 |
when | object | MUST | 触发条件(见 §5) |
then | string | MUST | 决策类型(见 §6) |
message | string | SHOULD | 决策消息(拦截性 then MUST 非空) |
instruction | string | MAY | 建议指令(ALLOW + instruction 场景) |
correction | string | MAY | 纠正文本(CORRECT 决策;求值输出的 primary_correction 来源,见 §7.0.3) |
unless | object/null | MAY | 豁免条件块(可选) |
explanation | string / object | MAY | 双语解释(规则为何存在、防止何种危害) |
alternative | string / object | MAY | 被拦截时建议的替代动作 |
legal_basis | string | MAY | 法规依据(条款引用) |
source_text | string | MAY | 所依据法规的原文摘录 |
4.2 完整示例
protocol: "erdl/v2"
version: "2.1.0"
metadata:
name: "my-first-rule-set"
description: "允许读文件操作"
category: coding
decision: ALLOW
tags: [example]
rules:
- name: "COD-001-allow-read"
description: "放行 read_file 工具的所有调用"
priority: 10
override: high
ring: 3
when:
logic: AND
conditions:
- field: "tool.name"
operator: eq
value: "read_file"
then: ALLOW
message: "read_file 调用已放行"
unless: null
5. when 条件表达式
when 是规则的内嵌条件表达式。ERDL 提供三种书写形态(Simple / Expression / 决策表),任一形态编译归一化到同一语义内核(表达式树),再经渲染还原为任一形态;另有一个可读投影 gloss(§5.5)从树确定性生成自然语言。
5.1 书写形态(投影面)
| 投影面 | 承载 | 适用 tier | 说明 |
|---|---|---|---|
| A · Simple | 30 运算符 | 0–2(MUST) | 安全底线,最常用的书写形态 |
| B · Expression | 完整 34 节点树 | ≥3 | 业务全景:逻辑组合/量词/算术/时间/聚合 |
| C · 决策表 | 矩阵 | — | 业务/财务人员首选,编译到同一内核 |
tier 是规则层级(0–5),由低到高表示规则的约束强度与适用范围——本规范中 tier 0–2 为安全底线(MUST 用 Simple),tier ≥3 为业务全景(可用 Expression)。when 与 expr 不得共存(E5)。
5.2 投影面 A:Simple(30 运算符)
Simple 是保留的既有语义单元集合,30 运算符 = 28 条件 + 2 修饰符,一字不改。它对应系统安全规则(tier 0–2)。
集合定义:
| 族 | 数量 | 运算符 |
|---|---|---|
| 比较 | 6 | eq · ne · gt · gte · lt · lte |
| 列表 | 2 | in · not_in |
| 字符串 | 5 | contains · not_contains · match · starts_with · ends_with |
| 边界否定 | 2 | not_starts_with · not_ends_with |
| 存在性 | 2 | exists · not_exists |
| 长度 | 5 | length_gt · length_gte · length_lt · length_lte · length_eq |
| 范围 | 2 | between · not_between |
| 计数 | 4 | count_gt · count_gte · count_lt · count_lte |
| 修饰符 | 2 | within(时间窗口)· rate(速率限制) |
语义约定(全运算符生效):
- 严格类型匹配:无隐式类型转换,
"100" gt 50恒为 false; - 同类型有序比较:数值用数值序,字符串用字典序(Unicode 码点序,
"2" gt "10"为 true);跨类型返回 false; - match 大小写敏感:默认大小写敏感,不提供内联不敏感选项;
- between 仅数值:闭区间
[min,max]仅支持数值,非数值返回 false; - 空值传播:字段缺失时,除 exists/not_exists 外统一返回 false(安全失败);
- 存在性唯一感知:仅 exists/not_exists 区分「缺失」与「值不符」;
- 列表上限:in/not_in 操作数 ≤256 项;
- 确定性保证:由封闭求值内核执行,无代码注入路径;
- 宽容别名:实现 MAY 接受
matches→match、neq→ne两个历史别名并归一。别名不是新增运算符(仍为 30 运算符全集);规范化形态 MUST 用规范名,别名不进树、不进哈希。不实现别名仍属合规。
权威编译映射:30 运算符全部有确定编译归宿,无悬空——13 直接节点(eq/ne/gt/gte/lt/lte·in·contains/starts_with/ends_with/match·exists·between)、6 not 派生(not_in/not_contains/not_starts_with/not_ends_with/not_exists/not_between)、9 length/count 组合(length_* 5 + count_* 4)、2 时间修饰符(within/rate)。
| # | Simple 运算符 | 编译归宿 | 表达式树表达 |
|---|---|---|---|
| 1-6 | eq ne gt gte lt lte | 直接节点 | 比较节点 |
| 7 | in | 直接节点 | 集合节点 in |
| 8 | not_in | not + exists 守卫 | exists(field) AND not(in(...)) |
| 9-12 | contains starts_with ends_with match | 直接节点 | 字符串节点 |
| 13 | not_contains | not + exists 守卫 | exists(field) AND not(contains(...)) |
| 14-15 | not_starts_with not_ends_with | not + exists 守卫 | exists(field) AND not(...) |
| 16 | exists | 直接节点 | 存在节点(值非 null 且非 undefined;空字符串 ""、0、false 均视为「存在」——「存在」≠「非空字符串」) |
| 17 | not_exists | not 组合 | not(exists(...)) |
| 18-22 | length_gt/gte/lt/lte/eq | 组合 + exists 守卫 | exists(field) AND length(field) 比较 n |
| 23 | between | 直接节点 | 范围节点(仅数值) |
| 24 | not_between | not + exists 守卫 | exists(field) AND not(between(...)) |
| 25-28 | count_gt/gte/lt/lte | 组合 + exists 守卫 | exists(field) AND aggregate(count(...)) 比较 n |
| 29 | within | 时间修饰 | 时间窗口(as_of 由引擎注入) |
| 30 | rate | 时间修饰 + 聚合 | 速率限制(temporal_state) |
exists 守卫(E11 空值传播的编译层保障):not_*(除 not_exists)与 length_*/count_* 组合派生 MUST 编译为 exists(field) AND <派生表达式>,而非裸 not(正向算子) 或裸 length/count(...) 比较。原因:正向算子对缺失字段返回 false、length(缺失) 返回 0,若直接 not 翻转或数值比较,空值传播被破坏(fail-open)。not_exists 是唯一例外——语义即「感知字段缺失」,保持裸 not(exists(...))。
有状态算子(within/rate):within 与 rate 是仅有的两个有状态算子,其求值依赖跨决策的滑动窗口内计数。这一状态不存储在表达式树节点中,而由独立的 Guard 状态管理器维护,以 temporal_state 字段进入审计记录——表达式树本身仍是纯函数(E1 成立),状态源可审计、可重算。
有状态算子真值语义(MUST):计数达阈值 → 条件成立(触发);未达阈值 → 记录本次事件并返回 false(放行)。
| 算子 | 阈值 | 首次/未超限 | 达阈值后 |
|---|---|---|---|
rate: "N/窗口" | N | 前 N 次:record + false | 第 N+1 次起:true |
within: "窗口" | 1 | 首次:record + false | 窗口内第 2 次起:true(去重) |
配套约束(均 MUST):① 计数后置(仅当正向条件成立才计数);② 计数隔离键(within 以 field+operator+value 为键,rate 以 field+operator+value+rate 为键);③ record 时机在「未超限」分支写入。
5.3 投影面 B:Expression(34 节点树)
Expression 开放完整内核表达力,面向复杂业务规则(tier ≥3)。语义内核是一棵类型化表达式树,由 34 个节点构成(归为 10 组),节点集冻结于 [FREEZE-2]:
| 组 | 节点 | 语义能力 |
|---|---|---|
| 取值 | field · var · 字面量 | 引用字段、上下文变量、常量(var 仅 $/$.path,禁读时钟与随机) |
| 逻辑 | and · or · not | 组合关系 |
| 比较 | eq · ne · gt · gte · lt · lte | 操作数可为字段、变量、字面量或算术子树 |
| 集合 | in | 标量属于集合 |
| 字符串 | contains · match · starts_with · ends_with | 模式匹配;match 走安全正则 |
| 存在/量纲 | exists · length · between | 存在性、长度(Unicode 码点)、闭区间 |
| 量词 | all · any · none | 数组逐元素判定;空数组一律 false |
| 算术 | add · sub · mul · div · round | 定点小数确定性运算 |
| 时间 | days_between · epoch_ms · date_add · date_part · month_last_day | 日期差、时间戳、日期推演、分量提取、月末取日 |
| 聚合 | aggregate(count/sum/avg/min/max) | 数组聚合 |
节点总数:取值 3 + 逻辑 3 + 比较 6 + 集合 1 + 字符串 4 + 存在/量纲 3 + 量词 3 + 算术 5 + 时间 5 + 聚合 1 = 34。「比较」6 运算符、「字符串」4 运算符、「算术」5 运算符、「量词」3 种类、「聚合」5 函数在实现中分别以参数化节点类型承载,故「34 个语义节点」在代码中映射为更少的类型字面量——二者是语义节点与类型投影的关系,非数量矛盾。
表达式书写示例:
when:
expr:
lt:
- div:
- sub: [{ field: "tool.args.price" }, { field: "context.cost" }]
- field: "tool.args.price"
- 0.15
5.4 投影面 C:决策表(矩阵形态)
决策表面向业务与财务人员,以行列结构表达多条件组合,编译到同一内核:
kind: decision_table
columns:
- field: "context.amount"
label: "申请金额"
rows:
- when: [["gte", 10000]]
then: "REQUEST_HUMAN"
priority: 100
- when: [["gte", 5000]]
then: "ESCALATE"
priority: 90
- when: [] # 默认行(无条件命中,兜底)
then: "ALLOW"
priority: 1
编译规则(E7):① 每行 when 条件组按字段列序编译为逻辑与(and),条件单元编译为比较节点;② 行序即优先级(自上而下首个命中,与 priority 一致,二者 MUST 不冲突);③ 默认行 when: [] 编译为字面量 true;④ then 值 MUST 属于 §6 决策类型枚举;⑤ 编译后产生与手写 Simple/Expression 相同的表达式树。
5.5 投影面 D:gloss(自然语言可读投影)
gloss 是从树确定性生成的自然语言表述:
gloss: "当(售价 减 成本)除以 售价 小于 15% 时,需人工审批" # 引擎生成,lint 强制一致
五条不变量(全部 MUST):
| # | 不变量 |
|---|---|
| G1 | gloss = render(树):由冻结渲染模板确定性生成 |
| G2 | 每条规则 MUST 携带 gloss,lint 校验 gloss == render(树),禁手写 |
| G3 | gloss 禁原始字段路径,MUST 用 Entity 的 display_name(中英双语) |
| G4 | gloss 为渲染产物(不进哈希),展示时实时 render(树) 呈现 |
| G5 | Simple 规则同样生成 gloss(编译为树后渲染)——阅读层不分层 |
gloss 渲染模板(逐节点,中英双语;{A}/{B}/{C} 为子表达式递归渲染结果):
| 节点 | 中文模板 | English template |
|---|---|---|
field | {field} | {field} |
var | {变量} | {variable} |
literal | {value} | {value} |
and | {A} 且 {B} | {A} and {B} |
or | {A} 或 {B} | {A} or {B} |
not | 非({A}) | not ({A}) |
eq | {A} 等于 {B} | {A} equals {B} |
ne | {A} 不等于 {B} | {A} does not equal {B} |
gt | {A} 大于 {B} | {A} is greater than {B} |
gte | {A} 大于等于 {B} | {A} is greater than or equal to {B} |
lt | {A} 小于 {B} | {A} is less than {B} |
lte | {A} 小于等于 {B} | {A} is less than or equal to {B} |
in | {A} 属于 {B} | {A} is in {B} |
contains | {A} 包含 {B} | {A} contains {B} |
match | {A} 匹配正则 {B} | {A} matches regex {B} |
starts_with | {A} 以 {B} 开头 | {A} starts with {B} |
ends_with | {A} 以 {B} 结尾 | {A} ends with {B} |
exists | {A} 已发生 | {A} exists |
length | {A} 的长度 | the length of {A} |
between | {A} 介于 {B} 与 {C} 之间 | {A} is between {B} and {C} |
all | {A} 中每一项均满足:{B} | every item in {A} satisfies: {B} |
any | {A} 中存在一项满足:{B} | some item in {A} satisfies: {B} |
none | {A} 中无一项满足:{B} | no item in {A} satisfies: {B} |
add | {A} 加 {B} | {A} plus {B} |
sub | {A} 减 {B} | {A} minus {B} |
mul | {A} 乘 {B} | {A} times {B} |
div | {A} 除以 {B} | {A} divided by {B} |
round | {A} 四舍五入 | {A} rounded |
days_between | {A} 与 {B} 之间的天数 | days between {A} and {B} |
epoch_ms | {A} 的时间戳 | the epoch milliseconds of {A} |
date_add | {A} 加 {B} 时长 | {A} plus {B} duration |
date_part | {A} 的 {part} | the {part} of {A} |
month_last_day | {A} 所在月的最后一日 | the last day of the month of {A} |
aggregate(count) | {A} 的元素个数 | the count of {A} |
aggregate(sum) | {A} 之和 | the sum of {A} |
aggregate(avg) | {A} 的平均值 | the average of {A} |
aggregate(min) | {A} 的最小值 | the minimum of {A} |
aggregate(max) | {A} 的最大值 | the maximum of {A} |
exists布尔字段特例:当字段名匹配is_*/has_*(布尔字段约定)时,exists渲染为{A} 为"是"(中文)/{A} is true(英文),而非{A} 已发生/{A} exists——布尔字段存在即真,避免「是否已告知 已发生」这类别扭表达。
6. then 决策类型
then 的值 MUST 属于以下 13 种决策类型:
| # | 决策类型 | 语义 |
|---|---|---|
| 1 | ALLOW | 放行 |
| 2 | DENY | 拦截 |
| 3 | CORRECT | 纠正偏差 |
| 4 | NOTIFY | 通知 |
| 5 | REQUEST_HUMAN | 请求人工裁决 |
| 6 | ESCALATE | 升级 |
| 7 | DELEGATE | 委派 |
| 8 | DEFER | 延期 |
| 9 | EMERGENCY_HALT | 紧急停止 |
| 10 | ROLLBACK | 回滚 |
| 11 | QUARANTINE | 隔离 |
| 12 | WORKFLOW | 工作流(状态机;子态 WORKFLOW_WAITING / WORKFLOW_PROGRESS) |
| 13 | GUIDE | 引导 |
7. 求值语义
7.0 求值概览
求值 = 表达式树(规则编译产物)对输入事实(fact)逐节点判定的纯函数过程(E1)。本节定义求值的输入契约、算法步骤与输出契约,供实现者与使用者对齐。
7.0.1 输入契约(事实对象)
求值输入是一个事实对象(fact),承载规则作用主体的当前状态,以 Entity(§3)为命名空间:
fact:
tool: # Entity: tool
name: "issue_refund"
args: { amount: 8000, order_id: "O1024" }
context: # 自由上下文字段(规则以 context.* 引用)
country: "CN"
role: "operator"
# 其他 Entity:agent / task / workflow / human / guardian(按需提供)
- 字段引用(
tool.name、context.amount、tool.args.amount)按事实对象的键路径解析(§3); as_of(求值时刻,UTC)与temporal_state(within/rate 滑动窗口状态)由引擎注入,属受控外部输入(E1);- 缺失字段按 E11 空值传播处理(§7.3(a))。
7.0.2 求值算法
输入:规则集 rules[] + 事实对象 fact
输出:决策结果(见 7.0.3)
1. 排序:按 priority 从小到大(值越小越优先)
2. 分组:按 ring 从 0 到 3 顺序执行(0 内核 → 1 恢复 → 2 审批 → 3 建议)
3. 每个 ring 内,按序求值每条规则:
a. unless 豁免先于 when 判定——命中豁免则记录后跳过该规则
b. 编译后的 when 表达式树对 fact 逐节点求值(true / false / 错误)
c. 首命中:每个 ring 内 first-match-wins(命中即短路该 ring)
d. override:仅 DENY → ALLOW 方向覆盖,不得覆盖到更不安全状态(§7.1)
4. 兜底:无规则命中 → metadata.decision(fallback 决策,§2.2)
5. 汇总:产出 decision + matched_rules + 证据(canonical_tree / hash / eval_trace)
- 求值错误按 E12 分 tier 折叠:tier≤2 及 Guard 上下文 fail-close,tier 3–5 折叠为 false;
EMERGENCY_HALT命中即短路;DENY不短路——继续求值以判断是否有 override ALLOW 覆盖。
7.0.3 输出契约(求值结果)
求值结果 MUST 包含以下字段:
| 字段 | 说明 |
|---|---|
decision | 最终决策(§6 枚举之一,或 fallback 决策) |
matched_rules | 命中的规则(按求值顺序) |
unless_exemptions | 被 unless 豁免的规则(单独记录,不计入 matched_rules) |
primary_instruction | 首要指令(ALLOW + instruction 场景) |
primary_reason | 首要理由(DENY 等拦截场景) |
primary_explanation | 首要解释(可中英双语) |
primary_correction | 纠正文本(CORRECT 决策;来源为规则字段 correction,见 §4.1) |
total_evaluated | 求值的规则总数 |
total_matched | 命中的规则总数 |
temporal_state | within/rate 滑动窗口状态快照(无命中时省略) |
求值证据(canonical_tree 快照、结果哈希、eval_trace)为可独立重算的派生产物(§8.2、E6)——canonical_tree 进哈希,eval_trace 不进哈希(§8.3)。
7.1 优先级与冲突解决
- 按
priority从小到大排序(值越小越先); - 同 priority 有
override标记的排前; override枚举:critical>high>normal>low(默认normal);- 同 priority 同 override 按定义顺序;
override仅允许 DENY → ALLOW 方向覆盖(不得覆盖到更不安全状态)。
7.2 求值约束(E1–E12,全部 MUST)
| 编号 | 约束 |
|---|---|
| E1 | 求值是纯函数:无副作用、无隐式外部状态、无时钟读取;within/rate 的状态注入(temporal_state)与 as_of 同级,属受控外部输入 |
| E2 | 定点小数 scale=14 + half-even + 字符串序列化;中间计算用高精度有界有理数,仅输出节点舍入 |
| E3 | 求值错误记 eval_warnings,折叠方向按 E12 分 tier |
| E4 | 资源上限(分级):Grade A 算术深度≤2 / 树深≤6 / 节点≤64 / 数组≤10000 / 单规则≤50ms / 量词不嵌套 / 正则步数≤10000;Grade B 树深≤10 / 节点≤256 / 算术深度≤4,量词嵌套≤2 层;Grade C 不适用 |
| E5 | 加载时类型检查;when 与 expr 不得共存 |
| E6 | 树即证据:canonical_tree(树快照)作为求值证据参与哈希;eval_trace 为可重算派生产物,不进哈希 |
| E7 | Simple 与 Expression 编译到同一求值核心,禁止两个求值器 |
| E8 | 量词安全折叠:空数组 → all/any/none 一律 false(反空洞真) |
| E9 | 禁读墙钟;as_of 由引擎注入并记入审计记录 |
| E10 | 字符串 NFC 规范化 |
| E11 | undefined 哨兵语义(空值传播,见 §7.3) |
| E12 | 求值错误处理:tier≤2 及 Guard 上下文缺省 fail-close,tier 3–5 折叠为 false |
内核显式排除:字符串拼接、正则替换、位运算、日期格式化、递归引用、用户自定义节点——以维持求值的封闭性与可验证性。
7.3 确定性语义(跨实现分叉防护)
以下语义 MUST 在文档与向量中显式标注,避免与标准实现产生语义误解:
(a) 空值传播(E11):Agent 上下文高度动态,字段缺失是常态。求值 MUST 三值逻辑安全失败:
| 场景 | 行为 |
|---|---|
| 字段不存在时的相等/数值比较 | 返回 false(非 NPE) |
== null / != null 检查 | 正常返回 true / false |
| 类型不匹配的比较 | 返回 false(禁止隐式转换) |
| 字段不存在时的算术运算 | 返回 false(条件)或 EvaluationError(算术表达式) |
(b) 量词空数组的安全折叠(E8):标准量词语义下 all(空)=true(空洞真)。本规范刻意偏离:all/any/none(空) 一律折叠为 false——防「无元素可校验却被判为放行」,并在审计记录中记录安全折叠。第三方实现 MUST 采用本折叠语义。
(c) 定点小数的中间精度(E2):中间计算采用高精度有界有理数(如 128 位整数分子/分母),仅输出节点按 scale=14 + half-even 舍入为字符串序列化(IEEE 754-2019 ROUND_HALF_EVEN)。
(d) 正则的 ReDoS 防护:match 节点 MUST 同时满足:① 单次匹配步数 ≤10000;② 输入长度上限;③ 优先确定性引擎(RE2 类)或安全语法子集。
(e) aggregate 空数组的安全折叠:
| 函数 | 空数组结果 | 依据 |
|---|---|---|
count(空) | 0 | 标准计数语义 |
sum(空) | 0 | 空和恒等元 |
avg(空) | false | 安全失败折叠(避免除零) |
min(空) | false | 安全失败折叠(标准 +Infinity,禁用) |
max(空) | false | 安全失败折叠(标准 −Infinity,禁用) |
aggregate 的 over MUST 为数组;非数组(缺失/标量/对象)返回 null + type_mismatch warning(折叠为 false)。count(缺失) 与 count(空数组) 语义不同:前者 type_mismatch,后者 0。
(f) 时间节点的 UTC 语义(E9):所有时间节点统一以 UTC 求值,保证跨实现、跨时区逐字节一致:
- 输入解析:date-only(
YYYY-MM-DD)按 UTC 解析;date-time 按 ISO 8601 带时区解析,无时区后缀按 UTC; - 分量提取(
date_part):一律取 UTC 分量; - 日期推演(
date_add、month_last_day):按 UTC 日历运算; - 时间差(
days_between):UTC 毫秒差 ÷ 86400000 向下取整(floor); - 序列化:ISO 8601 UTC(
toISOString)。
业务本地时区由引擎注入 as_of 时转换为 UTC 时刻,求值器以 UTC 纯函数运算。
7.4 when 最小完整度约束
when: "true" 的语义是「对所有操作生效」,仅适用于建议性规则:
| 规则 | 级别 |
|---|---|
when: "true" MUST NOT 与 then: DENY 搭配 | MUST NOT |
when: "true" MUST NOT 与 then: EMERGENCY_HALT 搭配 | MUST NOT |
when: "true" MUST NOT 与 then: CORRECT 搭配 | MUST NOT |
when: "true" MUST NOT 与 then: REQUEST_HUMAN 搭配 | MUST NOT |
when: "true" MAY 与 then: ALLOW + instruction 搭配 | MAY |
when: "true" MAY 与 then: NOTIFY 搭配 | MAY |
| 安全类规则(category=security)MUST 至少含 1 个 condition | MUST |
工具拦截类规则 SHOULD 含 tool.name 条件 | SHOULD |
文件操作规则 SHOULD 含 tool.args.path | SHOULD |
命令操作规则 SHOULD 含 tool.args.command | SHOULD |
8. 序列化与规范化
8.1 序列化
ERDL 文档以 YAML 承载,可无损转换为 JSON。规范化树(canonical_tree)以 JSON 对象形态序列化。
8.2 规范化树(Canonical Form)
表达式树是唯一求值、唯一哈希、唯一重算的基准对象。要使其哈希可跨实现逐字节一致,树 MUST 具有唯一规范化形式:
| 规范化规则 | 说明 |
|---|---|
| 节点序固定 | 子节点按规范顺序排列(左→右严格定序),与源书写顺序无关 |
| 字段名承重 | 字段引用路径承重——字段名发布即冻结 [FREEZE-1],别名 MUST 先行归一化 |
| 字面量规范 | 数字以定点小数字符串表示(scale=14 + half-even);字符串 NFC 规范化 |
| var 规范 | 仅支持 $ / $.path,路径段为确定字节序列 |
| 元数据剥离 | 注释、来源行号、格式、作者等非语义元数据一律不进规范化树 |
树哈希的对象是规范化树,而非任何特定实现的内存表示或序列化文本。 两个结构等价的树(仅字段书写顺序、空格、变量命名不同)规范化后产生完全相同的字节序列与哈希。
8.3 规范化树与 gloss 的关系
- gloss 由冻结渲染模板从树生成(G1);
- 进哈希的是树(规范化形式),不含 gloss 文本——树逐字节确定,满足哈希要求;
- gloss 文本不进哈希,因此措辞可有实现差异,不破坏树哈希的跨实现一致性;
- gloss 与树由渲染校验绑定(G2)——改 gloss 不改树即被判定无效。
这一「哈希树、校验 gloss 与树一致」的机制,使 gloss 在哈希模式下无需逐字节一致、也无需进哈希,即可获得树的密码学锚定。
9. 如何集成 ERDL
ERDL 的集成目标,是把关键判断从模型推理、框架代码或口头约定中抽离出来,变成可加载、可执行、可验证的规则资产。它通常以 YAML/JSON 规则文档承载,通过 when → then 完成决策,并输出可哈希、可逐字节复算的求值证据。以下三种集成路径,对应三类典型工程落点。
9.1 场景一:AI Agent(行为约束层 / Action Guard)
角色:在 AI Agent 链路中,ERDL 是 LLM 意图与系统执行之间的「确定性闸门」——Agent 可以生成动作,但动作是否允许执行,必须由规则求值决定。
模拟:客服 Agent 收到用户「退款 8000 元」的请求。LLM 将意图转换为工具调用:issue_refund(amount=8000, order_id=O1024)。在真正触达支付系统前,Action Guard 把工具名、参数、会话上下文打包成事实对象,交给 ERDL 求值。规则集中,R1 写作:when tool.name == "issue_refund" 且 tool.args.amount > 5000 → REQUEST_HUMAN;R2 写作:when tool.name == "issue_refund" → ALLOW。由于 R1 先命中,系统返回 REQUEST_HUMAN。Agent 停止调用支付工具,转为生成人工审批任务,并向用户返回「需人工复核」的话术。
集成要点:第一,规则在模型之外独立求值,Prompt 不再承担安全边界;第二,命中记录、输入摘要、canonical_tree 与结果哈希一并写入审计日志,任何一次拦截都可回放;第三,规则变更只需更新 ERDL 文档,不必重写 Agent 框架、工具实现或模型提示词。
9.2 场景二:MCP(协议分发 / 跨实现互操作)
角色:在 MCP 生态中,ERDL 规则通过 ERDL MCP Server 暴露为标准工具,任何 MCP 兼容 Agent 都能调用同一套规则,实现跨实现的合规分发与互认。
模拟:某企业将资金合规规则部署为 ERDL MCP Server,并暴露 guard_check 工具。第三方 Agent 准备执行一笔高额退款,但自身不掌握企业规则。执行前,它通过 MCP 调用 guard_check,传入动作描述:action="issue_refund"、amount=8000、channel="payment"。ERDL MCP Server 加载规则集,完成求值,返回:decision=REQUEST_HUMAN、matched_rule=R1、hash=0x9f...。第三方 Agent 根据返回值停止直接执行,转入人工审批流程,并将该决策凭据写入自己的执行日志。
集成要点:这里的关键是「规则即服务」。规则不再硬编码在某个 Agent 框架里,而是通过标准协议分发;不同厂商、不同语言、不同运行时的客户端,都可以消费同一份判断逻辑。由于返回结果携带规则命中与哈希信息,调用方即使不保存规则原文,也能把决策凭据归档,供后续审计或独立复算。规则升级时,只需更新 MCP Server 端,客户端无需重构执行链。
9.3 场景三:规则引擎(规则定义语言 / 确定性求值)
角色:在规则引擎内部,ERDL 是规则定义语言本身,负责以声明式方式表达业务策略,并提供确定性求值语义。
模拟:反洗钱引擎需要表达「大额交易且高风险国家 → 拦截」。业务团队编写 ERDL 规则:when context.amount > 10000 且 context.country in ["高风险国家列表"] → DENY。引擎加载 YAML 后,不会把它当成普通配置,而是编译为表达式树:比较节点处理金额阈值,集合节点判断国家归属,逻辑节点完成与运算。随后,引擎按照 E1–E12 求值语义执行,输出 DENY。该规则还可生成 canonical_tree 与哈希值;另一实现只要加载同一规则、同一输入,就能重算出相同结果。
集成要点:这里的核心价值是「可验证的确定性」。ERDL 不只让规则可读,还让引擎实现可以被测试向量约束。V-ENGINE 类测试向量可以证明:不同引擎、不同平台、不同版本,对同一规则应产生逐字节一致的结果。由此,规则引擎不再只是业务系统内部黑盒,而是可以接受第三方独立验证的执行器。监管方、审计方或平台方都能依据输入、规则哈希与求值证据,判断「引擎算得对不对」。
10. 示例与一致性验证
10.1 Quick Start(快速上手)
一个最小 ERDL 文档 + 一次求值,走完「写 → 加载 → 求值 → 得结果」全链路(管线见 §2.4)。
第一步 · 写规则(refund.erdl.yaml):
protocol: "erdl/v2"
version: "2.1.0"
metadata:
name: "refund-guard"
description: "退款金额管控"
category: coding
decision: ALLOW
rules:
- name: "SEC-001-refund-limit"
description: "退款超过 5000 需人工审批"
priority: 10
when:
logic: AND
conditions:
- field: "tool.name"
operator: eq
value: "issue_refund"
- field: "tool.args.amount"
operator: gt
value: 5000
then: REQUEST_HUMAN
message: "退款金额超过 5000,需人工审批"
第二步 · 加载 + 校验 + 编译:解析 YAML,校验通过后把 when 编译为表达式树(§2.4 步骤①②③)。
第三步 · 求值:给定事实对象:
fact:
tool:
name: "issue_refund"
args: { amount: 8000 }
规则 SEC-001 命中(tool.name == "issue_refund" 且 amount > 5000)。
第四步 · 结果:
decision: REQUEST_HUMAN
matched_rules: ["SEC-001-refund-limit"]
primary_reason: "退款金额超过 5000,需人工审批"
total_evaluated: 1
total_matched: 1
若输入改为 amount: 100,规则不命中,走 metadata.decision fallback → decision: ALLOW。
10.2 完整示例
见 §4.2(Simple 规则)与 §5.3(Expression 规则)、§5.4(决策表)。
10.3 一致性验证
本规范的语义 MUST 由可独立重算的测试向量证明。表达层向量(V-ENGINE / V-GLOSS / V-PROJ)覆盖:34 节点 × 4 场景(正常/边界/异常/空值)、E1-E12 语义、Simple 30 运算符编译映射、gloss 渲染模板。
五步验证法:加载向量输入 → 生成表达式树 → 重算求值结果 → 与答案对比 → 判定一致。
第三方 Runner 验证流程(从零到合规):
- 读本规范;
- 用自己选择的技术栈实现独立验证器(不引用任何既有实现代码);
- 加载测试向量,逐字节比对;
- 对照 Runner 契约确认自建实现满足契约;
- 提交结果至实现注册表,供第三方审计复验。
附录 A · 34 节点参考表
| 组 | 节点 | 数量 |
|---|---|---|
| 取值 | field · var · 字面量 | 3 |
| 逻辑 | and · or · not | 3 |
| 比较 | eq · ne · gt · gte · lt · lte | 6 |
| 集合 | in | 1 |
| 字符串 | contains · match · starts_with · ends_with | 4 |
| 存在/量纲 | exists · length · between | 3 |
| 量词 | all · any · none | 3 |
| 算术 | add · sub · mul · div · round | 5 |
| 时间 | days_between · epoch_ms · date_add · date_part · month_last_day | 5 |
| 聚合 | aggregate(count/sum/avg/min/max) | 1 |
合计 34 节点。
附录 B · Simple 30 运算符参考表
| 族 | 运算符 | 数量 |
|---|---|---|
| 比较 | eq · ne · gt · gte · lt · lte | 6 |
| 列表 | in · not_in | 2 |
| 字符串 | contains · not_contains · match · starts_with · ends_with | 5 |
| 边界否定 | not_starts_with · not_ends_with | 2 |
| 存在性 | exists · not_exists | 2 |
| 长度 | length_gt · length_gte · length_lt · length_lte · length_eq | 5 |
| 范围 | between · not_between | 2 |
| 计数 | count_gt · count_gte · count_lt · count_lte | 4 |
| 修饰符 | within · rate | 2 |
合计 30 运算符(28 条件运算符 + 2 条件修饰符)。
附录 C · 决策类型枚举(13 种)
见 §6。
附录 D · 函数委派与规则分级
对于内核显式排除、确有需求的场景,提供函数委派(FnRegistry)作为受控兜底:
| 约束 | 说明 |
|---|---|
| 注册制 | 函数 MUST 注册方可引用,未注册不可调用 |
| 沙箱执行 | 受限环境,受资源配额与超时约束 |
| 确定性豁免声明 | 用于 Guard 求值路径的函数 MUST 声明并保证确定性 |
| 审计可溯 | 每次调用记入审计记录,可离线核验 |
规则分级(Grade):
| Grade | 表达方式 | 审计 SLA |
|---|---|---|
| A | 纯 Simple(30 运算符) | 最高,纯文本可重算 |
| B | Expression 树 | 高,eval_trace MUST |
| C | 含函数委派 | 分层,C 级不得冒充纯文本可重算 |
含函数委派的规则(Grade C)MUST 在 gloss 中显式标记「含不可重算的函数委派」;函数委派的调用输入 + 输出哈希 MUST 纳入结果哈希的原像。
附录 E · 术语表
| 术语 | 一句话定义 |
|---|---|
| Entity(实体) | 规则作用的主体类型(agent/tool/task/workflow/human/guardian),字段引用的命名空间(§3) |
| Rule(规则) | when → then 决策单元 |
| when | 规则触发条件(编译为表达式树) |
| then | 规则命中后的决策类型(§6) |
| tier | 规则层级 0–5,由低到高表示约束强度;tier 0–2 用 Simple,≥3 可用 Expression |
| ring | 执行环 0–3(内核/恢复/审批/建议),求值按环序执行 |
| override | 覆盖级别 critical > high > normal > low,仅允许 DENY → ALLOW 方向 |
| 表达式树 | 求值语义内核(34 节点,10 组),三种书写形态编译归一化到它 |
| canonical_tree | 规范化树,唯一哈希、唯一重算的基准对象(§8.2) |
| gloss | 从树确定性生成的自然语言可读投影(§5.5) |
| eval_trace | 逐节点求值轨迹(可重算派生产物,不进哈希,E6) |
| eval_warnings | 求值过程中的非致命警告(E3) |
| temporal_state | within/rate 滑动窗口状态(有状态算子) |
| as_of | 引擎注入的求值时刻(UTC,E9) |
| 事实对象(fact) | 求值输入,承载 Entity 当前状态(§7.0.1) |
| fallback 决策 | 无规则命中时 metadata.decision 的兜底裁决(§2.2) |
| NFC | Unicode 规范化形式 C(字符串归一,E10) |
| ReDoS | 正则拒绝服务攻击;match 节点 MUST 步数上限防护(§7.3(d)) |
| half-even | 银行家舍入(ROUND_HALF_EVEN),E2 定点小数输出舍入 |
| 空值传播 | 字段缺失统一返回 false 的安全失败语义(E11) |
修订历史
| 版本 | 日期 | 变更 |
|---|---|---|
| v2.1 | 2026-09-03 | §4.1 新增 category(规则级覆盖)、enabled(启用标志)、correction(CORRECT 纠偏文本)三个可选字段,补全字段表与固定顺序;§7.0.3 补 primary_correction 来源交叉引用。协议 erdl/v2 不变;规则格式版本 2.0.0 → 2.1.0(新增可选字段,Non-breaking) |
| v2.0 | 2026-08-30 | 定稿 |
规范性引用
- [RFC 2119] Key words for use in RFCs to Indicate Requirement Levels.
© 2026 深圳市秒镜科技有限公司 · MIT License