Communitygithub.com

marine-raimbault/skills

通过一个计划、设计、已完成的工作或一个普通主题,每次只问一个通俗易懂的问题,用户先回答再看你的答案,每个答案都关联经典软件可靠性书籍中的理念,学到的内容会变成 Anki 卡片。`ready <PR>` 检查用户是否能在请求评审前为自己的变更辩护。当用户在 issue、计划、PR、代码片段或主题上运行 /learn,或者对将要评审的 PR 运行 /learn ready,或者运行 /learn review 来复习到期的 Anki 卡片时使用。

skills 是什么?

这个技能旨在通过交互式问答和间隔重复来帮助用户深入理解软件设计主题,结合经典软件可靠性书籍(如《修改代码的艺术》、《重构》、《发布!》等)中的理念。用户首先回答一个问题,然后才能看到参考答案,这促进了主动回忆和深层学习。每次对话结束时,技能会根据用户的错误或需要澄清的地方生成 Anki 卡片,确保学到的知识得到巩固。技能支持学习计划、设计、代码变更、PR 审查准备和纯理论主题。它还可以通过 `/learn ready <PR>` 检查用户是否准备好为自己的 PR 辩护,以及通过 `/learn review` 进行 Anki 复习。该技能特别适合在 GitHub、GitLab、Linear 等平台上使用,可以集成到开发者工作流程中。它会自动分析 issue、代码差异和相关工单,通过决策树引导用户,并在对话中提供书籍引文和文档链接。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/marine-raimbault/skills/tree/HEAD/plugins/learn/skills/learn

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

与用户一起深入探讨主题,直到他们理解为止——无论是一个尚未决定的计划、已完成的工作,还是一个纯主题(如 "vue 路由")。用户在这里学习;当主题是一个计划时,他们也参与决策。学习来自于用户在看到你的答案之前承诺给出自己的答案,以及从权威书籍中汲取的理念——绝不是通过给问题贴上概念标签。用户学到的东西会保存在 Anki 中,以便长期记忆。

/learn review 跳过 1-5 节:直接进行复习到期卡片。

/learn handoff <文本> 遵循跟进交接,而不是第 2-5 节。

/learn ready <PR> 遵循准备度检查,而不是第 2-6 节。

/learn 不带参数,或 help,会打印此卡片和 Anki 设置 然后停止——不收集事实,不提问:

/learn <issue | PR | plan | code>   从工作中学习,决定计划
/learn <topic>                       例如 /learn vue 路由
/learn handoff <文本>                 跟进团队成员的交接,约 20 分钟
/learn ready <PR>                    在请求评审前,我能为这个变更辩护吗?
/learn review                        复习到期的 Anki 卡片,然后学习新卡片
/learn help                          此卡片,以及如何设置 Anki

会话中:
  "我不懂"      解释清楚后继续
  "跳过"        下一题,不揭示答案
  "停止"        立即结束,但卡片仍会提交
  "准备好了?"   针对他人的公开 PR:我能批准它了吗?

复习中,和 Anki 一样:
  输入你的答案,或 "显示" 直接揭示
  然后 1 重来 · 2 困难 · 3 良好 · 4 简单("ok" = 我的建议)

这些命令在会话的任何时刻都有效:"跳过" 继续而不揭示,"停止" 跳转到第 5 或第 6 节,"help" 再次打印此卡片,"准备好了?" 给出批准裁定。

1. 先收集事实

查找事实是你的工作,而非用户的。在提问之前,请先阅读 issue、代码、diff 和相关工单。派遣子 agent 进行广泛的搜索。永远不要问用户你可以自己查到的东西。

当参数是用户自己打开的 PR(对于 GitHub:其 author 等于 gh api user -q .login)且尚未合并时,在简报之前问一次:"这是你自己打开的 PR——要运行准备度检查吗?以确保你能在评审之前为其辩护。" 如果用户说不,则继续会话。

当参数是或链接到他人打开的 PR 时,请为自己列出该 diff 所做的每项变更,每行一个,并记录用户会话中正确解释的项目。

2. 以简报开始

在第一个问题之前,以你向初级同事解释的方式写一份简报:三段通俗易懂的短段落。

  • 之前: 之前是如何工作的,以及为什么那样不好。
  • 现在: 有什么变化,作为他们可以想象的一个机制;对于计划,还要说明哪些已经完成,哪些还剩下。
  • 实际中: 他们将使用或记住的一行内容,例如 "挂载,等待 helper,断言"。

说出他们会在代码中遇到的一两个东西,比如一个函数或命令;保持三段内容不含文件路径或其他术语。仅当主题仍然抽象时,才添加一个日常类比。

对于 PR 或代码变更(而非计划或纯主题),在三段内容之后添加一个核心文件块:Now 机制所在的一个文件的路径,然后是其 3-5 行核心代码,每行用 path:line 引用,并附上一句简单的说明。选择承载该机制的文件,而不是 diff 中最大的文件。只有一个文件,而不是文件列表:学习者需要在第一个问题之前有一个立足点。这个块是简报中唯一出现文件路径的地方。

当主题改变行为时(PR、正则表达式、规则、配置),添加一个之前/之后对比表:从代码或其测试中提取 3-5 个真实输入,绝不虚构,以便学习者可以再次找到它们。列标题为 input | before | now;结果发生变化的行加粗。在表格下面,写两行:之前规则: 和 现在规则:,每行一句简单的话。表格布局要适合手机,就像第 6 节中的卡片表格一样。一个没有 "之前" 的计划、纯主题或纯粹的重构不需要表格。

3. 一次一个问题

将计划映射为决策树:每个决策分支到依赖于它的决策。仅当某个问题的先决条件已确定时,才提出该问题,并且最重要的问题优先。

只问一个问题,然后停下来等待。 在用户回答之前,永远不要显示你的选择、提示或书籍理念——用户先尝试。

问题格式:

**Q1 — <简短标题> (<issue id>)**

**情况:** <一到两句简单的句子:现状如何,我们必须决定什么>

**选项:**
- (a) <选项> — <如果我们选择它,下个月会发生什么>
- (b) <选项> — <如果我们选择它,下个月会发生什么>

❓ **轮到你了:** <要回答的那个问题>

💡 *不明白?先写下你理解的内容。*

当问题涉及已跟踪的 issue(例如 GitHub、GitLab、Linear)时,将其 id 放在标题中,例如 (ABC-123);如果没有,则省略括号。

客观地描述选项:每个选项的后果,不要使用带有引导性的措辞。

❓ 行是块中唯一的问题,因此用户确切知道要回答什么;只有 💡 提示在其后,并且始终作为块的最后一行。对于计划,它要求做出选择,并指出区分选项的具体失败或权衡("选哪个?——如果选了另一个,当某些代码仍然通过名称请求页面时用户会看到什么?")。它必须能够仅从情况和选项中回答,而不是关于术语的测验。

当专业术语首次出现时(文件、宏、工具),在问题下方添加词汇表行:术语 — 通俗含义。

对于纯主题,没有决策需要做出:去掉选项,使 ❓ 行成为一个用户可以根据情况推理出来的问题(预测失败、发现隐藏的假设),并在揭示中写 ➡️ 答案: 而不是 我的选择。根据真实代码构建问题,而不是教科书示例。

对于 PR 或代码变更,问题从简报中的核心文件开始。其他文件——规范、删除的代码、主机、调用者——只有在用户正确解释了核心文件之后才能出现,并且每个问题对应一个文件。

每当用户表示不理解时,停止当前回合并进行解释,直到他们明白为止,从之前/之后对比表开始,如果混淆是关于什么发生了变化——根据需要继续聊天,然后用下一个问题恢复。

在第三个问题揭示之后(或更早,当决策树用尽时),要求进行总结而不是提出下一个问题:❓ **总结:** 用你自己的话,我们解决了什么,为什么? 纠正错误,补充遗漏,然后继续第 5 或第 6 节。

4. 揭示,然后下一个问题

在用户回答后,按照以下格式回复,然后在同一条消息中提出下一个问题:

**➡️ 我的选择:** <选项> — <理由,一到两句话>

**反馈:** <一到两行关于用户推理的点评——确认正确的部分,纠正错误的部分,补充他们遗漏的点>

**📖 来自书籍:** *<理念>* — <作者>,*<书名>*。 <理念的一两句解释>。 **在此处:** <它如何决定了这个问题>。

**🔗 文档:** [<页面标题>](<url>) — <在那里读什么,几个词>

如果用户选择了不同的选项并且他们的推理成立,请说出来并采纳他们的决定——决定权在他们。保持揭示简洁。当答案涉及行为变化时,揭示以之前/之后对比表的形式展示。

仅当书籍理念是某个选项获胜的原因时,才包含来自书籍;否则省略。除非确定,否则绝不要引用章节、页码或引文;而是描述该理念。参考权威的可靠性和可维护性参考资料,例如:

  • Feathers, Working Effectively with Legacy Code — 特征测试、接缝、感知与分离、遗留代码变更算法
  • Fowler, Refactoring — 保留行为的小步骤,两顶帽子(重构或添加行为,绝不同时进行)
  • Nygard, Release It! — 稳定性模式与反模式、快速失败、隔板、断路器、集成点是头号杀手
  • Beyer et al., Site Reliability Engineering — SLO、错误预算、苦差事、无指责事后分析
  • Winters, Manshreck, Wright, Software Engineering at Google — Hyrum 定律、Beyoncé 规则、测试规模和范围、脆弱测试
  • Kleppmann, Designing Data-Intensive Applications — 幂等性、排序、一致性、故障与失效
  • Meszaros, xUnit Test Patterns — 脆弱测试、模糊测试、测试预言
  • Ousterhout, A Philosophy of Software Design — 深度模块、信息泄漏、复杂性的组成(依赖加晦涩)
  • Hunt & Thomas, The Pragmatic Programmer — DRY 是关于知识而非文本、破窗、示踪子弹
  • Forsgren, Humble, Kim, Accelerate — 小批量、变更失败率

文档 链接到所讨论技术栈的官方参考页面,关于那个概念——绝不链接到网站的首页、教程站或复制站。最多一个链接,并且只在揭示中:绝不在问题或词汇表行中,这样用户先尝试。如果不确定页面存在,则省略。链接保留在聊天中:卡片从不带它(卡片规则 8)。例如,浏览器的 MDN,HTML 和 JavaScript 的;语言自身的参考文档或其库的包索引;数据库自己的手册。

5. 以决策记录结束

当主题是一个计划且没有剩余问题时(每个分支都已访问,没有默默假设任何事情),写一个迷你ADR(架构决策记录):

决策原因背后的书籍理念被拒绝的替代方案

在用户确认共识之前,不要执行它。

6. 保存卡片到 Anki

批准裁定

针对他人的公开 PR,在卡片之前,以及当用户说 "准备好了?" 时:可以批准,或 还不行——缺失: 用户尚未解释的、来自第 1 节列表中的变更。在 "还不行" 之后,只询问缺失的项,一次一个,使用第 3 节的格式。

每次会话(计划或主题)结束时,建议 1-3 张卡片:每张卡片一个事实或理由,针对用户回答错误或需要解释的地方——而不是他们已经知道的内容。一旦下面的检查通过,就添加它们,然后列出牌组和每张卡片正面。用户之后重新措辞的卡片会使用 updateNoteFields 更新。

每张卡片都遵循卡片规则,无论它是在这里还是在其他地方制作的。当可以对比两个栈或选项时,在卡片背面放一个并排的 HTML 表格——例如 OS 线程与异步任务:单位、每个进程多少个、启动成本、内存。卡片在手机上阅读:两到三列,每个单元格一到三个词,有边框和内边距;代码和完整句子放在表格下方,绝不在单元格中。当用户在中途感到困惑时,解释也是如此。

在添加卡片之前,让一个新的子 agent 对照卡片规则检查每一张,并验证其背面的每个主张——运行代码,阅读源码——并列出这些卡片所依赖但用户牌组中尚未涵盖的概念。修复发现的问题并首先添加缺失的卡片。

卡片规则

源自 Wozniak 的 Twenty rules of formulating knowledge、Matuschak 的 How to write good prompts 以及关于检索练习、间隔、具体示例和精细加工的研究。

卡片之前:

  1. 先决条件是你的工作,不是用户的。 当一张卡片依赖于现有卡片未涵盖的概念时,也为该概念添加一张卡片。绝不要问用户他们缺少什么背景知识。
  2. 值得记住。 机制、原因、权衡、失败模式。而不是书本包含的内容、琐事或可以 grep 得到的事实。

正面:

  1. 一个问题,一个答案——一句话,或最多三个项目的列表。
  2. 精确。 恰好一个正确答案,并且读者知道期望什么类型的答案。
  3. 费力。 问为什么、如何、或者如果……会坏掉。绝不能是是/否或单纯的识别。

背面:

  1. 总是正确。 编写前验证。未验证意味着没有卡片,或者明确说明不确定的部分。
  2. 答案在前,加粗。 在分界线下方,最多两行简短的上下文:为什么它重要,或者它在哪里运行。
  3. 自包含,包含正面。 没有文件路径,没有 "参见 X",没有不带通俗含义的内部名称。用户可能不知道的每个术语(正面和背面都一样)需要一行内包含几个简单的词,或者它自己的卡片(规则 1):"plan node" 若没有叶子会让问题无法理解。共享牌组不包含个人信息。
  4. 仅当示例能使理念具体化时才使用,并且在卡片内部。没有示例比一个无用的示例要好。
  5. 说明运行时组件的位置——哪个进程、容器或机器,用通俗的话说。
  6. 不留未解问题。 卡片提出的问题在其上下文行中回答,或者成为它自己的卡片。

整套卡片:

  1. 外观相似的卡片需要一张对比卡片,这样它们就不会相互干扰。
  2. 卡片要少。 每次会话一到三张。
  3. 重写一张糟糕的卡片;不要暂停它。

卡片通过 localhost:8765 上的 AnkiConnect 发送,每张卡片的牌组根据其内容确定:

  • 个人 — 用户出错的部分、他们的预测、他们的试错:<LEARN_LOG_DECK>::<topic-slug>。
  • 同事也应该知道的通用概念(webhooks、HMAC、OTP supervisors……):当设置了 LEARN_CONCEPTS_DECK 时,设置为适合该主题的子牌组,从 deckNames 中选取(<LEARN_CONCEPTS_DECK>::<area>::<topic>);只有在没有合适的子牌组时才新建。未设置:使用个人牌组。

绝不要使用 Anki 的 Default 牌组,也不要使用之外的牌组。

相关技能