大模型时代,程序员该如何保持学习?
原文来源:https://www.ogzhanolguncu.com/blog/how-to-keep-learning-in-the-age-of-llms/
原作者:Oguzhan Olguncu
我想聊聊在大模型(LLM)时代,自己是如何继续学习新知识的,因为这件事如今真的变得非常艰难。不管是 LLM、AI 还是智能体(Agents),你怎么称呼它们都行,它们都能一键秒出答案(one-shot),彻底剥夺了你在犯错中探索的所有乐趣。这里我主要针对编程领域展开,但我想这套道理适用于任何事情。过去,我们学习框架、编程语言和设计模式等新事物,往往是先犯错、碰壁,然后再从中吸取教训。但现在一切都太快了,我们再也不愿意把时间花在“做错事”上,哪怕从长远来看,试错对我们其实更有益处。大脑恰恰是通过这种艰难的摸索与挣扎,才能真正把新知识学进脑子里。
坦白讲,我自己也难逃此劫。以前为了学懂某些东西,我会花大量精力反复死磕;但现在这种死磕感觉变得毫无意义,因为 LLM 随时能给出你想要的任何内容。正是这些思绪把我引向了这里——因为我遭遇了瓶颈期,而无论是出于职业发展需要还是纯粹为了好玩,我都必须学习更多新东西。以下便是我在大模型时代重新找回学习之法的心得体会。
当时我正在阅读一篇名为 Bitcask [^1] 的论文。如果你还没读过,且对编程感兴趣,我强烈建议你去读一读。这是一篇极其轻量的论文,哪怕你不是经验老到的资深程序员也能读懂,因为里面的概念非常容易消化;只要你愿意动手,实现起来也毫无门槛。总之,我开始阅读那篇论文,并且打算在没有任何 LLM 协助的前提下亲自动手实现一遍——大部分代码我也确实独立完成了。后来我在代码的某处卡壳了,很想借助 LLM。但这一次,我不是让它直接帮我写出卡壳部分的代码,而是让它把相关概念清晰地讲明白,从而让我能靠自己写出来。就在那时我意识到:我完全可以让 LLM 来教我知识,而不需要它直接丢给我全部答案。同时,它还可以包揽所有繁琐枯燥的杂活,让我不至于感到厌烦。假设你想通过构建一个聊天应用来学习新知识,你需要两部分:服务端和客户端。假定你只想练习服务端是如何运作的,如果对客户端毫无兴趣,何必非要把精力耗在写客户端上从而扼杀自己的学习热情呢?这正是 LLM 大显身手的舞台。你可以让 LLM 替你搞定那些无聊的部分,比如写测试、搭脚手架工具和做可视化。我绝不是说这些东西毫无用处,我只是说如果它们引不起你的兴趣,就不必非要勉强自己亲自动手。
于是我开始去看看别人是怎么做的,进而发现了一个名为“socratic-code-mentor(苏格拉底式代码导师)”的 Skill。它从不直接把答案塞到你手里,而是向你抛出恰到好处的问题,引导你自己找到答案——也正是这个推导过程,才能让知识真正印在脑海里。
这是一个简化后的示例:
我: 我的求和结果应该是 6,但实际得到的是
NaN:const nums = [1, 2, 3]; let sum = 0; for (let i = 0; i <= nums.length; i++) sum += nums[i]; console.log(sum); // NaNLLM:
nums里面一共有多少个元素?最后一个元素的索引是多少?我: 三个元素,所以最后一个索引是 2。
LLM: 你的循环在执行时,
i取到的值分别有哪些?我: 0, 1, 2, 3……噢!
nums[3]是undefined,而6 + undefined结果就是NaN。LLM: 没错:
index: 0 1 2 3 nums: [1, 2, 3] undefined i: ▲ ▲ ▲ ▲ ← 多走了一步你打算怎么修复它?
我: 改用
i < nums.length。
瞧,LLM 并没有直接冷冰冰地告诉我“你的循环多走了一步”。它问了我几个问题,画了一张小图,我就靠自己找出了 Bug。这个教训我绝对忘不掉,因为它迫使我自己把整个逻辑推导了一遍。
学习是通过亲手解决问题发生的,而不是被动全盘接受别人塞给你的事实。《认知天性》(Make It Stick,如果你对学习心理学感兴趣,这是一本极棒的书)说得很透彻:
“当你在看到解决方案之前,先被要求为了解决问题而努力挣扎一番时,后续呈现给你的解决方案就能学得更扎实、记得更牢靠。” [^2]
甚至我亲爱的朋友兼 CTO 也经常和我玩这套思维游戏。当我问他“为什么我们不按 X 方案来做呢?”,他会反问我“你觉得我们为什么要用 X 方案?”我阐述自己的理由,他针对我的回答再抛出另一个问题,最终我自己顺藤摸瓜找到了合理的答案。而且记忆深刻。
所以,你必须让自己的大脑得到适度的锻炼。这正是那个 Skill 所做的事——它逼你去质疑和思考。
第二个难题是如何保持学习热情,而这需要纪律。如果你正在构建一个有一定复杂度的项目来学习,不可能一蹴而就。因此,提前规划好你需要达成的目标,这样你就不会把时间浪费在“下一步该干嘛”的纠结上。否则,开启下一轮学习时,你往往会对着空白屏幕发呆 30 分钟。至少我是这样的。当我们缺乏动力或纪律时,很容易陷入拖延。
理想的目标是:你坐到电脑前,对 LLM 说一句“我们继续吧”,它就能从你上次停下的地方直接接手。出人意料的是,LLM 在帮你拆解目标清单这件事上表现得极其擅长。
假设你想写一个玩具级的 LevelDB,换句话说,一个 LSM-tree(如果你不是程序员且依然在读这篇文章,我很抱歉)。让你的 LLM 将其拆分成若干阶段,并附带明确的目标。这样每次开启新一轮编码时,你都能亲手打勾标记一项进展。
在读完 Bitcask 后,我想挑战一些更难且与数据库相关的项目,于是我着手构建一个 LSM-tree 并命名为 tinylsm(以后我大概会继续做我的 tiny-xyz 系列项目)。以下是我在 tinylsm 中使用的 PLAN.md 简化版:
# tinylsm — 构建计划
**黄金法则:** 每个阶段都必须以一个可运行的系统和全部通过的测试结束。
绝不同时开发两个半成品功能。
## 第 2 阶段 — WAL(预写日志)
**目标:** 在崩溃中幸存。所有写入操作在进入内存之前必须先写入日志,
这样在崩溃后我们能够重放日志,不丢失任何数据。
**步骤:**
1. 编解码单条记录。
2. 将记录追加写入文件。
3. 启动时重放文件;在遇到写了一半的最后一条记录时优雅停止。
**完成标准:** 在写入中途强杀进程,重新启动,所有已完成的写入完整恢复。
**陷阱:** 崩溃可能会把*最后一条*记录截断成一半。这是预期现象,并非数据损坏。
## 进展
- [x] 第 1 阶段 — 跳表(Skiplist)
- [x] 第 2 阶段 — 预写日志(WAL)
- [ ] 第 3 阶段 — 内存表(Memtable)
- [ ] ...
每一个阶段都包含相同的四个部分:一个目标、若干细分步骤、一个你可以明确核验的“完成标准”,以及可能坑到你的“陷阱”。底部的打勾清单就是“我们继续吧”的核心,LLM 读完它就知道你目前处于什么进度。
通过这种方式,你完全不必消耗任何意志力来纠结该干什么——而启动自己去行动所耗费的心力往往远超你的想象。现在这种情况更严重了,毕竟我们时刻都在承受来自 LLM、短视频等的持续多巴胺轰炸。我们渴望快速搞定事情。而一份清晰的计划恰恰能满足这一点。哪怕你精疲力竭,也只需完成微小的一步,就可以当成一次快速的小胜利。
而且以这种方式工作实际上能让你学到更多,因为你是在以短周期的节奏、跨越数天乃至数周反复重返同一个项目。这就像禅宗大师铃木俊隆关于漫步晨雾的比喻:你不会立刻察觉自己被淋湿,但渐渐地,全身早已湿透。[^3] 心理学上将此称为间隔练习(spaced practice):在各个学习周期之间留出少许遗忘时间,会迫使大脑把记忆重新提取出来,而正是这种费劲提取的过程,让记忆变得牢不可破。[^2]
到目前为止,我们探讨了如何正确利用 LLM 辅助学习(而非一键获取现成方案),以及如何通过规划来避免无休止的拖延。但还有关键的一环不可或缺。
就我个人而言,当能得到直观的可视化反馈时,我做事的享受程度会大幅提升。它可以是一个粗糙简陋的界面(half-assed UI)、一个交互式命令行(REPL),无论什么形式都行。它会督促我留在游戏中,并对接下来的进展保持好奇心。在写 tinylsm 时,我让 LLM 帮我写了一个微型的 REPL 工具,我可以在里面往数据库灌数据、看着各个 SSTable 层叠堆积,并进行读取性能压测:
tinylsm> bench
miss latency vs L0 height
88 tables ██████████████████████████████ 174.9µs
1 table ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1.7µs
说实话,亲眼看到自己写的分层压缩(compaction)代码让读取速度提升了 100 倍,确实极大地激发了我的编码热情。
我建议你在下一个大项目中也这么做。我在文章末尾分享的那个 Skill 同样带有这种机制,不过你尽可以发挥自己的想象力。有了 LLM,你可以构建任何能激励你走得更远的可视化小工具。
最后一点建议:让你的下一个项目雄心勃勃一些。在 LLM 诞生之前,实现像 LSM-tree 这样的系统意味着要在成堆的 GitHub 仓库里翻找代码,还要啃特定专业书籍的特定章节。而现在,你只需说一句“我想搞明白 XYZ 是怎么工作的”,LLM 就会去替你调研并把所需的一切整理出来。所以在某种程度上,事情变容易了,但我们也变懒了。我想事情一向如此——我们天生就喜欢偷懒。
总结
给像我一样的“懒人”做个类似 TL;DR 的要点梳理:
- 不要用 LLM 直接给你答案。把它当成你的导师。
- 让 LLM 包揽枯燥杂活:编写测试、搭建工具链等等。
- 提前借助 LLM 做好规划,这样你就能以最少的意志力消耗保持动力。如今我们所剩的意志力本就少得可怜。
- 让进展清晰可见:一个 REPL、一张图表,任何能让你持续留在游戏里的形式都行。
- 追求快速的小胜利。哪怕每天只写 30 分钟,持之以恒也远胜过猛肝两天然后连休十天。
- 保持好奇心,勇于尝试那些超出你现有能力范围的知识。哪怕你不是最顶尖的天才,你也可以对 LLM 说上 100 遍“我没听懂”,它依然会耐心地重新为你解释。所以,别只满足于简单的东西。
这是我使用的 Skill:socratic-code-mentor。我先找到了原作,然后根据自己的学习习惯进行了调整改造。拿去用吧,并把它改造成最适合你的样子。
最后,用一句我努力践行的格言来收尾:
“当你做一件事时,应当将自己彻底燃烧殆尽,就像一团熊熊燃烧的篝火,不留一丝自己的痕迹。”
—— 铃木俊隆 [^3]
参考文献
[^1]: Justin Sheehy and David Smith. Bitcask: A Log-Structured Hash Table for Fast Key/Value Data. Basho Technologies, 2010.
[^2]: Peter C. Brown, Henry L. Roediger III, and Mark A. McDaniel. Make It Stick: The Science of Successful Learning. Harvard University Press, 2014.
[^3]: Shunryu Suzuki. Zen Mind, Beginner’s Mind: Informal Talks on Zen Meditation and Practice. Weatherhill, 1970.