oh-my-muse/design-docs/专题-01-正文建议接受(Accept Suggestion)实现规范.md
zizi 33aad93bef 提交全维度文档review后的22项架构决策落地
基于产品/架构/流程/前端/后端五维度并行review,澄清并落地22项关键决策:

架构层:Governance按消费者归属拆散、MetaSchema独立模块、
Source传播改为事件驱动自治、去掉Candidate Decision Envelope
和needs_recheck中间状态、Neo4j决策为依赖RAGFlow GraphRAG

后端层:Entitlement统一为可变表+审计日志、API版本策略采用
X-API-Version Header、知识实体唯一键加scope字段

前端层:SSE实时通信(AI stream独立+事件统一)、正文持久化
IndexedDB安全网、Block粒度为场景/小节级

产品层:范围不变UX解决复杂度、知识确认默认自动+冲突时人工
2026-05-24 04:28:52 +08:00

360 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 专题-01正文建议接受Accept Suggestion实现规范
- 版本v5
- 更新日期2026-05-23
- 目标读者:产品 / 架构 / 前端 / 后端 / 测试
- 阅读时间2035 分钟
- 边界说明:本文档只收束“接受建议”这条跨文档主链路:用户怎么把 AI 候选写入正文、关联知识草稿怎么保留或失效、正文来源归因怎么落点、事务边界怎么切、前端怎么反馈。精确 Schema、状态机和统一错误模型由后续后端阶段承接当前阶段以 `架构-02``架构-04` 为准。
## 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 |
| 修改后合并 | `contentOverride` 进入目标 Block | 旧草稿立即失效AFTER_COMMIT 重新提取新的草稿 | Suggestion 归档为 accepted并保留 `final_content` 快照(如需要) |
| 拒绝 | 正文不变 | 关联草稿一起丢弃 | Suggestion 归档为 rejected |
### 2.2 为什么“修改后合并”不是新状态
- 用户决策仍然是“接受这条建议,只是我改了最终入正文的文本”。
- 它不应该发明新的 Active 状态,也不应该生成第二套历史状态机。
- 归档层统一记为 `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 写入必须放在同一事务里完成。
- 修改后合并时正文写入、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` |
| finalContent | 仅 `merge_after_edit` 需要,表示用户确认写入正文的最终内容 |
| decisionContext | UI 决策来源、候选版本、质量结果版本和必要审计摘要 |
| acceptPreconditionContext | 接受前置校验上下文必须覆盖输出合规、静态检查、质量结果版本、来源状态、来源事件影响、Action Policy、授权快照、作品资产 feature gate、`expectedRevision` 和幂等结果;系统在接受时实时校验,不封装为独立对象 |
命令结果至少返回以下语义:
| 语义 | 原样接受 | 修改后合并 |
|---|---|---|
| blockOutcome | 目标 Block 写入成功revision 递增 | 目标 Block 写入用户最终内容revision 递增 |
| suggestionOutcome | Suggestion 离开 Active进入 Archivedisposition=accepted | Suggestion 离开 Active进入 Archivedisposition=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 的硬闸门。
| 校验 | 失败结果 |
|---|---|
| actor 对作品、Block、Suggestion 有操作权限 | `PERMISSION_DENIED`,不写正文 |
| Suggestion 仍处于 Active / Shadow未过期、未失效 | `SUGGESTION_NOT_ACCEPTABLE`,不写正文 |
| expectedRevision 匹配目标 Block 当前 revision | `REVISION_CONFLICT`,进入显式冲突处理 |
| 接受前置校验通过(实时校验来源版本和授权状态),且覆盖输出合规、静态检查、质量结果版本、来源状态、授权快照、作品资产 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. 实时校验候选来源版本和授权状态(校验清单:候选来源、授权快照、市场作品资产 feature gate、质量门控、输出合规、静态检查、质量结果版本、幂等结果和当前 Block revision
4. 更新 Block 内容与 revision。
5. 如果候选包含 AI、市场、外部知识或授权知识来源写 Block Source Attribution。
6. 将 Suggestion 迁入候选归档(disposition=accepted)。
7. 保持关联知识草稿为待确认;仅当问题来源没有参与正文 lineage 时,才允许把草稿标记为不可确认或需重验。
8. 写 change log / audit log。
9. 写投影或后续提取 outbox。
任何一步失败,整笔事务回滚。
### 7.2 修改后合并事务
单个事务内完成:
1. 加载 Active Suggestion 并校验归属、状态与过期时间。
2. 加载目标 Block并用 `expectedRevision` 做并发保护。
3. 实时校验候选来源版本和授权状态(校验清单:候选来源、授权快照、市场作品资产 feature gate、质量门控、输出合规、静态检查、质量结果版本、幂等结果和最终正文内容
4.`contentOverride` 更新 Block 内容与 revision。
5. 默认继承上游候选的 lineage、授权快照、许可限制、召回状态和风险标记并写 Block Source Attribution。
6. 将 Suggestion 迁入候选归档(disposition=accepted),必要时记录 `final_content`
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. 点击“修改后合并”。
4. 服务端完成正文主事务,并让旧草稿失效。
5. UI 显示“已合并,旧知识草稿已失效,正在重新提取”。
6. 前端进入等待新草稿状态。
### 9.3 Reject
1. 用户点击“拒绝”。
2. Suggestion 离开待审区。
3. 正文无变化。
4. 关联草稿一起丢弃。
## 10. Acceptance Criteria
### Must-HaveP0
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 和幂等结果。
### Should-HaveP1
1. 成功后正文有短暂高亮反馈。
2. 修改后合并能显示“等待新草稿”的明确状态。
3. 同一 Block 下重复触发 Accept 时有临时互斥保护。
### Nice-to-HaveP2
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、授权快照和许可限制
- Reject 零正文副作用
- 409 冲突返回必要信息
- 投影失败不阻塞主事务
**Frontend E2E**
- 原样接受成功链路
- 修改后合并成功链路
- Reject 成功链路
- 409 冲突链路
- 本地脏状态下的失败恢复
## 12. 关联阅读
- `架构-02-核心数据结构与双轨模型.md`
- `架构-04-状态机与约束清单.md`
- `流程-02B-普通用户系统处理流程(系统视角).md`
- `流程-01B-普通用户操作流程(操作视角).md`