跳到正文

Engram

一条把「学一门东西」做成流水线的办法:课程架构师拆出第一性原理的概念图,一名看不见讲课过程的评分者盲评你写下的原话。再由 FSRS 排程,每次判分都在磁盘上留下一条收据。

Screenshot of Engram
编辑截图, 1 Oct 2026Engram ↗

这是什么

Engram 是一套装进编码 agent 里的学习系统。给它一个题目,它会搭出一张第一性原理的概念图,让你在被告知之前先预测、先尝试、先讲一遍,然后把你写下的原话交给另一个 agent 盲评——评分者只看得到主张、评分标准、提问、你的原话,以及你在看到任何对错信号之前自己选的把握程度,看不到讲课过程。每次判分都会写成一条磁盘上的收据,没有收据就不会有任何状态推进。一个只用标准库的 Python 单一文件承担 FSRS-4.5 排程、状态机、统计与 315 项自检;每一个日期与稳定度都由它自己算出来,而一项常驻自检会解析它自己的语法树,确保里面永远没有网络代码。阈值概念会拿到按七条契约生成的交互式 HTML 探索件。同一套技能与引擎跑在九种 agent 平台上,从它起步的 Claude Code,到 Codex、Antigravity 与 DeepSeek Harness。

谁做的一个只以用户名示人的个人维护者:GitHub 资料上有 38 个公开仓库、75 个关注者,账号 2022-01-03 注册,简介只有三个词,此外没有别的身份信息。仓库 204 次提交里有 189 次出自他,用的是两个提交署名;README 末尾一节列出同一作坊的另外四个插件,其中一个被它记为本项目验证手法的来源——以判据驱动的循环、收据,以及每到一阶段就重新对表。

它是怎么搭起来的

组成 · 6

组织这套系统的想法是:模型可以说话,但引擎不能被说服。凡是学习者被告知的、关于自己记忆的数字,都由一个只用标准库的 Python 文件算出来;而没有一名从未看过讲课过程的评分者写下的收据,任何状态都不会推进。这条想法把系统切成四块:一名跑在主对话里、被禁止判分的导师;一名在全新上下文里被拉起、只看得到评分标准与学习者原话的评分者;一个只能依据收据做调整的教练;以及一个独占所有日期计算的排程器。学习者的全部状态是一个目录里人类可读的 JSON,所以每次会话开始时导师都从磁盘重新对表,而不是相信自己对话里的记忆。第二个想法从第一个推出来:既然评分者是那件承重的仪器,它自己也要被审计——对着公开的对抗式金标跑;而引擎拒绝为一个它无法辩护的数字背书:拟合结果打不过当前参数就丢掉,评分者未受审计时导出直接拒绝执行,而不是给一句提醒。

scripts/engram.py
783,600 字节,也是这个项目里唯一做决定的部分:一个只用标准库的命令行程序,承担 FSRS-4.5 排程、状态 schema、只追加的收据日志、崩溃安全的暂存区、闭环率与保持率算术、自包含的 HTML 面板、评分者审计统计,以及 315 项自检——并由一项构建期检查钉住:它解析这个文件自己的语法树,一旦有人加进网络导入就让构建失败。
skills/ 与 agents/
三个技能文件——learn 26,845 字节、review 24,600 字节、coach 40,217 字节——外加一个共享目录,放着对话语法、子 agent 派生规则、问题语法与探索件契约;其上是三份 Claude Code 子 agent 定义,以及同一批子 agent 给 Codex 的三份 TOML 移植。评分者规范是其中最长的一份,13,463 字节,读起来像一本判分手册:先列出缺了什么,再给已有的记分;犹豫时往低处判;步骤要跑一遍再判断;永远不能让上限定成地板。
hooks/
全部环境感知面就这一层:一个 SessionStart 脚本从磁盘读入学习者模型与到期队列,有复习到期时打印一行提示,否则一言不发。它有好几个变体,因为各家宿主说话方式不同——给 OpenCode 的一对 TypeScript、给 Hermes 的 shell 脚本、给 DeepSeek Harness 的输出 JSON 的包装脚本,以及一个 OpenClaw 钩子包。这里刻意没有会话结束钩子:收据在判分那一刻就写了,没有任何东西需要在结束时冲刷。
平台层
九种宿主,每个都只配一层薄胶水而不是一个分支:.claude-plugin/ 带 marketplace 条目,.codex-plugin/ 与 .agents/plugins/ 走 Codex 自己的市场,.opencode-plugin/ 有 106 KB、V1 与 V2 两个入口共用同一个 npm 包,另有 .zcode-plugin/、带三个提示词模板的 pi/ 扩展,以及放着钩子桥与补丁块的 dsh/。一个引擎、多个宿主,差异留在清单里,而不是分叉里。
gold/ 与 experiments/
仪器与试验:assessor-gold.jsonl 103,538 字节,装着 89 项对抗式题目,答案按构造剥离,形状与真实的判分结果负载一模一样,于是审计评的是真的那个评分者,而不是一个特例;三个实验预设(contrast-first、probe-variation、topic-reconstruction)供预注册的单人试验使用,而结论只允许由引擎计算。
docs/ 与发布机器
十七份文档共 451 KB——基础、前作、架构、一份愿景、一份视觉编码文献审计、两套目标架构、两份路线图——旁边是 54,318 字节的发布协议、276,967 字节的变更日志、六份发布审计与五份写下来的用户会话。最说明问题的是那份协议:十七道按顺序排列的闸门,每一道都是因为抓住了前一道看不见的 bug 才被加上的,外加一份编号清单,列出这个项目不允许发布的七类 bug。

取舍,以及它替代了什么

  • 导师就是主对话本身,而不是一个子 agent 替代 给导师单独一个上下文的子 agent

    架构文档写下的理由是:师徒关系必须留在上下文里,只有判分需要全新上下文的隔离——所以被拆成子 agent 的是评分者,不是导师。

  • 把排程算术放进代码,而不是放进模型 替代 让模型自己算间隔与日期

    作为一条规矩写出:FSRS 数学、状态校验与 schema 迁移都是确定性的,所以它们以代码运行,从不以模型算术运行。引擎独占每一个数字;而本项目借用验证手法的那一代项目留下的教训是,凡是可检验的都必须由某个可执行的东西来检验。

  • 菜单只管导航,永远不管知识 替代 把上一个项目的方向键模式也用在提问上

    这是刻意反过来做的:后勤可以用方向键,但提取永远是开放式的作答,因为选择题测的是再认,而不是回想。

  • 共享数据用白名单构造,而不是黑名单删除 替代 写完再把学习者的原话删掉

    黑名单是一份每个版本都得守住的承诺,白名单则靠构造本身守住:每个字段都按名字构造,不存在能让原话混进去的代码路径。被剥离的字段清单随导出文件一起发布;而评分者未受审计时,导出是拒绝执行,不是给一句提醒。

  • 在公共数据里署名,而不是匿名 替代 一个放在负载里的加盐匿名哈希

    README 的论证是:一旦信封被签名,装在里面的加盐匿名哈希就是表演;一键上传与匿名不能兼得,只能选一个并说出来。它还说署名是更强的科学——保持率研究靠的是纵向关联,跟着同一个学习者跨月跟踪本身就是那个问题,所以有署名的 n=100 胜过匿名的 n=500。

依据docs/03-architecture.md(17,199 字符)、docs/09-target-architecture.md(28,990 字符)、skills/_shared/explorable-contract.md、agents/engram-assessor.md、RELEASE_PROTOCOL.md 与 README.md。

制作过程

6 个阶段
  1. 01

    五十三天里发了五十五个版本,两个提交署名

    仓库建于 2026-07-05,最后一次提交是 2026-08-27:五十三天里 204 次提交,七月 162 次、八月 42 次。它们归到四个账号——nagisanzenin 182 次、nagisanzeninzz 7 次、luanweslley77 11 次、mertso13 4 次——而作者本人的提交用了两个名字署名:nagisanzenin 124 次、quan-vin 65 次;204 次里有 180 次共用一个 Gmail 地址,issue #10 里一位贡献者正是用「Hi Quan」跟他打招呼的。项目一共发了 55 个 release、55 个 tag,节奏更像机器而不是人。2026-07-11 那天,06:00 到 13:06 之间落地了十四个版本——v0.6.0 到 v0.6.4 挤在四十一分钟里,接下来四个小时又是 v0.7.0、v0.7.1、v0.8.0、v0.8.1、v0.9.0 与 v1.0.0。2026-07-24 有九个版本——v1.3.0 一路到 v1.9.1,外加 2.0 的候选版——在四十秒内相继发布,标题里写的都是同一天。修得也和新得一样快:标题为「what the post-release review caught」的那个版本,距离它要修的那个版本只有十三到十八分钟,这样的事发生过四次。204 次提交里有 144 次带共同作者尾注,其中 143 条署名某个 Claude 模型——Fable 5 九十条、Opus 4.8 四十三条再加四条标注百万上下文、Opus 5 六条。周边是 1,439 星、105 个 fork、5 个 watcher、4 个未关闭 issue、MIT 许可与 2,221 KB 的体积,其中大头是一个 783,600 字节的 scripts/engram.py——正是这个单一文件让 GitHub 把整个仓库的语言标成 Python。

  2. 02

    一条收据里有什么,以及它凭什么算数

    「你真的回想出来了」这件事,证据是磁盘上的一个 JSON 对象,由引擎在判分那一刻写下。一条收据包含题目与节点、这次相遇的种类(encode、review、transfer、audit 或 pretest)、被问到的那个问题、你写下的原话、你在看到任何对错信号之前自报的把握程度、判级(recalled、partial 或 lapsed)以及它映射到的评分(easy、good、hard 或 again)、每个错误模型一行、逐条引用评分标准的评语,还有排程器的那几个数字:判分前后的稳定度、间隔天数、可提取度估计与下次到期日。有两个字段纯粹是护栏。sid 从暂存区一路跟到评分者、再跟到收据,于是同一份判分结果重复应用只会是空操作,而不会把一次复习算两遍;grader 存的是评分者的稳定身份,规范明确禁止模型报出自己的权重,因为那是伪造的数据。真正让收据算数的是结构而不是流程:评分者看不到讲课过程,只看得到主张、评分标准、提问和你的原话;而没有收据,任何状态都不会推进——节点上的 state、fsrs 与 artifact 三个字段,会从任何外部传入的数据里被剥掉。README 自己举的例子就是这套论证:一次导师以为很顺的课,最后判出 1 个 recalled、4 个 partial 和 1 个首次提取,而排程器相信的是评分者。

  3. 03

    排程器是代码,拟合不过关的参数会被丢掉

    凡是看起来像日期的东西都由 engram.py 算,模型不碰。README 把它说成聊天关不上的那道缺口——没有关于你的记忆、没有检验你到底会不会的测试、也没有为「你合上终端那一刻就开始的遗忘」做任何安排——所以安排交给 FSRS-4.5:每个节点各自持有稳定度与难度,目标保持率 0.90,一个学习者一份拟合。refit 先在有五十次可用复习之后拟合一个间隔倍数,再拟合你自己的参数:初始稳定度要 64 次可用复习,完整权重向量要 400 次——而拟合结果打不过当前参数的会被拒绝,而不是被发布。仓库里的第一个 issue 来自一位读代码的读者:next_difficulty 的均值回归目标是 init_difficulty(4),而 FSRS-4.5 的目标是 init_difficulty(3),也就是说这段代码一直悄悄跑着 FSRS-5 的难度规则,却挂着 4.5 的标签;它在 v0.3.0 被修好,并由一个不动点自检钉住。同一份报告还发现收据写在图保存之后,于是崩溃可能留下一次未经核实的推进;现在收据先写,最坏情况因此退化成一次无害的重刷。编码之后的前两个间隔也被压住,好让至少三次分散复习落在第一个月内;而限量复习队列按「每分钟能救回多少预期保持率」排序,而不是按最逾期优先。

  4. 04

    评分者也要被评分,而先失败的是那套金标

    因为评分者的判级同时驱动掌握度、保持率、校准与排程本身,项目自带一套 89 项的对抗式金标——流利但空洞、简短但正确、自信但错误、答案对理由错,以及 v1.14 新增的三个类比对齐陷阱——并在它上面只报一个数字:258 次盲判中 0 次判得比严格读法更宽。这个数字是在 86 项那版上、三轮独立运行挣来的,如今由一道 stale-gold 检查看管:金标一变,徽章自己作废。它是怎么来的才是值得留下的部分。v0.7.0 发布的是一枚 QWK 0.93 的徽章,随后一位发布后评审者用故意被愚弄的评分者又跑了一遍金标:被愚弄的那个得分更高,1.000 对 0.990——原因是金标作者自己做过五处宽松裁定,把相邻的事实算成了部分给分。改正这五处之后,一致率从 0.889 升到 0.965,而引擎现在会在每一次审计里把这件事说出口:这些改正正是被评分者自己的分歧引出来的,所以随后的「一致」度量的只是作者的让步意愿。金标里刻意留着一处真实分歧,README 写下的理由是:一套不留任何分歧的工具,什么也测不出来。引擎也拒绝只凭一致性给评分者发证书:可复现与正确是两件事。

  5. 05

    按契约生成的探索件,并且要在图上登记

    交互式 HTML 只为那些「内容本身值得动手」的概念生成——有一个参数可以拖、有一个过程会展开——而绝不会因为某个学习者自称视觉型。每一件产物都要过七条契约:不先给出承诺就什么也解不开;至少有一个带引导的可操作模型,走「预测、操作、解释」这一小循环,新手档位先来一遍示范驱动;内嵌两处自由回想提示,答案藏到你先作答为止;装饰为零,文字永不压在动态画面上;文件自包含、离线可开、约 120 KB 以内;结尾要求你从零把论证骨架重建一遍;页眉注释则记下节点、日期与所依据的学习者模型输入。文件用 artifact set 登记到图上,这个命令会校验文件确实存在;收据则记下判分时是否存在这样一件产物——stats.modality 比较的正是探索件编码过的概念与纯对话概念在首次提取上的差别,每臂至少六项才出结论。examples/ 里放着两件成品,一件由 artifact-smith 在真实的 v0.5.0 会话里生成,一件是照同一份契约手写的参考实现;两件都配了一个托管链接,而那个链接在采集日返回 404,这条发布路线留下的唯一痕迹是仓库根目录下一个 0 字节的 .nojekyll。

  6. 06

    两位外部贡献者,和一段五周的安静

    往这个仓库提交过的账号有四个,其中两个是作者本人。luanweslley77 贡献了十一次提交和五个 pull request,其中几个来回了好几轮评审——把插件源码从它自己解压出来的目录里搬走、用带版本标记的块替换原来的指令文件,并两次提出会连带删掉用户数据的改动,直到维护者把影响范围收窄。mertso13 加上 Antigravity 支持,并拿出 1.1.4 版 agy plugin validate 的输出作为证据。最锋利的文字在 bug 报告里:tyteachestech 发现 stash add 会在盲评者看到之前把作答截到 800 字符,于是一份详尽的答案中途断句、丢掉两条评分标准(v1.11.2 修好,上限提到 2400);DorusKeijzer 报告提问与评分标准经常对不上,这件事后来变成 v1.10.0 的 probe_gap 字段与一条修复命令;SK-DEV-AI 就 OpenCode 安装提了两条带文件和行号、可在源码里核实的缺陷,维护者按报告者给的数字逐位复现之后,两条都在 v1.13.2 修掉。维护者也给自己开 issue,其中一个版本的标题是「a regression my own fix caused」。最后一次提交是 2026-08-27;2026-09-22 来了一个 issue,至今没有回复,四个 issue 开着,仓库也没有归档:在八周那样的节奏之后安静五周,正是本记录写「维护中」而不是「活跃」的原因。

相关档案

全部档案 →