Hotdry.
systems-engineering

Hashcards:纯文本间隔重复系统的架构设计与同步策略

深入分析hashcards基于纯文本的间隔重复系统架构,探讨FSRS算法实现、数据持久化策略与跨平台同步的工程化解决方案。

在间隔重复系统(Spaced Repetition System)领域,Anki 和 Mochi 等工具长期占据主导地位,但它们都存在各自的局限性。2025 年出现的 hashcards 项目提出了一个革命性的设计理念:将闪卡存储为纯文本 Markdown 文件,而非传统的数据库格式。这一设计决策不仅改变了用户与学习材料的交互方式,更在工程层面带来了全新的架构挑战与机遇。

纯文本优先的设计哲学

hashcards 的核心设计理念源于一个简单的观察:知识工作者最熟悉的工具是文本编辑器。项目创始人 Fernando Borretti 在博客文章中详细阐述了他的设计思路:" 当学习教科书或网站时,我会在 Markdown 文件中编写闪卡。通常,我使用类似[foo]的简写来表示填空删除。然后使用 Python 脚本将简写转换为 Mochi 使用的{{1::foo}}符号。"

这种工作流程的自然延伸催生了 hashcards 的诞生。系统采用极简的文本格式:

Q: What is the role of synaptic vesicles?
A: They store neurotransmitters for release at the synaptic terminal.

C: Speech is [produced] in [Broca's] area.

每个 Markdown 文件对应一个 "牌组"(deck),文件名(不含扩展名)即为牌组名称。这种设计的优势在于:

  1. 零学习成本:用户无需掌握新的编辑界面,直接使用熟悉的文本编辑器
  2. 版本控制友好:Git 可以完美追踪闪卡的变化历史
  3. 脚本自动化:可以使用标准 Unix 工具或编程语言批量处理闪卡

内容寻址与数据持久化架构

hashcards 采用内容寻址(content addressing)机制来标识卡片。每张卡片通过其文本内容的 SHA-256 哈希值唯一标识。这一设计带来了重要的工程影响:

技术实现细节

  • 卡片哈希:card_hash = SHA256(card_text)
  • 编辑即重置:当卡片内容被修改时,哈希值改变,系统将其视为新卡片,学习进度重置
  • 数据库关联:SQLite 数据库中的cards表通过card_hash字段关联纯文本卡片

数据库架构设计体现了数据分离的原则:

-- cards表存储学习进度
CREATE TABLE cards (
    card_hash TEXT PRIMARY KEY,
    added_at TEXT NOT NULL,
    last_reviewed_at TEXT,
    stability REAL,
    difficulty REAL,
    interval_raw REAL,
    interval_days REAL,
    due_date TEXT,
    review_count INTEGER NOT NULL
);

-- reviews表记录每次复习历史
CREATE TABLE reviews (
    review_id INTEGER PRIMARY KEY,
    session_id INTEGER NOT NULL,
    card_hash TEXT NOT NULL,
    reviewed_at TEXT NOT NULL,
    grade TEXT NOT NULL,  -- 'forgot', 'hard', 'good', 'easy'
    stability REAL NOT NULL,
    difficulty REAL NOT NULL,
    interval_raw REAL,
    interval_days REAL,
    due_date TEXT NOT NULL
);

这种架构实现了内容与进度的分离:纯文本文件存储 "是什么",SQLite 数据库存储 "学得怎么样"。这种分离使得:

  1. 备份简单:只需备份文本文件和数据库文件
  2. 迁移灵活:可以在不同设备间复制整个目录
  3. 恢复可靠:即使数据库损坏,卡片内容仍然完整

FSRS 算法在 hashcards 中的工程实现

hashcards 采用 FSRS(Free Spaced Repetition Scheduler)算法,这是目前最先进的间隔重复算法。与传统的 SM-2 算法相比,FSRS 基于深度学习模型,能够更准确地预测记忆保留率。

算法参数调优

  • 稳定性(stability):卡片在记忆中保持的时间长度
  • 难度(difficulty):卡片的学习难度系数
  • 间隔(interval):下次复习的时间间隔

FSRS 算法的核心公式基于记忆的三因素模型:

下次复习间隔 = f(稳定性, 难度, 评分等级)

在 hashcards 中,FSRS 的实现考虑了以下工程细节:

  1. 实时计算:每次复习后立即更新卡片参数
  2. 边界处理:确保间隔在合理范围内(通常 1 天到数年)
  3. 进度持久化:所有参数都存储在 SQLite 中,支持会话恢复

性能优化策略

  • 批量更新:在一次复习会话中批量更新数据库
  • 延迟写入:非关键操作使用异步写入
  • 索引优化:对card_hashdue_date建立索引

跨平台同步的工程挑战与解决方案

纯文本存储的最大挑战在于多设备同步。与传统数据库应用不同,hashcards 没有内置的云同步机制,这既是设计选择,也是工程挑战。

同步冲突的三种类型

  1. 内容冲突:同一张卡片在不同设备上被不同修改
  2. 进度冲突:同一张卡片在不同设备上有不同的复习记录
  3. 结构冲突:牌组结构(文件组织)在不同设备上不一致

Git-based 同步策略

最直接的同步方案是使用 Git。hashcards 的纯文本特性使其天然适合 Git 工作流:

# 基本同步流程
git add .
git commit -m "Update flashcards"
git push origin main

# 在另一台设备
git pull origin main
hashcards drill ./Cards

Git 同步的最佳实践

  1. 分支策略:为每台设备创建独立分支,定期合并到主分支
  2. 冲突解决:使用 Git 的合并工具解决文本冲突
  3. 自动化脚本:编写 pre-commit 钩子验证闪卡格式

增量同步算法设计

对于不想使用 Git 的用户,可以设计自定义的增量同步算法:

def sync_cards(local_dir, remote_dir):
    # 1. 扫描本地和远程的卡片哈希
    local_hashes = scan_hashes(local_dir)
    remote_hashes = scan_hashes(remote_dir)
    
    # 2. 识别新增、修改、删除的卡片
    new_cards = remote_hashes - local_hashes
    modified_cards = find_modified(local_hashes, remote_hashes)
    deleted_cards = local_hashes - remote_hashes
    
    # 3. 合并数据库记录
    merge_databases(local_dir, remote_dir)
    
    # 4. 解决冲突
    resolve_conflicts(local_dir, remote_dir)

数据库同步策略

SQLite 数据库的同步需要特殊处理,因为直接复制数据库文件可能导致数据损坏:

安全的数据同步方案

  1. 导出 / 导入模式:使用hashcards export命令导出 JSON,在目标设备导入
  2. 增量合并:只同步新增的复习记录,忽略时间戳冲突
  3. 最后写入胜出:对于冲突的进度记录,采用时间戳最新的版本

可落地的部署参数与监控指标

部署配置清单

对于生产环境部署,建议以下配置:

  1. 存储参数

    • 最大牌组数量:无限制(受文件系统限制)
    • 单张卡片最大大小:10MB(考虑图片和音频嵌入)
    • 数据库文件大小预警阈值:1GB
  2. 性能参数

    • 并发用户数:单实例支持 50 个并发会话
    • 响应时间:卡片加载 < 100ms,复习提交 < 50ms
    • 内存使用:基础内存占用 < 50MB,每 1000 张卡片增加~10MB
  3. 同步参数

    • 自动同步间隔:15 分钟(移动设备),1 小时(桌面设备)
    • 冲突检测阈值:5 分钟内同一卡片的修改视为冲突
    • 备份频率:每日自动备份到云存储

监控指标体系

建立完整的监控体系确保系统稳定性:

  1. 性能指标

    • hashcards_response_time_avg:平均响应时间
    • hashcards_active_sessions:活跃会话数
    • hashcards_cards_reviewed_per_minute:每分钟复习卡片数
  2. 业务指标

    • hashcards_new_cards_per_day:每日新增卡片数
    • hashcards_review_completion_rate:复习完成率
    • hashcards_retention_rate_7d:7 天记忆保留率
  3. 系统健康指标

    • hashcards_database_size:数据库文件大小
    • hashcards_file_system_usage:文件系统使用率
    • hashcards_sync_success_rate:同步成功率

工程实践中的注意事项

安全性考虑

  1. 本地存储安全:数据库文件应加密存储,特别是包含学习历史时
  2. 网络传输安全:如果实现自定义同步,必须使用 TLS 加密
  3. 输入验证:严格验证用户输入的卡片内容,防止注入攻击

可扩展性设计

  1. 插件架构:支持第三方插件扩展功能
  2. API 设计:提供 REST API 供其他应用集成
  3. 格式兼容:支持导入 Anki、Mochi 等格式的卡片

故障恢复机制

  1. 自动备份:定期自动备份卡片和数据库
  2. 完整性检查hashcards check命令验证集合完整性
  3. 孤儿卡片管理hashcards orphans命令管理已删除卡片的残留记录

未来发展方向

hashcards 的纯文本架构为未来扩展提供了坚实基础:

  1. 协作学习:基于 Git 的协作闪卡编辑和复习
  2. AI 增强:集成 LLM 自动生成卡片或优化卡片内容
  3. 离线优先:完善的离线功能与智能同步
  4. 跨平台一致性:确保在所有平台上的体验一致

正如 Borretti 在博客中所说:"'Git 仓库中的 Markdown 文件 ' 给了我其他方法所缺乏的所有权感。" 这种所有权感正是 hashcards 设计的核心 —— 将学习材料的控制权完全交还给用户。

通过将复杂的间隔重复算法与简单的纯文本存储相结合,hashcards 在工程优雅性与用户友好性之间找到了平衡点。对于重视数据所有权、喜欢使用文本工具的知识工作者来说,它提供了一个既强大又透明的学习系统。


资料来源

  1. hashcards GitHub 仓库 - 项目架构与实现细节
  2. Hashcards: A Plain-Text Spaced Repetition System - 设计理念与比较分析
查看归档