Skip to content

Latest commit

 

History

History
247 lines (201 loc) · 12.1 KB

File metadata and controls

247 lines (201 loc) · 12.1 KB

v0.11 大目标版本系列设计:笔记组与学习循环

状态:已批准(用户授权全自主执行,2026-08-22) 依据:v4 路线图(已裁决采纳)· 需求池 v0.11 区段(REQ-195~198)· pain-points-v4 §7.4/§7.5/§11/附录B 范围:v0.11.0(组基建·容器侧)→ v0.11.1(feed 进料)→ v0.11.2(学习循环统一)→ v0.11.3(组结算)

一、总架构

统一产物层「笔记组」:一切采集产物归入组,组是唯一容器;容器地形(课程)与 feed 地形(碎片)只是组的两种形成方式(v4 §7.4 融合点)。自底向上四层:

原子层:note_groups/fragments/flashcards schema + 路由/调度/结算纯函数(TDD)
业务层:db_note_groups.rs / group_route.rs / spacy scheduler / settlement
系统层:commands_groups.rs / commands_fragments.rs / commands_flashcards.rs
UI 层:NotesPage 组侧栏 + 组卡片(路由可见可改)+ 复习面(最小化)+ 结算向导

硬约束(写入每层实现):

  • 提取优先:复习"先回忆再看",无被动重读模式
  • 弹性承诺:周契约,断签不清零(review_logs 无 streak 字段)
  • 动机App化防御:UI 最小化,引擎先有真牙
  • 碎片身份诚实:fragments 独立表,不与 notes 混装
  • feed 功能开关默认关;路由误判可见可改

二、v0.11.0 笔记组基建·容器侧(REQ-195~198)

2.1 数据模型(db_migrations.rs 惯例:建表幂等 + ensure_column)

CREATE TABLE IF NOT EXISTS note_groups (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    terrain TEXT NOT NULL DEFAULT 'container',   -- container/feed(管线分叉前提)
    kind TEXT NOT NULL DEFAULT 'standalone',     -- course 课程组/topic 主题组/standalone 独立组
    domain_tag TEXT,                             -- DomainKind kebab-case(主题组必填)
    source TEXT NOT NULL DEFAULT 'route',        -- route/series/manual
    series_key TEXT,                             -- series_detect 系列名(课程组幂等键)
    route_reason TEXT,                           -- 路由理由 JSON(命中信号明细,REQ-198)
    route_overridden INTEGER NOT NULL DEFAULT 0, -- 用户改判标记(修改即记忆)
    created_at INTEGER NOT NULL,
    updated_at INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_groups_series
    ON note_groups(series_key) WHERE series_key IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_groups_domain ON note_groups(domain_tag);
-- notes 补列(ensure_column 幂等;ON DELETE SET NULL——删组不断笔记资产)
ALTER TABLE notes ADD COLUMN group_id INTEGER REFERENCES note_groups(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_notes_group ON notes(group_id);

2.2 结构密度路由纯函数(group_route.rs,TDD golden 用例先行)

复用 vote_detect/vote_tier 加权投票范式(每信号一票 → 计票 → 置信):

pub struct GroupRouteSignals {
    pub has_series: bool,            // series_detect 命中 → 直接课程组(不进投票)
    pub chapter_density: f32,        // 章节数/小时(chapter_detect 产出)
    pub glossary_terms: usize,       // 术语表条目数(glossary 产出)
    pub ocr_text_density: f32,       // OCR 文本块密度 0..1
    pub profile_unknown: bool,       // 形态 unknown(四维解耦信号)
    pub tier_rich: bool,             // 画面档 rich/mid(video_tier_detect)
    pub domain_kind: Option<DomainKind>, // 领域命中(归主题组的前提)
}
pub enum RouteAction { OwnGroup, TopicGroup, NeedConfirm }
pub struct GroupRouteDecision {
    pub action: RouteAction,
    pub reasons: Vec<String>,  // 命中信号明细(前端路由理由展示)
}
pub fn route_group(signals: &GroupRouteSignals) -> GroupRouteDecision;

投票规则(附录B 判据:高密度自成一组、低密度归主题组):

  • 高密度票:chapter_density ≥ 2.0/h、glossary_terms ≥ 5、ocr_text_density ≥ 0.5、tier_rich
  • 低密度票:profile_unknown、chapter_density < 0.5、glossary_terms == 0
  • 高密度 ≥2 票 → OwnGroup;低密度 ≥2 票且 domain 命中 → TopicGroup; 票数不足/高低冲突 → NeedConfirm(诚实不乱判,vote_tier 同口径)
  • 零信号 → OwnGroup 兜底(会话产物默认独立成组,不埋进主题组——埋没比空洞更伤)

2.3 容器侧组化接线(commands_session_note.rs convert_to_note 内)

笔记落库后(单一落库出口):

  1. extract_series(session.title) 命中 → 课程组(series_key 幂等查找/创建, kind=course,source=series)——兑现 REQ-078 语义升级
  2. 无系列 → 收集信号跑 route_group:
    • OwnGroup → 独立组(name=笔记标题)
    • TopicGroup → 按 domain_tag 查/建主题组(kind=topic,terrain=container)
    • NeedConfirm → 独立组 + route_reason 标记待确认(UI 高亮)
  3. notes.group_id 回写;route_reason 落组(REQ-198 可见)

2.4 Commands(commands_groups.rs)+ UI

  • list_note_groups(含组内笔记数)/ get_note_group / create_topic_group
  • override_group_route(group_id, kind, domain_tag)——改判即记忆 (route_overridden=1,改判记录进 route_reason,REQ-198)
  • move_note_to_group(note_id, group_id) / rename_note_group
  • 前端:NotesPage 左侧栏新增「组」区段(按 v4 §7.5 容器组在每日/会话面语义内); 组卡片显示路由理由明细 + 待确认徽标 + 一键改判(v0.9.0 检测卡交互哲学延续); 笔记列表支持按组过滤

三、v0.11.1 feed 进料口(功能开关默认关)

3.1 fragments 原料层(独立表——碎片不是笔记,身份诚实)

CREATE TABLE IF NOT EXISTS fragments (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    text TEXT NOT NULL,
    image_path TEXT,                 -- 剪贴板图片(存 data_dir/fragments/)
    domain_tag TEXT,                 -- detect_domain 自动归组依据
    group_id INTEGER REFERENCES note_groups(id) ON DELETE SET NULL,
    source TEXT NOT NULL DEFAULT 'manual',  -- manual/clipboard
    created_at INTEGER NOT NULL
);

3.2 功能开关与捕获

  • feature_flags.json(应用数据目录):{ "feed_capture": false }——默认关, SettingsPage 开关;关闭时捕获 command 拒绝执行(不信任前端隐藏)
  • capture_fragment(text, image_b64?):文本 ≤2000 字;图片解码白名单格式 落 data_dir/fragments/;detect_domain 命中标题词 → 查/建 terrain=feed 主题组并挂 group_id;未命中 → 无组(结算面兜底归组)
  • DomainTag 归组粒度对齐领域(契约一:拒绝大类)

3.3 UI

SettingsPage 开关 + NotesPage 组侧栏显示 feed 主题组(仅开关开启时); 快速捕获输入框(组侧栏顶部,文本粘贴即捕获;图片走剪贴板)。

四、v0.11.2 学习循环统一(最大新建块)

4.1 调度器(FSRS 主 / SM-2 降级,接口不变)

  • 优先引入 fsrs = "6" crate(纯算法零网络,FSRS-6;补 ADR-018); 若构建环境受限 → 手写 sm2.rs 纯函数兜底(EF 系数版,调度接口一致)
  • 统一接口(scheduler.rs):
pub enum Rating { Again, Hard, Good, Easy }
pub struct CardState { pub stability: f32, pub difficulty: f32,
                       pub reps: u32, pub lapses: u32, pub last_review_ms: u64 }
pub struct ScheduleOutcome { pub next_state: CardState, pub due_at_ms: u64 }
pub fn schedule(state: &CardState, rating: Rating, now_ms: u64) -> ScheduleOutcome;
  • 弹性承诺:无 streak 概念;欠账卡仅按 due_at 排序呈现,不追债不清零

4.2 数据模型

CREATE TABLE IF NOT EXISTS flashcards (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id INTEGER NOT NULL REFERENCES note_groups(id) ON DELETE CASCADE,
    note_id INTEGER REFERENCES notes(id) ON DELETE SET NULL,
    fragment_id INTEGER REFERENCES fragments(id) ON DELETE SET NULL,
    front TEXT NOT NULL, back TEXT NOT NULL,
    kind TEXT NOT NULL DEFAULT 'fact',        -- 内容分型预埋:fact 先做
    state_json TEXT NOT NULL,                 -- CardState 序列化
    due_at INTEGER NOT NULL,
    created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_cards_due ON flashcards(due_at);
CREATE TABLE IF NOT EXISTS review_logs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    card_id INTEGER NOT NULL REFERENCES flashcards(id) ON DELETE CASCADE,
    rating TEXT NOT NULL,                     -- again/hard/good/easy
    reviewed_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS metrics_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    kind TEXT NOT NULL,                       -- card_reviewed/fragment_upgraded/
                                              -- group_settled/self_test_done
    payload_json TEXT NOT NULL DEFAULT '{}',
    created_at INTEGER NOT NULL
);

4.3 组→闪卡生成(本地规则版先行)

  • generate_group_cards(group_id):组内笔记的词汇表块术语(structure_note

    词汇表 行解析)+ glossary 候选 → front=术语 / back=术语首次出现的上下文句

    (锚点回链);fragment 文本 >2 句时首句为 front 其余为 back(提取优先)
  • AI 生成为可选增强:复用 ai_client 授权/成本/审计全套,默认关
  • 生成幂等:同 group+front 查重跳过

4.4 复习面(UI 最小化)+ 埋点

  • NotesPage 组卡片「复习」入口 → 复习队列(due_at ≤ now,按组过滤): 先显示 front → 点击「回忆完成」→ 展开 back → 四档评分(Again/Hard/Good/Easy)
  • 主题自测最轻形态:组内随机 5 卡快答(仅评分不展示对错叙事——N14 防御)
  • 每次评分落 review_logs + metrics_events(card_reviewed); fragment→card 落 metrics_events(fragment_upgraded)——Phase 4 门控数据从第一天记

五、v0.11.3 组结算机制(防沼泽仪式)

5.1 触发纯函数(settlement.rs)

pub struct SettlementSignals { pub item_count: usize, pub oldest_at_ms: u64,
                               pub last_settled_at_ms: Option<u64>, pub now_ms: u64 }
pub fn settlement_due(s: &SettlementSignals) -> bool;
// 阈值:item_count ≥ 50;或 周期:≥90 天未结算且 item_count ≥ 20

5.2 结算向导(用户可见仪式,三步)

  1. 核心提炼:组内词汇表/高频术语汇总为「组核心」笔记(本地规则; AI 精修可选,复用 AiRefine 协议)
  2. 重复合并:碎片/卡文本 Jaccard 去重(复用 asr_dedupe 归一化口径), 展示合并对由用户确认(不静默删)
  3. 低价值归档:无卡绑定且 180 天未访问的碎片标记 archived (fragments 补 status 列;不删除——可恢复)

5.3 settlements 记录

CREATE TABLE IF NOT EXISTS settlements (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id INTEGER NOT NULL REFERENCES note_groups(id) ON DELETE CASCADE,
    stats_json TEXT NOT NULL,       -- 提炼条数/合并对数/归档数
    created_at INTEGER NOT NULL
);
-- fragments 补列(ensure_column):status TEXT NOT NULL DEFAULT 'active'

结算完成落 metrics_events(group_settled)——北极星组成之一。

六、测试策略

  • 纯函数全 TDD(AAA + golden 用例):group_route(高/低/冲突/零信号/系列直判)、 schedule(四档评分间隔单调性、Again 重置)、settlement_due(阈值/周期边界)、 卡片生成幂等、去重合并
  • 数据层用 :memory: 库(db_notes_tests 同款 mem_db 范式)
  • command 层仅参数校验与编排,无业务逻辑(AGENTS.md §6)
  • 模型文件零依赖(调度器纯算法;FSRS crate 无模型资产)

七、版本交付与提交纪律

每个小版本独立交付闭环(代码 + 测试 + 需求池状态同步 + 版本文档 + 提交):

  • v0.11.0:feat(groups): ... 3-5 个原子提交(原子层/接线/commands/UI/文档)
  • v0.11.1 同上;v0.11.2 启动时补 ADR-018(fsrs 依赖引入)
  • v0.11.3 收尾全系列;需求池 REQ-195~198 状态逐版推进
  • 单文件 ≤300 行(超限即拆 + 豁免登记);所有新模块含 @ai-context 注释