.cache/index.db 设计与精准读取接口日期:2026-06-26(0.2 修订:2026-06-27,按 review 修派生数据物化策略) 状态:设计文档(O4 消解) 上游:
story-repo-spec-2026-06-10.md0.8、v7-prd.md1.0 目的:补全 spec §11 的表 DDL 和精准读取接口完整清单,作为 M1 实施的输入0.2 修订要点:随章号漂移的派生值(悬了太久章数、蓄积章数、是否超期)一律不物化,改查询时计算;文体指纹去掉时间戳身份以保证可重建;清理重复存储(卷号、别名、与主键重复的 UNIQUE);补连续弱钩报表接口。
定稿/、大纲/、文风/:工作区不入缓存(未定稿的内容不是真相源)chapters 表用途:章节 front matter 展开,支持按章号、卷号、章定位、钩子、情绪定位查询。
CREATE TABLE chapters (
chapter_num INTEGER PRIMARY KEY,
title TEXT NOT NULL,
volume_num INTEGER NOT NULL,
perspective TEXT, -- 视角角色
story_time TEXT, -- 书内时间(自由文本)
word_count INTEGER NOT NULL,
chapter_position TEXT NOT NULL, -- 推进|过渡|日常
hook_type TEXT, -- 危机钩-强 等
mood_position TEXT, -- 压抑|铺垫|小爽|大爽|转折
file_path TEXT NOT NULL, -- 相对于 repo 根目录
is_key_chapter BOOLEAN DEFAULT 0 -- 是否关键章(来源见说明)
);
CREATE INDEX idx_chapters_volume ON chapters(volume_num);
CREATE INDEX idx_chapters_position ON chapters(chapter_position);
CREATE INDEX idx_chapters_hook ON chapters(hook_type);
说明:
volume_num 建索引,不再额外存一份 volume_num_idxis_key_chapter 来源需明确:卷首章可由卷边界确定性推断;转折/高潮章无可靠源字段——M1 实施时若要标记,应在章 front matter 增一个字段承载,否则本列只标卷首,其余恒 0(不靠脚本猜语义,符合不变量 7)threads 表用途:三类线索条目统一存放(伏笔/悬念/感情线),支持按类型、状态、强度、悬了太久查询。
CREATE TABLE threads (
id TEXT PRIMARY KEY, -- 伏笔-031、悬念-008、感情线-012
type TEXT NOT NULL, -- foreshadow | suspense | romance
short_title TEXT NOT NULL, -- 灭门真凶
strength TEXT NOT NULL, -- 高|中|低
status TEXT NOT NULL, -- 进行|已收尾|已放弃
opened_chapter INTEGER NOT NULL,
planned_end TEXT, -- 第7卷 或 章号(强度"高"时必填,约束在写出器/重建器强制,DDL 不管)
last_advanced_chapter INTEGER, -- 最后推进章(可为空)
file_path TEXT NOT NULL
);
CREATE INDEX idx_threads_type ON threads(type);
CREATE INDEX idx_threads_status ON threads(status);
说明:
悬了多少章 = 当前最大章号 − last_advanced_chapter、是否超期 = 该值 > 阈值,都在查询/视图里现算(阈值取 book.yaml 各类型配置)。物化它们会让每次定稿都得刷新全表,且随时与真相脱节。planned_end 的"强度高必填"是业务约束,DDL 的可空 TEXT 管不住,由写出器与重建器校验secrets 表用途:信息差管理,支持泄密扫描、蓄积章数查询。
CREATE TABLE secrets (
id TEXT PRIMARY KEY, -- 信息差-021
short_title TEXT NOT NULL, -- 灭门真凶
known_to TEXT NOT NULL, -- JSON array: ["大长老", "神秘老者"]
reader_knows BOOLEAN NOT NULL, -- 读者是否已知
registered_chapter INTEGER NOT NULL,
keywords TEXT NOT NULL, -- JSON array: ["大长老", "灭门", "血书"]
file_path TEXT NOT NULL
);
CREATE INDEX idx_secrets_reader_knows ON secrets(reader_knows);
说明:
known_to 和 keywords 存为 JSON 字符串(node:sqlite 内置的 SQLite 支持 JSON 函数)蓄积章数 = 当前最大章号 − registered_chapter,查询时现算(同 threads 的"悬了太久"),避免每章刷新全表entities 表用途:名册(角色、地点、物品、势力),支持别名解析、位置查询、境界查询。
CREATE TABLE entities (
id TEXT PRIMARY KEY, -- 正名
type TEXT NOT NULL, -- character | location | item | faction
status TEXT, -- 在世|已死|失踪|封印...(角色专用)
location TEXT, -- 最后已知位置(角色专用)
realm TEXT, -- 境界(角色专用)
possessions TEXT, -- JSON array(角色专用)
last_changed_chapter INTEGER,
file_path TEXT NOT NULL
);
CREATE INDEX idx_entities_type ON entities(type);
CREATE INDEX idx_entities_status ON entities(status);
CREATE INDEX idx_entities_location ON entities(location);
-- 别名表(别名 → 正名 反查的唯一真相源;正名自身的别名清单从这里聚合,不在 entities 再存一份)
CREATE TABLE entity_aliases (
alias TEXT PRIMARY KEY,
entity_id TEXT NOT NULL,
FOREIGN KEY(entity_id) REFERENCES entities(id)
);
CREATE INDEX idx_entity_aliases_entity ON entity_aliases(entity_id);
说明:
entity_aliases 存一份(反查正名、聚合某实体的别名清单都靠它),不在 entities 再存 JSON 副本,避免两份脱同步fingerprints 表用途:文体指纹历史,支持文体漂移检测、体检。
CREATE TABLE fingerprints (
chapter_range_start INTEGER NOT NULL,
chapter_range_end INTEGER NOT NULL,
is_baseline BOOLEAN DEFAULT 0, -- 是否为基线指纹
-- 文体特征(部分常用项提列,加速查询)
avg_sentence_length REAL,
sentence_length_variance REAL,
avg_paragraph_length REAL,
common_phrase_frequency TEXT, -- JSON object
vocabulary_richness REAL,
fingerprint_data TEXT NOT NULL, -- 完整指纹 JSON(包含上述字段 + 更多)
PRIMARY KEY(chapter_range_start, chapter_range_end)
);
CREATE INDEX idx_fingerprints_baseline ON fingerprints(is_baseline);
说明:
(章段起, 章段止)——不带时间戳。带 computed_at 会让"删 .cache 重建"对不上(时间戳无法复现),破坏不变量 2。book.yaml 的 文体基线起/止 决定| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 读条目基本信息 | read-thread <id> --fields=基本信息 |
条目 ID | JSON:强度、状态、开启章、预计收尾 |
| 读条目履历 | read-thread <id> --履历 |
条目 ID | 履历列表(按章号排序) |
| 读条目收尾计划 | read-thread <id> --收尾计划 |
条目 ID | 收尾计划文本 |
| 读条目描述 | read-thread <id> --描述 |
条目 ID | 描述文本 |
| 列出悬了太久的条目 | list-threads --悬了太久 |
无 | JSON 数组:ID、类型、悬了多少章 |
| 列出某类型条目 | list-threads --type=<type> [--status=<status>] |
类型、状态(可选) | JSON 数组:条目列表 |
| 按强度筛选条目 | list-threads --strength=高 |
强度 | JSON 数组:条目列表 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 读总纲指定小节 | read-outline --总纲 --section=<标题> |
小节标题 | Markdown 文本 |
| 读总纲结局段 | read-outline --总纲 --结局 |
无 | Markdown 文本 |
| 读卷纲全文 | read-outline --卷=<N> |
卷号 | Markdown 全文 |
| 读卷纲指定小节 | read-outline --卷=<N> --section=<标题> |
卷号、小节标题 | Markdown 文本 |
| 列出所有卷纲 | list-volumes |
无 | JSON 数组:卷号、卷名、章数范围 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 读章节 front matter | read-chapter <num> --front-matter |
章号 | JSON:完整 front matter |
| 读章节正文 | read-chapter <num> |
章号 | Markdown 正文(不含 front matter) |
| 读章节结尾 N 字 | read-chapter <num> --tail=<N> |
章号、字数 | 正文末尾 N 字 |
| 读章节开头 N 字 | read-chapter <num> --head=<N> |
章号、字数 | 正文开头 N 字 |
| 读章摘要 | read-chapter <num> --摘要 |
章号 | 章摘要文本 |
| 读章节范围摘要 | read-chapters --range=<start>-<end> --摘要 |
章号范围 | JSON 数组:章号、摘要 |
| 读近 N 章结尾 | read-chapters --recent=<N> --tail=<M> |
章数、字数 | JSON 数组:章号、结尾 M 字 |
| 按章定位筛选章节 | list-chapters --章定位=推进 [--卷=<N>] |
章定位、卷号(可选) | JSON 数组:章号、标题 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 读当前卷时间线 | read-timeline --current-volume |
无(从状态推断) | Markdown 表格 |
| 读当前卷+上一卷时间线 | read-timeline --current-and-prev |
无 | Markdown 表格 |
| 读指定卷时间线 | read-timeline --卷=<N> |
卷号 | Markdown 表格 |
| 按在场角色筛选 | read-timeline --在场=<角色名> |
角色名 | 过滤后的 Markdown 表格 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 读角色卡 front matter | read-character <name> --front-matter |
角色正名 | JSON:境界、位置、状态、持有 |
| 读角色卡完整 | read-character <name> |
角色正名 | Markdown 全文(含 front matter) |
| 读角色卡指定小节 | read-character <name> --section=<标题> |
角色正名、小节标题 | Markdown 文本 |
| 解析别名 | resolve-alias <别名> |
别名 | 正名(或"未找到") |
| 列出所有角色 | list-characters [--status=<status>] |
状态(可选) | JSON 数组:正名、状态、位置 |
| 读世界观指定小节 | read-worldview --section=<标题> |
小节标题 | Markdown 文本 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 读信息差基本信息 | read-secret <id> --基本信息 |
信息差 ID | JSON:知情人、读者已知、关键词 |
| 读信息差内容 | read-secret <id> --内容 |
信息差 ID | 内容文本 |
| 列出未揭晓信息差 | list-secrets --reader-knows=false |
无 | JSON 数组:ID、短题、蓄积章数 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 关键词全文检索 | grep-story <关键词> |
关键词 | JSON 数组:章号、匹配行、上下文 |
| 正则表达式检索 | grep-story --regex=<pattern> |
正则表达式 | JSON 数组:章号、匹配行、上下文 |
| 接口 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 悬了太久清单 | report-overdue-threads |
无 | JSON:按类型分组的清单 |
| 信息差蓄积报表 | report-secret-accumulation |
无 | JSON:未揭晓信息差、蓄积章数 |
| 文体漂移报告 | report-style-drift |
无 | JSON:当前指纹 vs 基线的差异 |
| 条目活跃率报表 | report-thread-activity --卷=<N> |
卷号 | JSON:本卷开/推进/收尾条目清单 |
| 连续弱钩计数 | report-weak-hook-streak |
无 | JSON:末尾连续弱钩章数(全书近况/机检"连续弱钩上限"用) |
| 全书统计摘要 | report-book-stats |
无 | JSON:总章数、总字数、条目数、角色数 |
写章流程(§8 第 3 步备料)依赖的接口:
机检与全书近况依赖:
AI 角色与自动模式:
定稿/、大纲/、文风/(只读,不读 工作区/)book.yaml(当前章号、基线章号、阈值).cache/index.db 不存在或损坏时定稿/正文/ front matter 中提到的条目必须有对应文件定稿步骤(§8 第 8 步)完成一次 commit 后,触发增量更新:
-- 插入新章节
INSERT INTO chapters (...) VALUES (...);
-- 更新涉及的条目(只写"最后推进章",悬了太久查询时再算)
UPDATE threads SET last_advanced_chapter = ?, status = ? WHERE id = ?;
-- 更新涉及的角色
UPDATE entities SET location = ?, realm = ?, last_changed_chapter = ? WHERE id = ?;
-- 新增信息差
INSERT INTO secrets (...) VALUES (...);
注意:定稿只写"事实"列(最后推进章、状态、位置…),不写任何随当前章号漂移的派生值——那些在查询时算,所以每章定稿只动当章真正变化的几行,不刷全表。
.cache/、book.yaml 修改、检测到格式版本升级| spec 章节 | 表 | 接口 |
|---|---|---|
| §4.1 章节文件 | chapters |
2.4 正文读取 |
| §4.2 角色卡 | entities (type=character) |
2.6 设定读取 |
| §4.3 信息差 | secrets |
2.7 信息差读取 |
| §4.4 时间线 | 不入表(直接读文件) | 2.5 时间线读取 |
| §4.5 名册 | entities + entity_aliases |
解析别名 |
| §5 三类条目 | threads |
2.2 条目读取 |
| §6 文风铁律 | 不入表(直接读文件) | 无(AI 全文读取) |
| §6.2 文体指纹 | fingerprints |
报表:文体漂移 |
设计完成时间:2026-06-26
设计者:Claude Opus 4.8 + Codex 架构审查
状态:待审查 → M1 实施输入