60行配置文件斩获5万星:Karpathy如何用四条原则终结AI编程的混乱时代
当Andrej Karpathy在X上吐槽"模型会替你做错误假设、会把简单问题复杂化、还会顺手改掉无关代码"时,他大概没想到,会有人把这些抱怨整理成一套可执行的规则,并在两个月内收获超过54,000颗GitHub星标。
这不是一个复杂的项目。没有数百个文件,没有精巧的架构设计,甚至连"代码"都很少。它的核心就是一个60行的CLAUDE.md文件,里面只有四条原则:Think Before Coding、Simplicity First、Surgical Changes、Goal-Driven Execution。
但正是这种极简,击中了当下AI编程的最痛点——我们不再缺"会让模型写代码"的方法,缺的是"让模型写对代码"的纪律。
一、问题的本质:AI正在加速错误
1.1 从80/20到20/80的转变
Karpathy在原始推文中提到一个关键数据:他很短时间内就从"80%手写代码、20%用agent",切换到了"80% agent coding、20%手动修补"。这个转变不是个例,而是正在发生的普遍趋势。
GitHub Copilot、Claude Code、Cursor等产品已经把代码补全、重构、调试做到了相当流畅的程度。很多开发者体验到的不是"AI会不会写代码",而是"用AI写代码太快了,快到我还没反应过来它已经写错了"。
这种速度与质量的矛盾,在技术圈引发了两种截然不同的反应:
乐观派认为这只是模型的临时缺陷。等GPT-5、Claude 4或者更智能的模型出现,这些问题会自然消失。他们继续追求更长的上下文、更强的推理能力、更广的工具调用范围。
务实派开始反思:也许问题不在模型的"智力",而在于我们如何使用它。就像一个聪明但没有边界感的员工,他的聪明反而成了问题的一部分。
andrej-karpathy-skills代表的是务实派的思路——改变的不是模型,而是使用模型的方式。
1.2 三种致命错误模式
Karpathy在那个推文中总结了三种最常见也最致命的AI编程错误:
错误一:隐式假设与隐藏困惑
"模型会替你做错误假设,然后不检查、不澄清,也不主动暴露矛盾和取舍。"
这是最危险的一种。因为它不是能力不够,而是"太自信了"。模型在接收到一个模糊的需求后,通常会默默选择一个解释并继续执行。比如你说"加个登录验证",它可能选择session-based方案,完全忽略了你可能想要JWT;你说"优化这个函数",它可能选择空间换时间,完全不管你其实更关心内存占用。
这种错误最可怕的地方在于:它会制造大量的技术债,而你甚至不知道债在哪里。
错误二:过度工程化的倾向
"它特别喜欢过度设计,把本来100行能解决的问题膨胀成1000行。"
这是LLM的"天性"。被大量开源项目训练出来的模型,天然偏好"优雅"的抽象层、"完善"的错误处理、"扩展性"的设计模式。但这种偏好在实际工程中往往是灾难。
一个简单的脚本,模型可能会给你引入工厂模式、策略模式、配置化路由——因为它见过太多"好的代码"。但它不理解的是:这些"好代码"背后有明确的规模预期和迭代历史,而不是一开始就这么设计的。
错误三:边界失控的修改
"它有时会在执行任务时顺手改动评论、代码甚至结构,而这些改动和原任务根本无关。"
这是最让代码审查者崩溃的问题。你让模型修一个小bug,diff一看,它改了半个文件:变量名被重命名、注释被润色、几个"看起来没用"的函数被删除、文件末尾还多了一行空行。
模型的解释通常是"我在改善代码质量"。但它不理解的是:每次修改都是风险,每行改动都需要review。在大型项目中,这种"顺手优化"制造的merge conflict、behavior regression、 inexplicable bugs,代价远超它带来的"质量提升"。
二、四条原则:从抱怨到规则
forrestchang在andrej-karpathy-skills项目中做的,就是把Karpathy的三种问题,翻译成四条可执行的行为规则。让我们逐条深入。
2.1 Think Before Coding:让隐式思考显式化
核心指令:不要假设。不要隐藏困惑。暴露取舍。
这条原则本质上是给语言模型增加一个"思考阶段"。以下是具体规则:
在实现前:
- 显式声明你的假设
- 如果不确定,宁可问也不要猜
- 如果存在多种解释,全部列出来,不要偷偷选一个
- 如果存在更简单的方案,主动提出来
- 如果自己困惑了,停下来问清楚
为什么这能解决问题?
传统的prompt engineering是在"喂答案"。你告诉模型"用TypeScript写"、"按照这个模式"。但这种方法有一个盲点:它只覆盖了技术栈和风格,没有覆盖问题理解。
Think Before Coding解决的是:"你和模型说的话,模型真的听懂了吗?"
来看一个具体例子。假设你说:
"给这个表单加个验证"
模型可能有多种理解:
- 加前端实时验证
- 加后端API验证
- 两个都加
- 只验证必填字段
- 验证格式和长度
- 两者的组合
如果模型默默选择一个实现,你可能得到的是:
- 一个臃肿的、你不需要的前端验证库
- 漏掉你真正需要的后端安全校验
- 或者两者都有但架构混乱
而如果模型遵循Think Before Coding,它会先输出:
假设:
- 这是前端表单,需要在提交前验证
- 验证规则包括:必填、邮箱格式、密码长度
- 暂不添加后端验证(如需请告知)
请确认以上假设是否正确再继续。
这样,你有机会在错误的代码被写出来之前,纠正理解。
代码实现建议
把这个原则应用到你的CLAUDE.md中:
## Think Before Coding
在写任何非trivial的代码前:
1. **陈述假设** - 用2-3句话说明你对需求的理解
2. **列出选项** - 如果有多个可能方案,简要对比优劣势
3. **确认边界** - 明确什么在范围内、什么在范围外
4. **等待确认** - 对于复杂任务,先等人类确认再动手
你还可以加一条"强制思考"触发器:
如果任务涉及:
- 跨文件修改
- 架构变更
- 新增公共组件/函数
必须先输出【思考】section,让用户检查。
2.2 Simplicity First:对抗过度设计的武器
核心指令:写解决问题所需的最少代码。不做任何推测性设计。
这是对AI"过度聪明"的最直接对抗。具体规则:
不允许:
- 添加未被要求的功能
- 给只用一次的代码建抽象层
- 凭空增加"可配置性"和"灵活性"
- 为不可能发生的场景写防御性逻辑
- 支持"也许以后会用到"的扩展点
必须:
- 如果200行能解决,不要写500行
- 如果50行能解决,不要写200行
- 如果10行能解决,不要写50行
"简单"的度量标准
这条原则最难的部分是:什么算"简单"?
项目给的判断标准很实用:
问自己:"一个资深工程师会说这段代码过度复杂吗?"
如果答案是"会",那就简化。
这个标准之所以好,是因为它锚定在人类判断而不是机械规则。比如:
| 场景 | 模型倾向 | 资深工程师判断 |
|---|---|---|
| 单次脚本加CLI参数解析 | 会加上argparse、help、多模式 | 这是过度,直接硬编码或sys.argv |
| 一次性脚本加logging | 会配logging module、格式、输出文件 | print够用,别过度 |
| 临时函数加单元测试 | 写完整test case | 没必要,直接跑一下看结果 |
| 小组件加props验证 | 用TypeScript加位置类型 | 简单注释就能说明 |
实战案例
假设你让模型"读取CSV并计算平均值",错误的模型输出:
import pandas as pd
import logging
from typing import List, Dict, Optional
from pathlib import Path
class CSVAnalyzer:
"""一个通用的CSV分析器,支持多种统计方法。"""
def __init__(self, config: Optional[Dict] = None):
self.config = config or {}
self.logger = self._setup_logging()
def _setup_logging(self):
log_config = self.config.get('logging', {})
logging.basicConfig(
level=log_config.get('level', 'INFO'),
format=log_config.get('format', '%(asctime)s - %(levelname)s - %(message)s')
)
return logging.getLogger(__name__)
def analyze(self, file_path: str, column: str,
method: str = 'mean') -> float:
"""计算指定列的统计值。"""
# 验证文件存在
path = Path(file_path)
if not path.exists():
raise FileNotFoundError(f"文件不存在: {file_path}")
# 读取数据
try:
df = pd.read_csv(file_path)
except Exception as e:
self.logger.error(f"读取失败: {e}")
raise
# 验证列存在
if column not in df.columns:
raise ValueError(f"列不存在: {column}")
# 计算统计值
methods = {
'mean': df[column].mean(),
'median': df[column].median(),
'std': df[column].std()
}
result = methods.get(method)
if result is None:
raise ValueError(f"未知方法: {method}")
self.logger.info(f"计算完成: {method}={result}")
return result
# 使用
analyzer = CSVAnalyzer({'logging': {'level': 'DEBUG'}})
result = analyzer.analyze('data.csv', 'score')
print(result)
这是典型的过度设计:一个只需要5行的任务,被扩展成80行。
符合Simplicity First的写法:
import pandas as pd
df = pd.read_csv('data.csv')
print(df['score'].mean())
如果需要稍微通用一点:
import pandas as pd
df = pd.read_csv('data.csv')
print(df['score'].agg(['mean', 'median', 'std']))
这就是全部。没有类,没有配置,没有日志,没有扩展点。因为任务是这个,就只做这个。
如何在CLAUDE.md中强制执行
## Simplicity First
在写代码前问自己:
1. 这个抽象层是必需的吗?如果只用一次,inline它。
2. 这个配置项是真的需要吗?如果是常数,硬编码。
3. 这个错误处理覆盖的是可能的场景,还是想象的场景?
4. if这段代码超过30行,考虑是否过度。
**禁止的做法:**
- 为了"可能的未来需求"添加抽象
- 为了"代码美观"在一次性代码里建抽象
- 为了"健壮性"处理不可能发生的情况
**必须的做法:**
- 直接解决当前问题
- 用最少的代码行数
- 如果后来发现不够,再扩展
2.3 Surgical Changes:阻止改动失控
核心指令:只动必须改的。只清理自己制造的垃圾。
这是解决"手太长"问题的专门规则。详细规定:
修改现有代码时:
必须:
- 只改任务所需的代码
- 保持原有风格
- 保持原有注释的意图(除非明确要改)
禁止:
- "顺手优化"相邻代码
- 顺手改注释或格式
- 改动没坏的部分
- 在没有请求时清理"死代码"
- 重构只是"不够好"的代码
当你制造了孤儿代码时:
- 删除你的改动导致的无用imports/variables/functions
- 但不要删除本来就存在的无用代码(除非被要求)
改动边界检查
这个原则最实用的部分是一个"scope test":
每一行改动,都应该能追溯到用户的原始请求。
这给了你一个非常清晰的代码审查原则:看到一行改动,问"这是任务要求的吗?如果是,在哪?"
举个例子。假设你说"修改validate函数,增加邮箱格式检查"。
模型的错误输出:
- def validate(data):
- if not data.get('name'):
- return False, 'Name required'
- return True, None
+ def validate(data):
+ """验证用户输入数据。
+
+ Args:
+ data: 包含用户信息的字典
+
+ Returns:
+ tuple: (是否有效, 错误消息)
+ """
+ # 检查必要的输入字段
+ if not data.get('name'):
+ return False, 'Name is required' # 修改了错误消息
+
+ # 新增邮箱验证
+ import re
+ email = data.get('email', '')
+ if email and not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
+ return False, 'Invalid email format'
+
+ return True, None
+
+ #顺便修复了其他地方的validate调用
+ #(实际上模型自己加的改动,不在需求内)
这个diff有三个问题:
- 添加了不需要的docstring
- 修改了原有的错误消息措辞
- 注释风格也改了
正确的Surgical Changes版本:
- def validate(data):
- if not data.get('name'):
- return False, 'Name required'
- return True, None
+ def validate(data):
+ if not data.get('name'):
+ return False, 'Name required'
+
+ # Add email validation
+ email = data.get('email')
+ if email:
+ import re
+ if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
+ return False, 'Invalid email format'
+
+ return True, None
只动了需要动的部分:
- 添加了邮箱检查逻辑
- import放在使用处(如果文件顶部没import re,不需要为了"规范"移上去)
- 注释风格保持原样(虽然可能不是最好的,但一致性比"更好"重要)
在CLAUDE.md中的实现
## Surgical Changes
修改代码时遵循"外科手术"原则:
**改动前:**
1. 识别需要改的最小范围
2. 信原有代码风格(缩进、命名、注释风格)
3. 不主动"改进"相邻代码
**改动后:**
- 只删除你引入的未使用代码
- 不要删除已有的"坏代码"(除非任务明确包含清理)
- 每行改动都要有明确的"任务要求"来源
**Scope Check:**
如果diff里出现了一行改动,但你在原始需求里找不到对应——删掉这行。
2.4 Goal-Driven Execution:让AI学会"考试"
核心指令:定义成功标准。循环直到验证通过。
这是四条原则中最有Karpathy个人色彩的一条。因为他多次提到:
"LLM非常擅长围绕明确目标反复循环,直到达到可验证结果。"
"不要告诉它要做什么,给它成功标准,看它自己去解决问题。"
核心思想是:把命令式任务转换成可验证目标。
具体转换规则
| 命令式说法 | Goal-Driven转换 |
|---|---|
| "添加验证" | "写invalid inputs的测试用例,然后让它们通过" |
| "修bug" | "写一个能复现bug的测试,然后让它通过" |
| "重构X" | "保证前后测试都通过" |
| "优化性能" | "指标X从数值A优化到数值B,用benchmark验证" |
这个思路的关键在于:把"做什么"变成"什么算完成"。
模型在执行"做什么"时,很容易产生歧义——什么叫"优化了"?什么叫"可读性更好"?什么叫"足够健壮"?
但面对"测试通过"、"benchmark达到X"、"行为不变",模型就有了明确的验收标准。
多步骤任务的规划格式
对于复杂任务,Goal-Driven Execution要求模型先声明计划:
计划:
1. [步骤] → 验证: [检查方式]
2. [步骤] → 验证: [检查方式]
3. [步骤] → 验证: [检查方式]
例如"添加用户注册功能":
计划:
1. 创建User模型和数据库表 → 验证: migrate成功,表存在
2. 实现注册API endpoint → 验证: curl测试返回正确响应
3. 添加输入验证 → 验证: 无效输入返回400
4. 添加重复邮箱检查 → 验证: 相同邮箱注册返回error
5. 写集成测试 → 验证: pytest全部通过
这样做的好处:
- 每一步都有明确的完成标准
- 你可以在过程中检查
- 模型可以自主循环(测试没过?继续修)
为什么这个思路有效?
Karpathy的观察是:LLM最擅长两件事:
- 生成代码(这是基础能力)
- 循环优化直到满足条件(这是被忽视的能力)
当我们给模型一个目标("测试通过"),它可以:
- 写代码
- 运行
- 看结果
- 修改
- 再运行
- 直到通过
这个过程不需要人类介入,因为"通过/不通过"是机器可以判断的。
这比说"写得对一点"、"考虑边界情况"要有效得多——因为"对"和"边界情况"都需要人类判断,而"测试通过"不需要。
在CLAUDE.md中的实现
## Goal-Driven Execution
把任务转换成可验证目标:
**单步任务:**
原来:修复登录bug
改为:写一个复现bug的测试 → 让测试通过
**多步任务:**
先输出计划,每步附验证方式:
1. 步骤A → 验证: 条件X
2. 步骤B → 验证: 条件Y
3. 步骤C → 验证: 条件Z
**循环执行:**
- 如果验证未通过,继续修改
- 只有验证通过才声称"完成"
- 如果不确定是否需要验证,问
**禁止的做法:**
- 说"完成后"但不提供验证方法
- 在没有验证通过的情况下声称任务完成
- 用模糊的标准("更好的代码"、"优化了")
三、实战:如何部署这些原则
3.1 Claude Code集成
最简单的使用方式是安装为Claude Code插件:
# 在Claude Code中添加plugin marketplace
/plugin marketplace add forrestchang/andrej-karpathy-skills
# 安装plugin
/plugin install andrej-karpathy-skills@karpathy-skills
这会把这个项目作为skill安装到Claude Code,影响你的跨项目使用。
3.2 项目级配置
如果你只想在特定项目里强制执行这些规则,把CLAUDE.md下载到项目根目录:
curl -O https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md
或者在现有CLAUDE.md后追加:
echo "" >> CLAUDE.md
curl https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md >> CLAUDE.md
3.3 自定义扩展
项目的核心是"原则式"设计。你可以基于这四条,添加项目特定的规则:
## 项目特定规则
在遵循上述四条原则基础上,本项目额外要求:
### 代码风格
- 使用TypeScript strict模式
- 所有async函数必须处理错误
- 测试覆盖率不低于80%
### 架构约束
- 新增public API必须有对应的openapi spec
- 数据库迁移必须可回滚
- 环境变量集中管理在 src/config/
### 安全检查
- 用户输入必须sanitize
- 敏感信息不许裸露
- 第三方依赖要检查license
关键是不要与核心四原则冲突——它们处理的是"行为模式",你添加的是"技术约定"。
3.4 对其他AI工具的适配
虽然这个项目命名为andrej-karpathy-skills,暗示是Claude Code专用,但原则是通用的。
Cursor用户:可以在用户级prompt里添加四条原则的核心表述。Cursor支持system prompt配置。
GitHub Copilot用户:目前没有很好的方式强制执行。建议在项目的.vscode/settings.json里配置Copilot的提示(如果支持的话),或者通过.github/copilot-instructions.md传入(如果使用GitHub's new feature)。
自建agent用户:可以在你的system prompt里直接嵌入四条原则原文。因为它们本身就很简洁。
四、深度思考:为什么这个项目会火
在分析了项目内容后,让我们退一步,思考它为什么能在两个月内获得54,000+stars。
4.1 时机的巧妙
这个项目发布在2026年1月底,正好处于一个关键窗口期:
AI编程工具已经普及:GitHub Copilot有1.5亿用户,Claude Code快速崛起,Cursor拿到巨额融资。开发者不再问"要不要用AI",而是"怎么用好AI"。
问题开始暴露:大量的坏代码、过度设计、难维护的AI生产代码开始积累。技术债务的讨论从"AI写的代码对不对"转变为"AI写的代码能不能维护"。
解决方案稀缺:市面上的讨论集中在"模型能力"和"prompt技巧",很少有人讨论"AI软件开发的方法论"。
forrestchang的项目正好切入这个空白——不是教你怎么写prompt,而是教你怎么为AI设定"开发纪律"。
4.2 形式的极简
这个项目最聪明的设计是:60行就讲清楚了。
它不需要你:
- 安装复杂的框架
- 学习新的DSL
- 改变你的工作流
- 付出高昂的学习成本
只需要把一个文件贴到你的项目里,你就获得了一套经过行业权威验证的行为规则。
这种"零成本、即时生效"的设计,大大降低了接受门槛。很多开发者可能在5分钟内就决定采用——这种决策速度在更复杂的项目里是不可能的。
4.3 权威背书
Andrej Karpathy的身份给这个项目加了分。作为前OpenAI总监、Tesla AI前负责人,他的观点本身就带有技术可信度和趋势预判的性质。
他不是在空谈理论,而是基于他大量的实际使用经验("80% agent coding")。这种实战背书,比十篇学术论文更有说服力。
forrestchang聪明地引用了Karpathy的原话,让读者感到:这不是谁编出来的规则,而是一个顶级工程师的实战总结。
4.4 社区的共鸣
看看这个项目的issue区和discussions,你会发现一个有趣的现象:很多开发者在分享自己的"翻车经验"——模型擅自重构导致上线事故、过度设计导致的性能问题、边界失控的代码审查噩梦。
这种共鸣证明了四条原则击中了真实的痛点。它不是"理论上的最佳实践",而是"每个人都在战场上遇到的痛点"。
五、局限与思考
虽然这个项目极具价值,但我们也要清醒看待它的局限。
5.1 模型依赖性
四条原则本质上是"提示词纪律"——它们依赖模型理解并遵循指令。
如果你的模型:
- 不擅长长期记忆(经常忘记CLAUDE.md)
- 不擅长复杂推理(理解不了四条原则的边界)
- 有强烈的偏好(坚持某些"最佳实践")
那么这些原则的效果会打折扣。
未来我们会看到更强的模型,但也会看到更复杂的期望。约束的本质是:你在教模型什么不能做,而模型的理解能力决定了它能不能真的遵守。
5.2 任务类型的限制
这四条原则最优的适用场景是:
- 中等复杂度的开发任务
- 有明确需求边界的工作
- 存在可验证标准的项目
对于以下场景,效果可能有限:
探索性编程:当你在试错、原型验证时,"简单优先"和"外科手术式修改"可能反而是障碍。你需要快速迭代,而不是严格遵守纪律。
维护遗留代码:当你要处理别人写的、沉淀多年的代码时,"只改必须改的"可能导致你写了正确但不协调的代码。有时候局部的"改善"是为了不成为未来的负债。
极小任务:修一个typo,加一行日志——这种task用四条原则反而过度了。项目本身也说"对于trivial tasks,用你的判断"。
5.3 需要持续迭代
这是一个"版本1"的方法论。社区已经在讨论更复杂的版本:
- 用状态机或流程图固化决策逻辑
- 引入独立的"验证agent"检查假设
- 把四条原则编码成可自动检查的规则
未来的发展,可能是从"人类写CLAUDE.md"到"工具自动生成和检查行为约束"。但现在,我们还在第一批实践者阶段。
六、未来展望:从"约束AI"到"工程化AI"
andrej-karpathy-skills代表了一种新的工程观:AI编程的重点,正从"让模型多写代码"转向"给模型设定边界、验收条件和行为纪律"。
6.1 新的工程能力需求
未来的AI程序员,不仅要会用模型,更要会:
设计验收条件:什么算"完成了"?怎么验证?用什么指标?
设定行为边界:哪些代码可以改?哪些不能碰?改动范围怎么约束?
规划执行流程:任务怎么拆?每步怎么验证?怎么处理失败?
这些能力,和传统的"算法"、"数据结构"、"系统设计"一样,是新的基础能力。
6.2 Harness工程的崛起
四条原则本质上是一个"轻量级harness"——一套约束模型行为的框架。
未来我们会看到:
标准化的Harness协议:类似今天的package.json、pyproject.toml,我们可能会有agent.json或.ai/constraints.md,标准化定义模型可以做什么、不能做什么。
Harness即服务:你的团队可能有专门的"AI行为工程师",负责维护和迭代这些约束规则——就像今天的devops维护CI/CD。
跨项目的Harness共享:就像npm生态,我们会看到特定领域(web开发、データ分析、机器学习)的约束模板。
6.3 从"Prompt Engineering"到"Constraint Engineering"
这个项目的爆火,标志着一个范式转移:Prompt Engineering正走向Constraint Engineering。
早期我们关注的是"怎么让模型做X",现在我们开始关心"怎么让模型不做Y"。
这背后反映了AI工程从"能力聚焦"到"可靠性聚焦"的转变。这不是一个分支问题,而是一个维度问题:我们不仅要问"能不能",还要问"可控不可控"、"可预期不可预期"。
写在最后
当你读完这篇文章的时候,andrej-karpathy-skills的星标可能已经又涨了几千。这个项目在技术社区的传播速度,本身就证明了它的价值。
但它真正重要的不是那54,000颗星,而是它代表的思考方式:我们可以用工程方法解决AI的不可靠性。
这不是一个复杂的框架,不是一个需要学习成本的工具,甚至不是一个新技术。它只是把一位顶级工程师的"经验"和"纪律",压缩成了一个可以直接使用的配置文件。
如果你在用AI写代码,建议你试一试。不是因为它是"最佳实践",而是因为它是一面镜子——帮你看到自己和模型之间的"理解偏差",在坏代码被写出来之前,先纠正思路。
代码会继续写,bug会继续修。但希望你从今天开始,对每一次AI的改动多问一句:
这一行代码,是任务要求的吗?如果不是,删掉它。
这,就是这60行配置想告诉我们的。
项目地址:https://github.com/forrestchang/andrej-karpathy-skills
核心文件:https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md
当前星标:54,000+(仍在快速增长中)