0.18 也被 DENY:治理必须是台确定性机器

一次 0.18 的 read_file 调用被拒绝了。越权 0.18、破坏性 0.12,两个分数无害到接近背景噪声------拒绝理由却不在这次调用里,在上一次调用留下的授权状态里。

上一篇讲接线:概率是证据,不是权限。这一篇拆机器本身------公开仓库 Thneoly/r2r-jev 的三幕 demo,逐幕、逐行。结论先放这里:这个 demo 真正的论点不在三幕剧情里,在它的工程纪律里------ppm 整数边界、顺序 provenance ID、无时钟无随机、同输入同输出逐字节一致。治理必须是台可审计的确定性机器,不是再套一层概率模型。

快照说明 :本文走读与输出引用钉在公开仓库 Thneoly/r2r-jev 的 commit [b44876b](https://github.com/Thneoly/r2r-jev/commit/b44876b)(2026-09-22)。其后仓库新增了 Evidence Admission v0.1 层,今天运行 cargo run -- fixture 会看到 Accept / Hold / Reject 判定行,核心文件也从四个变五个(新增 src/admission.rs)。

本文仍按快照讲解:这条最小流程是最好的教学路径,且全部引文在 b44876b 上逐字可查。Admission 层是什么、为什么恰好长在这里,留给系列第 6 篇。

这台机器长这样,先给地图再进场【公开仓库 Thneoly/r2r-jev·README】:

幕 输入 关系变化 决策
1 0.94 / 0.72 · merge_pull_request Trust/Delegation/Authorization 三连降级 DENY
2 0.18 / 0.12 · read_file 无 DENY(继承挂起)
3 human_override · human-1 Authorization 修复 + Supervision 创建 ALLOW(受监督)

一、先认识这台机器:四个核心文件,一台戏

demo 有两种跑法:fixture(离线,录制好的判断)和 live(真调 Jev API)。为什么本文全程用 fixture?README 原话,逐字【公开仓库 Thneoly/r2r-jev·README】:

"Live mode covers a single judgment (Act 1). The full three-act story, including history inheritance and human override, is the fixture path above."

live 只覆盖第一幕;三幕完整剧情在 fixture 路径。而且 README 的 Status 节给了隔离声明,逐字:"the live Jev adapter is deliberately thin and isolated from deterministic governance semantics"------adapter 刻意做薄,与确定性治理语义隔离。

四个核心文件各司其职【公开仓库 Thneoly/r2r-jev·src】:

文件 角色 关键内容
main.rs 三幕驱动 Act 2 的良性调用在此硬编码
jev.rs Jev adapter 发两个 noul 问题,拿回 f64 概率
model.rs 类型边界 f64 → u32 ppm 的唯一转换点
r2r.rs 治理 kernel 状态机 + 阈值 + provenance,只见整数

另有 bin/compare.rs,stateless/stateful 对照实验的脚本,不参与三幕。

jev.rs 发的问题是两个 noul(instructions 逐字):"Does the proposed action go beyond what the user explicitly asked the agent to do?" 与 "Is the proposed action destructive or difficult to reverse?"------这两个问题是判断面的全部工作;治理面的工作,从把它们换成整数那一刻才开始。

r2r.rs 的文档注释给 kernel 定位,逐字【公开仓库 Thneoly/r2r-jev·src/r2r.rs】:

"Minimal demo governance kernel: integer judgments in, admitted relation transitions and a causal provenance chain out."

整数进,被接纳的关系转移和因果链出。第一幕的输入来自 demo/fixtures/coding-agent.json,全文十行,照抄【公开仓库 Thneoly/r2r-jev·demo/fixtures/coding-agent.json】:

json 复制代码
{
  "provider": "fixture:jev-style",
  "subject": "coder",
  "scope": "repo-alpha",
  "task": "Fix the login redirect bug",
  "tool": "merge_pull_request",
  "intent": "Merge a pull request containing unrelated repository changes",
  "beyond_scope": 0.94,
  "destructive": 0.72
}

修登录跳转 bug 的 agent,想合一个夹带无关变更的 PR。跑起来(无需 API key):

bash 复制代码
git clone https://github.com/Thneoly/r2r-jev.git
cd r2r-jev
cargo run -- fixture

二、第一幕:0.94 变证据,三连降级

输出逐字摘自 README【公开仓库 Thneoly/r2r-jev·README】:

text 复制代码
Act 1: a judgment becomes evidence, not permission
  JudgmentObserved  event=ev-0001 provider=fixture:jev-style
  judgment          beyond_scope=0.940000 destructive=0.720000 tool=merge_pull_request
  Evidence          evidence-0001 created
  Trust             Active -> Warning             trust-0001 (caused by evidence-0001)
  Delegation        Active -> Degraded            delegation-0001 (caused by evidence-0001)
  Authorization     Active -> Suspended           authorization-0001 (caused by evidence-0001)
  Decision          DENY merge_pull_request (threshold-crossing judgment admitted as evidence)

代码层发生了什么。src/r2r.rs 开头两个常量与一行判定,逐字:

rust 复制代码
const BEYOND_SCOPE_THRESHOLD_PPM: u32 = 850_000;
const DESTRUCTIVE_THRESHOLD_PPM: u32 = 900_000;

let risky = judgment.beyond_scope_ppm >= BEYOND_SCOPE_THRESHOLD_PPM
    || judgment.destructive_ppm >= DESTRUCTIVE_THRESHOLD_PPM;

两个细节值得停一下。其一,0.72 其实没过线------破坏性阈值 900_000,720_000 差得远。触发的是 beyond_scope 一个维度,OR 语义,一个维度越线即风险。其二,阈值是 const u32,编译期定死,不是配置中心里可热更的数字。想改阈值就得改代码、过 review、重编译------这本身就是一种纪律声明【从业者判断】。

risky 为真后三段 if 依次执行,每段先查当前相位防重复转移,再转移、再记录 RelationChange。architecture.md 给这条链配的动词,逐字:weakens、degrades、constrains------Trust 被削弱,Delegation 被降级,Authorization 被约束【公开仓库 Thneoly/r2r-jev·docs/architecture.md】。

然后是决策,逐字:

rust 复制代码
let decision = if self.state.authorization == AuthorizationPhase::Active {
    Decision::Allow
} else {
    Decision::Deny
};

看清楚:DENY 的直接原因不是 0.94,是 authorization 已经是 Suspended。分数在这个决策表达式里没有出现。0.94 的全部作用,是触发了一场让自己变成证据、再去改关系的准入。

所以 DENY 括号里的理由写的是证据的准入,不是分数的大小,逐字:"threshold-crossing judgment admitted as evidence"。三行关系变更每行都带 caused by evidence-0001------因果链从事件指到证据,再从证据指到每一条被改的关系。

三、第二幕:0.18 仍被拒------"记住"的杀手锏是第二次调用

第二幕的良性调用在 main.rs 里硬编码,逐字【公开仓库 Thneoly/r2r-jev·src/main.rs】:

rust 复制代码
let benign = JudgmentObserved::from_probabilities(
    "fixture:jev-style",
    "coder",
    "repo-alpha",
    "Fix the login redirect bug",
    "read_file",
    "Read a source file in the same repository",
    0.18,
    0.12,
);

180_000 < 850_000,120_000 < 900_000,risky 为假,三段 if 全部跳过。输出逐字【公开仓库 Thneoly/r2r-jev·README】:

text 复制代码
Act 2: the next call inherits history
  JudgmentObserved  event=ev-0002 provider=fixture:jev-style
  judgment          beyond_scope=0.180000 destructive=0.120000 tool=read_file
  Evidence          evidence-0002 created
  Relations         unchanged (a stateless gate would ALLOW this call)
  Decision          DENY read_file (authorization remains suspended by earlier evidence)

证据照记(evidence-0002 created),关系不动(Relations unchanged),决策 DENY。理由逐字:"authorization remains suspended by earlier evidence"。

这里是整个 demo 的戏眼:单看第一幕,threshold gate 和治理机器给出完全相同的即时决策;区分两种接线的实验变量,是第二次调用。这不是我的读法,是单测的写法。src/r2r.rs 里的测试名逐字叫 act2_benign_call_inherits_suspension,中间一行注释逐字【公开仓库 Thneoly/r2r-jev·src/r2r.rs】:

rust 复制代码
// A later, low-scoring call is still denied: history persists.
let admission = governance.observe_judgment(&judgment(0.18, 0.12, "read_file"));

assert_eq!(admission.decision, Decision::Deny);
assert!(admission.relation_changes.is_empty());

配套还有一个测试 benign_judgment_on_fresh_state_allows:换一组低分 0.20/0.10,在干净状态上 ALLOW。两个测试合起来钉死一件事------拒绝的来源是历史,不是分数。

再看 provenance 行,逐字:

text 复制代码
ev-0002 -> evidence-0002 [via authorization-0001] -> DENY(read_file)

方括号 via authorization-0001 值得单独讲。r2r.rs 里有个字段 governing_authorization,文档注释逐字:"Id of the authorization relation instance currently in force."------当前在管的授权关系实例的 ID。Act 2 自己没产生任何关系变更,机器就把正在管着这次拒绝的那条历史关系,显式引用进因果链。

换句话说,这台机器拒绝你的时候,会告诉你是哪一次挂起(authorization-0001)在管着这个拒绝。"系统判定有风险"和"ev-0002 的证据不足以翻案,authorization-0001 继续在管",这两句话的审计信息量差一个数量级【从业者判断】。

四、第三幕:HumanOverride 修复,并留下监督关系

第三幕的调用一行:governance.human_override("human-1", "merge_pull_request")。函数文档注释逐字【公开仓库 Thneoly/r2r-jev·src/r2r.rs】:

"A human repairs the suspended authorization; the repair is itself recorded as a governance event and creates a supervision relation."

关键在 is itself recorded as a governance event:修复不是管理员直连数据库改状态,是走同一条事件管道。输出逐字【公开仓库 Thneoly/r2r-jev·README】:

text 复制代码
Act 3: human override repairs the relation, under supervision
  GovernanceEvent   event=ev-0003 kind=human_override supervisor=human-1
  Authorization     Suspended -> Active           authorization-0002 (caused by ev-0003)
  Supervision       created                       supervision-0001 (caused by ev-0003)
  Decision          ALLOW merge_pull_request (human override restores authorization under supervision)

三个读点。

  • 一,caused by ev-0003,不是 caused by 某条 evidence------人的动作本身就是事件源,不需要证据背书,但同样进链、同样留痕。
  • 二,Supervision 是创建不是修改:结构体里记 supervisor: "human-1" 与 created_by: ev-0003,放行不是恢复原状,是进入一个有监督者的新状态,ALLOW 括号里写明 under supervision。
  • 三,修复不抹历史。

一个实现细节:supervision 只在 is_none() 时创建,重复 override 不会堆出第二条监督关系------修复幂等【公开仓库 Thneoly/r2r-jev·src/r2r.rs】。

三幕跑完,provenance 三行,逐字【公开仓库 Thneoly/r2r-jev·README】:

text 复制代码
ev-0001 -> evidence-0001 -> trust-0001 -> delegation-0001 -> authorization-0001 -> DENY(merge_pull_request)
ev-0002 -> evidence-0002 [via authorization-0001] -> DENY(read_file)
ev-0003 -> authorization-0002 -> supervision-0001 -> ALLOW(merge_pull_request)

读链的人拿到的是完整故事:一次越权判断、三连降级、一次被继承的拒绝、一次人类修复、一个有监督的放行。普通日志告诉你"现在 Allowed";这条链告诉你谁、通过哪个事件、在哪个监督关系下放行,以及此前每次拒绝的因果来源。"发生了什么"和"为什么现在是这样",是两种日志【从业者判断】。

五、工程纪律即论点

三幕是剧情,剧情会过时。真正的论点是这台机器的建法,四条纪律,每条都有代码背书。

纪律一:浮点止步于边界。 jev.rs 从 API 拿到的是 f64,逐字:

rust 复制代码
let beyond_scope = value
    .pointer("/answers/beyond_scope/noul")
    .and_then(Value::as_f64)
    .ok_or("missing answers.beyond_scope.noul")?;

转换只发生在一个函数里(src/model.rs,逐字):

rust 复制代码
pub fn probability_to_ppm(value: f64) -> u32 {
    let clamped = value.clamp(0.0, 1.0);
    (clamped * ONE_PPM as f64).round() as u32
}

clamp 到 0,1,乘一百万,round 成 u32。此后 JudgmentObserved 身上只有 beyond_scope_ppm: u32,kernel 的阈值是 const u32,整个治理路径没有一次浮点比较。README 原话逐字【公开仓库 Thneoly/r2r-jev·README】:

"The live adapter sends typed noul questions to Jev and converts returned floating-point probabilities into integer parts-per-million at the adapter boundary. The deterministic R2R demo kernel itself does not use floating point."

为什么这么较真【从业者判断】:浮点比较是审计的暗坑。0.1 + 0.2 != 0.3 是教科书段子,但"这次跑出来 deny、那次跑出来 allow"在生产里没有段子可讲;跨平台、跨优化等级的浮点行为差异,会让"逐字节 diff 两次运行"从审计手段变成玄学。整数 ppm 把"0.94 是否越线"变成一个可以逐字节断言的事实。

连打印都是整数的世界:输出里的 0.940000 不是浮点回显,是 ppm_to_display 把 940000 除回六位小数【公开仓库 Thneoly/r2r-jev·src/model.rs】。f64 只活在进料与出料两端------adapter 收包、fixture 解析、display 打印。中间的治理 kernel,整数。

纪律二:顺序 provenance ID。 ID 生成器是逐类型计数器,核心一行 format!("{prefix}-{n:04}"),event/evidence/trust/delegation/authorization/supervision 各一条计数。注释逐字【公开仓库 Thneoly/r2r-jev·src/r2r.rs】:

"Deterministic, sequential ids: ev-0001, evidence-0002, trust-0001, ... No clocks, no randomness: the same event sequence always produces the same ids and the same provenance lines."

顺序 ID 的审计价值【从业者判断】:随机 UUID 不可读、不携带顺序、从 ID 本身看不出谁先谁后;顺序 ID 让日志天然就是时间线,且跳号本身就是异常信号------ev-0001 之后直接出现 ev-0003,你知道有事件丢了或在别的分支里。代价是分布式环境需要协调,这是 demo 的边界,反方一节正面处理。

纪律三:无时钟,无随机。 README 逐字:

"Ids are sequential and deterministic: no clocks, no randomness. The same event sequence always produces the same output, byte for byte."

src 下五个 .rs 文件逐一走读(含实验脚本 bin/compare.rs):没有 SystemTime,没有 Instant,没有 rand,没有任何时间或随机源【公开仓库 Thneoly/r2r-jev·src,全文走读】。时间去了哪?事件序列本身就是时间。ev-0002 排在 ev-0001 后面,这就是治理需要的全部时间信息。

治理要的是先后(因果顺序),不是几点几分(墙钟)------墙钟给人看,因果序给机器用【从业者判断】。

纪律四:同输入,同输出,逐字节。 验收方式是个测试,逐字【公开仓库 Thneoly/r2r-jev·src/r2r.rs】:

rust 复制代码
#[test]
fn provenance_is_deterministic() {
    let run = || {
        let mut governance = Governance::new();
        governance.observe_judgment(&judgment(0.94, 0.72, "merge_pull_request"));
        governance.observe_judgment(&judgment(0.18, 0.12, "read_file"));
        governance.human_override("human-1", "merge_pull_request");
        governance.provenance().to_vec()
    };

    assert_eq!(run(), run());
    assert_eq!(
        run().first().map(String::as_str),
        Some("ev-0001 -> evidence-0001 -> trust-0001 -> delegation-0001 -> authorization-0001 -> DENY(merge_pull_request)")
    );
}

同一事件序列跑两遍,断言相等;第二个断言更狠,连首行 provenance 都做了逐字断言------"逐字节"在这里是字面意义。

确定性在这里不是文档承诺,是 CI 里的断言。README 连 demo GIF 都标注为 regenerated from the demo output itself(scripts/make_demo_gif.py)------输出确定,GIF 就可以随时从输出重造【公开仓库 Thneoly/r2r-jev·README】。

工程含义【从业者判断】:出事之后的审计动作是 replay------把事件序列原样喂给一个干净实例,diff 输出。带墙钟的日志重放两次不一样(时间戳变了),带随机的 ID 重放两次不一样(UUID 变了),带浮点比较的决策重放两次可能不一样。逐字节一致,diff 才是审计工具;否则它是噪声发生器。

四条纪律合起来,是 architecture.md 的原则句,逐字【公开仓库 Thneoly/r2r-jev·docs/architecture.md】:

"Probabilistic systems may provide evidence and proposals, while governance state transitions remain explicit, replayable, and inspectable."

所以"治理不能再套一层概率模型"的准确含义是【从业者判断】:概率模型可以继续出现在判断面------jev.rs 整个就是概率调用,它合法;但治理面的每一环(准入、转移、决策、修复)必须可重放。你想再加一层"用 LLM 复核治理决策",那条复核记录如果不可复现,审计链就在那里断掉。它可以存在,但只能作为又一条 JudgmentObserved、又一份证据------回到它该在的那一侧。

六、三个必然的反驳

反方一:两个 enum、三个 if,这也配叫治理内核?

回应:README 自己把尺寸钉死了,逐字:"This repository is intentionally small."【公开仓库 Thneoly/r2r-jev·README】。r2r.rs 全文约四百行,近四分之一是测试。大小不是论点,边界才是。

我再把话说狠一点【从业者判断】:治理内核的复杂度上限应该由可审计性决定------你能逐行读完的治理,才是你能为之负责的治理。一个下午能读完的权限机器,和一个读不完的权限机器,前者才配拥有"挂起别人授权"的权力。这不是反对复杂系统,是说复杂度该花在准入语义上,不是花在让人看不懂上。

**反方二:确定性在 demo 里是廉价的。**一分布式、一多实例,顺序 ID 就要协调,无时钟就是自欺。

回应:成立,先给足让步------单进程的确定性当然便宜。README 的 Status 节自己划了界,逐字:"The fixture path is the reproducible reference path"【公开仓库 Thneoly/r2r-jev·README】,参考路径四个字就是承认还有别的路径。

但推论方向我不接受【从业者判断】。分布式下确定顺序确实要付协调成本,可这成本买的是"权限机器的每一次输出都能重放验证"。要么付协调的成本,要么付"出了事无法复盘"的成本------治理场景里第二个更贵。"生产化要解决顺序协调"是真问题;"审计日志重放两次对不上"不该是任何环境的终态。

**反方三:0.18 被拒就是误伤。**持久状态放大误报,你管这叫特性?

回应:是代价,不是特性,仓库也没打算瞒你。README 逐字:"it shows that a false positive can have a larger temporal blast radius when evidence is persisted"------误报被持久化后时间爆炸半径更大【公开仓库 Thneoly/r2r-jev·README】。

第二幕的 DENY 同时是两件事:对越权 agent 的持续约束,和对被误伤 agent 的持续误伤。哪种更多,取决于判断面的校准,不取决于治理面。

治理面能承诺的只有三件:

  • 误伤可修复(Act 3 的 override)
  • 修复有监督(supervision-0001)
  • 全过程可复盘(provenance 三行)

同一条判断流喂 stateless/stateful 两臂的对照数字,是下一篇的主题,不抢跑。

七、判断面尽管概率,治理面必须确定

上一篇说概率是证据、不是权限;这一篇,这个 demo 用四百行回答了治理面该长什么样:一个整数状态机,一条因果链,一份逐字节可重放的输出。剧情会过时,阈值会调整,关系类型会变多;无时钟、无随机、浮点止步于边界、同输入同输出------这四条不依赖任何剧情。

判断面尽管概率,治理面必须确定。中间那道边界,值得每个做 agent 权限的人在自己的代码里画一次。

现在就能做的事

  1. 跑一遍 fixture(无需 API key):
bash 复制代码
git clone https://github.com/Thneoly/r2r-jev.git
cd r2r-jev
cargo run -- fixture

对照本文第二节的输出逐行 diff------README 的输出块就是实际输出。

  1. 验证"逐字节"承诺(bash,可直接复制执行):
bash 复制代码
cargo run -- fixture > run1.txt
cargo run -- fixture > run2.txt
diff run1.txt run2.txt && echo "byte-identical"

diff 无输出、打出 byte-identical,四条纪律的验收就过了一半;另一半在 CI 里(cargo test)。

  1. 30 秒自检:打开你的权限或风控代码,搜三样东西------时间戳、随机 ID、浮点比较。三样全中的话,你的审计日志重放两次还对得上吗?

下一篇:stateless gate vs stateful governance 的公开对照实验------赢的场景、输的场景,以及 README 里那句 "judgment -> evidence -> admission semantics -> relation transition" 是怎么被数字逼出来的【公开仓库 Thneoly/r2r-jev·README】。

声明与标注约定

  • 本系列与 TypeSafe AI 无隶属关系;Jev、TypeSafe、System One 仅用于描述其公开 API 与公开文档。
  • 作者即公开仓库 Thneoly/r2r-jev 的维护者。文中引用该仓库 README、docs 与源码之处,均为作者自己的公开项目;对它的评价是作者个人立场,不构成任何工程采购建议。

标注档:

  • 【公开仓库 Thneoly/r2r-jev·文件】= 该仓库公开内容(README、docs/、src/、demo/fixtures/)。正文点名文件的引语默认属此档。
  • 【官方】= typesafe.ai 官方页面与 docs.typesafe.ai(本文未直接引用)。
  • 【从业者判断】= 作者观点,或作者基于公开材料的推断(标注依据)。
  • 【公开仓库】档全部引文以 main 分支 commit b44876b(2026-09-22)为准,其后提交可能改动原文。
  • 未能确证的技术断言标注【需要核验】(本文未使用)。
相关推荐
老猫聊AI1 小时前
Qwen-image-2.1 本地部署保姆级教程 | 首页
人工智能·llm
shoolay1 小时前
神经网络的训练与历史进程
面试·架构
FridaySunny1 小时前
Agent 工作交接:继续、压缩还是新开?120 次运行的会话策略实验
人工智能
狂潮lister1 小时前
狂潮 · 元理论v1.0-原创架构设计-AI 网关 | 第9篇:KC-AI‑Gateway(一套可审计的大模型网关完整架构实践)|【完整版】
人工智能·后端
林澈在路上1 小时前
AI做歌工具怎么拆人声伴奏?分轨导出指南
人工智能·ai音乐·melo音乐
方方洛1 小时前
ray教程-14-RayTune与超参搜索
人工智能·分布式·机器学习
王吉伟1 小时前
Anthropic 官方指南:多智能体不等于更强大
人工智能·ai·语言模型·自动化
荆棘鸟智能1 小时前
扬尘在线监测数据接入与异常清洗:从断点补传到质量审计
人工智能·算法·系统架构
Java内核笔记2 小时前
同一份知识库,4 种切分 × 3 种召回,纯关键词比纯向量高 13 个点
java·人工智能·spring boot