> 两类错误代价差一个数量级时,"不做"必须是一等输出,不是兜底分支
自动填表这类场景里,两类错误的代价差了一个数量级:
// TABLE_OF_CONTENTS
结论先行¶
自动填表这类场景里,两类错误的代价差了一个数量级:
- 漏填一个字段:用户多花 5 秒。
- 静默填错一个字段:赞助需求答反、现雇主栏里是去年的公司—— 用户丢掉这次申请,而工具丢掉全部信任,一次就够。
既然如此,设计上就不能把"匹配不上"当成异常分支, 它必须是和"填入"同级的一等输出: 置信度低于阈值、或第一名与第二名的间隔太小 → 跳过,不是选最近的那个。
这个决定会反过来约束整条链路,而且约束得很具体。
为什么¶
最危险的输入恰恰是字符串相似度最高的那些。 选项里的
"No — I will require sponsorship" 和 "Yes — I will not require sponsorship"
编辑距离很近,而含义相反。取最近邻在极性问题上系统性地出错,
而极性问题正是代价最高的那一类。
所以需要一个专门的强制转换层,把这件事从"顺手归一化一下"提升成一个有名字的阶段:
规范化 → 别名表 → 相似度阈值 → 跳过。
"US" vs "United States of America"、"CA" vs "California"、
以及上面那种否定式选项,是实际出错的地方,不是理论上的边角。
同一条原则的第二次应用:永不覆盖非空字段¶
比"填错"更糟的只有一件事:毁掉用户刚刚亲手输入的东西。 所以写入只进空字段;非空但与档案不一致的,进冲突区,给一个"用我的值"的按钮。
这里有个值得单独记的收益:保守的写入规则让逆操作变简单了。 因为只写空字段,Undo 的全部职责就收缩成"恢复为空"—— 于是撤销是可靠的,而不是尽力而为的。 如果允许覆盖,Undo 就得保存并恢复任意旧值,那是一个会在边角情况下失败的功能。
选更保守的写入规则,往往不是牺牲能力,而是换来一个能真正工作的撤销。
附带一个诚实的后果要写进文档:持有文件的 input[type=file]
按"非空"处理、永不触碰——因为脚本无法恢复一个已被替换的附件。
能力边界由"逆操作能不能做到"决定,这条推导是干净的。
怎么用¶
任何"自动替用户做决定"的功能,开工前先做这三件事:
- 把两类错误的代价写成数字或量级,哪怕是粗估。 差一个数量级以上,就必须有显式 abstain;差不多,才可以取最近邻。
- 给 abstain 一个出口和一个计数器。 它要能被观测—— "跳过率"是产品质量指标,不是失败日志。只有能看见它, 才谈得上调阈值。
- 找出"相似但相反"的那一类输入(否定式、极性、反义选项), 单独写规则,不要交给相似度。这类输入的特征是: 相似度最高的时候,恰恰是最不该相信相似度的时候。
- 问一句"逆操作能做到吗",再决定正操作的范围。 撤销不了的事情(覆盖已有文件、发出去的消息、已提交的表单)就不要自动做。
出处¶
自己的一个自动填表浏览器扩展(私有未发布)的设计决策记录: "错填是要优化的失败模式"、"专门的强制转换层,阈值以下跳过而不是取最近"、 "永不覆盖非空字段,冲突单列——Undo 因此收缩成恢复为空"。 相关:把所有拒绝都压成 429,等于让客户端在重试永远不可能成功的时候重试、 测不出失败的门禁比没有门禁更糟——它在制造信心。