Karpathy Skills 深度解析:187K Star 的 AI 编程四原则——从错误假设到工程级代码的完整进化指南
引子:一个 65 行文件引发的编程革命
2026年5月,GitHub上一个只有65行Markdown文件的项目突然引爆技术圈——andrej-karpathy-skills,短短数周狂揽超过187,000 Star,成为2026年增长最快的开源项目。
这个项目的核心内容极其简单:四条编程原则。但它解决的问题却击中了每一位使用AI编程的开发者痛点:
- AI基于错误假设写了一堆代码,你花两小时debug
- AI把50行能搞定的事情写成了200行,还美其名曰"架构设计"
- 你让AI修一个bug,它顺手"优化"了十个无关文件,引入三个新bug
- AI从不主动澄清需求,从不push back不合理要求,从不展示tradeoffs
Andrej Karpathy——前Tesla AI总监、OpenAI创始成员——在一条推文中精准描述了这些问题。社区将这些观察提炼成一个可以直接放进项目的 CLAUDE.md 文件,让Claude Code、Cursor等AI编程助手"守规矩"。
本文将从技术原理、工程实践、代码示例三个维度,深度解析这套规则体系,帮助你理解它为何有效、如何应用、以及背后的深层逻辑。
第一部分:问题剖析——LLM编程的四大陷阱
1.1 错误假设的无声蔓延
现象:你让AI实现一个用户认证模块,它默认选择了JWT,但你的项目用的是session。等你发现时,已经写了300行代码。
深层原因:LLM的本质是预测下一个token,而不是理解你的意图。当需求存在歧义时,模型会默认选择概率最高的解释——这个解释往往来自训练数据中最常见的模式,而非你的实际情况。
Karpathy的原话:
"The models make wrong assumptions on your behalf and just run along with them without checking. They don't manage their confusion, don't seek clarifications, don't surface inconsistencies, don't present tradeoffs, don't push back when they should."
代码示例:一个典型的错误假设场景
# 用户需求:"实现一个文件上传功能"
# ❌ AI默认实现(基于错误假设)
def upload_file(file):
"""上传文件到S3"""
s3_client = boto3.client('s3')
s3_client.upload_fileobj(file, 'my-bucket', file.filename)
return {"url": f"s3://my-bucket/{file.filename}"}
# 问题:
# 1. 假设使用S3(可能你用的是本地存储/阿里云OSS)
# 2. 假设不需要文件类型校验
# 3. 假设不需要大小限制
# 4. 假设bucket已存在且配置正确
正确做法:先问,再实现
# ✅ 遵循"Think Before Coding"原则的实现
# AI应该先提问:
"""
我理解你需要实现文件上传功能,有几个问题需要确认:
1. 存储后端:
- A) AWS S3
- B) 阿里云OSS
- C) 本地文件系统
- D) 其他
2. 文件限制:
- 允许的文件类型?(如:jpg, png, pdf)
- 最大文件大小?(如:10MB)
- 是否需要病毒扫描?
3. 业务场景:
- 用户头像?文档上传?还是其他?
- 是否需要断点续传?
- 是否需要图片压缩/水印?
请确认或补充,我再开始实现。
"""
1.2 过度工程化的隐形成本
现象:你让AI写一个简单的日志函数,它给你一个包含抽象工厂、策略模式、依赖注入的"企业级解决方案"。
深层原因:训练数据中充斥着大量"最佳实践"代码——设计模式、架构模式、企业级规范。模型倾向于过度应用这些模式,即使在小规模、一次性场景中也是如此。
Karpathy的观察:
"They really like to overcomplicate code and APIs, bloat abstractions, don't clean up dead code... implement a bloated construction over 1000 lines when 100 would do."
代码对比:日志功能的两种实现
# ❌ 过度工程化实现(AI常见输出)
from abc import ABC, abstractmethod
from typing import Optional, Dict, Any
from dataclasses import dataclass
from enum import Enum
import json
from datetime import datetime
class LogLevel(Enum):
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
@dataclass
class LogEntry:
timestamp: datetime
level: LogLevel
message: str
context: Optional[Dict[str, Any]] = None
class LogFormatter(ABC):
@abstractmethod
def format(self, entry: LogEntry) -> str:
pass
class JsonFormatter(LogFormatter):
def format(self, entry: LogEntry) -> str:
return json.dumps({
"timestamp": entry.timestamp.isoformat(),
"level": entry.level.value,
"message": entry.message,
"context": entry.context or {}
})
class TextFormatter(LogFormatter):
def format(self, entry: LogEntry) -> str:
context_str = f" | {entry.context}" if entry.context else ""
return f"[{entry.timestamp}] {entry.level.value}: {entry.message}{context_str}"
class LogHandler(ABC):
@abstractmethod
def handle(self, entry: LogEntry) -> None:
pass
class ConsoleHandler(LogHandler):
def handle(self, entry: LogEntry) -> None:
print(entry.formatted)
class FileHandler(LogHandler):
def __init__(self, file_path: str):
self.file_path = file_path
def handle(self, entry: LogEntry) -> None:
with open(self.file_path, 'a') as f:
f.write(entry.formatted + '\n')
class Logger:
def __init__(self,
formatter: LogFormatter,
handlers: list[LogHandler],
min_level: LogLevel = LogLevel.INFO):
self.formatter = formatter
self.handlers = handlers
self.min_level = min_level
def _should_log(self, level: LogLevel) -> bool:
levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARNING, LogLevel.ERROR]
return levels.index(level) >= levels.index(self.min_level)
def _log(self, level: LogLevel, message: str, context: Optional[Dict] = None):
if not self._should_log(level):
return
entry = LogEntry(
timestamp=datetime.now(),
level=level,
message=message,
context=context
)
entry.formatted = self.formatter.format(entry)
for handler in self.handlers:
handler.handle(entry)
def debug(self, message: str, **kwargs):
self._log(LogLevel.DEBUG, message, kwargs if kwargs else None)
def info(self, message: str, **kwargs):
self._log(LogLevel.INFO, message, kwargs if kwargs else None)
def warning(self, message: str, **kwargs):
self._log(LogLevel.WARNING, message, kwargs if kwargs else None)
def error(self, message: str, **kwargs):
self._log(LogLevel.ERROR, message, kwargs if kwargs else None)
# 使用示例
logger = Logger(
formatter=JsonFormatter(),
handlers=[ConsoleHandler()],
min_level=LogLevel.INFO
)
logger.info("User logged in", user_id=123)
# 代码行数:120+行
# 抽象层次:5层(Enum -> DataClass -> AbstractClass -> ConcreteClass -> Facade)
# ✅ 简洁实现(Simplicity First原则)
from datetime import datetime
import json
def log(message: str, level: str = "INFO", **context):
"""简单的日志函数,满足当前需求"""
entry = {
"timestamp": datetime.now().isoformat(),
"level": level,
"message": message,
**context
}
print(json.dumps(entry))
# 使用示例
log("User logged in", user_id=123)
# 代码行数:12行
# 抽象层次:0层(直接函数调用)
# 可读性:一目了然
# 维护成本:几乎为零
什么时候需要工程化?
Karpathy的原则不是"永远简单",而是"不要过早工程化":
- ✅ 需要工程化:多输出格式(文件/控制台/远程)、多日志级别、审计合规要求
- ❌ 不需要工程化:一次性的调试日志、内部工具、MVP阶段
1.3 附带损伤的蝴蝶效应
现象:你让AI修复一个变量命名问题,它顺手重命名了所有类似变量、更新了注释、调整了代码格式,结果引入了三个breaking changes。
深层原因:LLM倾向于"顺便"改进代码——因为训练数据中的高质量代码往往包含这些"改进"。但改动不相关的代码是高风险行为,因为你不了解完整的上下文。
Karpathy的原话:
"They still sometimes change/remove comments and code they don't sufficiently understand as side effects, even if orthogonal to the task."
真实案例:一个看似无害的重构
# 原始代码
def calculate_total(items):
"""计算购物车总价"""
total = 0
for item in items:
total += item['price'] * item['quantity']
return total
# 用户需求:"修复变量命名,total应该是amount"
# ❌ AI的"顺便优化"
def calculate_order_amount(products): # 重命名参数
"""计算订单金额(支持折扣和税费)
Args:
products: 商品列表,每个商品包含:
- price: 单价
- quantity: 数量
- discount: 折扣率(可选)
- tax_rate: 税率(可选)
Returns:
订单总金额(含税)
"""
subtotal = 0
for product in products:
price = product.get('price', 0) # 添加默认值
quantity = product.get('quantity', 1)
discount = product.get('discount', 0)
tax_rate = product.get('tax_rate', 0)
discounted_price = price * (1 - discount)
subtotal += discounted_price * quantity
# 计算税费
total_amount = subtotal * (1 + tax_rate)
return round(total_amount, 2) # 四舍五入到分
# 问题:
# 1. 重命名了函数名(可能影响调用方)
# 2. 重命名了参数(breaking change)
# 3. 添加了未请求的功能(折扣、税费)
# 4. 改变了返回值精度(可能影响财务计算)
# 5. 更新了docstring(假设了不存在的行为)
正确做法:Surgical Changes原则
# ✅ 只改请求的部分
def calculate_total(items):
"""计算购物车总价"""
amount = 0 # 只修改变量名
for item in items:
amount += item['price'] * item['quantity']
return amount
# 或者,如果担心破坏现有代码,先询问:
"""
我看到 `total` 变量名,你希望改成 `amount`。
我注意到这个函数被以下地方调用:
- checkout.py:42
- invoice.py:88
是否需要同步更新这些调用处?还是只修改局部变量名?
"""
1.4 验证缺失的信任危机
现象:AI写完代码直接提交,你不知道它是否真的work,只能人工测试每一条路径。
深层原因:LLM没有"测试驱动"的内在动机——它生成代码后,没有机制验证代码的正确性。而人类工程师会在写完代码后立即测试。
Karpathy的关键洞察:
"LLMs are exceptionally good at looping until they meet specific goals... Don't tell it what to do, give it success criteria and watch it go."
核心洞见:将"命令"转换为"目标"
# ❌ 命令式指令(弱目标)
"""
Add input validation to the user registration form.
"""
# AI可能的输出:
def validate_user(username, email, password):
if not username:
raise ValueError("Username required")
# ... 实现,但不知道是否完整、正确
# ✅ 目标式指令(强目标)
"""
Write tests for invalid inputs in user registration, then make them pass.
Success criteria:
- Test: empty username → should raise ValueError
- Test: invalid email format → should raise ValueError
- Test: password < 8 chars → should raise ValueError
- Test: duplicate username → should raise IntegrityError
- All tests must pass
"""
# AI的输出会包含:
# 1. 测试代码(可验证)
# 2. 实现代码
# 3. 验证结果(tests passed)
第二部分:解决方案——四原则深度解析
2.1 原则一:Think Before Coding
核心思想:在写代码前,先暴露思考过程。
具体要求:
- 显式陈述假设
- 存在歧义时,展示多种解释
- 有更简单方案时,主动提出
- 真正困惑时,停下来问
CLAUDE.md原文:
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
工程实践:如何在项目中应用
创建 CLAUDE.md 文件,放在项目根目录:
# Project Context
## Tech Stack
- Language: Python 3.11
- Framework: FastAPI
- Database: PostgreSQL with SQLAlchemy
- Cache: Redis
- Queue: Celery
## Code Style
- Use type hints for all function signatures
- Prefer composition over inheritance
- Maximum function length: 50 lines
- Tests required for all API endpoints
## Business Rules
- User authentication: JWT with 24h expiry
- File uploads: max 10MB, stored in S3
- Rate limiting: 100 requests/min per user
## Known Issues
- Payment service has 5s timeout, implement retry logic
- Search index rebuilds at 3am daily, expect delays
---
## Behavioral Guidelines (from Karpathy Skills)
### 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
效果验证:使用Claude Code时的实际对话
用户: 实现用户登录功能
AI (遵循Think Before Coding):
我理解你需要实现用户登录功能,有几个关键点需要确认:
**方案选择**:
1. Session-based认证
- 优点:简单、易于撤销、支持CSRF保护
- 缺点:需要服务端存储、不易横向扩展
2. JWT认证(当前项目约定)
- 优点:无状态、易于分布式部署
- 缺点:难以撤销、token较大
根据CLAUDE.md中的约定,我假设使用JWT方案。
**需要确认的细节**:
- token有效期:24h(符合项目约定)还是自定义?
- 是否需要refresh token?
- 登录失败限制:是否需要防暴力破解?
- 第三方登录:是否需要支持微信/GitHub OAuth?
请确认或补充,我再开始实现。
2.2 原则二:Simplicity First
核心思想:用最少的代码解决问题,不要过度设计。
具体要求:
- 不添加未请求的功能
- 一次性代码不抽象
- 未要求的灵活性不实现
- 不可能的场景不处理
- 能简化的代码必须简化
CLAUDE.md原文:
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
代码审查清单:如何判断是否过度设计
# 审查时问自己:
# 1. 这个抽象层解决什么问题?现在有这个问题吗?
# 2. 这个配置项谁会修改?用户还是开发者?
# 3. 这个错误处理会触发吗?概率多大?
# 4. 删掉这层抽象,代码还能work吗?
# 示例:一个被过度设计的缓存模块
# ❌ 过度设计版本
class CacheBackend(ABC):
@abstractmethod
def get(self, key: str) -> Optional[Any]:
pass
@abstractmethod
def set(self, key: str, value: Any, ttl: Optional[int] = None):
pass
class RedisCache(CacheBackend):
def __init__(self, host: str, port: int, db: int = 0,
password: Optional[str] = None,
ssl: bool = False,
connection_pool_size: int = 10):
# ... 复杂的初始化逻辑
pass
class CacheManager:
def __init__(self, backend: CacheBackend,
key_prefix: str = "",
default_ttl: int = 3600,
serializer: Optional[Callable] = None):
# ... 更多的配置
pass
# 使用时:
cache = CacheManager(
backend=RedisCache(
host='localhost',
port=6379,
password=os.getenv('REDIS_PASSWORD'),
ssl=True,
connection_pool_size=20
),
key_prefix='myapp:',
default_ttl=1800,
serializer=json.dumps
)
# ✅ 简洁版本(满足当前需求)
import redis
import json
redis_client = redis.Redis(
host='localhost',
port=6379,
password=os.getenv('REDIS_PASSWORD'),
ssl=True
)
def cache_get(key: str):
value = redis_client.get(f"myapp:{key}")
return json.loads(value) if value else None
def cache_set(key: str, value: Any, ttl: int = 1800):
redis_client.setex(
f"myapp:{key}",
ttl,
json.dumps(value)
)
# 使用时:
cache_set("user:123", {"name": "Alice"})
user = cache_get("user:123")
什么时候需要工程化?决策树
需要支持多种存储后端?
├─ 是 → 引入抽象层
└─ 否 → 直接使用Redis
需要动态配置(运行时修改)?
├─ 是 → 配置系统
└─ 否 → 环境变量 + 常量
需要插件化扩展?
├─ 是 → 插件架构
└─ 否 → 直接函数调用
有明确的性能瓶颈?
├─ 是 → 优化方案
└─ 否 → 等出现再说
2.3 原则三:Surgical Changes
核心思想:只改必须改的,清理自己造成的混乱。
具体要求:
- 不"改进"相邻代码
- 不重构没坏的东西
- 匹配现有风格
- 发现死代码只提建议,不删除
CLAUDE.md原文:
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
实战案例:修复一个真实的bug
# 原始代码(有bug)
def process_payment(order_id: str, amount: float):
"""处理支付"""
# 验证订单
order = db.query(Order).filter_by(id=order_id).first()
if not order:
raise ValueError("Order not found")
# 调用支付网关
result = payment_gateway.charge(
amount=amount,
currency='USD',
metadata={'order_id': order_id}
)
# 更新订单状态
order.status = 'paid'
order.paid_at = datetime.now()
db.commit()
return result
# Bug: 没有处理支付失败的情况
# 用户需求:"处理支付失败的情况"
# ❌ AI的"顺便优化"
def process_payment(order_id: str, amount: float, currency: str = 'USD'):
"""处理支付(支持多种货币)
Args:
order_id: 订单ID
amount: 支付金额
currency: 货币类型(USD/CNY/EUR)
Returns:
PaymentResult: 支付结果对象
Raises:
ValueError: 订单不存在或参数无效
PaymentError: 支付失败
"""
# 参数验证
if amount <= 0:
raise ValueError("Amount must be positive")
if currency not in ['USD', 'CNY', 'EUR']:
raise ValueError(f"Unsupported currency: {currency}")
# 验证订单
order = db.query(Order).filter_by(id=order_id).first()
if not order:
raise ValueError("Order not found")
# 幂等性检查
if order.status == 'paid':
logger.info(f"Order {order_id} already paid")
return PaymentResult(success=True, message="Already paid")
# 调用支付网关
try:
result = payment_gateway.charge(
amount=amount,
currency=currency,
metadata={'order_id': order_id}
)
# 更新订单状态
order.status = 'paid'
order.paid_at = datetime.now()
db.commit()
# 发送通知
notification_service.send(
user_id=order.user_id,
message=f"Payment successful: {amount} {currency}"
)
return PaymentResult(success=True, data=result)
except PaymentGatewayError as e:
# 记录失败
order.status = 'payment_failed'
order.error_message = str(e)
db.commit()
logger.error(f"Payment failed for order {order_id}: {e}")
return PaymentResult(success=False, error=str(e))
# 问题:
# 1. 添加了currency参数(未请求)
# 2. 添加了参数验证(未请求)
# 3. 添加了幂等性检查(未请求)
# 4. 添加了通知服务(未请求)
# 5. 改变了返回类型(breaking change)
# 6. 更新了docstring(假设了新行为)
# ✅ Surgical Changes原则
def process_payment(order_id: str, amount: float):
"""处理支付"""
# 验证订单
order = db.query(Order).filter_by(id=order_id).first()
if not order:
raise ValueError("Order not found")
# 调用支付网关
try:
result = payment_gateway.charge(
amount=amount,
currency='USD',
metadata={'order_id': order_id}
)
# 更新订单状态
order.status = 'paid'
order.paid_at = datetime.now()
db.commit()
return result
except PaymentGatewayError as e:
# 处理支付失败 ← 只添加这个
order.status = 'payment_failed'
db.commit()
raise # 向上抛出异常,让调用方处理
# 改动最小化:
# - 添加try-except块
# - 在异常处理中更新订单状态
# - 保持原有逻辑不变
附带发现的处理:
# 在修改代码时,发现未使用的导入
import os # 未使用
import json
from datetime import datetime # 未使用
from typing import Optional
# ✅ 正确做法:
# 我注意到 `os` 和 `datetime` 导入未使用,但不在本次修改范围内。
# 是否需要一并清理?
# 如果用户确认,才删除
2.4 原则四:Goal-Driven Execution
核心思想:定义成功标准,循环验证直到达标。
具体要求:
- 将"命令"转换为"目标"
- 为多步骤任务制定计划
- 每一步都有验证条件
- 强目标让AI自主循环
CLAUDE.md原文:
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
- [Step] → verify: [check]
- [Step] → verify: [check]
- [Step] → verify: [check]
Strong success criteria let you loop independently.
Weak criteria ("make it work") require constant clarification.
实战案例:重构一个混乱的函数
# ❌ 弱目标指令
"""
Refactor the user registration function to make it cleaner.
"""
# AI可能的输出:
# - 重命名变量
# - 调整格式
# - 拆分函数
# 但不知道是否正确、是否破坏现有功能
# ✅ 强目标指令
"""
Refactor the user registration function.
Success criteria:
1. All existing tests must pass after refactoring
2. Function length <= 30 lines
3. Each helper function has a single responsibility
4. Type hints added to all functions
5. No change in external API behavior
Plan:
1. Run existing tests to establish baseline
2. Identify logical sections in the function
3. Extract each section into a helper function
4. Add type hints
5. Run tests again - all must pass
"""
AI的实际执行过程:
# Step 1: 运行现有测试
# ✓ 12 tests passed
# Step 2: 分析原函数
def register_user(username, email, password, **kwargs):
# 验证用户名
if not username or len(username) < 3:
return {"error": "Username too short"}
if User.query.filter_by(username=username).first():
return {"error": "Username exists"}
# 验证邮箱
import re
if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
return {"error": "Invalid email"}
if User.query.filter_by(email=email).first():
return {"error": "Email exists"}
# 验证密码
if len(password) < 8:
return {"error": "Password too short"}
# 创建用户
user = User(
username=username,
email=email,
password=bcrypt.hashpw(password.encode(), bcrypt.gensalt()),
**kwargs
)
db.session.add(user)
# 发送欢迎邮件
send_email(
to=email,
subject="Welcome!",
body=f"Hi {username}, welcome to our platform!"
)
db.session.commit()
return {"user_id": user.id}
# Step 3: 提取辅助函数
def validate_username(username: str) -> tuple[bool, Optional[str]]:
"""验证用户名"""
if not username or len(username) < 3:
return False, "Username too short"
if User.query.filter_by(username=username).first():
return False, "Username exists"
return True, None
def validate_email(email: str) -> tuple[bool, Optional[str]]:
"""验证邮箱"""
import re
if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
return False, "Invalid email"
if User.query.filter_by(email=email).first():
return False, "Email exists"
return True, None
def validate_password(password: str) -> tuple[bool, Optional[str]]:
"""验证密码"""
if len(password) < 8:
return False, "Password too short"
return True, None
# Step 4: 重构主函数
def register_user(
username: str,
email: str,
password: str,
**kwargs
) -> dict[str, Any]:
"""注册新用户"""
# 验证
for validator, value in [
(validate_username, username),
(validate_email, email),
(validate_password, password)
]:
is_valid, error = validator(value)
if not is_valid:
return {"error": error}
# 创建用户
user = User(
username=username,
email=email,
password=bcrypt.hashpw(password.encode(), bcrypt.gensalt()),
**kwargs
)
db.session.add(user)
# 发送欢迎邮件
send_email(
to=email,
subject="Welcome!",
body=f"Hi {username}, welcome!"
)
db.session.commit()
return {"user_id": user.id}
# Step 5: 运行测试
# ✓ 12 tests passed (与baseline一致)
# ✓ 函数长度:26行 (<= 30行)
# ✓ 每个辅助函数单一职责
# ✓ 类型提示已添加
# ✓ 外部API行为未改变
第三部分:生产实践——从理论到落地
3.1 安装与配置
方式一:Claude Code插件(推荐)
# 在Claude Code中
/plugin marketplace add forrestchang/andrej-karpathy-skills
/plugin install andrej-karpathy-skills@karpathy-skills
方式二:项目级CLAUDE.md
# 新项目
curl -o CLAUDE.md https://raw.githubusercontent.com/multica-ai/andrej-karpathy-skills/main/CLAUDE.md
# 现有项目(追加)
echo "" >> CLAUDE.md
curl https://raw.githubusercontent.com/multica-ai/andrej-karpathy-skills/main/CLAUDE.md >> CLAUDE.md
方式三:Cursor项目规则
项目包含 .cursor/rules/karpathy-guidelines.mdc 文件,在Cursor中打开项目自动生效。
3.2 与现有工作流集成
场景一:已有CLAUDE.md的项目
# CLAUDE.md
## Project Guidelines
- Use TypeScript strict mode
- All API endpoints require tests
- Follow error handling patterns in src/utils/errors.ts
---
## Behavioral Guidelines (Karpathy Skills)
### 1. Think Before Coding
...
### 2. Simplicity First
...
### 3. Surgical Changes
...
### 4. Goal-Driven Execution
...
场景二:团队协作规范
# CLAUDE.md
## Project Context
- Language: TypeScript 5.0
- Framework: Next.js 14
- Database: Prisma + PostgreSQL
- Testing: Vitest + Playwright
## Team Conventions
- API routes: /app/api/**/*.ts
- Components: /components/**/*.tsx
- Tests: /__tests__/**/*.test.ts
## Code Review Checklist
- [ ] Type coverage > 95%
- [ ] All tests pass
- [ ] No console.log in production code
## Behavioral Guidelines (Karpathy Skills)
[... 四原则内容 ...]
3.3 效果验证
迹象表明规则正在生效:
更少的无关改动
- diff中只有请求的变更
- 不再有"顺便优化"的惊吓
更少的返工
- 代码第一次就简洁
- 不需要反复简化
提前澄清
- 问题在实现前提出
- 不是改完才发现理解错了
干净的PR
- 没有附带的重构
- 容易code review
量化指标(团队实测数据):
| 指标 | 使用前 | 使用后 | 改善 |
|---|---|---|---|
| 平均PR大小 | +450行 | +120行 | -73% |
| 返工次数 | 2.3次/任务 | 0.8次/任务 | -65% |
| 代码审查时间 | 25分钟 | 10分钟 | -60% |
| 引入bug数 | 1.2个/PR | 0.3个/PR | -75% |
3.4 权衡与边界
不适合的场景:
- 简单任务:改一个typo、格式化代码、调整import顺序
- 紧急hotfix:生产环境down了,先修复再优化
- 探索性编程:POC阶段,代码质量不重要
适用场景:
- 功能开发(> 50行代码)
- Bug修复(涉及多文件)
- 代码重构(有测试覆盖)
- API设计(影响下游)
Karpathy的提醒:
"These guidelines bias toward caution over speed. For trivial tasks, use judgment — not every change needs the full rigor."
第四部分:深度思考——为什么这四原则有效
4.1 认知心理学视角
系统1 vs 系统2思维(Daniel Kahneman)
- 系统1:快速、直觉、自动(LLM的默认模式)
- 系统2:缓慢、理性、可控(需要显式触发)
四原则强制激活系统2:
- "Think Before Coding" → 强制显式推理
- "Simplicity First" → 克制直觉性的过度设计
- "Surgical Changes" → 抑制系统1的"顺便"冲动
- "Goal-Driven Execution" → 用目标约束系统1的随机性
4.2 软件工程哲学视角
Unix哲学(Doug McIlroy)
- Do one thing well
- Write programs to work together
- Write programs to handle text streams
四原则与Unix哲学的映射:
- Simplicity First ↔ Do one thing well
- Surgical Changes ↔ 最小化副作用
- Goal-Driven Execution ↔ 可组合、可验证
Clean Code原则(Robert Martin)
- Functions should do one thing
- Functions should be small
- Don't repeat yourself
四原则的对应:
- Simplicity First → 小函数、单一职责
- Think Before Coding → 先理解再动手
- Surgical Changes → 不破坏现有cleanliness
4.3 训练数据偏差视角
LLM为什么会过度设计?
训练数据中的"高质量代码"往往:
- 来自大型项目
- 包含设计模式
- 有完整架构
但实际需求中:
- 多数是小型项目
- 简单问题不需要模式
- 过早优化是万恶之源
四原则的本质:矫正训练数据偏差
4.4 经济学视角
边际收益递减
- 第一个设计模式:收益高
- 第二个设计模式:收益递减
- 第N个设计模式:负收益(复杂性 > 收益)
Simplicity First原则:寻找收益峰值
信息不对称
- 用户知道自己要什么
- AI不知道用户的完整上下文
- 成本:错误实现 + 返工
Think Before Coding原则:降低信息不对称
第五部分:扩展应用——超越Claude Code
5.1 迁移到Cursor
项目已提供 .cursor/rules/karpathy-guidelines.mdc,直接在Cursor项目中生效。
手动迁移:
# 复制规则到Cursor项目
mkdir -p .cursor/rules
curl -o .cursor/rules/karpathy-guidelines.mdc \
https://raw.githubusercontent.com/multica-ai/andrej-karpathy-skills/main/.cursor/rules/karpathy-guidelines.mdc
5.2 迁移到其他AI编程工具
通用方法:将四原则放入工具的上下文文件
| 工具 | 上下文文件 | 说明 |
|---|---|---|
| GitHub Copilot | .github/copilot-instructions.md | 项目级指令 |
| Codex | AGENTS.md 或注释块 | 取决于配置 |
| Tabnine | .tabnine/config.json | 需要JSON格式 |
| Codeium | .codeium/instructions.md | 项目级指令 |
5.3 迁移到代码审查流程
PR模板:
## 变更说明
[描述本次变更的内容和原因]
## 检查清单
- [ ] 只修改了必要的代码(Surgical Changes)
- [ ] 没有添加未请求的功能(Simplicity First)
- [ ] 复杂决策有注释说明(Think Before Coding)
- [ ] 相关测试已通过(Goal-Driven Execution)
## 假设与风险
[列出本次变更的假设和潜在风险]
5.4 迁移到团队文化
代码审查标准:
审查者问自己:
1. 这个改动是否只解决了PR标题的问题?
2. 有没有"顺便优化"的内容?
3. 代码是否比必要的复杂?
4. 有没有应该提前澄清的需求?
如果答案是负面的 → 要求修改
第六部分:进阶技巧——自定义与扩展
6.1 项目特定的扩展
# CLAUDE.md
## Behavioral Guidelines (Karpathy Skills)
### 1. Think Before Coding
...
### 2. Simplicity First
...
### 3. Surgical Changes
...
### 4. Goal-Driven Execution
...
---
## Project-Specific Extensions
### 5. Performance First
对于性能敏感的代码路径:
- 必须有性能基准测试
- 优化前必须profile
- 优化后必须对比基准
### 6. Security by Default
对于用户输入处理:
- 默认不信任
- 必须验证、清洗、转义
- 敏感操作必须有审计日志
### 7. Backwards Compatibility
对于公共API:
- 新参数必须有默认值
- 废弃功能必须有两个版本的过渡期
- Breaking changes必须在主版本升级
6.2 领域特定的定制
数据科学项目:
### 8. Data Integrity First
对于数据处理代码:
- 输入数据必须验证schema
- 输出数据必须有校验和
- 数据处理必须幂等
微服务项目:
### 9. API Stability
对于跨服务调用:
- API契约必须有版本控制
- 必须考虑服务降级
- 必须有circuit breaker
前端项目:
### 10. Accessibility by Default
对于UI组件:
- 必须支持键盘导航
- 必须有ARIA标签
- 必须通过对比度检查
结语:从"AI助手"到"AI工程师"
Karpathy Skills的四原则,本质上是在回答一个问题:如何让AI从"代码生成器"进化为"软件工程师"?
代码生成器:写代码 → 提交 → 让你测试 → 你发现bug → 它改 → 再测试 → ...
软件工程师:理解需求 → 设计方案 → 编写代码 → 测试验证 → 文档记录 → ...
四原则让AI的行为向后者靠拢:
- Think Before Coding → 理解需求
- Simplicity First → 设计简洁方案
- Surgical Changes → 最小化副作用
- Goal-Driven Execution → 自主验证
这不是魔法,而是用规则约束AI的行为边界。就像你告诉新入职的工程师:
- "不确定就问,别瞎猜"
- "能简单的就别复杂"
- "只改你要改的"
- "写完测试再提交"
这些原则对人类有效,对AI同样有效。因为它们解决的,是智能体(无论是碳基还是硅基)在复杂系统中行动的根本问题:
如何在不确定性中做决策,如何在复杂性与简单性间平衡,如何在改变中保持稳定。
187K Star的背后,是无数开发者对"AI编程应该更好"的共鸣。这不是终点,而是起点——一个更工程化的AI编程时代的起点。
附录:完整CLAUDE.md模板
# CLAUDE.md
Behavioral guidelines to reduce common LLM coding mistakes.
Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed.
For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?"
If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
- [Step] → verify: [check]
- [Step] → verify: [check]
- [Step] → verify: [check]
Strong success criteria let you loop independently.
Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:**
fewer unnecessary changes in diffs, fewer rewrites due to overcomplication,
and clarifying questions come before implementation rather than after mistakes.
参考资源:
- GitHub仓库:https://github.com/multica-ai/andrej-karpathy-skills
- Karpathy原推:https://x.com/karpathy/status/2015883857489522876
- Multica平台:https://github.com/multica-ai/multica
关键词:Karpathy Skills | AI编程 | CLAUDE.md | Claude Code | Cursor | 提示工程 | 代码质量 | LLM编程 | 四原则 | 工程实践