撤销与重做在输出不可复现时语义失效,撤销后回不到原来那次结果
别名: 撤销失效 · 不可复现撤销 · sampled-state undo
概念解释
撤销的合同是:退回上一个世界,而且那个世界还在。重做是把这份合同正向再走一遍。生成结果若只存在于那一次采样里,界面却只存了「再跑一遍」的指令,撤销就会跑出另一个世界。不可复现输出下的撤销坍塌(undo collapse)不是快捷键坏了,是被撤销的对象没有被当成需要保存的状态。
人按 Cmd+Z 期望看见刚才那一版标题,不是「再生成一个标题」。
机制
经典文档模型把每一次提交收成一份快照或一份逆操作。生成界面常把提交收成「用这个提示再请求一次」。请求可以重放,采样不能。于是撤销被实现成重放,重放碰到新的随机数,状态就丢了。
更糟的是部分应用把流式输出的中间帧也推进撤销栈,或把「停止生成」当成一步。栈里于是充满不可命名的半成品。用户要回的是某一个自己点头过的完成态,栈却只能提供「再抽一次」或「空白」。合同在实现层被换成了另一份,表面上还叫撤销。
怎么研究
任务:生成 → 接受 → 再生成覆盖 → 撤销。记录撤销后的文本是否与接受态逐字相同。再比较三种实现:只重放提示、保存输出快照、保存完整采样记录(提示+种子+输出)。因变量:身份恢复率、用户是否察觉「这不是刚才那个」、是否不敢再点撤销。
眼动和回溯访谈要分开「以为回到了」和「真的回到了」。表面相似会制造假恢复。度量必须用哈希或逐字比对,不能用「看起来差不多」。
边界
若每次生成都写入不可变版本(聊天气泡、版本列表),撤销可以退化成「把焦点移回上一条」,语义仍成立。真正坍塌的是原地替换且不留底稿的编辑器。只追加、不覆盖的时间线天生可逆。协作场景里「撤销」还涉及他人已读哪一版,那是同步问题,超出单次采样。温度零且种子随结果一起存储时,重放可以恢复,前提是解码栈完全一致——这个前提在模型或服务端一变就碎。
怎么落地
- 每一次被用户看见的完成态都要作为不可变快照留下,撤销只是指针后移,禁止用「再请求」冒充。
- 覆盖式再生成之前,自动把当前完成态推进历史;不要等用户先想起来去复制。
- 撤销栈里只放完成态和用户的手工编辑,不放流式中间帧。
- 验证:生成 A,再生成 B 覆盖,按撤销。屏幕上必须是 A 的逐字拷贝。把 A 的哈希记在测试里,换模型版本后这条测试仍应通过——它测的是产品有没有存快照,不是模型稳不稳定。
延伸
- 同组:L1.01.1 同一输入可能得到不同输出 · L1.01.2 界面惯例默认操作可重复且结果稳定 · L1.01.3 用户会把偶发正确误判为稳定能力 · L1.01.4 界面控件默认承诺同一操作得到同一结果,生成式功能违背这一承诺 · L1.01.5 用户无法通过重试区分是自己表述不当还是系统本身在波动 · L1.01.7 把重新生成呈现为「刷新」会暗示上一个结果只是加载失败 · L1.01.8 把可变性显式呈现为多个并列方案,比藏在单一结果背后更诚实
- 相邻:L1.11 非确定性输出的可复现问题 · L2.12 结果的迭代修改与局部重生成 · I3.02 乐观更新
- 站内检索:
undo collapse·snapshot versus replay·non-reproducible undo