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

12 KiB
Raw Blame History

S7 生成主链全文通路(分支①)执行计划(执行版)

  • 版本v1.0
  • 日期2026-07-07
  • 承接:评审版 v0.2(现状已三路测绘校正;三个材料级点所有者 2026-07-07 已裁:①独立加密缓存表 ②采纳恒回传完整正文 ③护栏+合规审上移到完整正文)。设计意图/边界/主权红线以 review v0.2 为准,本文只做任务级拆解;两文冲突以 review v0.2 为准并回改本文。
  • 读者:执行 agent每个 S7x 可独立派工)与项目所有者。

0. 执行纪律(每步适用)

  1. 主权红线(最高优先):完整正文只允许存在于「执行线程内存 / 瞬态加密缓存TTL+决定即清) / SSE 传输 / 客户端」;任何一步都不得把完整正文写入 muse_ai_suggestionmuse_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;纯机械(配置值、菜单)可 haikuS7d 的 Dify fail-closed 语义与 live 验收如遇跨模块硬判可升 fable 或主会话终裁。

1. 步骤总图

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-serverMuseAiRuntimeClientRuntimeResultRealNewApiMuseAiRuntimeClientRealDifyMuseAiRuntimeClientMuseAiCandidateReviewServiceMuseAiRuntimeProjectionService

动作

  1. 运行时结果加不落库全文字段facade/MuseAiRuntimeClient.javaRuntimeResultL70-78增加仅内存字段(如 fullOutput中文注释写明「provider 完整正文,仅进程内传递,永不持久化到任何列/表;落库只用 outputSummary 摘要」。
  2. 两个 real client 成功分支捕获完整正文
    • RealNewApiMuseAiRuntimeClient.success()L163-192shortOutputSummaryL172之前保留完整 contentL171先跑护栏见 3通过后把完整正文塞进 RuntimeResult.fullOutput,仍派生 60 字摘要供落库。
    • RealDifyMuseAiRuntimeClient chat/workflowL227-285answerL230/textL253同理捕获注意 Dify 完成原因硬编码为常量L239不可用作完整性信号。
  3. 护栏 + 合规审上移到完整正文(裁决③)MuseAiCandidateReviewService.reviewL42-84的输入从摘要改为完整正文——完整性校验(长度/结构合理性,判断截断,不信 provider finishReason+ 凭据痕迹合规扫描均作用于完整正文;疑似截断/不合规 → 返回 RuntimeFailure(可重试),不产出候选MuseAiRuntimeProjectionServiceL250-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 对齐 MuseAiRuntimePayloadStoreL24-46的加密 + 短 TTL 语义。表注释写明「瞬态出向完整正文TTL + 决定即清,非系统记录,永不作为正文来源落 Canonical」
  2. executor 写入 + 终态 purgeMuseAiRuntimeJobExecutor.executeL75-95在拿到 RuntimeResult 后、投影前,把 fullOutput 写入瞬态存储;沿用现有终态 finallyL62-64 runtimePayloadStore.remove 同处)追加瞬态全文的兜底清理(异常/超时也不残留)。
  3. 事件表发 chunk 引用行:放宽 MuseAiRuntimeProjectionService.appendTaskEventisValidTerminalEventL319-322以允许写一个非终态 chunk 事件——sequence_no 排在 done 之前,payload_summary 只放指向瞬态存储的引用(如 {contentRef, sequenceNo}不含正文。保持每任务至多一条终态的部分唯一索引不变chunk 非终态,不受该约束)。
  4. SSE 读侧回取MuseAiTaskStreamServiceImpl.chunkDataL279-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-studioAIPanel.tsxuseAcceptSuggestion.tssse.ts(健壮性)、CandidatePanel.tsx(如需)

动作

  1. 显示/交接零改动确认AIPanel.tsxstreamContentL48/56已接线 chunk.data.contentL130-132并实时渲染L189handleStreamDoneL63-89优先流正文L71-75→ 完整正文经既有值拷贝链落到 CandidatePanel 可编辑 reviewedTextL97-103。本步只需回归验证该链在真 chunk 下贯通。
  2. 采纳恒回传完整正文(裁决②)useAcceptSuggestion.tsshouldMergeAfterEditL39-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 -bvitest runvite 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.difyapp=「muse-写作透传」credentialRef→S1 凭据,须落 Muse 专属 workspacemuse.ai.dify.enabled=true 进单人配置;non-stream-read-timeout-seconds 对齐 ≥180s 总预算(现 Dify 90/180
  2. 完整性信号Dify 完成原因硬编码常量(RealDifyMuseAiRuntimeClient L239不可信 → S7a 的完整正文护栏是唯一截断信号,补 Dify 形态断言。
  3. fail-closed 反向RoutingMuseAiRuntimeClientL18-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-closedlocal+real-PG 回归
  • 里程碑M1=S7a捕获+护栏,无 Dify 依赖)/ M2=S7b+S7c全文经 SSE 可见、所见即所写采纳,仍 New-API/ M3=S7d切 Dify。每 M 点回写总账可暂停评估。

3. 关键风险(承 review §11

瞬态正文暴露TTL+决定即清+加密+脱敏日志、断连丢正文致静默采纳截断S7c 区分不完整/为空)、单 chunk 体积必要时分片、Dify 完成原因不可信(护栏为唯一信号)。