Echo Loop 间隔复习调度实现全解析:从6小时到28天,以及FSRS记忆调度基础设施
【免费下载链接】Echo-LoopEcho Loop 是一款科学、高效的 AI 英语听说训练 App,通过精听、跟读、盲听、复述和间隔复习,自动驱动学习者把每一段音频真正练懂、练熟、练到会说。项目地址: https://gitcode.com/echo-loop/Echo-Loop
Echo Loop 是一款 AI 英语听说训练 App,它的核心能力之一,就是间隔复习调度:一段音频首学完成后,系统会自动安排 7 轮复习,从 6 小时后一直排到 28 天后;与此同时,项目还新建了一套基于FSRS 记忆算法的通用记忆调度基础设施,为收藏句子、词汇、意群等内容的科学复习打下地基。本文带你从用户视角和源码视角,把这两套"记忆引擎"一次性讲清楚。
一、用户视角:复习节奏从 6 小时到 28 天
1. 七轮复习排期表:为什么是"6小时→28天"
Echo Loop 对每段音频的复习节奏,是刻意贴合记忆曲线设计的"先密后疏"节奏。阶段定义位于 lib/database/enums.dart 的LearningStage枚举,其中intervalHours表示距上一轮完成后的等待时长:
| 轮次 | 阶段 | 距上一轮等待 | 距首次学习的累计时间 |
|---|---|---|---|
| 1 | review0 | 6 小时 | 6 小时 |
| 2 | review1 | 18 小时 | 1 天 |
| 3 | review2 | 24 小时 | 2 天 |
| 4 | review4 | 48 小时 | 4 天 |
| 5 | review7 | 72 小时 | 7 天 |
| 6 | review14 | 168 小时 | 14 天 |
| 7 | review28 | 336 小时 | 28 天 |
对应的取值逻辑见 lib/database/enums.dart#L166-L176。可以看到阶段名(review1/review2/review4/review7/review14/review28)正是"首学后第几天"的累计里程碑——先密集巩固,再逐步拉长间隔,直到 28 天完成最后一轮。
2. 下次复习时间是怎么算出来的?
复习调度不需要复杂算法,核心就是一个加法:
nextReviewAt = 上一轮完成时间(lastStageCompletedAt)+ 当前阶段间隔(intervalHours)
计算逻辑在 lib/models/learning_progress.dart#L202-L214。围绕这个时间点,还有三个贴心的细节设计:
- 🕐宽限窗口:到点后不是"错过就逾期"。review0 到点后6 小时内、其余各轮到点后24 小时内补做都不算逾期,见 learning_progress.dart#L220-L226。
- 🔓手动解锁:等不及?点击"立即解锁"即可提前开始当前轮,且不会篡改历史完成时间,后续轮次仍按实际完成时间顺延。
- ⏸️暂停学习:某段音频可以暂停出复习调度,进度完整保留,随时原地恢复。
3. 每一轮复习做什么?
复习不是简单重听。每一轮复习阶段都会按学习计划派生若干子步骤,主要包括:难句补练(把精听/盲听中卡壳的句子重新练会)、盲听(不看文本听全文)、段落复述(用自己的话复述段落)。
各轮次的子步骤组合由 lib/models/learning_plan.dart 中的LearningPlan.standard统一管理,并采用"计划版本快照"机制——新版 App 调整了复习子步骤顺序(比如 v2 把盲听后置、允许跳过),存量音频仍保留创建时的旧计划,互不干扰。
二、源码视角:FSRS 记忆调度基础设施全景
固定七轮复习解决的是"音频学习计划"的调度。但收藏的句子、词汇、意群,它们的遗忘速度因人而异,固定的"第 N 天复习"并不够聪明。为此,Echo Loop 新建了一套与业务解耦的通用记忆调度基础设施,位于 lib/features/memory_scheduler/,默认接入业界公认的 FSRS 记忆算法。完整的规划文档见 docs/memory-scheduler-infrastructure-plan.md。
1. 分层架构:第三方库只许待在一个房间里
未来业务(句子/词汇/意群/音频计划) ↓ MemoryScheduler(应用门面接口) ↓ ↓ 调度 Repository 模型注册表 ↓ ↓ Drift DAO MemoryModelAdapter(接口) ↓ FsrsMemoryModelAdapter ↓ package:fsrs- domain/:纯 Dart 值对象与接口,不依赖 Flutter、数据库和 FSRS;
- application/:编排用例与事务,入口是 memory_scheduler.dart;
- data/:Drift DAO 与数据映射;
- adapters/fsrs/:全项目唯一允许导入
package:fsrs的位置,核心是 fsrs_memory_model_adapter.dart。
上层业务永远只见应用自有类型(MemorySchedule、MemoryRating等),UI 与数据库里不会出现任何 FSRS 第三方类型。
2. 四个关键设计决策
| 决策 | 做法 | 好处 |
|---|---|---|
| 逐项绑定 Profile | 每条调度记录固定profileId + profileVersion | 升级算法不重排存量数据 |
| 独立调度表 | 新建memory_schedules+memory_review_events两张表 | 句子/词汇/意群/音频共用一套 schema |
| 快照 + 事件历史 | 当前状态快照用于日常查询,评分事件只追加 | 既快又可审计、可重放 |
| Adapter 隔离 | FSRS 库严格封装在 adapter 内部 | 未来换算法,调用方零改动 |
3. FSRS 参数如何被"冻结"
为了避免依赖升级悄悄改变行为,基础设施把fsrs包精确锁定在 2.0.1,并把默认 Profilefsrs.default@1的全部参数逐项写死在 config/memory_profiles.dart:
- 📊 21 个模型权重(与 FSRS 5 版默认值一致)
- 🎯期望保持率 90%:评分 Good 时,下一次复习前记住这条内容的预期概率
- ⏱️ 学习步骤 1 分钟 / 10 分钟,重学步骤 10 分钟,最大间隔 36500 天
- 🎲 开启 fuzzing:到期时间加一点随机扰动,打散复习峰值
4. 四种评分与"间隔预览"
复习时用户给内容打一个分数——忘记 / 困难 / 好 / 简单(对应 FSRS 的 Again/Hard/Good/Easy)。界面在展示按钮前,会一次性调用previewRatings()拿到四种评分各自的下次到期时间,用户可以直观看到"选简单能拖多少天,选忘记要多久后重学"。预览是纯只读计算,不写数据库。
5. 数据可靠性:事务、幂等与并发
移动端场景下,网络抖动、重复点击、多入口并发都是真实风险。评分提交(review())的流程设计得相当严谨:
- 短读事务内按
operationId做幂等检查——同一次评分动作重试会复用原键,不会重复计分; - 算法计算在写事务之外完成(纯函数);
- 写事务内用
revision乐观锁条件更新,再在同一事务内插入带dueBefore/dueAfter的审计事件,任一步失败整体回滚。
对应的数据库结构经过 schema v47 → v48 迁移,迁移行为由 test/database/v47_to_v48_migration_test.dart 等测试守护。
三、两套调度的关系:各司其职
| 固定七轮复习 | FSRS 记忆调度基础设施 | |
|---|---|---|
| 服务对象 | 音频学习整体 | 句子 / 词汇 / 意群等记忆项 |
| 节奏 | 6小时→1天→2天→4天→7天→14天→28天 | 按个人表现动态计算间隔 |
| 输入 | 阶段完成时间 | 忘记/困难/好/简单 四档评分 |
| 现状 | 已全量上线 | 基础设施已建成,逐步接入业务 |
一句话总结:固定七轮保证"每段音频都能被完整练熟",FSRS 基础设施保证"每条记忆都在最容易遗忘的时刻被复习"。前者是学习节奏,后者是记忆科学,二者共同构成 Echo Loop 的复习引擎。
想体验这套间隔复习流程,或参与讨论实现细节?欢迎加入 Echo Loop 的学习社区:
【免费下载链接】Echo-LoopEcho Loop 是一款科学、高效的 AI 英语听说训练 App,通过精听、跟读、盲听、复述和间隔复习,自动驱动学习者把每一段音频真正练懂、练熟、练到会说。项目地址: https://gitcode.com/echo-loop/Echo-Loop
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考