oh-my-muse/docs/agent-specs/2026-07-07-S7-生成主链全文通路-execution.md
lili 916d6c877f feat(ai): S7a 捕获 provider 完整正文 + 护栏/审上移到完整正文
分支①第一步(provider 无关):RuntimeResult 加仅内存 fullOutput 字段承载 provider 完整正文
(供 S7b 瞬态通路),两个 real client(New-API/Dify chat+workflow)在派生 60/80 字摘要前捕获
完整正文并跑护栏,通过才透出。护栏 guardFullOutput 纯静态 provider 无关:空/超长/凭据痕迹/
疑似截断(≥60 字未以句末标点结束)→失败可重试,不信 provider finishReason(Dify 硬编码常量、
New-API 只记录)。「审」输入从脱敏摘要上移到 result.fullOutput(缺省回退摘要,兼容 shadow/历史)。

主权红线(三层亲验):content_snapshot.content 仍写摘要(resolveContentText,不变);完整正文仅
作 review() 局部变量,findings 落库只含布尔/marker 名/长度、审 ID 指纹用 hashCode+length 不可逆,
无正文明文入库;fullOutput 无任何 set/put/序列化/持久化出口,toString 只打 length。

验证:ai 模块 79 用例全绿(MuseAiCandidateReviewServiceTest 11 / MuseAiRuntimeClientTest 22 /
MuseAiRuntimeProjectionServiceTest 6 / MuseAiTaskServiceTest 40),BUILD SUCCESS。改动严格 7 文件。
真实生成经护栏的活体验证并入 M1(S7b real-PG)。附:DDL 协调 S7b 瞬态表改 V36(S6 batch 占 V35)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 23:59:27 -07:00

136 lines
12 KiB
Markdown
Raw Permalink 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.

# S7 生成主链全文通路(分支①)执行计划(执行版)
- 版本v1.0
- 日期2026-07-07
- 承接:[评审版 v0.2](2026-07-07-S7-生成主链全文通路-review.md)(现状已三路测绘校正;三个材料级点所有者 2026-07-07 已裁:①独立加密缓存表 ②采纳恒回传完整正文 ③护栏+合规审上移到完整正文)。**设计意图/边界/主权红线以 review v0.2 为准,本文只做任务级拆解**;两文冲突以 review v0.2 为准并回改本文。
- 读者:执行 agent每个 S7x 可独立派工)与项目所有者。
---
## 0. 执行纪律(每步适用)
1. **主权红线(最高优先)**:完整正文只允许存在于「执行线程内存 / 瞬态加密缓存TTL+决定即清) / SSE 传输 / 客户端」;**任何一步都不得把完整正文写入 `muse_ai_suggestion``muse_content_block` 或任何长期列**。每步验收含「候选表 content_snapshot.content 仍是摘要、完整正文不在长期列」的机械断言。
2. **完成=机械验证**每步「验收」命令真跑留证改动后按依赖链回归ai 模块单测 → local → real-PG涉 Dify 再跑 live
3. **契约先行**`chunk.data.content`/`sequenceNo` 已声明(`docs/api-contracts/ai/openapi.yaml` SSEChunkEvent只填充不改 schema如需新增瞬态取全文端点则先改契约再实现。DDL 只走 `sql/muse/V<next>__*.sql`(编号取当前最大+1现状最大 V34
4. **最小改动 + 中文注释**:只动本步清单文件;完整正文相关字段/表必须有「永不落长期库、TTL、决定即清」的中文注释与脱敏日志。
5. **回写**:每步完成回写 `docs/mvp/进度总账.md` + `muse-module-ai/.agent`;不新增过程文档。
6. **环境与坑**real-PG/live 按 `.agents/knowledge/external-deps-and-gotchas.md` §四(清代理、`_test` 库、`-am` 防 stale jar、`-DreuseForks=false`)。
7. **派工分档**:各步执行子代理 `opus`;纯机械(配置值、菜单)可 `haiku`S7d 的 Dify fail-closed 语义与 live 验收如遇跨模块硬判可升 `fable` 或主会话终裁。
---
## 1. 步骤总图
```mermaid
flowchart LR
S7a["S7a 捕获+护栏<br/>(provider 无关)"] --> S7b["S7b 瞬态载体+SSE 送达"]
S7b --> S7c["S7c 前端采纳基准+所见即所写"]
S7a --> S7d["S7d 生成 provider 切 Dify"]
S7c --> DONE["S7 验收"]
S7d --> DONE
DEP["依赖: Muse 专属 Dify workspace"] -. 仅 S7d .-> S7d
```
**S7a→S7b→S7c 不依赖 Dify workspace可先行连跑**S7d 依赖 Dify workspace 就绪,可与 S7c 并行准备、workspace 到位后收口。
---
## S7a 捕获完整正文 + 护栏上移provider 无关,不依赖 Dify
| | |
|---|---|
| 前置 | 无 |
| 触及 | `muse-module-ai-server``MuseAiRuntimeClient`RuntimeResult`RealNewApiMuseAiRuntimeClient``RealDifyMuseAiRuntimeClient``MuseAiCandidateReviewService``MuseAiRuntimeProjectionService` |
**动作**
1. **运行时结果加不落库全文字段**`facade/MuseAiRuntimeClient.java``RuntimeResult`L70-78增加**仅内存**字段(如 `fullOutput`中文注释写明「provider 完整正文,仅进程内传递,永不持久化到任何列/表;落库只用 outputSummary 摘要」。
2. **两个 real client 成功分支捕获完整正文**
- `RealNewApiMuseAiRuntimeClient.success()`L163-192`shortOutputSummary`L172**之前**保留完整 `content`L171先跑护栏见 3通过后把完整正文塞进 `RuntimeResult.fullOutput`,仍派生 60 字摘要供落库。
- `RealDifyMuseAiRuntimeClient` chat/workflowL227-285`answer`L230/`text`L253同理捕获注意 Dify 完成原因硬编码为常量L239不可用作完整性信号。
3. **护栏 + 合规审上移到完整正文(裁决③)**`MuseAiCandidateReviewService.review`L42-84的输入从摘要改为**完整正文**——完整性校验(长度/结构合理性,判断截断,不信 provider finishReason+ 凭据痕迹合规扫描均作用于完整正文;疑似截断/不合规 → 返回 `RuntimeFailure`(可重试),**不产出候选**。`MuseAiRuntimeProjectionService`L250-283审通过后 `content_snapshot.content` 仍写摘要L328 不变、三审字段照旧L361-377
4. **落库口径不变**:候选表恒为摘要 + 三审;`fullOutput` 不进 `outputSummary` Map、不进任何列。
**验收**
- ai 模块单测:两 adapter 返回 `fullOutput`(日志脱敏,只打长度/hash护栏对完整正文——截断样本→failure/可重试、正常→pass合规扫描命中凭据痕迹→failure。
- 静态/代码审查断言:`fullOutput` 无任何持久化路径(不被 mapper/DO 引用)。
- `run-p1r-verification.sh local` 全绿。
**回滚**`fullOutput` 字段与护栏输入改动 revert 即回;无 DDL、无契约变更。
---
## S7b 瞬态载体 + SSE 送达(裁决①:独立加密缓存表)
| | |
|---|---|
| 前置 | S7a |
| 触及 | 新增瞬态全文表DDL、executor、`MuseAiRuntimeProjectionService`(事件写入)、`MuseAiTaskStreamServiceImpl`(读侧 chunk、采纳/放弃 ownerpurge |
**动作**
1. **瞬态全文存储(仿 `MuseAiRuntimePayloadStore`**:新增 `sql/muse/V36__ai_generated_fulltext_transient.sql` 建独立表key by taskId、加密载荷列、`expires_at` TTL、租户列。**DDL 版本协调S6 核验确认)**:当前最大 V34S6 的 `runtime_batch_id` 迁移占 V35本瞬态表占 V36若实际提交顺序不同按提交时最大版本+1 顺延、勿撞号。DAO/Store 对齐 `MuseAiRuntimePayloadStore`L24-46的加密 + 短 TTL 语义。**表注释写明「瞬态出向完整正文TTL + 决定即清,非系统记录,永不作为正文来源落 Canonical」**。
2. **executor 写入 + 终态 purge**`MuseAiRuntimeJobExecutor.execute`L75-95在拿到 `RuntimeResult` 后、投影前,把 `fullOutput` 写入瞬态存储;沿用现有终态 `finally`L62-64 `runtimePayloadStore.remove` 同处)追加瞬态全文的兜底清理(异常/超时也不残留)。
3. **事件表发 chunk 引用行**:放宽 `MuseAiRuntimeProjectionService.appendTaskEvent``isValidTerminalEvent`L319-322以允许写**一个非终态 chunk 事件**——`sequence_no` 排在 done 之前,`payload_summary` 只放**指向瞬态存储的引用**(如 `{contentRef, sequenceNo}`**不含正文**。保持每任务至多一条终态的部分唯一索引不变chunk 非终态,不受该约束)。
4. **SSE 读侧回取**`MuseAiTaskStreamServiceImpl.chunkData`L279-285遇到带 `contentRef` 的 chunk 行时回瞬态存储取完整正文填 `content`;缓存缺失(已 purge/过期)→ `content=""`(用户已决定,无害)。回放/轮询/重连语义不变(重放 chunk 引用行→再回取TTL 内可续)。
5. **决定即清**采纳Content `mergeBlockSuggestion` owner与放弃reject owner成功路径 purge 该 taskId 的瞬态全文。
**验收**
- real-PG IT新增仿 `P1rContentMergeGeneratedSuggestionIT` 模式):真生成后 SSE 重放的 chunk 事件 `content` = 完整正文(来自瞬态存储,长度 > 摘要阈值);**候选表 `content_snapshot.content` 仍是摘要、完整正文不在任何长期列**(主权不变式断言);采纳/放弃后瞬态表该行已删断线重连seq 0 全量重放仍能取到完整正文TTL 内)。
- 契约结构门绿(`chunk.data.content` 为已声明字段,非破坏)。
- `local` + `real-pg` 全绿。
**回滚**:以开关门控 chunk 引用行发射 + 瞬态写入;关闭即回退「仅 done」旧行为V35 迁移向前兼容(表未用即空)。
---
## S7c 前端接成采纳基准 + 所见即所写(裁决②)
| | |
|---|---|
| 前置 | S7b |
| 触及 | `muse-studio``AIPanel.tsx``useAcceptSuggestion.ts``sse.ts`(健壮性)、`CandidatePanel.tsx`(如需) |
**动作**
1. **显示/交接零改动确认**`AIPanel.tsx``streamContent`L48/56已接线 `chunk.data.content`L130-132并实时渲染L189`handleStreamDone`L63-89优先流正文L71-75→ 完整正文经既有值拷贝链落到 `CandidatePanel` 可编辑 `reviewedText`L97-103。本步只需回归验证该链在真 chunk 下贯通。
2. **采纳恒回传完整正文(裁决②)**`useAcceptSuggestion.ts``shouldMergeAfterEdit`L39-51改为**恒带 `finalContent`** = 用户审定的完整正文(原样采纳也带);即采纳恒以「用户回传最终正文」语义写 Canonical。**须先核对后端 `suggestion-merges` 合同**:确认 `accept_as_is + finalContent` 或统一走 `modify_then_merge` 的后端语义,避免前端恒传但后端忽略。契约文档 `docs/api-contracts/content/*``MergeBlockSuggestionRequest` 同步(如语义调整)。
3. **健壮性:区分「流不完整」与「流为空」**:因完整正文不落长期库,中途断连可能只收到截断的流正文,而 `handleStreamDone` 优先非空 refL71-75会静默采信截断。改为收到 done 但流正文疑似不完整(如与 done 的完整性标记/长度不符)→ 提示重生成,**不静默采纳截断**;确实为空→回源(此时只剩摘要,明确提示而非当正文采纳)。
4. `quality_check` 事件当前被 AIPanel 丢弃(次要),本步不强制接入。
**验收**
- `tsc -b``vitest run``vite build` 绿;`AIPanel.contract.test.tsx` 扩展「真 chunk→完整正文→恒回传 finalContent」断言。
- MSW-off Playwright `ai-generation.spec` 扩展:渲染正文长度 > 阈值且与 provider 输出一致(非 60/80 字摘要);采纳后 DB 反查 Canonical 落**完整正文**(长度一致)、归因 ai_suggestion、revision 递增。
- 隔离/回归:创作主线其余 spec 无回归。
**回滚**revert 前端提交;后端不受影响。
---
## S7d 生成 provider 切 Dify依赖 Muse 专属 Dify workspace
| | |
|---|---|
| 前置 | S7afullOutput 透出S7b/c 不阻塞;**依赖 Dify workspace 就绪** |
| 触及 | 在用系统 Agent 版本 config、单人配置、`RoutingMuseAiRuntimeClient` fail-closed |
**动作**
1. **provider 切换**:在用系统 Agent 版本 config 设 `runtimeProvider=dify` + `providerRef.dify`app=「muse-写作透传」credentialRef→S1 凭据,须落 Muse 专属 workspace`muse.ai.dify.enabled=true` 进单人配置;`non-stream-read-timeout-seconds` 对齐 ≥180s 总预算(现 Dify 90/180
2. **完整性信号**Dify 完成原因硬编码常量(`RealDifyMuseAiRuntimeClient` L239不可信 → S7a 的完整正文护栏是唯一截断信号,补 Dify 形态断言。
3. **fail-closed 反向**`RoutingMuseAiRuntimeClient`L18-53现有 requiresSourceRefs fail-closed L37-40补「dify 未配/凭据错 → 拒绝、不回退 New-API」路径与断言。
4. **New-API 保留**:本步不摘 New-API 配置S8 验收后才摘)。
**验收**
- live IT新增/扩展 `P1rDify*`opt-inDify 生成 → Shadow 候选(三审齐、审的是完整正文)→ 完整正文经 SSE 可见 → 回传采纳写 Canonicalfail-closed 反向dify 凭据错→拒绝,无 New-API 回退)。
- `local` + `real-pg` 全绿MSW-off `ai-generation.spec` 真 Dify 复跑绿。
**回滚**:在用 Agent `runtimeProvider` 拨回 `new-api` 即回(兼容逻辑未动);配置 revert 独立。
---
## 2. 全局完成判据
- review v0.2 §9 验收全部逐条留证:**主权不变式**(候选/正文表无完整正文、瞬态表决定即清)、**可见**(前端渲染完整正文)、**所见即所写**Canonical=回传完整正文)、**护栏**(作用于完整正文)、**Dify 形态 + fail-closed**、**local+real-PG 回归**。
- 里程碑M1=S7a捕获+护栏,无 Dify 依赖)/ M2=S7b+S7c全文经 SSE 可见、所见即所写采纳,仍 New-API/ M3=S7d切 Dify。每 M 点回写总账可暂停评估。
## 3. 关键风险(承 review §11
瞬态正文暴露TTL+决定即清+加密+脱敏日志、断连丢正文致静默采纳截断S7c 区分不完整/为空)、单 chunk 体积必要时分片、Dify 完成原因不可信(护栏为唯一信号)。