StudyLoop:本地优先的学习系统,以及 AI 失败时的退路

用 IndexedDB、三个学习模块和可替换的 QuestionProvider,把学习计划、每日巩固与间隔复习串成一个闭环。

本文目录

StudyLoop 是一个本地优先的个人学习系统。它没有把“学习”简化成一个番茄钟,而是拆成三个互相连接、但边界清楚的模块:

  • 学习计划:接下来学什么,每天做什么。
  • 每日巩固:今天学的内容是否真的理解了。
  • 复习计划:已学过的内容,之后怎样记牢。

这三个模块共享知识库,但不混用任务。学习任务只记录完成情况;巩固题和复习题才影响掌握度。这个边界是整套设计里最重要的部分。

为什么本地优先

应用默认单用户、免登录。学习任务、笔记、题目、计划和答题记录都保存在浏览器 IndexedDB;关闭页面、重启设备后仍然存在。

联网只用于可选的 AI 出题服务。没有配置 AI 或断网时,系统仍可使用本地模板题与已缓存的题目。复习计划创建时会预生成并缓存基础题目,因此已建立的计划不会因为网络中断而失效。

这带来两个实际问题:

  1. 导出/导入不是附属功能:浏览器存储可能被清理,数据必须能完整导出。
  2. AI 不是主存储:模型失败不能把用户输入一起清空。

三个模块如何连接

学习计划(安排新内容)
   ↓ 完成今日学习任务
每日巩固(记录实际所学并即时出题)
   ↓ 用户确认纳入长期记忆
复习计划(按目标天数间隔复习)
   ↓ 暴露薄弱点
每日巩固

流转必须由用户确认。勾选“已学习”不会自动生成大量题目,也不会自动修改复习计划。这个约束看起来保守,但能避免 AI 把一次误操作放大成几十道题。

概念所属模块是否影响掌握度
学习任务学习计划否,只记录完成
当日巩固会话每日巩固是,形成初始掌握度
长期复习会话复习计划是,更新间隔与下次日期
笔记与知识点共享知识库被答题记录间接更新

AI 的边界

后端是一个本地 Fastify 代理,读取 .env.local 里的 DeepSeek 配置。API Key 不会进入前端代码或 IndexedDB;日志和导出文件也不应包含密钥。

出题层用统一的 QuestionProvider 接口隔离:

  1. 本地模板提供器:无需联网,从定义句、列表、步骤、对比和代码块生成基础题。
  2. DeepSeek 提供器:负责知识点提取、题型选择、干扰项和解析。
  3. 未来本地模型:可以接 WebLLM,但不进入 MVP。

DeepSeek 返回 JSON,经过 Zod schema 二次校验。空内容、截断或校验失败时只重试一次;仍失败就切到本地模板,并明确告诉用户。这里的原则是:AI 可以失败,但学习流程不能卡死。

复习计划不是“每天随机出题”

复习计划创建时,用户先确认知识点,再选择 3–60 天的目标、开始日期和轻量/标准/强化三档强度。系统预生成阶段日程和基础题目,首次打开当天时恢复“今日到期复习”,不需要网页一直运行。

答题后更新掌握度和下次复习日期;错题会在后续日期重新出现。主观题先展示参考答案,再由用户选择“不会 / 模糊 / 掌握 / 轻松”。

这也解释了为什么不能把学习任务和复习任务合成一个“待办数字”:学习任务是新内容,复习任务是旧内容,它们的逾期处理方式不同。

值得复用的设计

  • 数据先于 AI:用户输入、计划、答题记录都在本地;AI 只是可替换的题目来源。
  • Provider 隔离:本地规则和云端模型实现同一个接口,失败可切换。
  • Schema 二次校验:模型输出不是“看起来像 JSON”就直接用。
  • 用户确认边界:模块之间不自动流转,避免错误扩散。
  • 可解释的复习:用户能看到“为什么今天复习这些内容”,而不是只看到一个日期。

小结

StudyLoop 的工程价值不在“接了 DeepSeek”,而在于它把 AI 放在一个可失败、可替换、可审计的位置。对个人学习工具来说,这比多一个模型名称重要得多。

项目仍在迭代;上面的模块边界和数据流来自公开的 README 与设计文档,具体实现以仓库为准。

讨论

评论由 GitHub Discussions 提供,点击后加载。未加载前不会产生第三方请求。