(Entity-Rule Definition Language · 实体规则定义语言)
状态: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] 解释。
ERDL(Entity-Rule Definition Language,实体规则定义语言)是一种以 YAML/JSON 承载的声明式规则定义格式,用于精确表达实体结构与行为规则。它包含两类声明:Entity(实体)定义数据结构;Rule(规则)定义 when → then 决策。Entity 界定对象的结构,Rule 规定条件的后果;二者共同构成可执行、可审查、可验证的规则表达。
ERDL 是多方语义层:它不仅是规则格式,更是人、LLM、系统与审计四方共享的语义约定层。
| 参与方 | ERDL 的角色 |
|---|---|
| 人(业务/领域专家) | 自然语言规则的精确翻译结果,可读、可审 |
| LLM(通用大模型) | 结构化输入,消除歧义,支持确定性求值 |
| 系统(规则引擎) | 标准化规则描述,并以 fn 委派控制调用边界 |
| 审计(监管/合规) | 比代码与自然语言更清晰的可追溯规则记录 |
在这一语义层中,规则决定一切:人表达意图,LLM 翻译语义,系统执行决策,审计复核证据。ERDL 的确定性语义层为 LLM 提供明确方向:使用者以自然语言描述规则,经 ERDL 精确翻译后,由 LLM 基于结构化语义确定性执行——对话界面即为统一入口。
AI 治理的核心难题,不是模型能否给出答案,而是概率性输出如何满足监管、审计与追责所要求的确定性边界。ERDL 以 YAML/JSON 承载声明式规则,将实体结构、行为约束和处置动作固化为可验证的确定性规则层,使 AI 的「应当如何行动」从训练假设转变为可检查对象。
从信任到验证:传统治理依赖对齐评估或事后解释,难以证明某次具体决策遵循了哪些约束。ERDL 将约束表达为 when → then 规则,每次求值绑定 canonical_tree 快照与结果哈希,可独立重算、逐字节跨实现复核。治理方不再只是相信模型「被正确训练」,而是可以验证某次行为是否命中规则、命中哪条规则、为何得到该结果。
可追责的证据链:ERDL 求值结果可哈希、可重算,可将规则版本、输入快照、决策输出与哈希锚定形成审计记录。审计不是复述日志,而是可重现的证明过程——为何放行、为何拦截、由谁批准,均可回溯查验,避免事后解释成为唯一依据。
机制化的人机权责边界:ERDL 可通过 REQUEST_HUMAN、ESCALATE 等决策类型,将高风险操作、敏感实体变更、不可逆动作等强制交由人工裁决。人的最终决策权不再依赖流程口号,而是被编码为可执行、可测试、可审计的规则路径。
合规即代码与跨组织互认:法规、平台政策和内部红线可转写为 ERDL 规则,并通过测试向量持续验证。不同组织、不同实现或第三方审计机构可基于同一规则与规范化树独立复算,形成「信任但验证」的治理范式,使合规从声明文档变为可运行、可检查、可互认的治理事实。
语义载体是内核,不是语法。 语义 = 树 = 哈希。 三者在规范化形式下合一。
任何以「运算符语法」为语义载体的方案,都会因新需求而被迫线性扩张运算符,成本永不收敛。因此本规范把语义收敛到唯一的内核(表达式树),而把多种书写形态作为内核的确定性投影——它们不是各自独立的语言,而是同一语义的不同视图。规则决定一切:规则的有效性不取决于书写入口或实现形态,而取决于唯一、可重算、可哈希、可逐字节验证的规范化语义。
一个 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 |
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 | 标签 |
protocol: "erdl/v2" 开头;protocol 大版本内,新增约束对存量规则 SHOULD Non-breaking(加载时 Warning 而非 Error);跨大版本(如 erdl/v1 → erdl/v2)为 breaking change,不适用向后兼容承诺。例外:when:"true" + 拦截性 then 在任何版本均拒绝加载(Error)。一个 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 互斥等;违规拒绝加载。within/rate 的状态由 temporal_state 受控注入。输入事实(fact)与求值结果(output)的完整契约见 §7.0。
Entity 是规则作用的主体(经 context 传递)。ERDL 预置以下 Entity 类型:
| Entity 类型 | 说明 |
|---|---|
agent |
单个 Agent 实例 |
tool |
Agent 调用的工具 |
task |
Agent 执行的任务 |
workflow |
多 Agent 编排流程 |
human |
人类审批者 |
guardian |
监管者(supervisor) |
规则中的字段引用(如 tool.name、context.amount)以 Entity 为语义命名空间。字段路径承重:字段名一旦发布即冻结 [FREEZE-1],别名 MUST 先行归一化为规范名。
Rule 是 ERDL 的核心单元:Rule = Metadata + When(条件)+ Then(动作)+ Audit(审计)。When 编译为表达式树(§5),Then 为决策类型(§6),Audit 记录规则求值的审计信息。
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 | 所依据法规的原文摘录 |
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
when 是规则的内嵌条件表达式。ERDL 提供三种书写形态(Simple / Expression / 决策表),任一形态编译归一化到同一语义内核(表达式树),再经渲染还原为任一形态;另有一个可读投影 gloss(§5.5)从树确定性生成自然语言。
| 投影面 | 承载 | 适用 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)。
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;"2" gt "10" 为 true);跨类型返回 false;[min,max] 仅支持数值,非数值返回 false;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 时机在「未超限」分支写入。
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
决策表面向业务与财务人员,以行列结构表达多条件组合,编译到同一内核:
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 相同的表达式树。
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——布尔字段存在即真,避免「是否已告知 已发生」这类别扭表达。
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 | 引导 |
求值 = 表达式树(规则编译产物)对输入事实(fact)逐节点判定的纯函数过程(E1)。本节定义求值的输入契约、算法步骤与输出契约,供实现者与使用者对齐。
求值输入是一个事实对象(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);输入:规则集 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)
EMERGENCY_HALT 命中即短路;DENY 不短路——继续求值以判断是否有 override ALLOW 覆盖。求值结果 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)。
priority 从小到大排序(值越小越先);override 标记的排前;override 枚举:critical > high > normal > low(默认 normal);override 仅允许 DENY → ALLOW 方向覆盖(不得覆盖到更不安全状态)。| 编号 | 约束 |
|---|---|
| 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 |
内核显式排除:字符串拼接、正则替换、位运算、日期格式化、递归引用、用户自定义节点——以维持求值的封闭性与可验证性。
以下语义 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 求值,保证跨实现、跨时区逐字节一致:
YYYY-MM-DD)按 UTC 解析;date-time 按 ISO 8601 带时区解析,无时区后缀按 UTC;date_part):一律取 UTC 分量;date_add、month_last_day):按 UTC 日历运算;days_between):UTC 毫秒差 ÷ 86400000 向下取整(floor);toISOString)。业务本地时区由引擎注入 as_of 时转换为 UTC 时刻,求值器以 UTC 纯函数运算。
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 |
ERDL 文档以 YAML 承载,可无损转换为 JSON。规范化树(canonical_tree)以 JSON 对象形态序列化。
表达式树是唯一求值、唯一哈希、唯一重算的基准对象。要使其哈希可跨实现逐字节一致,树 MUST 具有唯一规范化形式:
| 规范化规则 | 说明 |
|---|---|
| 节点序固定 | 子节点按规范顺序排列(左→右严格定序),与源书写顺序无关 |
| 字段名承重 | 字段引用路径承重——字段名发布即冻结 [FREEZE-1],别名 MUST 先行归一化 |
| 字面量规范 | 数字以定点小数字符串表示(scale=14 + half-even);字符串 NFC 规范化 |
| var 规范 | 仅支持 $ / $.path,路径段为确定字节序列 |
| 元数据剥离 | 注释、来源行号、格式、作者等非语义元数据一律不进规范化树 |
树哈希的对象是规范化树,而非任何特定实现的内存表示或序列化文本。 两个结构等价的树(仅字段书写顺序、空格、变量命名不同)规范化后产生完全相同的字节序列与哈希。
这一「哈希树、校验 gloss 与树一致」的机制,使 gloss 在哈希模式下无需逐字节一致、也无需进哈希,即可获得树的密码学锚定。
ERDL 的集成目标,是把关键判断从模型推理、框架代码或口头约定中抽离出来,变成可加载、可执行、可验证的规则资产。它通常以 YAML/JSON 规则文档承载,通过 when → then 完成决策,并输出可哈希、可逐字节复算的求值证据。以下三种集成路径,对应三类典型工程落点。
角色:在 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 框架、工具实现或模型提示词。
角色:在 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 端,客户端无需重构执行链。
角色:在规则引擎内部,ERDL 是规则定义语言本身,负责以声明式方式表达业务策略,并提供确定性求值语义。
模拟:反洗钱引擎需要表达「大额交易且高风险国家 → 拦截」。业务团队编写 ERDL 规则:when context.amount > 10000 且 context.country in ["高风险国家列表"] → DENY。引擎加载 YAML 后,不会把它当成普通配置,而是编译为表达式树:比较节点处理金额阈值,集合节点判断国家归属,逻辑节点完成与运算。随后,引擎按照 E1–E12 求值语义执行,输出 DENY。该规则还可生成 canonical_tree 与哈希值;另一实现只要加载同一规则、同一输入,就能重算出相同结果。
集成要点:这里的核心价值是「可验证的确定性」。ERDL 不只让规则可读,还让引擎实现可以被测试向量约束。V-ENGINE 类测试向量可以证明:不同引擎、不同平台、不同版本,对同一规则应产生逐字节一致的结果。由此,规则引擎不再只是业务系统内部黑盒,而是可以接受第三方独立验证的执行器。监管方、审计方或平台方都能依据输入、规则哈希与求值证据,判断「引擎算得对不对」。
一个最小 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。
见 §4.2(Simple 规则)与 §5.3(Expression 规则)、§5.4(决策表)。
本规范的语义 MUST 由可独立重算的测试向量证明。表达层向量(V-ENGINE / V-GLOSS / V-PROJ)覆盖:34 节点 × 4 场景(正常/边界/异常/空值)、E1-E12 语义、Simple 30 运算符编译映射、gloss 渲染模板。
五步验证法:加载向量输入 → 生成表达式树 → 重算求值结果 → 与答案对比 → 判定一致。
第三方 Runner 验证流程(从零到合规):
| 组 | 节点 | 数量 |
|---|---|---|
| 取值 | 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 节点。
| 族 | 运算符 | 数量 |
|---|---|---|
| 比较 | 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 条件修饰符)。
见 §6。
对于内核显式排除、确有需求的场景,提供函数委派(FnRegistry)作为受控兜底:
| 约束 | 说明 |
|---|---|
| 注册制 | 函数 MUST 注册方可引用,未注册不可调用 |
| 沙箱执行 | 受限环境,受资源配额与超时约束 |
| 确定性豁免声明 | 用于 Guard 求值路径的函数 MUST 声明并保证确定性 |
| 审计可溯 | 每次调用记入审计记录,可离线核验 |
规则分级(Grade):
| Grade | 表达方式 | 审计 SLA |
|---|---|---|
| A | 纯 Simple(30 运算符) | 最高,纯文本可重算 |
| B | Expression 树 | 高,eval_trace MUST |
| C | 含函数委派 | 分层,C 级不得冒充纯文本可重算 |
含函数委派的规则(Grade C)MUST 在 gloss 中显式标记「含不可重算的函数委派」;函数委派的调用输入 + 输出哈希 MUST 纳入结果哈希的原像。
| 术语 | 一句话定义 |
|---|---|
| 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 | 定稿 |
© 2026 深圳市秒镜科技有限公司 · MIT License