工程师从实践出发积累的系统化 AI 工程lessons,涵盖 tokenizer、提示、代码生成等实战主题。
跨越 20 个阶段、由 Repo 驱动的 AI 学习
第一次写 tokenizer 时,我用的是一个 for 循环。我手动统计 byte pair,合并出现频率最高的组合,然后等了大约四十秒,看着它处理完一小段语料。输出很慢。输出很难看。但输出是正确的。
GitHub Repo:https://github.com/rohitg00/ai-engineering-from-scratch
然后,我把同样的输入交给 tiktoken,看着它在四十毫秒内完成处理。
就是在那一刻,tiktoken 不再是什么魔法。它做的事情与我前一天晚上写出来的一样,只不过用了 Rust,展开了循环,而且缓存已经预热。它不再只是一个 library,而是我的代码,只是更快。
接下来的十八个月里,我一直遵循着这条原则:先亲手构建一个小型版本,再用生产环境中的 library 运行同样的东西。因为你已经写过那个更小的版本,所以 framework 不再是一个黑箱。
我按照这条原则写了 435 节课。它们组成了一套名为 ai-engineering-from-scratch 的免费课程,并采用 MIT 许可证。本文讲的就是这条原则、它为什么有效,以及当我把它贯穿 AI engineering 的二十个阶段后,最终得到了什么。
去年发布的一项 CS 学生调查让我印象深刻。大约 84% 的学生每天都会使用 AI 工具,但只有约 18% 的人认为自己已经做好准备,可以在工作中用这些工具交付实际成果。
这种差距与能否获得工具无关,问题在于教学内容的组织方式。
你可以 fine-tune 一个模型,却从未亲手写过一次 forward pass。你可以把 Agent 接到一个函数上,却从未定义过 attention。你可以执行 pip install transformers、发布一个 demo,却从未手动计算过 gradient。Framework 接受这样的交换条件。但当你的 loss curve 第一次发散、tokenizer 处理日语时消耗的 token 是英语的十倍,或者 Agent 因为 context window 有一半都塞满了重复的 boilerplate 而开始输出 hallucination 时,这种交换就失效了。你导入的 library,其 README 里不会告诉你这些问题。
我尝试过的大多数课程,要么只讲数学,一行代码也不写;要么只讲代码,一行数学也不写。少数两者兼顾的课程,又会在第一节课直接跳到 PyTorch。我想要的是两者之间的那一层,所以我自己把它写了出来。
每一个值得掌握的 algorithm,都分成两个部分。
Build It:只使用 Numpy 和 stdlib,不使用 framework。你先在纸上一步步推导 chain rule,然后亲手写出 backprop。你在循环里统计 byte pair,然后把它称为 tokenizer。你把三个矩阵组合起来,然后把它称为 attention。代码很慢,但足够短,可以一次读完。你可以在任何地方插入 print 语句。
Use It:使用相同的 algorithm 和相同的数据,但这一次通过 PyTorch、sklearn、tiktoken,或者任何实际用于生产环境的工具来运行。你比较两边的输出差异,观察 framework 如何隐藏那些繁杂细节。因为小型版本就放在旁边的文件里,所以 framework 不再是一个黑箱。
关键在于,这两个部分缺一不可。只有 Build It,你得到的只是无法扩展的玩具代码。只有 Use It,你得到的只是一个出了问题也无从调试的 library 调用。把两者结合起来,你既拥有了一个可以用于实际交付的工具,也在脑中建立起了它如何工作的模型。
下面是第一节 transformer 课程的核心部分。不使用 framework,也不使用 checkpoint。这里运行的数学原理,与 Llama、GPT-class models,以及大多数你听说过的 open weights 内部运行的原理完全相同。
import numpy as np
def attention(Q, K, V, mask=None):
d_k = Q.shape[-1]
scores = Q @ K.swapaxes(-1, -2) / np.sqrt(d_k)
if mask is not None:
scores = np.where(mask, scores, -1e9)
weights = np.exp(scores - scores.max(axis=-1, keepdims=True))
weights /= weights.sum(axis=-1, keepdims=True)
return weights @ V
# Toy run: 4 tokens, 8-dim
rng = np.random.default_rng(0)
Q = rng.standard_normal((4, 8))
K = rng.standard_normal((4, 8))
V = rng.standard_normal((4, 8))
out = attention(Q, K, V)
print(out.shape) # (4, 8)
这就是全部。softmax 中用于保证数值稳定性的技巧,也就是在执行 exp 之前减去最大值,是最容易让读者困惑的部分,因此课程专门用一段内容推导了它。在这个阶段的后续课程中,mask 会成为 encoder attention 与 decoder attention 之间的区别。
课程的第二部分会通过 torch.nn.MultiheadAttention 运行相同的示例,并在 head count 设置为 1 后,检查两者的输出是否在数值精度范围内一致。现在,PyTorch 不再是黑箱。它就是你的代码,只不过针对 CUDA 做了编译。
形式相同,但位于技术栈的另一层。Phase 2 使用手写的 gradient descent 构建 linear regressor,不使用 optimizer,也不使用 autograd。
import numpy as np
def fit(X, y, lr=0.01, steps=1000):
w = np.zeros(X.shape[1])
b = 0.0
n = len(X)
for _ in range(steps):
pred = X @ w + b
err = pred - y
w -= lr * (X.T @ err) / n
b -= lr * err.sum() / n
return w, b
六行数学,其中三行是 gradient。课程使用 scikit-learn 重新执行相同的拟合后,得到的 coefficient 完全一致。再通过 PyTorch 的 optim.SGD 重新运行相同的循环后,两条 loss curve 也会重合。现在,你不再需要凭信念去相信 optim.SGD。
把这样的过程堆叠二十个阶段,你会在 Phase 10 写出一个小型 LLM,在 Phase 14 实现一个能够工作的 Agent loop,再在 Phase 19 构建一个 multi-agent system。这座高塔的每一层,都在 framework 版本下方放着一个手工构建的版本。
还有一件事,是我开始写这套课程之前完全没有预料到的。除了代码之外,每一节课还会产出以下四种可复用成果中的一种。
用于特定任务的 prompt template。可以直接放进 Claude、Cursor 或 Codex 的 skill spec。职责明确的 Agent definition。把课程代码作为工具暴露出去的 MCP server。
到了 Phase 19,你已经拥有数百个这样的成果。它们并不是什么新奇玩具。当一个真实任务落到你桌上,而你想起“我在 Phase 12 为这个写过一个 retrieval skill”时,它们就是你真正会拿来使用的东西。这套课程在学习过程中是一本教材,学完之后则变成了一个工具箱。
直接在浏览器中阅读。打开 aiengineeringfromscratch.com 上的任意课程即可。不需要设置环境,也不需要 clone。
git clone https://github.com/rohitg00/ai-engineering-from-scratch.git
cd ai-engineering-from-scratch
python phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py
把 skills 安装到你的 Agent 中。支持 Claude、Cursor、Codex,以及其他一些工具。
npx skills add rohitg00/ai-engineering-from-scratch
然后在 Agent 中运行 /find-your-level。回答十个问题后,Agent 会为你选择一个阶段,并给出所需时间的估算。如果你以前交付过 ML 项目,可以从 Phase 7 开始。如果你来自 frontend 背景,则可以从 Phase 1 开始。
这不是视频课程,不是复制粘贴式部署,不是五分钟的 YouTube 速成讲解,也不是“助你拿下高级职位的十个 prompt”。
这些课程内容密集,数学也是真实的。所有内容都可以在一台笔记本电脑上运行:用 Python 实现 backprop,用 TypeScript 实现 attention,用 Rust 编写一个玩具 GPU kernel,再用 Julia 实现 Bayesian sampler。如果你只想调用 API,这套课程会让你觉得进度很慢。如果你想知道 API 为什么能够工作,这就是那条学习路径。
我写下这些内容,是因为互联网上没有我刚入门时希望它存在的那种课程。我选择免费发布,是因为当初那个依赖 open-source 资源的我,才是最需要它的人。
这条原则奏效了。正是因为它,这套规模如此庞大的课程才能在十八个月里始终保持一致,没有自相矛盾。先构建一个小型版本,再通过 framework 运行同样的东西。这样,framework 就不再是魔法。
如果这套课程对你有帮助,请为 Repo 点一颗 star,这意味着下一个需要它的人能够更早发现它。如果缺少了什么内容,请提交一个 issue,我会把对应的课程写出来。
对于进一步的操作,你可以考虑屏蔽此人和/或举报滥用行为。