Co-authored-by: Cursor <cursoragent@cursor.com>
18 KiB
笔临 P5.2 写作日志 + AI 知识抽取 设计规格
版本:1.0
日期:2026-07-07
范围:P5.2 — writing_logs 日更追踪 + 驾驶舱热力图/连更 + AI 章节知识自动抽取与审核增强
前置:P6 AI 知识上下文已交付(v0.7.0,aa2c7df)
参考:design/readme.md v1.1 §6.7 / §8 / §9、docs/superpowers/specs/2026-07-06-bilin-p5-workflow-knowledge-design.md、docs/superpowers/specs/2026-07-07-bilin-p6-ai-context-design.md
目标版本:v0.8.0
1. 背景与目标
1.1 阶段定位
v0.7.0 已交付知识半自动推荐与四种 AI 模式注入,但知识库条目仍依赖手动录入;驾驶舱缺少日更写作数据(writing_logs、热力图、连更),dailyWordGoal 仅有设置项未驱动 UI。本阶段(P5.2)补齐 写作数据闭环 与 AI 知识抽取上游,形成「成章/保存 → 抽取 → 审核 → P6 注入」完整链路。
1.2 已确认决策(Brainstorming 2026-07-07)
| 维度 | 决策 |
|---|---|
| 交付范围 | 核心包:writing_logs + 热力图 + 连更 + AI 抽取;不含番茄钟 / 里程碑徽章 / 预计完本 |
| 抽取触发 | 成章自动(默认开)+ 手动「抽取本章」+ 全局 knowledgeAutoExtract 开关 |
| 统计范围 | 当前书籍:热力图、连更、dailyWordGoal 达标均按本书当日净增 |
| 日更累计 | 保存差分 + 成章补计:CHAPTER_UPDATE 记录净增;finishChapter 补计未入账字数 |
| 重复处理 | AI 合并建议 + 预填更新:suggestedMergeId + proposedPatch;确认后 patch 已有条目或另存为新条目 |
| 架构 | 方案 2:全局 writing_logs + 每书 chapter_writing_snapshots + 独立 KnowledgeExtractionService |
| schema | v7 迁移(每书);全局 writing_logs.sqlite(userData) |
| AI 测试 | 集成/E2E 含抽取时禁止 Mock,走 LM Studio |
1.3 验收标准
用户能完成:
- 编辑章节并保存后,驾驶舱今日字数与 365 天热力图更新;
dailyWordGoal > 0时状态栏进度条反映本书当日进度(绿≥100%,橙≥80%) - 连更天数在驾驶舱展示(仅当
dailyWordGoal > 0);中断后归零 - 交互/自动/向导 成章落盘后,若
knowledgeAutoExtract开启,知识库「待审核」出现本批 AI 抽取项(含置信度) - 在知识库或章节操作区点击 「抽取本章」 手动触发;设置中可关闭自动抽取
- 待审核项含 合并建议 时,打开预填编辑器;确认后更新已有
approved条目,或选择「作为新条目」 - 「全部采纳高置信度」批量通过 ≥ 阈值(默认 0.8)的纯新建条目;含合并建议的条目不自动批量采纳
- 采纳后的条目可被 P6
suggestContext推荐并注入 AI 上下文
1.4 本规格不实现(明确排除)
| 项 | 归属 |
|---|---|
| 番茄钟、7/30/100 天里程碑徽章、达标推送通知 | §8 延后 |
| 预计完本、写作速度仪表盘、时段热力图 | §9 延后 |
| 角色弧线简报、Cytoscape 关系图谱 | P6+ / §20.3 |
| 语义 embedding 检索 | 后续 |
| 注入历史 / 反向链接 | P6.1 |
| 导入导出、平台投稿预设 | P7 |
关系「建议」自动写入 settings 表 |
P6+(本阶段仅 relationship 类型知识条目) |
2. 架构
2.1 进程模型
┌──────────────────────────────────────────────────────────────────┐
│ 主进程 │
│ · WritingLogService(userData/writing_logs.sqlite) │
│ · ChapterWritingTracker(差分 + 成章补计) │
│ · KnowledgeExtractionService(AiClient + 解析 JSON) │
│ · KnowledgeService 扩展(合并 patch、批量高置信度采纳) │
│ · CockpitService 扩展(热力图、连更、今日进度) │
│ · chapter.handler / interactive|auto finishChapter 挂钩 │
└────────────────────────────┬─────────────────────────────────────┘
│ IPC
┌────────────────────────────▼─────────────────────────────────────┐
│ 渲染进程 │
│ · CockpitModal 热力图 + 连更 + 今日进度 │
│ · EditorLayout 状态栏日更进度条 │
│ · KnowledgePanel 抽取批次、合并建议 UI、批量高置信度 │
│ · SettingsPage knowledgeAutoExtract / 置信度阈值 │
│ · 章节列表/编辑器「抽取本章」入口 │
└──────────────────────────────────────────────────────────────────┘
2.2 模块边界
| 模块 | 职责 | 依赖 |
|---|---|---|
WritingLogService |
按 (date, bookId) 读写/累加日字数 |
全局 sqlite |
ChapterWritingTracker |
维护 chapter_writing_snapshots,计算 delta |
每书 DB、WritingLogService |
KnowledgeExtractionService |
构建 prompt、调用 AI、解析、落库 pending | AiClientService, KnowledgeRepository |
KnowledgeService |
合并 patch、批量采纳、拒绝 | 现有 CRUD |
CockpitService |
扩展 WritingStats 聚合 |
WritingLogService, GlobalSettings |
原则:抽取异步不阻塞成章;抽取失败 toast + 可重试;写作追踪同步、轻量。
3. 数据模型
3.1 全局 userData:writing_logs.sqlite
CREATE TABLE writing_logs (
date TEXT NOT NULL, -- YYYY-MM-DD,本地时区
book_id TEXT NOT NULL,
word_count INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (date, book_id)
);
由 WritingLogService 在 userDataDir 管理,与 books_registry.json 同级。
3.2 每书 schema v7
新文件 schema-v7.sql:
CREATE TABLE chapter_writing_snapshots (
chapter_id TEXT PRIMARY KEY,
logged_word_count INTEGER NOT NULL DEFAULT 0,
finish_supplemented INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (chapter_id) REFERENCES chapters(id) ON DELETE CASCADE
);
-- knowledge_entries 扩展列(迁移时 ALTER 或重建后拷贝)
-- extraction_confidence REAL
-- merge_target_id TEXT -- 建议合并的 approved 条目 id
-- extraction_batch_id TEXT -- 同次抽取 UUID,便于 UI 分组
migrate.ts:CURRENT_VERSION = 7;v6→v7 对已有 knowledge_entries 执行 ALTER TABLE ADD COLUMN(三列均可 NULL)。
3.3 类型扩展(src/shared/types.ts)
export interface WritingDayLog {
date: string
bookId: string
wordCount: number
}
export interface WritingStats {
todayWords: number
dailyGoal: number
goalProgress: number // 0–1+,goal=0 时为 0
streakDays: number // goal=0 时为 0
heatmap: WritingDayLog[] // 最近 365 天,含 wordCount=0 的日期
}
export interface ExtractedKnowledgeItem {
type: KnowledgeType
title: string
content: string
importance?: number
confidence: number
foreshadowStatus?: ForeshadowStatus
suggestedMergeId?: string
proposedPatch?: Partial<CreateKnowledgeInput>
}
export interface KnowledgeExtractionResult {
batchId: string
chapterId: string
items: ExtractedKnowledgeItem[]
}
扩展 CockpitSummary:
writingStats?: WritingStats
扩展 KnowledgeEntry:
extractionConfidence?: number
mergeTargetId?: string
extractionBatchId?: string
扩展 GlobalSettings:
knowledgeAutoExtract: boolean // default true
knowledgeExtractConfidenceThreshold: number // default 0.8
4. 日更追踪
4.1 差分算法(ChapterWritingTracker)
recordDelta(bookId, chapterId, newWordCount)(在 CHAPTER_UPDATE 成功后调用):
- 读取
chapter_writing_snapshots.logged_word_count(无则 0) delta = newWordCount - logged- 若
delta !== 0:WritingLogService.addToday(bookId, delta)(当日累计不低于 0) - 更新 snapshot:
logged_word_count = newWordCount
supplementOnFinish(bookId, chapterId, wordCount)(在 finishChapter 创建章节后调用):
- 若 snapshot 不存在,创建
logged=0, finish_supplemented=0 gap = wordCount - snapshot.logged_word_count- 若
gap > 0:addToday(bookId, gap),更新logged = wordCount - 设
finish_supplemented = 1
删除章节时 CASCADE 删除 snapshot;writing_logs 历史不回溯修正(接受已知限制)。
4.2 连更计算
- 仅当
dailyWordGoal > 0时计算 - 从今日向前遍历
writing_logs(本书),连续天数满足word_count >= dailyWordGoal - 今日未达标时 streak 不计入今日(展示为截至昨日的连更)
4.3 时区
使用主进程 Intl 或 toLocaleDateString('sv-SE') 生成本地 YYYY-MM-DD,与作者所在系统时区一致。
5. AI 知识抽取
5.1 触发点
| 触发 | 条件 | 行为 |
|---|---|---|
| 成章落盘 | finishChapter 成功且 knowledgeAutoExtract |
入队 extractForChapter(bookId, chapterId) |
| 手动 | knowledge:extractChapter IPC |
同上 |
| 设置关闭 | knowledgeAutoExtract === false |
仅手动触发 |
抽取在 setImmediate / 微任务队列异步执行,不延长成章 IPC 响应时间。
5.2 Prompt 与解析
输入:章节标题 + 正文纯文本(stripHtml,上限 12000 字,超出截断并注明)+ 本书已有 approved 条目标题列表(上限 30 条,供合并判断)。
输出 JSON 数组(严格解析,失败重试一次「仅返回 JSON」):
[
{
"type": "foreshadow",
"title": "测灵石异常",
"content": "入门考核时石头发热,暗示主角特殊灵根",
"importance": 4,
"confidence": 0.92,
"foreshadowStatus": "buried",
"suggestedMergeId": null,
"proposedPatch": null
},
{
"type": "character",
"title": "林远状态更新",
"content": "通过入门考核,声望上升",
"importance": 3,
"confidence": 0.85,
"suggestedMergeId": "uuid-of-existing-entry",
"proposedPatch": {
"content": "已通过入门考核,同门态度由轻视转为敬畏",
"importance": 4,
"lastMentionChapterId": "<chapterId>"
}
}
]
类型:五种全做 — character / foreshadow / relationship / location / fact。
落库:每条 → knowledge_entries,status=pending,source_chapter_id=chapterId,extraction_confidence,extraction_batch_id,merge_target_id(若有)。
5.3 合并审核 UX(决策 D)
| 场景 | UI |
|---|---|
无 mergeTargetId |
待审核列表常规展示;采纳 → approve |
有 mergeTargetId |
卡片标注「建议更新:{目标标题}」;点击打开 KnowledgeEditorDialog 预填 proposedPatch + 目标条目只读摘要 |
| 确认更新 | knowledge:approveMerge → patch 目标 approved 条目,删除或标记本 pending 为 rejected |
| 作为新条目 | 清除 mergeTargetId,保留 pending,用户编辑后采纳 |
批量「全部采纳高置信度」:仅处理 mergeTargetId IS NULL 且 confidence >= threshold 的 pending 项。
5.4 IPC 新增
| 通道 | 参数 | 返回 |
|---|---|---|
writing:getStats |
{ bookId } |
WritingStats |
knowledge:extractChapter |
{ bookId, chapterId } |
KnowledgeExtractionResult |
knowledge:approveMerge |
{ bookId, pendingId } |
KnowledgeEntry(更新后的目标) |
knowledge:batchApproveHighConfidence |
{ bookId, threshold? } |
number(采纳条数) |
CHAPTER_UPDATE handler 内联调用 ChapterWritingTracker(无需新 IPC)。
6. UI 规格
6.1 驾驶舱(CockpitModal)
在现有四卡片下方新增:
- 今日进度:
todayWords / dailyGoal文字 + 细进度条(data-testid="cockpit-today-progress") - 连更:
🔥 {streakDays} 天(dailyGoal=0时隐藏,data-testid="cockpit-streak") - 热力图:Canvas 52×7 格,365 天;颜色深浅按
word_count分档(0 / 1–999 / 1000–2999 / ≥3000);今天描边高亮(data-testid="cockpit-heatmap")
6.2 状态栏(EditorLayout #editor-statusbar)
当 dailyWordGoal > 0 且当前有打开书籍:
- 显示
今日 1234 / 3000 字+ 迷你进度条 - CSS class:
goal-met(≥100%)、goal-near(≥80%) data-testid="status-daily-goal"
6.3 知识库(KnowledgePanel)
- 待审核 Tab 顶部:本批抽取分组(按
extractionBatchId折叠,显示来源章节) - 条目卡片:置信度徽章、合并建议标签
- 工具栏:「全部采纳高置信度」
data-testid="knowledge-batch-high-confidence" - 合并项:「查看更新建议」
data-testid="knowledge-merge-review-{id}"
6.4 抽取入口
- 章节列表项右键或编辑器菜单:「抽取本章知识」
data-testid="chapter-extract-knowledge" - 知识库工具栏:「从当前章抽取」(无选中章时 disabled)
6.5 设置(SettingsPage)
写作 / AI 分区:
knowledgeAutoExtract开关data-testid="settings-knowledge-auto-extract"knowledgeExtractConfidenceThreshold数字输入(0.5–1.0,步进 0.05)data-testid="settings-extract-threshold"
7. 错误处理
| 场景 | 处理 |
|---|---|
| AI 抽取 JSON 解析失败 | 重试一次;仍失败 toast「抽取失败,请稍后重试」,写 console.error |
| 章节正文为空 | 跳过抽取,toast「章节无内容」 |
suggestedMergeId 指向已删除条目 |
视为无合并建议,按新建处理 |
addToday 扣减后当日 < 0 |
钳制为 0 |
| 成章时抽取进行中 | 允许并行;同章多次抽取生成新 batchId,旧 pending 保留 |
8. 测试
8.1 测试 ID
| ID | 层级 | 场景 | 预期 |
|---|---|---|---|
| UT-LOG-01 | 单元 | recordDelta 保存 +200 字 |
当日 +200 |
| UT-LOG-02 | 单元 | recordDelta 删 100 字 |
当日 -100(不低于 0) |
| UT-LOG-03 | 单元 | supplementOnFinish gap=500 |
当日 +500,finish_supplemented=1 |
| UT-LOG-04 | 单元 | 连更 3 天达标 | streakDays=3 |
| UT-EXT-01 | 单元 | 解析合法抽取 JSON | 返回 N 条 ExtractedKnowledgeItem |
| UT-EXT-02 | 单元 | 含 suggestedMergeId |
merge_target_id 正确写入 |
| IT-LOG-01 | 集成 | CHAPTER_UPDATE 两次 |
writing_logs 累计正确 |
| IT-EXT-01 | 集成 | extractChapter 有正文 |
返回 ≥1 pending |
| IT-EXT-02 | 集成 | approveMerge |
目标 approved 内容更新 |
| IT-EXT-03 | 集成 | 抽取 + P6 suggestContext |
新采纳条目出现在推荐中 |
| E2E-LOG-01 | E2E | 写 100 字保存 | 驾驶舱今日字数更新 |
| E2E-LOG-02 | E2E | 热力图可见 | cockpit-heatmap 存在 |
| E2E-EXT-01 | E2E | 成章后待审核有抽取项 | pending 列表非空 |
| E2E-EXT-02 | E2E | 手动抽取本章 | 待审核增加 |
| E2E-EXT-03 | E2E | 合并建议确认更新 | 目标条目内容变化 |
8.2 超时
- IT-EXT-01 / IT-EXT-02 / IT-EXT-03:240s
- E2E-EXT-01 / E2E-EXT-03:300s
8.3 原则
集成/E2E 含 AI 抽取时 禁止 Mock,使用 LM Studio 默认配置。
9. i18n 键(节选)
"cockpit.todayProgress": "今日 {{current}} / {{goal}} 字",
"cockpit.streak": "连更 {{days}} 天",
"cockpit.heatmap": "写作热力图",
"status.dailyGoal": "今日 {{current}} / {{goal}}",
"knowledge.extractChapter": "抽取本章知识",
"knowledge.extractFromCurrent": "从当前章抽取",
"knowledge.batchHighConfidence": "全部采纳高置信度",
"knowledge.mergeSuggest": "建议更新:{{title}}",
"knowledge.approveMerge": "确认更新",
"knowledge.saveAsNew": "作为新条目",
"knowledge.extracting": "正在抽取知识…",
"knowledge.extractFailed": "知识抽取失败,请稍后重试",
"settings.knowledgeAutoExtract": "成章后自动抽取知识",
"settings.extractThreshold": "高置信度阈值"
10. 文件映射
| 文件 | 职责 |
|---|---|
src/main/db/schema-v7.sql |
snapshots + knowledge 扩展列 |
src/main/db/migrate.ts |
v7 迁移 |
src/main/db/repositories/writing-log.repo.ts |
全局 writing_logs CRUD |
src/main/db/repositories/chapter-writing-snapshot.repo.ts |
每书 snapshot |
src/main/services/writing-log.service.ts |
日更累加、连更、热力图数据 |
src/main/services/chapter-writing-tracker.service.ts |
差分 + 补计 |
src/main/services/knowledge-extraction.service.ts |
AI 抽取 |
src/main/services/knowledge-extraction-prompt.ts |
prompt 构建 + JSON 解析 |
src/main/services/cockpit.service.ts |
扩展 WritingStats |
src/main/ipc/handlers/writing.handler.ts |
writing:getStats |
src/main/ipc/handlers/knowledge.handler.ts |
抽取/合并 IPC |
src/main/ipc/handlers/chapter.handler.ts |
挂钩 tracker |
src/renderer/components/cockpit/WritingHeatmap.tsx |
Canvas 热力图 |
src/renderer/components/cockpit/CockpitModal.tsx |
扩展布局 |
src/renderer/components/knowledge/KnowledgePanel.tsx |
批次/合并 UI |
tests/main/writing-log.test.ts |
UT-LOG-* |
tests/main/knowledge-extraction.test.ts |
UT-EXT-* / IT-EXT-* |
e2e/writing-logs.spec.ts |
E2E-LOG-* |
e2e/knowledge-extraction.spec.ts |
E2E-EXT-* |
11. Spec Coverage Checklist
| design/readme 要求 | 本规格 |
|---|---|
| §6.7 每章自动抽取 + 审核 | §5 |
| §6.7 合并/忽略/批量采纳 | §5.3 |
| §8 writing_logs + 热力图 + 连更 | §3–§4、§6.1 |
| §8 番茄钟/里程碑 | 排除 §1.4 |
| §9 驾驶舱内嵌分析 | 热力图 + 今日进度(轻量) |
| P5 延后「AI 抽取」 | 本阶段交付 |
| P5 延后 writing_logs | 本阶段交付 |
| P6 知识注入上游 | §5 + IT-EXT-03 |
12. 版本与 DoD
package.json→ 0.8.0README.md功能概览追加 P5.2 一行SettingsPageabout 版本号更新npm run build && npm run test && npm run test:e2e全绿- LM Studio 集成测试按 §8.2 超时执行
本规格为笔临 P5.2 子项目。角色弧线、关系图谱、导入导出等各自在后续 spec 中增量修订。