jevbooks

← 全部设计模式

黑匣子

记录与回放 · Flight recorder

记概率,不记动作

不是「看不懂内部」的那种黑箱,是飞机上那个把每一秒都记下来的匣子:每次拍板都把原始的数和结论一起存着,以后想改线,重放记录就行,不用再去问模型。

.81.12.55重放翻了 12 条

问题

你做了一个清理工具。它把仓库扫一遍,让 Jev 判断每个文件是不是构建留下来的垃圾,概率超过 0.8 就删。上线三个月,大家挺满意——几万个没用的文件清掉了。

这三个月里它也删错过几次。你决定把线提到 0.9,动手之前想先拿到两个数:这三个月里有多少次会因此不删?新线拦不拦得住当初删错的那几次?

你打开日志,每一行都是「deleted build/tmp.log」。当时那个概率是多少,没人记。想回答那两个问题,只能把三个月的文件重新喂给模型再跑一遍——而其中很多文件早就不在了。你现在是闭着眼睛在改线。

解决方案

黑匣子模式说:空难调查靠匣子把每一秒都还原出来,你的每一次拍板也该还原得出来。第一步是把拍板那段代码写成纯函数(同样的输入永远给同样的输出):输入是这次的几个数和上一次的结论,输出是一个动作;函数里不调模型、不读时钟、不去读配置文件。

第二步,每拍一次板记一条:原始的几个数、这次的结论、上一次的结论,以及输入的哈希(一小串能代表这份输入、又看不出原文的字符)。不记原文,一半是为了省地方,更多是因为输入里可能有不该留下来的东西。

这样一来,改线就变得很便宜。拿记录把新函数重跑一遍,数数有多少条结论会翻。这一步一个 token 都不花,几万条几秒钟就跑完。有个团队把它做成了一条命令,改完阈值一运行,屏幕上打出「changed: 12」;另一个团队用同样的办法回放了 93 个真实会话里的 1256 次编辑,花了 22 美分。

问 · 答 · 拍

  1. 把文件路径和开头那几千个字(发给 Jev 看的那份材料,术语叫 state)放进去,问它这是不是构建留下来的垃圾。
  2. 0.83。
  3. 超过 0.8,文件删掉,同时留下一行:当时的数是 0.83,结论是删。三个月后你想把线改到 0.9,就拿这三个月的记录重算一遍,发现有十二条会翻。

名字从哪来

飞机上的匣子不会记「今天飞得挺好」。它一秒一秒地记高度、速度、舵面位置,正因为如此,事后才有人答得出「要是晚三秒拉杆会怎样」。你的日志里该留下的也是这一类东西:那些数,而不是那个动词。

什么时候用

任何带着一条线、以后要调、要审计、要跟人解释的决策——也就是你画过的每一条线。

什么时候别用

跑一次就扔的脚本。或者输入里带着个人数据、又没法只留一个哈希的场合——那样记录本身就成了负担。

做这件事的项目

  • huncho — 在 System One 模型上将决策作为代码:类型化问题、滞回、日志、校准。
  • abide — 让编码代理通过 Jev 检查遵守项目规则。
  • jev-axi — 为 TypeSafe 的 Jev 提供的 CLI:从 shell 快速获得校准判断。

用这种模式的全部项目 →

看谁的代码
  • hunchosrc/replay.ts:79-90

    把旧记录重新灌进拍板那个函数,换上新的线,打印出有多少条结论变了。不调模型,不花钱。

  • hunchosrc/journal.ts:35-64

    记录里存什么写得清清楚楚:那几个数、结论、上一次的结论,以及输入的哈希——原文一律不存。

  • abidebenchmarks/replay/README.md

    从 93 个真实会话里重建出 1256 次编辑再跑一遍,花了 22 美分、两分钟——结果好不好也一并诚实公布。

想看代码
const decide = (a, prev, t = { del: 0.8, ask: 0.6 }) =>
  a.leftover.p >= t.del ? "delete" : a.leftover.p >= t.ask ? "ask" : "keep";   // 纯函数:不调模型,不读时钟

const outcome = decide(answers, previous);
journal.write({ at: Date.now(), answers, previous, outcome, stateHash: sha(state) });

// 三个月后,动线之前,零 token:
const NEW = { del: 0.9, ask: 0.6 };
const moved = journal.read().filter((r) => decide(r.answers, r.previous, NEW) !== r.outcome);
console.log(`changed: ${moved.length}`);
jevbooks 怎么认出它

我们怎么知道一个项目用没用这一招?拿它的 README 问 Jev 一句话,Jev 给个百分比,数字越高说明越像。你要抄这道题,记住一点:别问「这个项目好不好」,要问「有没有讲到这一件具体的事」。

questions.json · 每份 README 都发这道题
{
  "journal-replay": {
    "type": "noul",
    "instructions": {
      "what": "Does the text describe keeping a journal or JSONL log of Jev's answers and the resulting decisions that is replayed, audited, or used to tune thresholds without calling the model again?",
      "not_for": "Ordinary request logging with no replay or tuning use."
    },
    "criteria": {
      "true": "The text explicitly describes this. One sentence is enough; other content does not cancel it.",
      "false": "The text does not describe this."
    }
  }
}

用 Jev 做一个 →