ERDL 语言规范
声明式 AI 治理语义规范 · v2.1
一个规范、一棵规范树、一个哈希——同一条规则、同一个输入,在任何符合规范实现上产生字节级一致的结果与哈希。
核心数字
34
节点表达式树内核(含算术 / 量词 / 聚合 / 时间 / 状态算子)
13
决策类型(ALLOW / DENY / CORRECT / REQUEST_HUMAN / ESCALATE …)
30
操作符 Simple 投影面(纯条件,最高审计等级)
3种
投影面 —— Simple / Expression / Decision Table,编译到同一棵语义树
2种
表达格式 —— YAML / JSON,实现无关、跨平台
为什么是 ERDL
| 问题 | ERDL 如何解决 |
|---|---|
| LLM 输出是概率性的 | 确定性的 when → then 护栏,在模型之外求值——安全边界从不放在 Prompt 里 |
| 规则跨实现漂移 | 318 条 JCS + SHA-256 向量强制字节级一致 |
| 合规需要审计轨迹 | 每次求值都产出密码学可验证的哈希 |
| 业务人员读不懂代码 | 三种投影面,编译到同一棵语义树 |
三种投影面:同一条规则,三种写法
业务人员写「决策表」、集成者写「Simple 条件」、内核用「表达式树」——三者编译到同一棵规范树、同一个哈希。
# Simple 投影面(30 操作符,最高审计等级)
when:
logic: AND
conditions:
- field: "tool.name" operator: eq value: "issue_refund"
- field: "tool.args.amount" operator: gt value: 5000
then: REQUEST_HUMAN同一条规则,三种语言 —— 一个非工程师能看懂哪一个?
政策条文(来自法务,一句话)
「员工不得将客户数据发送给公司域名列表之外的任何收件人。」
现在,三种语言把它写成可执行的规则。
写法一:ERDL(Simple 形态)
protocol: "erdl/v2"
metadata:
name: "customer-data-egress"
decision: ALLOW # 未命中规则时放行
rules:
- name: "SEC-001"
description: "客户数据不得外发"
when:
logic: AND
conditions:
- field: "data.classification"
operator: eq
value: "customer_data"
- field: "recipient.domain"
operator: not_in
value: ["internal.example.com", "partner.example.com"]
then: DENY
message: "客户数据不得发送给外部域名"
引擎自动生成的自然语言(gloss 投影):
当「数据分类」等于「客户数据」,且「收件人域名」不属于
[internal.example.com, partner.example.com]时,拒绝。
写法二:Rego
package egress
import rego.v1
default allow := false # ← 忘写这行就 fail-open
internal_domains := {"internal.example.com", "partner.example.com"}
internal_recipient if { # ← 一个「查询」,不是「判定」
input.recipient.domain in internal_domains
}
deny if { # ← 否定一个查询
input.data.classification == "customer_data"
not internal_recipient
}
allow if { # ← 再否定一次
not deny
}
写法三:Cedar
forbid(
principal, # ← 授权三元组,需懂实体模型
action == Action::"send", # ← 类型化 action,需 schema
resource
)
when {
resource.classification == "customer_data" &&
!(resource.recipient.domain in ["internal.example.com", "partner.example.com"])
};
一个非工程师,需要「额外懂」什么才能读对?
| ERDL | Rego | Cedar | |
|---|---|---|---|
| 要懂「判定」还是「查询」 | 判定(DENY) | 查询(allow 是一个集合) | 判定(forbid) |
| 要懂「默认值」吗 | 不用(decision: ALLOW 写在明处) | 要——default allow := false 漏写就 fail-open | 不用(forbid 天然禁止) |
| 要懂「双重否定」吗 | 不用 | 要——not internal_recipient + allow if { not deny } | 要一点(!) |
| 要懂「实体模型/schema」吗 | 不用(字段就是字段) | 不用 | 要——principal/action/resource + 类型化 |
| 三种等号陷阱 | 无 | 有(:= / == / =) | 无 |
结论一目了然:
- ERDL:一个「字段 + 运算符 + 值」三元组,就是自然语言的骨架。法务/合规看一眼
when里的两行条件,就能复述出「客户数据 + 非内部域名 → 拒绝」——和 gloss 生成的自然语言一字不差。 - Rego:读者要同时理解「查询模型」「默认值样板」「negation-as-failure」「双重否定」「三种等号」——这五样任何一样不懂,都可能把「该拒的」读成「该放的」。
- Cedar:读者要懂「授权三元组 + 实体 schema + forbid 优先级」——这是为工程师设计的授权模型,不是为法务设计的规则表。
为什么 ERDL 能「说人话」,Rego/Cedar 不能?
根源不在「语法糖」,在语义模型:
- ERDL 的 Simple 形态,语义是「判定 + 条件」,和「政策条文」同构——政策说「不得」,规则写
then: DENY;政策说「任何」,规则有原生any/all。规则 = 政策的结构化重写,不经过语义翻译。 - Rego 的语义是「查询」——规则是「计算满足条件的集合」,政策里的「禁止」要先翻译成「算出违规集合」,再翻译回「allow = not deny」。翻了两次,每一次都是漂移点。
- Cedar 的语义是「授权三元组」——它天生为「谁对什么资源做什么」的访问控制设计,不是为「企业政策条文」设计。把一句「不得外发客户数据」塞进 principal/action/resource 框架,本身就要一次语义重构。
所以「普通人简介一下就能懂」不是营销话术,是语义模型的选择:当规则的语义模型和政策的语义模型同构时,读规则 ≈ 读政策;不同构时,读规则 = 先学一门工程师的抽象语言。
附:本对比中 ERDL 为真实规范语法(Simple 形态 + gloss 投影),Rego/Cedar 为「熟练工程师的正确标准写法」,未做任何丑化。
快速开始(30 秒)
$ npm install @openoba/erdl import { loadErdlFile, Evaluator } from '@openoba/erdl'
const { rules, metadata } = loadErdlFile('refund.erdl.yaml')
const result = new Evaluator().evaluate(
rules,
{ tool: { name: 'issue_refund', args: { amount: 8000 } } },
{ fallbackDecision: metadata.decision },
)
console.log(result.decision) // 'REQUEST_HUMAN'规范全文
完整规范: 站内全文(中文) · English (on-site) · npm 包 @openoba/erdl