A29第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

人工确认与写入操作

人工确认与写入操作 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能给 AI 的写操作(改代码、保存内容、删资料)装上确认闸门:模型产出的是「可查看的建议」,人在预览后决定应用与否,并且能说清「生成的建议」和「实际应用的结果」为什么必须当成两件事。

问题场景

你对助手说「帮我把这个函数改成异步的」。两种实现差别巨大:

  • 实现 A:助手直接改文件、保存、回一句「已完成」。你打开一看,它顺手重构了半个文件,还删了个它认为没用的导出。
  • 实现 B:助手给出一段 diff——改了哪几行、为什么——等你点「应用」,点完才真的落盘。

A 的问题不只是「改得不好」,而是职责错位:模型的职责是提出建议,应用的职责是你的。A28 说过注入改变模型的「意愿」;模型若直接握着写盘能力,意愿出错就等于事故。确认闸门是最后一道防线。

核心解释

写操作的正确流程分四步,每步的职责边界要钉死:

  1. 建议(proposal):模型产出结构化的变更提案,存库、可丢弃、无副作用。
  2. 预览:用户能看到完整变更内容——代码改动用 diff,内容保存用前后对照,删除操作列出影响清单。
  3. 确认:用户显式动作(点按钮),系统记录「谁、何时、确认了哪个 proposal」。
  4. 应用(apply):服务端执行真实写入,结果回写 proposal 状态并反馈给会话。
// 建议与应用分离的数据模型
interface Proposal {
  id: string;
  taskId: string;            // 关联到 A26 的任务(轨迹可查)
  kind: "edit_code" | "save_content" | "delete_doc";
  payload: {                 // 结构化变更:可渲染成 diff,可直接执行
    path?: string;
    before?: string;         // 原文(编辑类必带,应用前再校验一次没被别人改过)
    after: string;           // 建议的原文替换
    reason: string;          // 模型给的理由,预览时展示
  };
  status: "pending" | "applied" | "rejected" | "stale"; // stale=应用前原文已变
  confirmedBy: string | null;// 确认人;null 即未确认,应用接口直接拒绝
}

为什么 before 必须随建议存下来:从建议生成到用户点确认之间,原文可能已经被改过(人手动改了、另一个任务改了)。应用时要校验当前内容仍等于 before,不等就标 stale 拒绝应用——否则一次确认应用的是「过期假设」,覆盖掉别人的修改。

拒绝也是一等结果:用户点了「拒绝」,这个事实要回传给会话里的模型——「用户拒绝了该修改」,模型据此调整下一个建议(比如问清楚顾虑)。拒绝静默丢弃是最差的体验:模型不知道被拒,下一轮又提一个一样的。

如何验证:走一遍完整流程,检查三点——未确认的 proposal 调应用接口必须被拒(API 层校验,不信前端);应用后 proposal 状态变成 applied 且会话里能看到结果;应用前手动改动原文,再点确认,得到 stale 而不是错误覆盖。

示例拆解

一次「改代码建议」的生命周期:

{
  "proposal": {
    "id": "prop_01",
    "kind": "edit_code",
    "payload": {
      "path": "lib/documents/parse.ts",
      "before": "const WINDOW = 800;",
      "after": "const WINDOW = 600;",
      "reason": "600 字窗口让检索粒度更细,命中片段更聚焦(依据:实验区对比)"
    },
    "status": "pending",
    "confirmedBy": null
  }
}

关键点:

  • 建议生成不经过写工具:模型在普通生成里产出 JSON 建议(A09 的结构化输出约束在这里复用),不存在「apply_edit」这种模型可直接调的写工具——应用是独立接口,唯一入口是确认动作。这延续 A28 的能力兜底:模型有提建议的能力,没有落盘的能力。
  • 应用结果回传会话:applied 之后,把「改了什么文件、diff 摘要」作为工具结果喂回模型,它才能基于真实结果继续(而不是假设成功了)——和 A15 的「回传真实结果继续生成」同一个原则。
  • 预览要完整可读:diff 之外必须带 reason;用户不该在看不到理由的情况下批准变更。拒绝率高通常是 reason 写得烂。
  • 与 A26/A27 的配合:proposal 的状态变更属于任务轨迹(记录在 task_step);应用接口要幂等(同一个 proposal 应用两次只生效一次,按 proposal id 去重)。

动手任务

  1. 在纸上画出「保存笔记」写操作的完整状态机(pending → applied/rejected/stale),标出每个状态转换由谁触发(模型 / 用户 / 系统)。
  2. 推演竞态:建议生成后、确认前,原文被手动改了三行。写出应用接口的校验逻辑伪代码,说明为什么「不校验 before」会造成静默覆盖。
  3. 判断任务:站内学习助手目前只有只读工具(A15)。如果给它加「保存笔记到 /notes」的能力,列出必须新增的机制(至少三条:proposal 结构、确认接口、幂等/轨迹)。

常见错误

  • 把写操作做成普通工具:模型一「决定」就直接落盘,确认环节不存在——A28 的注入防护在最危险的地方失效。
  • 预览只给摘要不给全文:「修改了 3 处」这种描述没法批准。看不到 diff 就不该出现确认按钮。
  • 确认了但应用失败没有反馈:用户以为保存了,实际落盘失败。应用结果必须回写并展示(成功/失败/stale 三态都有明确提示)。
  • 拒绝后不告知模型:下一轮又提同样的建议,用户烦死。拒绝是反馈,要回传。

检查与复盘

自查清单:

  • 能说出建议与应用的四步流程及各自职责归属
  • 能解释 before 校验防什么(过期假设覆盖他人修改)
  • 能验证「未确认不能应用」是 API 层的约束而不是前端约定

参考资料

检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1读代码中等

阅读写操作建议的数据模型。应用(apply)接口在执行前必须校验什么?before 字段为什么必须随建议一起存?

interface Proposal {
  id: string;
  kind: "edit_code" | "save_content" | "delete_doc";
  payload: {
    path?: string;
    before?: string;  // 原文
    after: string;    // 建议的替换内容
    reason: string;
  };
  status: "pending" | "applied" | "rejected" | "stale";
  confirmedBy: string | null;
}

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...