5.7 KiB
Semantic QA Gap Report
Scope
本报告只针对当前仓库里正在使用的 manual-sync 主链,审查它与“#标签 触发的语义 QA 列表拆卡”需求之间的差距。
主链边界是:
- settings / validation
CardIndexingServiceRenderConfigServiceManualCardRendererDiffPlannerServiceAnkiBatchExecutorMarkdownWriteBackServicePluginState
Current Reality
1. 当前主链确实是 manual-sync,不是旧 syncRegistry 链
真实入口是:
FileIndexerServiceCardIndexingServiceDiffPlannerServiceAnkiBatchExecutorMarkdownWriteBackServicePluginState
旧 domain/card/* 与 domain/sync/* 仍在仓库里,但本轮新功能应接入 manual-sync,而不是另起一条平行同步路径。
2. 当前 card type 只支持两种内部类型
CardType 目前只有:
basiccloze
这一点贯穿了:
PluginSettingsNoteModelFieldMappingRenderConfigServiceNoteFieldMappingServiceCardIndexingServicePluginStateManualCardRenderer
因此,语义 QA 如果要使用“独立 note type + 独立 field mapping”,就不能只伪装成现有 basic。
3. 当前索引模型是“一段标题块 -> 一张 IndexedCard”
CardIndexingService 现在的基本假设是:
- 命中 QA 标题级别 -> 生成一张
basic - 命中 Cloze 标题级别 -> 生成一张
cloze
标题块 body 目前被整体视为一张卡的正文,没有任何“标题块内部拆多卡”的结构。
4. 当前 marker / writeback / pending-writeback 已经支持“一个文件多卡”,但默认单位是 block
当前 marker 语义是:
<!--ID: noteId-->MarkdownWriteBackService依赖blockStartLine + rawBlockHashCardMarkerService.applyBatch()禁止同一个blockStartLine的重复写回
这意味着:
- 语义 QA 可以接入现有 marker 主链
- 但每张子卡必须拥有自己的 block 定位,而不能继续共用父标题的
blockStartLine
5. 当前 backlink 使用的是 headingText
ManualCardRenderer 当前会把 card.heading 同时用于:
- 题面标题渲染
createBacklink()的 heading anchor
这与语义 QA 需求直接冲突:
- 题面应是“父标题 + 子项名”
- backlink 仍应回到原父标题,而不是 synthetic question
所以现有模型缺少“显示题面”和“源 heading anchor”之间的分离。
6. 当前 settings UI 只暴露 basic / cloze 两套 mapping
PluginSettingTab 当前只支持:
- QA / Basic note type + mapping
- Cloze note type + mapping
没有:
- semantic marker 输入
- semantic QA note type
- semantic QA field mapping
- settings-driven preview area
7. 当前 settings validation 是“结构校验 + lazy mapping”,不是“强制所有 mapping 已就绪”
validatePluginSettings() 现在会校验:
- 枚举值
- note type 非空
- 已存在 mapping 的结构是否合法
它不会强制 basic/cloze mapping 必须已经存在。
这意味着 prior AI plan 中“全局 settings validation 必须立即强制 semantic mapping 完整”不完全适配当前仓库;若直接照搬,会破坏现有“先刷新 note type 再保存 mapping”的使用方式。
What Can Be Reused
以下能力可以直接复用:
- 现有 manual-sync 主链
RenderConfigService的 note type / deck 决策位置NoteFieldMappingService的 title/body 映射逻辑ManualCardRenderer的 markdown / backlink / media 渲染能力PluginState的 noteId 主身份持久化MarkdownWriteBackService/pendingWriteBack/ orphan handling 主流程
What The Prior AI Plan Got Right
以下方向与真实仓库吻合:
- 需要独立内部 card type,不能只伪装成现有 QA/basic
- 应接入当前主链,而不是新建平行路径
- 最适合的解析入口确实是标题解析阶段
- 需要 settings 页输入 marker、note type、field mapping、preview
- 需要稳定子卡 identity,且必须兼顾 marker writeback 与 orphan handling
What The Prior AI Plan Got Wrong Or Overstated
1. “只改标题解析”是不够的
真实仓库里还必须同步改:
CardTypePluginSettingsNoteFieldMappingServiceRenderConfigServicePluginStateManualCardRendererbacklink anchor
2. “全局 settings validation 立即强制 semantic mapping 完整”不适合当前仓库
当前仓库的 note field mapping 是 lazy workflow:
- 先选 note type
- 从 Anki 读字段
- 再保存 mapping
所以 semantic mapping 应沿用这套模式,而不是把默认设置直接变成不合法。
3. “题面 = 父标题 + 子项名”还需要额外处理 backlink anchor
这不是 prior AI plan 中显式提到的点,但在当前实现里是必须补的模型差距。
Main Gaps To Implement
- 扩展
CardType为三态,并让 semantic QA 走 basic-like field mapping 但独立 note type。 - 在
PluginSettings/ settings UI 中新增 semantic marker、semantic note type、semantic mapping controls 和 preview。 - 在
CardIndexingService中实现“带标签的 QA 级标题 -> 按一级列表项拆多卡”。 - 为子卡引入稳定的 child key / child-local block identity。
- 分离“显示题面标题”和“backlink 锚点标题”。
- 让 marker writeback 能在同一父标题块内为多个子卡各自写
<!--ID: noteId-->。 - 补 settings / indexing / rendering / sync / persistence 的测试闭环。
Scope Risks
本轮容易漂移的点:
- 做成编辑器实时预览
- 引入 emoji / wikilink trigger
- 改动旧 syncRegistry 链
- 重构 deck 逻辑或 orphan 逻辑
- 试图做“一级列表同行正文智能截断”之外的复杂 NLP 解析
这些都不在本轮范围内。