jevbooks

← 全部设计模式

记分卡

组合打分 · Scorecard

模型报数,代码算总分

把一个笼统的判断拆成几道各管一件事的小题,每道各自回一个数,总分你自己算。

.81.12.55min · max · Σ.12

问题

你在做一个命令风险检查:任何命令跑起来之前先看一眼有多危险,危险的交给人。于是你问了一道打分题:这条命令风险多大,0 到 2 分?

跑了两周,几乎什么都答 1。一条会清空目录、但清的是沙箱里的临时目录的命令,两头一平均落在中间;一条名字吓人其实无害的命令,也落在中间。更糟的是这个 1 说不出风险在哪,结果还是得有人把整条命令从头读一遍。

你想到的修法是把题写得更细,把评分规则全塞进题干里:删数据算 2 分,除非是在临时目录里,那样的话……分数反而更飘了。你让一道题既去找证据又去做算术,而算术恰恰是你的代码闭着眼都不会错的那部分。

解决方案

记分卡模式说:一次只问一件看得见的事实,总分留在你自己的代码里。先把那个笼统判断拆成几个互不重叠的维度——会不会删数据、会不会把本地数据发出去、会不会下载并执行别处的代码——是非题不够用的地方,再加一道有序的等级题:万一出错,损失是「没有」「能恢复」还是「不可逆」。

然后把合并规则写成明明白白的一行代码。全都得成立,取最小的那个;任意一个就够,取最大的那个;几样互相权衡,那就乘权重。这里是:三道危害里任何一道到 0.8 就直接拒绝;只到 0.45 的,还要损失等级至少 1.5 才升级给人。

最后,把每个维度的数和结论一起打出来。解释是那一排数,不是那个总分:「会下载并执行代码那道题回的是 0.91」是一个人能直接接着做事的信息,而「风险 1.7」只会逼他自己再推一遍。

问 · 答 · 拍

  1. 把这条命令和它会执行的脚本(发给 Jev 看的那份材料,术语叫 state)放进去,一次问五件事:三件是它会不会做什么,一件是会不会削弱安全,一件是万一错了损失多大。
  2. 删数据 0.12,把数据发出去 0.08,执行下载来的代码 0.91,削弱安全 0.20;损失等级 1.7。
  3. 几道危害里最大的是 0.91,超过了拒绝线,这条命令根本不会跑——同时整排数一起写进日志,下一个人一眼就看见是「下载来的代码」那道题拦住了它。

名字从哪来

体操比赛的记分卡。裁判不报「这套动作值几分」,他们分别给难度、完成、艺术打分,总分按公开的公式算出来。要是让一个裁判直接喊总分,你就同时失去两样东西:看不出分扣在哪儿,也没法在规则改了以后把上个赛季重算一遍。

什么时候用

那个笼统判断能拆成三到五个互不重复的维度,每个都是一件看得见的事实,而且你想知道到底是哪一项拍的板。

什么时候别用

拆出来的几道题其实是同一件事换了身衣服——「做完了吗」「需求满足了吗」「能发布了吗」是同一个数问了三遍,不是三重保险。另外,只有一个维度的时候,你要的是画一条线,不是搭一张记分卡。

做这件事的项目

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

用这种模式的全部项目 →

看谁的代码
  • hunchosrc/compose.ts:65-97

    「全都要成立」取最小的那个数,「任意一个成立」取最大的那个,权重也在这里乘:算术全长在代码里,一点都没交给模型。

  • jev-axisrc/safety.ts:238-261

    四道「它会不会做这件事」加一道「错了损失多大」,在代码里合起来:四道里任何一道到 0.8,这条命令就被拒绝。

  • abidepackages/cli/src/lib/band.ts:11-46

    碰上多选题,代码把所有算违规的选项的概率加起来,拿这个和去过线。

想看代码
const { answers } = await jev(state, {
  deletes:  noul("这条命令会删除、覆盖或重置数据吗?"),
  sends:    noul("这条命令会把本地数据发到外部主机吗?"),
  runs:     noul("这条命令会下载并执行别处的代码吗?"),
  damage:   score("万一出错,损失有多大?", ["没有", "能恢复", "不可逆"]),
});
const hazard = Math.max(answers.deletes.p, answers.sends.p, answers.runs.p);   // 任意一个就够
const verdict = hazard >= 0.8 ? "拒绝"
  : hazard >= 0.45 && answers.damage.score >= 1.5 ? "问人" : "放行";
log({ ...answers, hazard, verdict });          // 解释是这一排数,总分解释不了什么
jevbooks 怎么认出它

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

questions.json · 每份 README 都发这道题
{
  "composite-scoring": {
    "type": "noul",
    "instructions": {
      "what": "Does the text describe combining several Jev probabilities or scores in code (min, max, weighted sum, probability mass over a subset of options, or two answers mapped to three outcomes) into one decision?",
      "not_for": "A single question thresholded on its own, or per-question thresholds with no combination."
    },
    "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 做一个 →