我给自己做的 AI 家教(灵犀学堂)加了个”自我改进引擎”。第一次 cron 运行就甩了我一巴掌:10 项补习调度,9 项是遗忘风险——一个学生 8 天没练,掌握度从 83% 直接归零。这篇记录我怎么把艾宾浩斯遗忘曲线从心理学课本搬进代码:一个公式、一套稳定性动力学、三条自动管线,以及四个真实踩坑。


1. 缘起:AI 家教凭什么知道学生”其实已经忘了”?

做教育 Agent 之前,我以为自适应学习就是”记录学生答对答错,算出掌握度”。直到我给系统加了一个每天自动运行的自我改进引擎(AutoCalibrator / AutoRemediator / AutoStrategyTuner 三管线),第一次跑出来的报告让我意识到一个致命问题:

1
2
3
4
[遗忘提醒] yadinae | gk_it_02
遗忘风险: gk_it_02 (原始 80% → 有效 0%, 衰减 100%)
[遗忘提醒] demo_student_001 | ch01
遗忘风险: ch01 (原始 62% → 有效 0%, 衰减 100%)

原始掌握度 80%,有效掌握度 0%? 这意味着:这个学生上次练习时确实答对了 80% 的题,但那是 8 天前。系统如果只看历史对错,就会一直以为他”会了”,直到考试崩盘。

绝大多数 AI 教育产品都死在这一步:只记录静态掌握度,不建模遗忘。而我之前做的 AI 备课系统(《我给 AI 当”教务主任”:手搓一个自动备课系统》)恰恰也只做到了”讲得好”,没做到”记得住”。

这篇把整个方案拆开:从一行公式,到可运行的调度系统。


2. 核心方法:一行公式 + 一套动力学 + 三条管线

2.1 遗忘曲线公式(10 行代码)

艾宾浩斯遗忘曲线说:记忆按指数衰减。工程化后就是一行核心公式:

1
2
3
4
5
6
7
def forgetting_curve(raw_mastery: float, hours_since_practice: float, stability_days: float) -> float:
"""Ebbinghaus 遗忘曲线计算有效掌握度"""
if hours_since_practice <= 0.5: # 半小时内不衰减
return raw_mastery
decay_constant = max(stability_days * 24, 1.0) # 防除零
retention = math.exp(-hours_since_practice / decay_constant)
return max(0.0, raw_mastery * retention)

关键设计:原始掌握度(raw mastery)≠ 有效掌握度(effective mastery)。系统里永远存两套数——历史对错推出来的原始值,和经过时间衰减后的有效值。所有教学决策只看有效值。

2.2 稳定性动力学:让”忘得快慢”本身可学习

不同知识点、不同学生,遗忘速度不同。所以我把”忘得多快”建模成一个可变的 stability(记忆稳定性,单位:天)

1
2
3
4
STABILITY_BOOST_CORRECT   = 1.5   # 答对:稳定性 ×1.5
STABILITY_BOOST_SEQUENCE = 0.3 # 连续答对:额外 +0.3/次
STABILITY_DECAY_UNUSED = 0.7 # 不复习:每天 ×0.7
MAX_STABILITY_DAYS = 30.0 # 上限 30 天
  • 答对 → 稳定性升高(stability * 1.5 + 连续答对数 * 0.3),学得越扎实忘得越慢
  • 答错 → 稳定性减半(stability * 0.5),并记录误区(misconception)
  • 长期不练 → 稳定性每天 ×0.7 自然衰减,而且衰减后的稳定性还会反过来加速遗忘(有效稳定性进入公式的 decay_constant)

这样系统就能区分”刚学的知识”(stability≈1 天,一天不练就忘)和”反复巩固的知识”(stability≈30 天,一个月不忘)——这就是间隔重复(spaced repetition)的自动实现:该复习时它知道,不用人肉排课表

2.3 贝叶斯平均:防止”答对 1 题就以为学会了”

小样本陷阱:学生答对 1 题,掌握度直接算 100%?我用贝叶斯平均压低虚高:

1
2
# PRIOR_K=1, PRIOR_N=2 → 新学生默认 0.5,答对 1 题后 0.67
self.mastery = (self.correct_count + 1) / max(total + 2, 1)

新学生先验掌握度 0.5,答对 1 题只到 0.67,答对 2 题才 0.75——数据越少,越不敢说”会了”。这是”不虚构掌握程度”的第一道防线。

2.4 三条自动管线:从”知道忘了”到”自动补课”

光有公式没用,得让它跑起来。自我改进引擎每天 cron 跑一次,三条管线各管一段:

管线 职责 关键阈值
AutoCalibrator 校准学生画像 + 遗忘曲线参数 + 偏差检测 交互数 ≥ 5 才校准
AutoRemediator 检测遗忘风险/先修缺口/连续答错 → 调度补习 有效/原始 < 0.5 且有效 < 0.6 → 遗忘提醒
AutoStrategyTuner 评估教学实验效果 → 优化策略 实验数 < 2 不做建议(安全守卫)

AutoRemediator 的遗忘检测核心(真实代码):

1
2
3
4
# 如果有效掌握度 < 原始掌握度 × 0.5 → 遗忘严重
decay_ratio = eff / max(raw, 0.01)
if decay_ratio < 0.5 and eff < 0.6:
actions.append({"type": "forgetting_reminder", ...})

调度出的补习通过 EventBus 投递给学生门户(站内通知),而不是只写进日志——“检测到了”不等于”触达了”,这条我们在踩坑里付出了代价。

2.5 偏差检测:识别”过度自信”的学生

更有意思的是 AutoCalibrator 的掌握度偏差校正:如果一个知识点”原始掌握度高但保持率低”(衰减 > 0.3 且原始 > 0.5),说明这个学生可能是过度自信(overconfidence)——当时答对了但根本没真懂,属于”假会”。系统会标记偏差类型并个性化其遗忘曲线(这类学生要更早复习)。


3. 踩坑记录:四个坑,每个都是学费

坑 1:补习调度”只计数不投递”——检测到了,学生却不知道

症状:cron 报告显示”调度 10 项补习”,但学生门户通知为空。

根因AutoRemediator.run_all() 把 action 算出来了,但没接 EventBus 投递——只统计了数量,没有执行。commit a2f1ce2 修复:run_all 后真正 persist + 逐条投递通知。

教训:任何”自动调度”系统,验收标准必须是端到端——从检测到触达用户,中间断一环就是空转。这跟我在《我的 Agent 假装上班了 5 天》里诊断的自主系统空转是同一类病。

坑 2:迭代字典时 RuntimeError(dictionary changed size during iteration)

症状:AutoRemediator 检查先修缺口时崩溃。

根因comprehensive_analysis() 内部会经 get_knowledge_state() 就地补建缺失的先修节点状态(写回同一字典),直接 for kp_id in states.items() 迭代就会触发 RuntimeError。

修复:迭代前打快照 for kp_id, state in list(states.items())

教训:懒加载 + 就地写回的 API 是迭代陷阱的温床。凡是”读取时会隐式补数据”的容器,迭代一律先 list() 快照。

坑 3:多进程共享 memory_dump.json 互相覆盖

症状:cron 和 Web 服务同时跑,记忆文件被互相覆盖,学生数据”丢失”。

根因:多个 Python 进程写同一个 JSON 文件,没有锁。

修复fcntl 文件锁 + 磁盘合并(落盘前合并内存态与盘上态)。冷启动数据不足的学生被安全跳过,不参与校准——数据量不够时不做决策,这是自我改进系统最重要的安全阀。

坑 4:把”掌握度”当静态值存

设计教训(不是 bug):早期版本 mastery 只有一个数,后来拆成 raw / effective / stability 三件套。凡是涉及”人”的状态数据,时间维度必须显式建模——这是这套系统最值钱的一个决定。


4. 总结

一句话 takeaway:AI 家教要教的不是”答对过”,而是”还记得住”——把遗忘建模进状态,系统才会在真正需要的时候出手。

行动清单:

  1. 状态三件套:原始掌握度 + 有效掌握度 + 记忆稳定性,分开存
  2. 一个公式effective = raw * exp(-hours / (stability * 24))
  3. 贝叶斯防虚高(correct + 1) / (total + 2),小样本不宣称掌握
  4. 自动触达:检测到遗忘风险 → EventBus 通知,端到端验证
  5. 安全阀:数据不足不决策;实验不足不下结论

这套方案实测数据(2026-08-11 运行):4 名学生、10 项补习操作(9 项遗忘提醒 + 1 项先修缺口),最高衰减 100%(原始 83% → 有效 0%)。如果没有这套引擎,这 10 项学习风险会在考试时才暴露。

相关阅读:

如果你也在做自适应学习 / 记忆类产品,欢迎交流你的”遗忘建模”方案。