# 专题-01:正文建议接受(Accept Suggestion)实现规范 - 版本:v6 - 更新日期:2026-07-20 - 目标读者:产品 / 架构 / 前端 / 后端 / 测试 - 阅读时间:20–35 分钟 - 边界说明:本文档只收束“接受建议”这条跨文档主链路:用户怎么把 AI 候选写入正文、关联知识草稿怎么保留或失效、正文来源归因怎么落点、事务边界怎么切、前端怎么反馈。精确 Schema、状态机和统一错误模型由后续后端阶段承接;当前阶段以 `架构-02` 和 `架构-04` 为准。 - 变更记录:v6(2026-07-20)补齐编辑后新 candidateVersion、重新 detector、`accept_preflight` 与 CAS 接受边界;实验阶段不改 API/DB。v5(2026-05-23)收束接受建议、知识草稿、来源归因和事务边界。 ## 1. 目标与范围 Accept Suggestion 是 AI 候选从待审层(Shadow)进入正文规范数据(Canonical)的唯一合法入口。 这个能力要同时解决四件事: - 把候选文本安全写入目标 Block。 - 写入候选归档和必要的正文来源归因(Block Source Attribution)。 - 保持“接受正文”和“确认知识”两条用户决策分离,接受候选不写 Local KB。 - 在修改后合并时,让基于旧候选文本的知识草稿失效,并基于最终正文启动重新提取链路。 - 把正文、知识、历史与投影边界切干净,避免“看起来成功,其实系统已经脏了”。 ### 1.1 In Scope - 接受建议(原样接受、修改后合并) - 拒绝建议 - 关联知识草稿的保留 / 失效语义 - 正文来源归因与授权快照落点 - 前端正文乐观更新、回滚与冲突处理 - 审计、归档、outbox 写入与最终一致刷新 ### 1.2 Out of Scope - 建议生成本身 - 知识提取算法本身 - 全书解析的章节批量确认 - 一致性检查面板本身 - AST 级精细 diff ## 2. 统一语义(先把话说死) ### 2.1 三条用户结果路径 | 路径 | 正文结果 | 知识草稿结果 | 历史结果 | |---|---|---|---| | 原样接受 | Suggestion 内容进入目标 Block | 与当前 Suggestion 绑定的草稿保持待确认;不得自动入 Local KB | Suggestion 归档为 accepted | | 修改后合并 | 用户编辑先生成递增的 candidateVersion,重新通过 detector 和 `accept_preflight` 后,该版本正文进入目标 Block | 旧版本草稿在新版本被接受时失效;AFTER_COMMIT 重新提取新的草稿 | 最终 candidateVersion 归档为 accepted,旧版本保留审计链且不可接受 | | 拒绝 | 正文不变 | 关联草稿一起丢弃 | Suggestion 归档为 rejected | ### 2.2 为什么“修改后合并”不是新状态 - 用户决策仍然是“接受这条建议,只是我改了最终入正文的文本”。 - 它不应该发明新的 Active 状态,也不应该生成第二套历史状态机。 - 编辑动作只生成新的待审 candidateVersion,不直接写正文;归档层最终统一记为 `accepted`,但必须保留“这是 modified merge”的版本链与审计语义。 ### 2.3 Stale Draft 规则 只要源文本发生变化,旧知识草稿就必须被视为 stale: - 不能继续确认旧草稿。 - 不能把旧草稿偷偷并入正式知识。 - 必须重新走提取 -> 校验 -> 风险分级链路,生成新的草稿。 即使源文本没有变化,接受候选也不等于确认知识草稿。知识草稿进入 Local KB 的唯一入口仍是用户在知识确认入口显式确认。正文保存可以触发后续提取、校验和知识治理流程,但不能直接写 Local KB。 ## 3. 相关 SSOT - 双轨模型:`架构-02-核心数据结构与双轨模型.md` - 状态机与端点前后置条件:`架构-04-状态机与约束清单.md` - 普通用户系统链路:`流程-02B-普通用户系统处理流程(系统视角).md` - 普通用户操作链路:`流程-01B-普通用户操作流程(操作视角).md` 本文档只做一件事:把这些分散约束收束成一条可落地的 Accept 链路。 ## 4. 核心不变式 ### 4.1 正文与建议 - Block 正文是正式正文的唯一真相。 - Block revision 是写入锚点。 - AI Suggestion 只保存待审对象;进入终态后必须进入候选归档。 - 如果候选使用了 AI、市场资产、外部知识或授权知识来源,写入正文 revision 时必须同步写正文来源归因(Block Source Attribution),保留来源链路、授权快照、许可限制和召回状态。 ### 4.2 关联知识草稿 - 生成场景下的知识草稿可以关联到 AI Suggestion,但它仍是独立待审对象。 - 原样接受时,关联知识草稿保持待确认,不得自动写入 Local KB。 - 修改后合并时,基于旧候选文本的知识草稿统一失效,不允许继续沿用。 - 知识草稿确认必须另走知识确认入口,且必须重新校验来源状态、授权快照、source hash、目标版本和风险标记。 ### 4.3 事务边界 - Accept 主事务内不得发起外部 AI / 提取 / 校验调用。 - 原样接受时,正文写入、候选归档、正文来源归因、关联草稿状态保留、change log、outbox 写入必须放在同一事务里完成。 - 修改后合并时,只有新 candidateVersion 重新通过 detector 和 `accept_preflight` 后,正文写入、Suggestion 归档、旧草稿失效与审计才进入主事务;重新提取只能走 AFTER_COMMIT 异步链路。 ### 4.4 历史与投影 - Accept / Reject 后,Active 区不能再看到这条 Suggestion。 - 接受候选写入正文后,可以触发投影或重新提取 outbox;但投影和提取失败不得反向回滚正文。 - 投影失败不能反向打断已经成功的 Accept 主事务。 ## 5. 决策命令语义合同 本节只定义产品层面的命令合同,不定义后端 endpoint、请求 JSON 或数据库字段。后续后端阶段可以按此语义设计 API,但不得改变这里的用户决策、幂等、来源和知识草稿边界。 ### 5.1 Accept 命令 Accept 命令必须携带以下语义: | 语义 | 要求 | |---|---| | commandId / idempotencyKey | 同一 actor、suggestion、command 和目标 revision 的重复提交必须返回同一结果,不得重复写正文、归档或 outbox | | actor / work / targetBlock | 当前用户、作品和目标 Block | | suggestion | 仍处于 Active / Shadow 的候选 | | expectedRevision | 用户决策基于的目标 Block revision,必填 | | acceptMode | `accept_as_is` 或 `merge_after_edit`;后者只能引用已经重新检测通过的编辑版本 | | candidateVersion / candidateSha256 | 本次实际接受的不可变候选版本及正文哈希;编辑后必须递增版本并重新计算哈希 | | decisionContext | UI 决策来源、候选版本、质量结果版本和必要审计摘要 | | acceptPreconditionContext | 接受前置校验上下文,必须覆盖输出合规、静态检查、质量结果版本、来源状态、来源事件影响、Action Policy、授权快照、作品资产 feature gate、`expectedRevision` 和幂等结果;系统在接受时实时校验,不封装为独立对象 | 命令结果至少返回以下语义: | 语义 | 原样接受 | 修改后合并 | |---|---|---| | blockOutcome | 目标 Block 写入成功,revision 递增 | 目标 Block 写入已重新检测通过的编辑版本正文,revision 递增 | | suggestionOutcome | Suggestion 离开 Active,进入 Archive,disposition=accepted | 最终 candidateVersion 离开 Active,进入 Archive,disposition=accepted;旧版本保留 modified merge 版本链且不可接受 | | knowledgeDraftOutcome | 关联草稿保持待确认,仍需单独进入知识确认入口 | 基于旧候选文本的草稿失效,不允许继续确认 | | followupTask | 可触发投影刷新,不要求即时返回草稿数量 | AFTER_COMMIT 启动重新提取或投影刷新任务 | ### 5.2 Reject 命令 Reject 只做三件事: - 迁出 Suggestion - 丢弃关联草稿 - 写历史 / 审计 Reject 不得修改正文,不得触发新的提取。 ### 5.3 幂等与重复提交 同一命令重复到达时: - 已成功接受的命令必须返回同一 Block revision、Archive 结果和 followupTask 摘要。 - 已失败且可重试的命令必须说明失败原因和当前可恢复动作。 - 已失败且不可重试的命令不得创建新的正文 revision。 - 同一幂等键携带不同 actor、work、targetBlock、suggestion、`expectedRevision`、授权快照或接受前置校验输入时,必须返回幂等冲突,不得复用旧结果。 - Archive、change log、audit log 和 outbox 必须能通过命令幂等语义去重。 ## 6. 接受前置条件 Accept 在任何写正文动作前必须完成前置校验。前置校验失败时,候选不能被接受,正文不能写入。 服务端必须在接受时实时校验候选来源版本和授权状态;如果校验缺失、过期、质量结果版本不匹配、`expectedRevision` 不匹配、授权快照变化、来源状态变化、作品资产 feature gate 变化或幂等结果不可复用,必须在写正文前重算。重算结果不是提示文案,而是写 Canonical 的硬闸门。 ### 6.1 candidateVersion、detector 与 `accept_preflight` 用户编辑候选时不得把 `contentOverride` 直接送进 Accept 主事务。编辑动作必须: 1. 基于当前候选生成严格递增的新 candidateVersion 和 candidateSha256,旧版本立即失去接受资格但保留审计。 2. 重新运行 detector;报告必须绑定新 candidateVersion、candidateSha256、contextSnapshotSha256 和 qualityPolicyVersion,旧报告不得复用。 3. detector 绿证据成立后才进入 `accept_preflight`;编辑内容未重新检测、检测超时、报告版本不符或仍有高严重度问题时失败关闭。 4. `accept_preflight` 实时校验 `mode=production`、`acceptanceEligible=true`、候选未过期、上下文快照和来源未失效、授权仍有效、detector 报告精确绑定当前候选、`expectedRevision` 一致及幂等结果可复用。 5. 诊断、评测和回放候选固定 `acceptanceEligible=false`,即使正文相同或 detector 通过也不能进入 Canonical。 接受写入采用 compare-and-set(CAS):只有服务端当前记录仍匹配 `runId + attempt + candidateVersion + candidateSha256 + currentState + expectedRevision` 时,才允许原子完成正文 revision 递增和候选终态迁移。旧 attempt、旧 candidateVersion、迟到 detector 结果、重复状态事件或 revision 已变化时返回冲突或既有幂等结果,不能覆盖新版本或 Canonical。具体生命周期只在 [架构-04 §5](架构-04-状态机与约束清单.md) 定义,本节拥有接受命令的前置与原子写边界。 | 校验 | 失败结果 | |---|---| | actor 对作品、Block、Suggestion 有操作权限 | `PERMISSION_DENIED`,不写正文 | | Suggestion 仍处于 Active / Shadow,未过期、未失效 | `SUGGESTION_NOT_ACCEPTABLE`,不写正文 | | expectedRevision 匹配目标 Block 当前 revision | `REVISION_CONFLICT`,进入显式冲突处理 | | mode=production 且 acceptanceEligible=true | `CANDIDATE_NOT_ACCEPTANCE_ELIGIBLE`,不写正文 | | detector 绿报告精确绑定 candidateVersion、candidateSha256、contextSnapshotSha256 和策略版本 | `QUALITY_EVIDENCE_STALE` 或 `DETECTOR_NOT_PASSED`,不写正文 | | 接受前置校验通过(实时校验来源版本和授权状态),且覆盖输出合规、静态检查、质量结果版本、来源状态、授权快照、作品资产 feature gate、expectedRevision 和幂等结果 | 按校验结果阻断、需重验或要求显式确认 | | 输出合规、语义安全围栏和静态检查通过 | `OUTPUT_COMPLIANCE_BLOCKED` 或 `STATIC_CHECK_FAILED`,不写正文 | | 候选正文 lineage 中所有来源有有效 Authorization Snapshot | `SOURCE_AUTH_INVALID`,候选 invalidated / blocked | | 候选正文 lineage 不含 revoked / recalled / delisted / blocked / owner_missing / unauthorized 来源 | `SOURCE_BLOCKED`,候选 invalidated / blocked | | 候选使用市场作品资产时,存在有效 `workAssetUsePrecheckId`、owner 预检、授权快照、lineage 和 feature gate | `SOURCE_FEATURE_DISABLED` 或 `OWNER_PRECHECK_REQUIRED`,候选不可接受 | 只有与正文无关的关联知识草稿出现 stale、需重验或不可确认时,才允许“正文接受成功、草稿需单独处理”。一旦问题来源参与了候选正文 lineage,就必须阻断正文写入,不能只把 Knowledge Draft 标记为不可确认。 ## 7. 后端实现规范 ### 7.1 原样接受事务 单个事务内完成: 1. 加载 Active Suggestion 并校验归属、状态与过期时间。 2. 加载目标 Block,并用 `expectedRevision` 做并发保护。 3. 执行 `accept_preflight`,实时校验接受资格、candidateVersion/hash、detector 绿报告、上下文快照、候选来源版本和授权状态(校验清单:候选来源、授权快照、市场作品资产 feature gate、质量门控、输出合规、静态检查、质量结果版本、幂等结果和当前 Block revision)。 4. 以 candidateVersion、candidateSha256、当前候选状态和 `expectedRevision` 做 CAS,更新 Block 内容与 revision。 5. 如果候选包含 AI、市场、外部知识或授权知识来源,写 Block Source Attribution。 6. 将 Suggestion 迁入候选归档(disposition=accepted)。 7. 保持关联知识草稿为待确认;仅当问题来源没有参与正文 lineage 时,才允许把草稿标记为不可确认或需重验。 8. 写 change log / audit log。 9. 写投影或后续提取 outbox。 任何一步失败,整笔事务回滚。 ### 7.2 修改后合并事务 编辑动作先在事务外形成新的待审 candidateVersion 并重新运行 detector;只有 detector 绿且 `accept_preflight` 通过后,单个接受事务才完成: 1. 加载最终 Active candidateVersion,并校验归属、状态、候选哈希、版本链与过期时间。 2. 加载目标 Block,并用 `expectedRevision` 做并发保护。 3. 执行 `accept_preflight`,确认 detector 绿报告精确绑定最终 candidateVersion/candidateSha256/contextSnapshotSha256,并实时校验来源版本和授权状态。 4. 以 candidateVersion、candidateSha256、当前候选状态和 `expectedRevision` 做 CAS,把该候选正文写入 Block 并递增 revision;不得接受临时 `contentOverride`。 5. 默认继承上游候选的 lineage、授权快照、许可限制、召回状态和风险标记,并写 Block Source Attribution。 6. 将最终 candidateVersion 迁入候选归档(disposition=accepted),保留完整编辑版本链与最终正文哈希。 7. 将旧知识草稿标记为失效,不允许继续确认。 8. 写 change log / audit log,标记这是 modified merge。 9. AFTER_COMMIT 发布重新提取事件或创建异步任务记录。 这里的关键不是任务名字,而是语义:**重新提取只能发生在提交之后,不能把外部调用塞进正文主事务。** 如果用户改写后的最终正文确实不再依赖某些授权来源,系统也不能只靠文本差异自动洗白。只有在单独的来源归因证明、原创性确认或人工决策记录成立时,才允许降低对应限制;该过程必须审计,并且不得让 revoked / recalled / delisted / blocked / owner_missing / unauthorized 来源通过改写绕过。 ### 7.3 Reject 事务 单个事务内完成: 1. 加载 Active Suggestion。 2. 校验归属与状态。 3. 迁入 `suggestion_archive(disposition=rejected)`。 4. 丢弃关联知识草稿。 5. 写 `audit_log`。 Reject 不得更新 Block,不得创建重新提取链路。 ### 7.4 失败矩阵 | 失败类型 | 正文结果 | 用户可见恢复动作 | |---|---|---| | 无权限或作品不可访问 | 不写入 | 返回作品或权限提示 | | Suggestion 已过期、已归档或已失效 | 不写入 | 重新生成或关闭候选 | | 来源 revoked / recalled / delisted / blocked / owner_missing / unauthorized | 不写入,候选 invalidated / blocked | 查看来源摘要、重新生成 | | 市场作品资产 feature gate 关闭 | 不写入 | 仅允许阅读、收藏或授权记录,不允许作为 AI 上下文 | | owner 预检或 workAssetUsePrecheck 缺失 | 不写入 | 回到作品资产使用预检 | | 质量门控 blocked 或 needs_recheck | 不写入 | 重验、重生成或修改后重新评估 | | 输出合规或静态检查失败 | 不写入 | 修改输入或重新生成 | | revision 冲突 | 不写入 | 基于最新 revision 重试或放弃 | | 重复命令 | 不重复写入 | 返回既有结果 | ## 8. 前端实现规范 ### 8.1 状态拥有 - 正文 Block:服务端 Block revision 是唯一真相;前端可以乐观展示,但必须能按服务端结果确认或回滚。 - Active Suggestion:待审区状态持有。 - 关联知识草稿提示与角标:以最终一致方式刷新,不把瞬时数字当真相。 ### 8.2 Accept 成功反馈 - 原样接受:`已合并,关联知识草稿仍待确认` - 修改后合并:`已合并,旧知识草稿已失效,正在重新提取` 禁止反馈: - “已合并,发现 N 条草稿待审核” - “已合并,草稿已经进入正式知识” - “已合并,请稍后看系统是否成功” ### 8.3 冲突处理 当服务端返回 `409 REVISION_CONFLICT`: - 停止当前乐观链路。 - 如果本地没有后续编辑,回滚到上一个稳定状态。 - 如果本地已经继续编辑,进入显式冲突处理态,禁止静默覆盖。 - 给用户清晰的下一步:放弃本次合并,或基于新 revision 重试。 ### 8.4 成功后的刷新策略 - 成功后移除当前 Suggestion 卡片。 - 失效相关正文、知识、历史和角标查询。 - 原样接受优先刷新正文和待确认知识草稿提示;修改后合并优先进入“等待新草稿”状态。 ## 9. 用户可观察流程 ### 9.1 原样接受 1. 用户点击“接受”。 2. 正文进入乐观更新状态。 3. 服务端完成主事务。 4. 正文切换为已保存态。 5. Suggestion 卡片从待审区移除。 6. UI 显示“已合并,关联知识草稿仍待确认”。 7. 知识视图与历史视图最终一致刷新,用户可进入知识确认入口单独处理草稿。 ### 9.2 修改后合并 1. 用户点击“修改”。 2. 调整最终入正文的结构化内容。 3. 系统创建递增的 candidateVersion 并重新运行 detector;检测中不能点击接受。 4. detector 绿后,用户点击“修改后合并”,服务端执行 `accept_preflight` 与 CAS。 5. 服务端完成正文主事务,并让旧版本知识草稿失效。 6. UI 显示“已合并,旧知识草稿已失效,正在重新提取”。 7. 前端进入等待新草稿状态。 ### 9.3 Reject 1. 用户点击“拒绝”。 2. Suggestion 离开待审区。 3. 正文无变化。 4. 关联草稿一起丢弃。 ## 10. Acceptance Criteria ### Must-Have(P0) 1. Accept 成功后,目标 Block 正确更新,revision 正确递增。 2. Suggestion 成功迁入 Archive,不再留在 Active。 3. 原样接受时,关联知识草稿不得写入 Local KB,只能保持待确认、需重验或不可确认状态。 4. 修改后合并时,旧草稿立即失效,不允许继续确认。 5. 修改后合并只启动重新提取异步链路,不在主事务里做外部调用。 6. Reject 对正文零副作用。 7. 409 冲突必须有显式恢复路径。 8. 成功提示必须区分原样接受与修改后合并两条路径。 9. 包含 AI、市场、外部知识或授权知识来源的正文 revision 必须写 Block Source Attribution。 10. 候选正文 lineage 中存在 revoked、recalled、delisted、blocked、owner_missing、unauthorized 或无效授权快照时,Accept 必须失败。 11. 候选使用市场作品资产时,必须经过作品资产使用预检和 feature gate;否则不能接受。 12. 修改后合并默认继承上游 lineage 和许可限制,不能用 `contentOverride` 洗白来源。 13. Accept / Merge 写 Canonical 前必须实时校验来源版本和授权状态,校验清单必须覆盖输出合规、静态检查、质量结果版本、来源状态、授权快照、作品资产 feature gate、expectedRevision 和幂等结果。 14. 用户编辑必须生成递增 candidateVersion 并重新运行 detector;未重新检测或 detector 非绿不得进入 `accept_preflight`。 15. 只有 `mode=production`、`acceptanceEligible=true` 且候选/上下文/检测/策略版本一致时才能接受;诊断和评测候选永不可接受。 16. Accept / Merge 必须以 candidateVersion、candidateSha256、当前状态和 `expectedRevision` 做 CAS,旧版本和迟到结果不得覆盖 Canonical。 ### Should-Have(P1) 1. 成功后正文有短暂高亮反馈。 2. 修改后合并能显示“等待新草稿”的明确状态。 3. 同一 Block 下重复触发 Accept 时有临时互斥保护。 ### Nice-to-Have(P2) 1. 更细粒度的对比视图。 2. 建议卡片快捷键。 ## 11. Test Plan ### 11.1 Manual - 原样接受:正文更新,Suggestion 消失,知识视图可看到待确认草稿。 - 修改后合并:正文更新,旧草稿失效,系统稍后出现新草稿。 - Reject:正文不变,Suggestion 消失,知识不被污染。 - 409 冲突:前端进入可恢复状态,不覆盖用户新输入。 - 重复点击已接受对象:幂等,不重复污染正文与历史。 ### 11.2 Automated **Backend Unit / Integration** - 原样接受正常路径 - 原样接受不确认知识草稿 - 候选正文来源 stale / revoked / recalled / delisted / blocked / owner_missing / unauthorized 时按决策规则阻断接受或要求重验;只有非正文关联草稿允许单独标记需重验 - Block Source Attribution 写入路径 - 市场作品资产缺 feature gate / owner 预检 / `workAssetUsePrecheckId` 时阻断接受 - 修改后合并触发旧草稿失效 - 修改后合并不能清空上游 lineage、授权快照和许可限制 - 编辑后 candidateVersion 递增并重新 detector;旧检测报告和旧候选不可接受 - `acceptanceEligible=false`、detector 非绿、上下文哈希漂移或策略版本漂移时 `accept_preflight` 失败 - CAS 冲突、迟到 detector 和重复命令不能覆盖新 candidateVersion 或正文 revision - Reject 零正文副作用 - 409 冲突返回必要信息 - 投影失败不阻塞主事务 **Frontend E2E** - 原样接受成功链路 - 修改后合并成功链路 - Reject 成功链路 - 409 冲突链路 - 本地脏状态下的失败恢复 ## 12. 关联阅读 - `架构-02-核心数据结构与双轨模型.md` - `架构-04-状态机与约束清单.md` - `流程-02B-普通用户系统处理流程(系统视角).md` - `流程-01B-普通用户操作流程(操作视角).md`