Files
bilin/docs/superpowers/specs/2026-07-08-bilin-p4-interactive-design.md
T
bing 5cfc7c10b9 docs: add P4 interactive writing design spec for v0.4.0
Define state machine, schema v4, IPC, prompts, UI, and LM Studio test timeouts for interactive writing mode only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 16:48:26 +08:00

16 KiB
Raw Blame History

笔临 P4 交互式写作设计规格

版本1.0
日期2026-07-08
范围:P4 严格(选项 A)— 仅实装「交互式写作」全流程(§6.5.1)
前置P2.1 + P3 已交付(v0.3.08ce35a1
参考design/readme.md v1.1 §6.5.1、design/ui_pc.html AI 面板交互写作原型

目标版本v0.4.0


1. 背景与目标

1.1 阶段定位

v0.3.0 已交付 AI 对话模式;「交互 / 自动 / 向导」写作 Tab 仍为占位。本阶段实装 交互式写作:以场景为单位的剧情走向建议 → 场景生成 → 命名暂停 → 微调确认 → 循环 → 成章落盘,全流程状态持久化并可恢复。

1.2 已确认决策(Brainstorming 2026-07-08

维度 决策
范围 P4 严格:仅 §6.5.1 交互式写作;自动 / 向导仍占位
实现方案 方案 1:专用状态机 + 独立 DB 表 + 逐步 IPC
成章落盘 当前活跃卷 新建章节origin=interactive,合并全部场景后自动打开编辑器
命名暂停 完整实装:生成中暂停,5 选项 + 自定义输入,选定后续写
状态持久化 schema v4interactive_flows / interactive_scenes,重启可恢复
启动门槛 硬拦截:无章节正文且无大纲/设定时 Tab 禁用
AI 后端 LM Studio(延续 P3),http://127.0.0.1:1234gemma-4-e4b-it
测试原则 禁止 Mock;集成 / E2E 打真实 LM Studio
测试超时 本地模型慢,Vitest AI 用例 180240sPlaywright 全局 180s,交互 E2E 300s
工程 test-results/ 加入 .gitignore

1.3 验收标准

用户能完成:

  1. 在有足够上下文的书籍中进入「交互写作」Tab(门槛不满足时 Tab 禁用并提示)
  2. 确认 / 编辑 AI 上下文后,收到 3 条剧情走向建议(每条 ≥50 字说明)
  3. 选择走向(可附补充指令)后,流式生成一个场景(600–1500 字)
  4. 生成中若 AI 要求命名,流程暂停并展示 5 个命名选项;选定后自动续写完成场景
  5. 对场景输入微调指令,AI 针对当前场景修订(不重写全章)
  6. 满意后进入下一场景循环,或点击「结束本章」
  7. 结束时在当前活跃卷创建新章节,内容为全部场景合并 HTML,标记为交互初稿,编辑器自动打开
  8. 关闭应用并重启后,未完成的交互流程恢复到上次步骤
  9. 切换回「对话」模式时,同会话聊天历史仍可见;再次进入交互模式时流程按规格重新初始化

1.4 本规格不实现(明确排除)

归属
§6.5.2 自动写作 P4.1+
§6.5.3 向导式写作 P4.1+
§6.3 文风学习与风格滑杆 P4.1+
§6.6 大纲生成章节骨架 P4.1+
§6.7 知识抽取与审核工作流 P4.1+
§6.4 Skill / MCP P4+
命名结果自动写入设定库 本阶段仅写入正文
导入导出、驾驶舱等 P5+

2. 架构

2.1 进程模型

┌──────────────────────────────────────────────────────────────┐
│ 主进程                                                        │
│  · schema v4interactive_flows / interactive_scenes         │
│  · chapters.origin 列(manual | interactive                  │
│  · InteractiveWritingService(步骤机 + 持久化)                 │
│  · InteractivePromptBuilder(各步 prompt / JSON 约束)        │
│  · AiClientService(复用:JSON 步非流式,场景步流式 SSE)        │
│  · AiContextBuilder(复用)                                    │
│  · IPCinteractive:* + interactive:streamChunk 事件           │
└────────────────────────────┬─────────────────────────────────┘
                             │ contextBridge
┌────────────────────────────▼─────────────────────────────────┐
│ 渲染进程                                                       │
│  · AiPanelwritingMode=interactive 时挂载 InteractivePanel    │
│  · InteractivePanel:步骤 UI(走向卡片、命名暂停、场景预览)     │
│  · useInteractiveStoreIPC + 流式缓冲                          │
│  · ContextEditorModal(复用,上下文确认步)                     │
└──────────────────────────────────────────────────────────────┘

约束(延续 P0P3):

  • 渲染进程禁止直接 fetch AI API
  • 流式通过 webContents.send 推送,不阻塞 IPC invoke
  • 交互流程绑定 ai_session_idai_messages 仍记录关键轮次作审计

2.2 步骤机

Step 名称 AI 持久化字段
context_confirm 上下文确认 context_json
plot_suggest 剧情走向 JSON ×3
plot_select 用户选择 plot_selection_json
scene_generate 场景生成 流式 可能写入 naming_pending_json
naming_pause 命名暂停 流式续写 naming_pending_json 清空后
scene_review 场景确认/微调 流式(可选)
loop 下一场景 回到 plot_suggest interactive_scenes 追加
completed 成章完成 status=completed

切换到交互模式:调用 interactive:start,创建或重置 flowstatus=in_progress)。切换离开交互模式不删除 flow;再次 start 时重新初始化。

2.3 Schema v4(每本书 .sqlite

-- schema-v4.sql
ALTER TABLE chapters ADD COLUMN origin TEXT NOT NULL DEFAULT 'manual';

CREATE TABLE IF NOT EXISTS interactive_flows (
    id                   TEXT PRIMARY KEY,
    session_id           TEXT NOT NULL,
    step                 TEXT NOT NULL,
    status               TEXT NOT NULL DEFAULT 'in_progress',
    context_json         TEXT NOT NULL DEFAULT '{}',
    plot_selection_json  TEXT,
    naming_pending_json  TEXT,
    created_at           TEXT NOT NULL DEFAULT (datetime('now')),
    updated_at           TEXT NOT NULL DEFAULT (datetime('now')),
    FOREIGN KEY (session_id) REFERENCES ai_sessions(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS interactive_scenes (
    id           TEXT PRIMARY KEY,
    flow_id      TEXT NOT NULL,
    sort_order   INTEGER NOT NULL DEFAULT 0,
    content_html TEXT NOT NULL DEFAULT '',
    plot_label   TEXT NOT NULL DEFAULT '',
    refined_count INTEGER NOT NULL DEFAULT 0,
    created_at   TEXT NOT NULL DEFAULT (datetime('now')),
    FOREIGN KEY (flow_id) REFERENCES interactive_flows(id) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_interactive_flows_session ON interactive_flows(session_id);
CREATE INDEX IF NOT EXISTS idx_interactive_scenes_flow ON interactive_scenes(flow_id, sort_order);

migrate.tsversion 3 → 4 应用上述 DDL。


3. 共享类型

export type InteractiveStep =
  | 'context_confirm'
  | 'plot_suggest'
  | 'plot_select'
  | 'scene_generate'
  | 'naming_pause'
  | 'scene_review'
  | 'completed'

export type InteractiveFlowStatus = 'in_progress' | 'completed' | 'abandoned'
export type ChapterOrigin = 'manual' | 'interactive'

export interface PlotOption {
  id: 'A' | 'B' | 'C'
  label: string
  summary: string
}

export interface NamingPause {
  elementType: string
  context: string
  options: string[] // 长度 5
}

export interface PlotSelection {
  optionIds: Array<'A' | 'B' | 'C'>
  userNote?: string
}

export interface InteractiveScene {
  id: string
  sortOrder: number
  contentHtml: string
  plotLabel: string
  refinedCount: number
}

export interface InteractiveFlow {
  id: string
  sessionId: string
  step: InteractiveStep
  status: InteractiveFlowStatus
  scenes: InteractiveScene[]
  namingPending?: NamingPause | null
  contextSummary?: string
}

export interface InteractiveGateResult {
  eligible: boolean
  reason?: 'no_chapter' | 'no_outline_or_setting'
}

export interface InteractiveStreamChunkEvent {
  flowId: string
  phase: 'scene' | 'refine' | 'naming_resume'
  delta: string
  done: boolean
}

Chapter 接口增加可选 origin?: ChapterOrigin(读取时填充,默认 manual)。


4. IPC 接口

4.1 通道列表

通道 说明
interactive:checkGate 门槛检查
interactive:getFlow 按 session 恢复 flow
interactive:start 初始化 / 重置流程
interactive:confirmContext 保存上下文 binding,进入 plot_suggest
interactive:suggestPlots AI 返回 3 条走向
interactive:selectPlot 记录用户选择
interactive:generateScene 开始场景流式生成
interactive:resolveNaming 命名选择后续写
interactive:refineScene 场景微调(流式)
interactive:acceptScene 确认场景,写入 scenes 表
interactive:finishChapter 合并场景 → 新建章节
interactive:abort 中止当前 AI 请求

事件:interactive:streamChunkInteractiveStreamChunkEvent

4.2 门槛逻辑(checkGate

eligible = true 当满足任一:

  • 存在至少 1 章,stripHtml(content).trim().length > 0
  • outline_items 表非空
  • settings 表非空

否则 eligible = falsereason 区分无章节正文 vs 无大纲设定。

4.3 成章逻辑(finishChapter

  1. 校验 activeVolumeId 存在(来自调用方或书籍默认第一卷)
  2. sort_order 合并 interactive_scenes.content_html,段落间插入空行
  3. chapter.create(volumeId, title ?? '交互初稿')origin='interactive'status='draft'
  4. flow.status = 'completed'step = 'completed'
  5. 创建 type='ai' 快照(标签「交互写作成章」)
  6. 返回 { chapterId }

5. Prompt 策略

InteractivePromptBuilder 为各步构建 ChatMessage[]

5.1 走向建议(suggestPlots

  • System:你是小说创作助手;基于上下文给出 3 个互斥剧情走向;仅返回 JSON
  • 格式:{ "plots": [{ "id": "A", "label": "...", "summary": "..." }] }
  • 约束:恰好 3 条;summary ≥50 汉字
  • 非流式;解析失败重试 1 次(追加「仅返回 JSON,不要 markdown」)

5.2 场景生成(generateScene

  • 注入:上下文块 + 已确认场景摘要 + 选定走向 + 用户补充
  • 若需新命名:先返回单行 JSON
    {"type":"naming_required","elementType":"角色","context":"...","options":["名1",...,"名5"]}
  • 否则流式输出场景 HTML<p> 段落,6001500 字)
  • 交互步 max_tokens 覆盖为 4096(不改全局 aiConfig.maxTokens

5.3 命名续写(resolveNaming

  • User:用户选定名称 {chosen},请继续完成当前场景
  • 流式输出剩余正文

5.4 场景微调(refineScene

  • User:在保持情节连贯前提下,按指令修改当前场景:{instruction}
  • 流式输出完整修订后场景(覆盖当前草稿)

6. 错误处理

场景 行为
AI 超时 IpcError recoverabletoast「AI 响应超时」;留在当前步,可重试
JSON 解析失败 自动重试 1 次;仍失败则 toast + 留在 plot_suggest
流式中断 保留已收 delta;提供「重新生成」
无活跃卷 finishChapter 返回错误码,提示创建卷
切换书籍 DB 状态保留;getFlow 恢复
云端后端 + 离线 与 P3 一致禁用输入;LM Studio 不受影响
interactive:abort 中止 fetch;不清除 flow 状态

7. UI 规格

7.1 入口

  • AiPanel 模式 Tabinteractive 可点击;auto / wizardfeature.comingSoon
  • 挂载时 checkGate!eligible → Tab disabled + title 提示
  • writingMode === 'interactive' 时主区渲染 InteractivePanel

7.2 InteractivePanel 布局

  1. 步骤指示器 — 上下文 → 走向 → 场景 → 确认
  2. 主内容(按 flow.step
    • context_confirm:摘要 +「编辑上下文」+「开始交互」
    • plot_suggest / plot_selectPlotOptionCard ×3,多选 + 补充输入
    • scene_generate / naming_pause:流式预览;命名时覆盖为选项列表 + 自定义
    • scene_review:全文预览 + 微调输入 +「下一场景」/「结束本章」
  3. 底栏 — 主操作按钮;与 ai-offline-banner 联动

样式参考 design/ui_pc.html.ai-mode-tab.plot-opt.opt-label

7.3 成章后导航

finishChapter 成功 → openBooksetSelectedChapter(chapterId)setView('editor') → toast interactive.chapterCreated


8. 工程配套

8.1 .gitignore

test-results/

8.2 测试超时配置

位置
playwright.config.ts timeout 180_000
E2E 交互用例 test.setTimeout 300_000
E2E 助手回复 expect 120_000
Vitest IT-INT-01/02/03 180_000 / 240_000 / 240_000
现有 IT-AI-01 保持 90_000 或升至 180_000
现有 IT-NAME-01 保持 180_000

8.3 LM Studio 前置

与 P3 相同:BILIN_AI_BASE_URL / BILIN_AI_MODEL 环境变量由主进程 global-settings 读取;渲染进程不使用 process.env


9. 测试要求

9.1 单元 / 集成(Vitest,禁止 Mock

用例 ID 场景 超时
IT-INT-01 suggestPlots 返回 3 条合法 JSON 180s
IT-INT-02 generateScene 流式正文非空 240s
IT-INT-03 naming JSON → resolveNaming 后续有内容 240s
IT-INT-04 acceptScene ×2 + finishChapter 创建 origin=interactive 章节 60s
IT-INT-05 checkGate 门槛 true/false 分支 5s
IT-MIG-04 schema v3→v4 迁移 5s

9.2 E2EPlaywright + Electron

用例 ID 场景 超时
E2E-INT-01 选走向 → 生成 → 接受 → 结束本章 → 编辑器有内容 300s
E2E-INT-02 空书交互 Tab 禁用 30s
E2E-INT-03 重启后恢复 flow 步骤 300s

文件:e2e/interactive-writing.spec.ts


10. 文件清单(实现参考)

文件 职责
src/main/db/schema-v4.sql v4 DDL
src/main/db/migrate.ts v3→v4
src/main/db/repositories/interactive.repo.ts flow / scene CRUD
src/main/services/interactive-writing.service.ts 步骤机
src/main/services/interactive-prompt-builder.service.ts Prompt
src/main/ipc/handlers/interactive.handler.ts IPC
src/shared/ipc-channels.ts 新通道
src/shared/types.ts 交互类型 + ChapterOrigin
src/preload/index.ts API + 事件
src/renderer/components/ai/InteractivePanel.tsx 主 UI
src/renderer/components/ai/PlotOptionCard.tsx 走向卡片
src/renderer/stores/useInteractiveStore.ts 状态
e2e/interactive-writing.spec.ts E2E
tests/main/interactive-*.test.ts 集成测试

11. 规格自检

检查项 结果
Placeholder 扫描 自动/向导明确排除至 P4.1+;无 TBD
内部一致性 步骤机、IPC、DB、UI 步骤名称一致
范围 单阶段可交付 v0.4.0
歧义 成章=新建章节+interactive origin;门槛=硬拦截

本规格为笔临 P4 子项目。自动写作、知识抽取等各自在后续 spec 中增量修订。