docs: add P4.1 auto and wizard writing design spec for v0.5.0

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 20:43:53 +08:00
parent a8e0ba9ac9
commit ab33213f99
@@ -0,0 +1,482 @@
# 笔临 P4.1 自动写作 + 向导式写作设计规格
**版本**1.0
**日期**2026-07-09
**范围**P4.1 — 实装 §6.5.2 自动写作 + §6.5.3 向导式写作(统一写作流引擎)
**前置**:P4 交互式写作已交付(v0.4.0,`a8e0ba9`
**参考**`design/readme.md` v1.1 §6.5.2–§6.5.3、`design/ui_pc.html` AI 面板模式 Tab
**目标版本**v0.5.0
---
## 1. 背景与目标
### 1.1 阶段定位
v0.4.0 已交付交互式写作(§6.5.1);「自动写作」「向导」Tab 仍为 `feature.comingSoon` 占位。本阶段在同一 AI 会话体系下实装:
- **自动写作**:用户设定章节目标与字数范围 → AI 规划场景顺序 → 连续流式生成 → 达标或手动停止 → 成章落盘
- **向导式写作**:5 步引导收集参数 → AI 策略预览 → 自动生成 → **handoff 至交互面板**做场景微调
两模式共享 P4 的场景存储、命名暂停、流式推送与成章逻辑,并通过 **handoff** 与交互写作衔接。
### 1.2 已确认决策(Brainstorming 2026-07-09
| 维度 | 决策 |
|------|------|
| 范围 | **P4.1**:§6.5.2 + §6.5.3 同时交付;§6.3 / §6.6 / §6.7 仍排除 |
| 架构 | **方案 1**:统一写作流引擎,扩展 P4 表与 Service,不重写 |
| 向导第 4 步 | **选项 A**:复用 `ContextEditorModal` 确认上下文(含大纲/设定中的伏笔线索),不引入知识库表 |
| 向导风格 | 节奏三选一(舒缓/紧张/混合)+ 可选自由文本 `styleNote`**不做**文风滑杆 |
| 向导生成后 | 自动切入 **InteractivePanel**toast「已进入交互微调」) |
| 自动暂停 | 可「继续自动」或「转入交互微调」(handoff,保留 scenes |
| 成章落盘 | 活跃卷新建章节;`origin=auto``origin=wizard`;合并全部场景 HTML |
| AI 快照 | 成章时创建 `type='ai'` 快照(标签「自动写作成章」/「向导写作成章」;**顺带补齐** P4 interactive 成章快照) |
| 状态持久化 | **schema v5**`interactive_flows``flow_mode` / `mode_config_json``interactive_scenes` 不变 |
| 启动门槛 | **硬拦截**:复用 P4 `checkInteractiveGate` 逻辑(无正文且无大纲/设定时 Tab 禁用) |
| 同会话 flow | 每 `session_id` 仅一个 `status=in_progress` flow;按 `flow_mode` 区分模式 |
| AI 后端 | **LM Studio**(延续 P3/P4),`http://127.0.0.1:1234``gemma-4-e4b-it` |
| 测试原则 | **禁止 Mock**;集成 / E2E 打真实 LM Studio |
| 测试超时 | Vitest AI 用例 **240s**Playwright 全局 **180s**auto/wizard E2E **300s** |
### 1.3 验收标准
用户能完成:
1. 在有足够上下文的书籍中进入「自动写作」或「向导」Tab(门槛不满足时 Tab 禁用并 tooltip 提示)
2. **自动写作**:设定本章目标(大纲选取和/或手写)+ 目标字数范围 → AI 返回场景规划 → 连续流式生成场景(支持命名暂停)→ 字数达范围或点击「结束本章」→ 新建章节 `origin=auto`,编辑器自动打开
3. **自动写作干预**:生成中点击「暂停」→ flow 进入 `paused`;可「继续自动」或「转入交互微调」→ 交互面板可见已生成场景并可微调/续写
4. **向导写作**:依次完成 5 步(目标 → 节奏/风格 → 视角人物 → 上下文确认 → 策略预览)→ 确认后开始生成 → 生成完成后自动显示交互面板,可微调场景
5. 向导成章后章节 `origin=wizard`;自动成章 `origin=auto`
6. 关闭应用并重启后,未完成的 auto/wizard flow 恢复到上次步骤
7. 切换回「对话」模式时同会话聊天历史仍可见;再次进入 auto/wizard/interactive 时 **恢复已有 flow**(不无条件 reset
8. 所有成章路径均创建 `type='ai'` 快照
### 1.4 本规格不实现(明确排除)
| 项 | 归属 |
|----|------|
| §6.3 文风学习与风格滑杆 | P4.2+ |
| §6.6 大纲生成章节骨架 | P4.2+(可独立 spec |
| §6.7 知识自动抽取与审核工作流 | P4.2+ |
| §6.4 Skill / MCP | P4+ |
| 知识库伏笔追踪 UI | P4.2+ / P5 |
| 导入导出、驾驶舱等 | P5+ |
| 命名结果自动写入设定库 | 本阶段仅写入正文 |
---
## 2. 架构
### 2.1 进程模型
```
┌──────────────────────────────────────────────────────────────────┐
│ 主进程 │
│ · schema v5interactive_flows.flow_mode / mode_config_json │
│ · chapters.originmanual | interactive | auto | wizard │
│ · SceneGenerationCore(从 P4 抽取:流式、命名暂停、HTML 规范化) │
│ · AutoWritingService(目标→规划→连续生成→暂停/成章/handoff) │
│ · WizardWritingService5 步→策略预览→委托 Auto→handoff
│ · InteractiveWritingServiceP4 不变;handoff 目标) │
│ · checkWritingGate(复用 interactive-gate 逻辑,三模式共用) │
│ · AiClientService / AiContextBuilder(复用) │
│ · IPCauto:* / wizard:* + interactive:streamChunk(增 flowMode
└────────────────────────────┬─────────────────────────────────────┘
│ contextBridge
┌────────────────────────────▼─────────────────────────────────────┐
│ 渲染进程 │
│ · AiPanelwritingMode=auto|wizard 时挂载 AutoPanel/WizardPanel │
│ · handoff 后 writingMode 保持或切 interactive,显示 InteractivePanel│
│ · useAutoStore / useWizardStore + useInteractiveStore │
│ · ContextEditorModal(向导步骤 4 + 自动可选编辑上下文) │
└────────────────────────────────────────────────────────────────────┘
```
**约束**(延续 P0P4):
- 渲染进程禁止直接 `fetch` AI API
- 流式通过 `webContents.send` 推送,不阻塞 IPC invoke
- 写作 flow 绑定 `ai_session_id`;关键轮次可写入 `ai_messages` 作审计(可选,非阻塞)
### 2.2 Handoff 规则
`auto``wizard` 转入交互微调时:
1. `interactive_scenes` 列表 **不变**
2. `flow_mode` 更新为 `'interactive'`
3. `step` 设为:
-`scene_draft_html` 非空 → `'scene_review'`
- 否则若已有 scenes → `'plot_suggest'`(进入下一场景循环)
- 否则 → `'context_confirm'`
4. `mode_config_json` 保留(只读参考),不删除
5. 渲染进程 `setWritingMode('interactive')` + toast
自动模式 **暂停**(非 handoff)时:`step='paused'``flow_mode` 仍为 `'auto'`
### 2.3 步骤机
#### 2.3.1 interactiveP4 不变)
| Step | 名称 |
|------|------|
| `context_confirm` | 上下文确认 |
| `plot_suggest` | 剧情走向 |
| `plot_select` | 用户选择 |
| `scene_generate` | 场景生成 |
| `naming_pause` | 命名暂停 |
| `scene_review` | 场景确认/微调 |
| `completed` | 成章完成 |
#### 2.3.2 auto
| Step | 名称 | AI |
|------|------|-----|
| `goal_setup` | 目标/字数/大纲 | 无 |
| `scene_plan` | 等待用户触发规划 | JSON 场景列表 |
| `auto_generate` | 连续生成(含 naming_pause 子状态) | 流式 |
| `paused` | 用户暂停 | 无 |
| `completed` | 成章完成 | 无 |
生成循环:按 `scenePlan` 顺序逐场景生成;每场景完成后追加 `interactive_scenes`;累计字数(stripHtml 计)≥ `wordMin` 且 AI 判定目标达成或用户点击「结束本章」时停止。
#### 2.3.3 wizard
| Step | 名称 | AI |
|------|------|-----|
| `wizard_goal` | 步骤 1:本章目标 | 无 |
| `wizard_rhythm` | 步骤 2:节奏 + styleNote | 无 |
| `wizard_pov` | 步骤 3:视角人物(设定 character | 无 |
| `wizard_context` | 步骤 4ContextEditorModal | 无 |
| `wizard_preview` | 步骤 5:策略预览 | 非流式摘要 |
| `wizard_generating` | 委托 Auto 引擎生成 | 流式 |
| → handoff | 见 §2.2 | — |
---
## 3. Schema v5
### 3.1 DDL(每本书 `.sqlite`
```sql
-- schema-v5.sql
ALTER TABLE interactive_flows ADD COLUMN flow_mode TEXT NOT NULL DEFAULT 'interactive';
ALTER TABLE interactive_flows ADD COLUMN mode_config_json TEXT NOT NULL DEFAULT '{}';
-- chapters.origin 在应用层扩展为 manual | interactive | auto | wizard
-- v4 已有 origin 列,v5 迁移不 ALTER(字符串枚举扩展)
```
`migrate.ts``CURRENT_VERSION=5`v4→v5 应用上述 ALTER。
### 3.2 mode_config_json
```typescript
export type WritingFlowMode = 'interactive' | 'auto' | 'wizard'
export type AutoRhythm = 'relaxed' | 'tense' | 'mixed'
export interface ScenePlanItem {
label: string
summary: string
}
export interface AutoWritingConfig {
chapterGoal: string
outlineItemIds?: string[]
wordMin: number // 默认 2000
wordMax: number // 默认 4000
scenePlan?: ScenePlanItem[]
currentPlanIndex?: number // auto_generate 进度
}
export interface WizardWritingConfig extends AutoWritingConfig {
rhythm: AutoRhythm
styleNote?: string
povSettingId?: string
strategyPreview?: string // HTML
contextBinding?: AiContextBinding // 步骤 4 确认结果
}
```
### 3.3 共享类型扩展
```typescript
export type ChapterOrigin = 'manual' | 'interactive' | 'auto' | 'wizard'
export type AutoStep =
| 'goal_setup'
| 'scene_plan'
| 'auto_generate'
| 'paused'
| 'completed'
export type WizardStep =
| 'wizard_goal'
| 'wizard_rhythm'
| 'wizard_pov'
| 'wizard_context'
| 'wizard_preview'
| 'wizard_generating'
// InteractiveFlow 扩展:
// flowMode: WritingFlowMode
// modeConfig: AutoWritingConfig | WizardWritingConfig | Record<string, never>
// step: InteractiveStep | AutoStep | WizardStep
```
---
## 4. 服务层
### 4.1 SceneGenerationCore
`InteractiveWritingService` 抽取(或内部组合):
- `generateSceneStream(flowId, planItem, config, onChunk, signal)`
- `resolveNaming(flowId, chosenName, ...)`
- `normalizeSceneHtml(raw: string): string`
- 命名暂停检测:复用 `tryParseNamingFromBuffer`
### 4.2 AutoWritingService
| 方法 | 说明 |
|------|------|
| `start(sessionId)` | 创建 `flow_mode=auto`, `step=goal_setup` |
| `getFlow(sessionId)` | 按 session 查 in_progress |
| `setGoal(flowId, config: Partial<AutoWritingConfig>)` | 写入 mode_config`step=scene_plan` |
| `planScenes(flowId)` | AI JSON → `scenePlan``step=auto_generate` |
| `generateNext(flowId, onChunk, signal)` | 生成当前 plan 项;处理 naming;追加 scene;更新 index |
| `pause(flowId)` | `step=paused`abort 进行中的生成 |
| `resume(flowId)` | `step=auto_generate`,继续下一项 |
| `handoffToInteractive(flowId)` | §2.2 |
| `finishChapter(flowId, volumeId, title?)` | 合并 scenes → 新建章 `origin=auto` + AI 快照 |
| `shouldStop(flowId)` | 字数 ≥ wordMin 且(最后一项 plan 完成 或 AI 返回 stop 标记) |
### 4.3 WizardWritingService
| 方法 | 说明 |
|------|------|
| `start(sessionId)` | `flow_mode=wizard`, `step=wizard_goal` |
| `setGoal` / `setRhythm` / `setPov` | 逐步写入 mode_config,推进 step |
| `confirmContext(flowId, binding)` | 步骤 4`step=wizard_preview` |
| `buildPreview(flowId)` | AI 生成 strategyPreview HTML |
| `startGenerate(flowId, onChunk, signal)` | `step=wizard_generating`;构建 AutoWritingConfig 并调用 AutoWritingService 全量生成 |
| `handoffToInteractive(flowId)` | 生成完成后自动调用 §2.2 |
### 4.4 成章与快照(三模式统一)
`finishChapter` 路径均:
1. 合并 `interactive_scenes.content_html`
2. `chapters.create(..., origin)`
3. `snapshots.create(chapterId, merged, type='ai', label=...)`
4. `flow.status=completed`, `step=completed`
---
## 5. IPC
### 5.1 通道(`ipc-channels.ts` 追加)
```typescript
// auto
AUTO_CHECK_GATE: 'auto:checkGate' // 实际复用 gate service
AUTO_GET_FLOW: 'auto:getFlow'
AUTO_START: 'auto:start'
AUTO_SET_GOAL: 'auto:setGoal'
AUTO_PLAN_SCENES: 'auto:planScenes'
AUTO_GENERATE: 'auto:generate' // 生成下一场景或批量至 pause/stop
AUTO_PAUSE: 'auto:pause'
AUTO_RESUME: 'auto:resume'
AUTO_HANDOFF_INTERACTIVE: 'auto:handoffInteractive'
AUTO_FINISH_CHAPTER: 'auto:finishChapter'
AUTO_GET_SCENE_DRAFT: 'auto:getSceneDraft'
AUTO_ABORT: 'auto:abort'
// wizard
WIZARD_GET_FLOW: 'wizard:getFlow'
WIZARD_START: 'wizard:start'
WIZARD_SET_GOAL: 'wizard:setGoal'
WIZARD_SET_RHYTHM: 'wizard:setRhythm'
WIZARD_SET_POV: 'wizard:setPov'
WIZARD_CONFIRM_CONTEXT: 'wizard:confirmContext'
WIZARD_BUILD_PREVIEW: 'wizard:buildPreview'
WIZARD_START_GENERATE: 'wizard:startGenerate'
WIZARD_HANDOFF_INTERACTIVE: 'wizard:handoffInteractive'
WIZARD_ABORT: 'wizard:abort'
```
流式事件 `interactive:streamChunk` payload 扩展:
```typescript
{ flowId, flowMode: WritingFlowMode, phase: 'scene' | 'naming' | 'refine', delta: string, done: boolean }
```
### 5.2 Preload / electron-api.d.ts
暴露 `window.electronAPI.auto.*``window.electronAPI.wizard.*` 命名空间。
---
## 6. Prompt 设计
### 6.1 场景规划(auto planScenes
输入:章节目标、大纲条目摘要、已有上下文摘要、wordMin/wordMax。
输出 JSON
```json
{
"scenes": [
{ "label": "场景1", "summary": "…≥80字…" },
{ "label": "场景2", "summary": "…" }
],
"estimatedWords": 3200
}
```
约束:3–6 个场景;总预估字数在 [wordMin, wordMax] 内。
### 6.2 自动场景生成
输入:当前 plan item、累计 scenes 摘要、AutoWritingConfig。
输出:HTML 段落(600–1500 字/场景);命名暂停格式同 P4。
### 6.3 向导策略预览(wizard buildPreview
输入:WizardWritingConfig 全量 + AiContextBuilder 摘要。
输出 HTML 一段,模板语义:
> 我将以 **[视角人物]** 视角,用 **[节奏]** 节奏,写一段达成 **[目标]** 的剧情。风格说明:…。重点上下文:…。
---
## 7. 渲染进程 UI
### 7.1 AiPanel
- 移除 `auto` / `wizard``comingSoon`
- `handleModeClick('auto'|'wizard')`checkGate → loadFlow → 无 flow 则 start**不**无条件 reset 已有 flow
- `writingMode === 'auto'``<AutoPanel />`
- `writingMode === 'wizard'``<WizardPanel />`handoff 后 `writingMode='interactive'``<InteractivePanel />`
### 7.2 AutoPanel
| data-testid | 元素 |
|-------------|------|
| `auto-panel` | 根容器 |
| `auto-goal-input` | 本章目标 |
| `auto-outline-picker` | 大纲多选 |
| `auto-word-min` / `auto-word-max` | 字数范围 |
| `auto-plan-scenes` | 触发规划 |
| `auto-start-generate` | 开始/继续生成 |
| `auto-scene-preview` | 流式预览 |
| `auto-pause` | 暂停 |
| `auto-handoff-interactive` | 转入交互 |
| `auto-finish-chapter` | 结束本章 |
步骤条:`目标 → 规划 → 生成 → 完成`
### 7.3 WizardPanel
| data-testid | 元素 |
|-------------|------|
| `wizard-panel` | 根容器 |
| `wizard-step-indicator` | 15 步指示 |
| `wizard-goal-input` | 步骤 1 |
| `wizard-rhythm-*` | 步骤 2 节奏选项 |
| `wizard-style-note` | 步骤 2 风格说明 |
| `wizard-pov-select` | 步骤 3 |
| `wizard-context-edit` | 步骤 4 打开 ContextEditorModal |
| `wizard-preview` | 步骤 5 策略摘要 |
| `wizard-start-generate` | 开始生成 |
生成完成后隐藏 WizardPanel,显示 InteractivePanel(同 session)。
### 7.4 i18n 键(zh-CN / en
`auto.gateBlocked``auto.gateTooltip``auto.step.*``auto.pause``auto.resume``auto.handoffInteractive``auto.finishChapter``auto.chapterCreated`
`wizard.step.*``wizard.rhythm.*``wizard.previewTitle``wizard.startGenerate``wizard.handoffToast`
及步骤条、placeholder、错误 toast 文案。
---
## 8. 错误处理
| 场景 | 行为 |
|------|------|
| planScenes JSON 解析失败 | 重试一次(追加「仅返回 JSON」);仍失败 → toast + 保持 `scene_plan` |
| 生成中用户 pause | AbortController abort`step=paused`;已生成 scenes 保留 |
| 无活跃卷 finishChapter | IPC 错误码;toast「请先创建分卷」 |
| 离线 / 非 lmstudio 且无网络 | 同 P3/P4disabled + offline banner |
| wizard 某步必填缺失 | 下一步按钮 disabled |
| handoff 时无 scenes 且无 draft | 仍 handoff 到 `context_confirm`toast 提示可手动续写 |
---
## 9. 测试
### 9.1 单元 / 集成(Vitest,真实 LM Studio
| ID | 场景 | 超时 |
|----|------|------|
| UT-MIG-05 | v4 DB 迁移到 v5flow_mode 列存在 | — |
| IT-AUTO-01 | planScenes 返回 ≥2 个 scene | 240s |
| IT-AUTO-02 | generateNext 写入非空 scene | 240s |
| IT-AUTO-03 | pause 后 step=pausedresume 继续 | 240s |
| IT-AUTO-04 | handoff 后 flow_mode=interactive | — |
| IT-WIZ-01 | buildPreview 非空 HTML | 240s |
| IT-WIZ-02 | startGenerate 完成后 scenes.length ≥ 1 | 300s |
| IT-FIN-01 | auto finishChapter → origin=auto + ai 快照 | — |
| IT-FIN-02 | interactive finishChapter → ai 快照(P4 补齐) | — |
### 9.2 E2EPlaywright300s
| ID | 场景 |
|----|------|
| E2E-AUTO-01 | 自动:设目标 → 规划 → 生成 → 成章 → 编辑器非空 |
| E2E-AUTO-02 | 自动:暂停 → 转交互 → accept scene 可见 |
| E2E-WIZ-01 | 向导 5 步 → 生成 → InteractivePanel 可见 |
| E2E-AUTO-03 | 空书 auto tab disabled(门槛,无 AI |
---
## 10. 文件清单(实现参考)
| 文件 | 职责 |
|------|------|
| `src/main/db/schema-v5.sql` | v5 DDL |
| `src/main/db/migrate.ts` | v4→v5 |
| `src/main/db/repositories/interactive.repo.ts` | flow_mode / mode_config CRUD |
| `src/main/services/scene-generation-core.service.ts` | 共享生成逻辑 |
| `src/main/services/auto-writing.service.ts` | 自动写作步骤机 |
| `src/main/services/wizard-writing.service.ts` | 向导步骤机 |
| `src/main/services/auto-prompt-builder.service.ts` | plan / scene / preview prompt |
| `src/main/ipc/handlers/auto.handler.ts` | auto IPC |
| `src/main/ipc/handlers/wizard.handler.ts` | wizard IPC |
| `src/renderer/components/ai/AutoPanel.tsx` | 自动 UI |
| `src/renderer/components/ai/WizardPanel.tsx` | 向导 UI |
| `src/renderer/stores/useAutoStore.ts` | auto 状态 |
| `src/renderer/stores/useWizardStore.ts` | wizard 状态 |
| `tests/main/migrate-v5.test.ts` | 迁移 |
| `tests/main/auto-*.test.ts` | 集成 |
| `tests/main/wizard-*.test.ts` | 集成 |
| `e2e/auto-writing.spec.ts` | E2E |
| `e2e/wizard-writing.spec.ts` | E2E |
---
## 11. Spec 自检
| 检查项 | 结果 |
|--------|------|
| Placeholder 扫描 | 无 TBD/TODO;排除项已列 §1.4 |
| 内部一致性 | handoff / 步骤机 / IPC / UI testid 对齐 |
| 范围 | 单阶段可交付 v0.5.0;§6.7 已通过 ContextEditor 降级 |
| 歧义 | 成章 origin 三分;同 session 单 in_progress flow;恢复 flow 不 reset |
---
*本规格为笔临 P4.1 子项目。知识抽取、大纲骨架等各自在后续 spec 中增量修订。*