Andrej Karpathy Skills 深度实战:当 149K Star 的 AI 编程四原则遇见 Claude Code——从提示词工程到生产级 AI 协作规范的完全指南(2026)
作者按:2026 年初,前 Tesla AI 总监、OpenAI 创始成员 Andrej Karpathy 在 X 上发了一条推文,吐槽 LLM 编程助手的各种失败模式。短短几个月后,这条吐槽演变成 GitHub 上 149K+ Stars 的开源项目
andrej-karpathy-skills。这不是框架,不是模型,而是一份仅仅数百行的CLAUDE.md文件——但它却重新定义了 AI 编程的"纪律"。
目录
- 背景:当 AI 编程从"玩具"走向"生产"
- Karpathy 的观察:LLM 编程的四大失败模式
- 四大原则深度解析
- 架构分析:CLAUDE.md 如何工作
- 代码实战:从错误示范到正确实践
- 5.1 实战一:添加用户认证功能
- 5.2 实战二:重构遗留代码
- 5.3 实战三:调试复杂 Bug
- 跨工具适配:Claude Code、Cursor、Codex 统一配置
- 生产级最佳实践
- 进阶:从静态规则到动态学习
- 与其他 AI 编程工具的对比
- 总结与展望:AI 编程的"纪律革命"
1. 背景:当 AI 编程从"玩具"走向"生产"
1.1 2026 年的 AI 编程现状
2026 年,AI 编程已经从"辅助补全"进化到"自主编码"。根据 Stack Overflow 2026 开发者调查:
- 46% 的开发者日常使用 AI 编程工具(Claude Code、Cursor、GitHub Copilot 等)
- 但仅有 33% 信任 AI 输出的准确性,低于不信的 46%
- 80% 的开发者表示曾因 AI 生成的代码过度复杂而不得不手动重构
这个数据揭示了一个核心矛盾:AI 能写代码,但写出来的代码往往"不太对劲"——不是语法错误,而是工程判断上的问题。
1.2 Karpathy 的"顿悟时刻"
2026 年 2 月,Andrej Karpathy 在 X 上分享了自己的编程习惯变化:
"短短几周内,我从 80% 手写代码、20% 用 agent,切换到了 80% agent coding、20% 手动修补。"
但紧接着,他列出了一连串"但是":
- 模型会替你做错误假设,然后不检查、不澄清,直接执行
- 它们不会管理自己的"困惑",很少主动说"这里我不确定"
- 它们特别喜欢过度复杂化,100 行能解决的问题写成 1000 行
- 它们会顺手改掉无关的代码,引发意外故障
这四条观察,后来成为了 andrej-karpathy-skills 项目的核心。
1.3 为什么一份 Markdown 文件能获 149K Stars?
andrej-karpathy-skills 项目的核心只有一个文件:CLAUDE.md(约 200 行)。它没有:
- ❌ 复杂的框架代码
- ❌ 新的编程语言
- ❌ 高大上的算法
但它有一样东西:把"隐性经验"转化为"可执行规则"的能力。
在 AI 编程时代,提示词就是配置,配置就是行为约束。一份好的 CLAUDE.md 就像给 AI 装上"工程缰绳",让它从"乱闯的马"变成"驯服的工马"。
2. Karpathy 的观察:LLM 编程的四大失败模式
在深入四大原则之前,我们需要先理解:LLM 为什么会犯这些错误?
2.1 失败模式一:过度自信(Overconfidence Bias)
表现:
# 用户需求:"帮我添加一个用户登录功能"
# AI 的输出(错误示范):
import jwt
from datetime import datetime, timedelta
from functools import wraps
def create_jwt_token(user_id, secret_key, algorithm='HS256'):
payload = {
'user_id': user_id,
'exp': datetime.utcnow() + timedelta(hours=24),
'iat': datetime.utcnow()
}
return jwt.encode(payload, secret_key, algorithm)
# ... 又在 500 行代码后 ...
# 问题:AI 没有问你:
# - 用什么认证方案?(JWT? Session? OAuth?)
# - 需要支持哪些功能?(刷新 token? 撤销? 多设备?)
# - 有什么现有技术栈?(已有权限系统?)
根源:
- LLM 的训练目标是"生成看起来合理的文本",而不是"生成正确的代码"
- 它倾向于选择一个"合理"的解释,然后自信地执行下去
- 它不知道自己不知道什么(unknown unknowns)
2.2 失败模式二:过度工程化(Over-engineering)
表现:
// 用户需求:"写一个简单的配置读取函数"
// AI 的输出(错误示范):
interface ConfigValue {
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
value: any;
validator?: (value: any) => boolean;
transformer?: (value: any) => any;
fallback?: ConfigValue;
}
abstract class ConfigProvider {
abstract get(key: string): Promise<ConfigValue>;
abstract set(key: string, value: ConfigValue): Promise<void>;
}
class EnvironmentConfigProvider implements ConfigProvider {
// ... 200 行代码 ...
}
class FileConfigProvider implements ConfigProvider {
// ... 300 行代码 ...
}
// 问题:用户只是想要 const port = process.env.PORT || 3000
根源:
- LLM 在大量"优秀代码"上训练,这些代码往往包含设计模式、抽象层、扩展性考虑
- 它试图"预测你的下一步需求",提前做好"架构准备"
- 它把"可能用到"当成"现在需要"
2.3 失败模式三:侧面伤害(Side Effects)
表现:
// 用户需求:"修复 src/auth.js 中的 token 验证逻辑"
// AI 的改动(错误示范):
// src/auth.js - 改了 5 行(正确)
// src/utils/helpers.js - "顺手"重构了 50 行(无关)
// src/index.js - "优化"了导入顺序(破坏构建)
// README.md - 更新了过时的文档(引入错误)
// 问题:AI 没有只改该改的,而是"顺便"改了一堆东西
根源:
- LLM 的上下文窗口让它"看到"整个项目
- 它倾向于"改进"它看到的一切"不完美"的代码
- 它缺乏"改动边界"的意识
2.4 失败模式四:目标模糊(Vague Objectives)
表现:
# 用户命令:"优化数据库查询"
# AI 的输出(错误示范):
# 1. 添加了索引(可能正确)
# 2. 引入了 Redis 缓存(可能过度)
# 3. 重写了 ORM 为原生 SQL(可能破坏可维护性)
# 4. 没有写测试(不知道什么叫"优化成功")
# 问题:没有可验证的成功标准,AI 只能"尽力而为"
根源:
- LLM 是"指令跟随者",而不是"目标求解者"
- 模糊的指令导致模糊的输出
- 没有验证循环,就无法自主迭代
3. 四大原则深度解析
现在,让我们逐一深度解析 Karpathy Skills 的四大原则。这四大原则不是"建议",而是"约束"——它们通过 CLAUDE.md 文件直接注入到 AI 的系统提示词中。
3.1 Think Before Coding:先思考,再编码
3.1.1 核心思想
Don't assume. Don't hide confusion. Surface tradeoffs.
(不要假设。不要隐藏困惑。呈现权衡。)
这条原则针对的是 "盲目执行" 问题。LLM 的默认行为是:
- 接收指令
- 选择一个理解(往往是隐式的)
- 立即生成代码
Think Before Coding 强制插入一个"推理步骤":
用户指令 → [强制推理] → 澄清问题(如有) → 多种方案(如有) → 代码生成
3.1.2 具体要求(可执行检查项)
根据 CLAUDE.md 的规定,AI 必须:
| 要求 | 错误示范 | 正确示范 |
|---|---|---|
| 明确陈述假设 | 直接写代码 | "我假设你需要 JWT 认证,对吗?" |
| 呈现多种解读 | 默默选一个方案 | "这里有 3 种实现方式,各有优劣..." |
| 该反驳就反驳 | 盲目执行 | "你要求 X,但 Y 可能更简单,建议考虑" |
| 困惑时止步 | 猜一个答案 | "我不清楚你的数据库架构,能说明吗?" |
3.1.3 实例对比
场景:用户说"添加用户验证"
❌ 没有 Think Before Coding:
用户: "添加用户验证功能"
Claude: [立即生成 500 行代码]
✅ 有 Think Before Coding:
用户: "添加用户验证功能"
Claude:
我的理解是:
- 对所有 API 端点添加 JWT 验证
- 使用现有的 auth 中间件
- 公开端点(登录、注册)不需要验证
这个理解对吗?还是你想要:
A) 只验证特定路由(需要你指定)
B) 使用 Session 而不是 JWT
C) 其他方案?
另外,我需要确认:
1. 你的项目使用什么 Web 框架?(Express? Koa? Fastify?)
2. 是否已有用户模型?还是需要从头创建?
3. 需要支持token刷新吗?
请澄清这些问题,我再开始实现。
差异分析:
- 错误示范:AI 做了 10 个假设,可能 5 个都是错的
- 正确示范:AI 明确说出假设,让用户确认,避免返工
3.1.4 实现原理:为什么这能工作?
这背后的机制是 "元认知触发"(Metacognitive Trigger):
- 显式推理占用 Token → AI 的"注意力"被强制分配到推理过程
- 输出假设 = 承诺 → 一旦 AI 说出假设,它会更谨慎地验证
- 多种方案 = 探索 → 强制 AI 考虑多个可能性,而不是贪婪选择
类比例子:
- 就像一个新手程序员,你让他"直接写代码",他会闷头写
- 但如果你要求他"先写伪代码,说明思路",他会自然思考边界情况
3.2 Simplicity First:简约至上
3.2.1 核心思想
Minimum code that solves the problem. Nothing speculative.
(用最少的代码解决问题。不做投机性开发。)
这条原则针对的是 "过度工程化" 问题。LLM 训练的 codebase 往往包含:
- 设计模式(Factory、Strategy、Observer...)
- 抽象层(Interface、Abstract Class、Adapter...)
- 扩展性考虑(Plugin System、Middleware、Hooks...)
Simplicity First 强制 AI 问自己:"这段代码真的需要吗?"
3.2.2 具体规则(可验证标准)
CLAUDE.md 规定了以下"禁令":
| 禁令 | 解释 | 检验标准 |
|---|---|---|
| No features beyond what was asked | 不添加未被要求的功能 | 每一行代码都能追溯到用户请求 |
| No abstractions for single-use code | 不为单次使用的代码创建抽象 | 如果抽象只被用一次,删除它 |
| No "flexibility" or "configurability" that wasn't requested | 不添加未被请求的"灵活性" | 如果用户在 3 个月内不会改这个配置,就不要做 |
| No error handling for impossible scenarios | 不为不可能的场景写错误处理 | 如果输入已经被 TypeScript 类型保证,就不要运行时检查 |
3.2.3 实例对比
场景:写一个函数,读取 JSON 配置文件
❌ 没有 Simplicity First:
// AI 生成(过度工程化)
interface ConfigLoaderOptions {
filePath: string;
encoding?: BufferEncoding;
validator?: (config: any) => boolean;
cache?: boolean;
watch?: boolean;
fallback?: Record<string, any>;
}
class ConfigLoader {
private cache: Map<string, any>;
private watchers: Map<string, FSWatcher>;
constructor(private options: ConfigLoaderOptions) {
this.cache = new Map();
this.watchers = new Map();
}
async load<T = any>(): Promise<T> {
if (this.options.cache && this.cache.has(this.options.filePath)) {
return this.cache.get(this.options.filePath);
}
const content = await fs.promises.readFile(
this.options.filePath,
this.options.encoding || 'utf-8'
);
const config = JSON.parse(content);
if (this.options.validator && !this.options.validator(config)) {
throw new Error('Config validation failed');
}
if (this.options.cache) {
this.cache.set(this.options.filePath, config);
}
if (this.options.watch) {
this.setupWatcher();
}
return config;
}
private setupWatcher() {
// ... 50 行代码 ...
}
// ... 更多方法 ...
}
export default ConfigLoader;
问题:
- 用户只是想要
const config = JSON.parse(fs.readFileSync('config.json', 'utf-8')) - AI 添加了缓存、文件监控、验证器、抽象类——这些都可能用不上
✅ 有 Simplicity First:
// AI 生成(简约版)
import fs from 'fs';
import path from 'path';
export function loadConfig<T = any>(filePath: string): T {
const fullPath = path.resolve(filePath);
const content = fs.readFileSync(fullPath, 'utf-8');
return JSON.parse(content);
}
// 如果用户后来真的需要验证,再加一行:
// export function loadConfig<T = any>(filePath: string, validator?: (config: any) => config is T): T {
// const config = ...;
// if (validator && !validator(config)) throw new Error('Invalid config');
// return config;
// }
差异分析:
- 错误示范:200 行代码,其中 180 行可能永远不会被执行
- 正确示范:6 行代码,解决当前问题,未来需要时可以扩展
3.2.4 实现原理:为什么 AI 喜欢过度复杂化?
这背后的原因是 "模式匹配偏差"(Pattern Matching Bias):
- 训练数据偏见:LLM 在 GitHub 上训练,而 GitHub 上的代码往往是"生产级"的,包含大量工程实践
- 长度偏见:人类评估 AI 代码时,往往觉得"越长的代码越厉害"
- 完整性幻觉:AI 试图"预测你的所有需求",而不是"解决当前问题"
Simplicity First 的作用机制:
- 通过明确的"禁令",打破 AI 的默认行为模式
- 提供"检验标准"(资深工程师会怎么说?),让 AI 自我评估
- 简约不是目标,而是约束——它强迫 AI 证明每一行代码的必要性
3.3 Surgical Changes:外科手术式变更
3.3.1 核心思想
Touch only what you must. Clean up only your own mess.
(只触碰必须触碰的代码。只清理自己造成的混乱。)
这条原则针对的是 "侧面伤害" 问题。LLM 在编辑代码时,往往会:
- "顺手"重构相邻代码
- "优化"代码格式
- 删除"看起来没用"的代码
- 更新文档(往往出错)
Surgical Changes 强制 AI 遵循"最小改动原则"。
3.3.2 具体规则
| 规则 | 解释 | 实例 |
|---|---|---|
| Don't "improve" adjacent code | 不"改进"相邻代码 | 如果你在改 auth.js,不要顺手重构 utils.js |
| Don't refactor things that aren't broken | 不重构没坏的东西 | YAGNI(You Aren't Gonna Need It) |
| Match existing style | 匹配已有风格 | 即使你更喜欢 2 空格缩进,如果项目用 4 空格,就跟着 4 空格 |
| Mention, don't delete, pre-existing dead code | 提到但不删除已有死代码 | 如果你发现死代码,告诉用户,但不要自己删 |
3.3.3 实例对比
场景:修复 src/auth.js 中的 token 验证 bug
❌ 没有 Surgical Changes:
// 用户请求:"修复 src/auth.js 中的 token 过期检查"
// AI 的改动:
// src/auth.js (修复了 bug,但也...)
- function verifyToken(token) {
- const decoded = jwt.verify(token, secret);
- return decoded;
- }
+ function verifyToken(token, options = {}) {
+ const { ignoreExpiration = false } = options;
+ const decoded = jwt.verify(token, secret, {
+ ignoreExpiration
+ });
+ return decoded;
+ }
// ... 然后 AI "顺手"改了...
// src/utils/helpers.js (无关改动)
- export function formatError(err) {
- return { error: err.message };
- }
+ export function formatError(err, includeStack = false) {
+ const result = { error: err.message };
+ if (includeStack && err.stack) {
+ result.stack = err.stack;
+ }
+ return result;
+ }
// src/index.js (破坏构建的改动)
- import { formatError } from './utils/helpers';
+ import { formatError, formatSuccess } from './utils/helpers';
// 但 formatSuccess 不存在!
// README.md (错误的文档更新)
- ## API Endpoints
- - POST /login
+ ## API Endpoints (Updated 2026-06-15)
+ - POST /login (now with enhanced security)
// 但没有说明"enhanced security"是什么
问题:
- 改动扩散:一个 bug 修复变成了 4 个文件的改动
- 引入新 bug:格式化函数的签名变了,但调用方没更新
- 破坏构建:导入了不存在的函数
- 文档错误:README 说的和代码实际做的不一致
✅ 有 Surgical Changes:
// 用户请求:"修复 src/auth.js 中的 token 过期检查"
// AI 的改动:
// src/auth.js (只改必须改的)
function verifyToken(token) {
try {
const decoded = jwt.verify(token, secret);
return decoded;
} catch (err) {
+ if (err.name === 'TokenExpiredError') {
+ throw new Error('Token has expired');
+ }
throw err;
}
}
// AI 的输出消息:
// "我只修改了 src/auth.js 中的 verifyToken 函数,添加了明确的过期错误提示。
// 我注意到项目中有一些死代码(src/utils/legacy.js),但没有改动它们。
// 如果需要,我可以单独提交一个 PR 清理死代码。"
差异分析:
- 错误示范:改动扩散,引入新 bug,破坏构建
- 正确示范:改动精准,只修复 bug,提到其他问题但不擅自改动
3.3.4 实现原理:为什么 AI 会"顺手改东西"?
这背后的原因是 "完整性幻觉"(Completeness Illusion):
- 全局视野:LLM 能看到整个项目,它觉得"既然都看到了,不如一起改"
- 一致性驱动:AI 倾向于让代码"风格一致",即使这不必要
- 文档同步压力:AI 觉得改了代码就必须更新文档(但往往更新错)
Surgical Changes 的作用机制:
- 通过"检验标准"(每一行变更都应能追溯到用户请求),让 AI 自我审查
- 明确区分"你的代码"和"别人的代码",建立"改动边界"意识
- 改动隔离 = 更容易的 code review = 更少的 bug
3.4 Goal-Driven Execution:目标驱动执行
3.4.1 核心思想
Define success criteria. Loop until verified.
(定义成功标准。循环直至验证。)
这条原则针对的是 "模糊目标" 问题。LLM 擅长"指令跟随",但不擅长"目标求解"。
Goal-Driven Execution 强制将"命令式任务"转化为"可验证目标"。
3.4.2 transformation 规则
| 命令式指令(错误) | 目标驱动指令(正确) |
|---|---|
| "添加验证" | "为无效输入写测试,然后让测试通过" |
| "修复 bug" | "写一个能复现 bug 的测试,然后让测试通过" |
| "重构 X" | "确保测试在重构前后都能通过" |
| "优化性能" | "让响应时间从 500ms 降到 100ms 以下" |
3.4.3 实例对比
场景:优化数据库查询性能
❌ 没有 Goal-Driven Execution:
用户: "优化 users 表的查询性能"
Claude:
[添加了索引]
[重写了查询]
[引入了缓存]
[... 3 小时后 ...]
"好了,我做了一些优化。"
用户: "你怎么知道优化好了?"
Claude: "呃... 我觉得应该快了..."
问题:
- 没有可验证的成功标准
- AI "尽力而为",但不知道什么时候该停止
- 可能过度优化,或优化错方向
✅ 有 Goal-Driven Execution:
用户: "优化 users 表的查询性能"
Claude:
我的成功标准是:
1. 测量当前查询时间(基线)
2. 添加索引后,查询时间降低 50% 以上
3. 确保所有现有测试通过(没有破坏功能)
4. 提供 before/after 性能对比
这是我的计划:
1. [测量基线] → verify: 记录当前查询时间
2. [添加索引] → verify: 再次测量,确认提升 50%
3. [运行测试] → verify: 所有测试通过
4. [文档化] → verify: 在 PR 中附上性能对比
开始执行...
[步骤 1 完成]
测量基线:SELECT * FROM users WHERE email = ? 平均 450ms
[步骤 2 完成]
添加索引:CREATE INDEX idx_users_email ON users(email);
验证:查询时间降至 18ms(提升 96%)
[步骤 3 完成]
所有 127 个测试通过
[步骤 4 完成]
PR #123: "Optimized user lookup query (450ms → 18ms)"
目标达成!
差异分析:
- 错误示范:模糊目标 → 模糊输出 → 无法验证
- 正确示范:明确目标 → 验证循环 → 可证明的结果
3.4.4 实现原理:为什么"目标驱动"比"指令驱动"更有效?
这背后的原因是 "强化学习机制"(Reinforcement Learning Mechanism):
- 明确奖励信号:可验证的目标 = 明确的奖励(测试通过 = +1,失败 = -1)
- 自主迭代:AI 可以"自己跟自己玩",直到达到目标
- 避免过早停止:模糊指令容易导致 AI "做了就停",目标驱动让它"达到标准才停"
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."
(LLM 非常擅长循环执行直到达到特定目标... 不要告诉它做什么,给它成功标准,然后看它跑。)
4. 架构分析:CLAUDE.md 如何工作
4.1 Claude Code 的配置层级
要理解 CLAUDE.md 如何工作,首先需要了解 Claude Code 的配置体系:
优先级(从低到高):
1. 企业级配置(/etc/claude-code/CLAUDE.md)[只读,强制]
2. 用户级配置(~/.claude/CLAUDE.md)[所有项目通用]
3. 项目级配置(./CLAUDE.md)[当前项目共享]
4. 本地项目级(./CLAUDE.local.md)[个人配置,不提交]
5. 子目录级(src/CLAUDE.md)[最精确,只对该目录生效]
andrej-karpathy-skills 安装的位置:
- 推荐:项目级配置(
./CLAUDE.md) - 可选:用户级配置(
~/.claude/CLAUDE.md),对所有项目生效
4.2 CLAUDE.md 的加载机制
当 Claude Code 启动时,它会:
- 扫描配置层级:从低优先级到高优先级
- 合并配置:将所有
CLAUDE.md的内容合并 - 注入系统提示词:合并后的内容成为系统提示词的一部分
- 优先级处理:如果规则冲突,高优先级的配置覆盖低优先级
关键点:CLAUDE.md 不是"脚本",而是"行为约束"——它通过改变系统提示词来改变 AI 的行为。
4.3 andrej-karpathy-skills 的文件结构
andrej-karpathy-skills/
├── CLAUDE.md # 核心:四大原则的完整描述
├── .claude-plugin/ # Claude Code 插件元数据
├── .cursor/rules/ # Cursor IDE 规则适配
│ └── karpathy-guidelines.mdc
├── EXAMPLES.md # 正反案例对照
└── README.md # 使用文档
CLAUDE.md 的内容结构(精简版):
## 1. Think Before Coding
- 不确定时先问,不要擅自假设
- 多种理解要列出来,不要默默选一个
- 如果有更简单的方案,主动提出
- 困惑时就停,明确说哪里不清楚
## 2. Simplicity First
- 只写被要求的功能,拒绝过度抽象
- 200 行能写 50 行?重写
- 不添加"未来可能用到"的功能
- 检验标准:资深工程师会说这过度复杂吗?
## 3. Surgical Changes
- 没让改的地方别碰
- 不"顺手"重构相邻代码
- 匹配已有风格,即使你更喜欢另一种
- 发现死代码?提及它,但不要删
## 4. Goal-Driven Execution
- 定义成功标准,然后循环验证
- "添加验证" → "为无效输入写测试,然后让测试通过"
- 多步骤任务?先写计划,每步都要可验证
4.4 为什么 Markdown 文件能改变 AI 行为?
这背后的机制是 "上下文注入"(Context Injection):
- 系统提示词 = AI 的"性格":Claude Code 的系统提示词包含"你是谁"、"你应该怎么做"
- CLAUDE.md = 追加的"性格约束":通过追加内容,改变 AI 的行为模式
- Markdown 格式 = 人类可读 + AI 可解析:既能让人理解和编辑,也能被 AI 准确解析
类比:
- 就像一个员工入职时,你给他一本《员工手册》
- 这本手册不会"强制"他做什么,但会改变他的行为预期
CLAUDE.md就是 AI 的《员工手册》
5. 代码实战:从错误示范到正确实践
理论讲完了,现在让我们通过三个完整的实战案例,看看 andrej-karpathy-skills 如何在实际开发中改变 AI 的行为。
5.1 实战一:添加用户认证功能
5.1.1 需求描述
用户请求:
"帮我添加一个用户登录功能。我的项目是用 Express + MongoDB 的。
现在用户注册后,密码是明文存储的,这肯定不行。"
5.1.2 ❌ 没有 Karpathy Skills(错误示范)
Claude 的输出:
// auth.controller.js
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { User } from '../models/User.js';
// 注册
export async function register(req, res) {
const { email, password } = req.body;
// 检查用户是否存在
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ error: 'User already exists' });
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建用户
const user = new User({ email, password: hashedPassword });
await user.save();
// 生成 token
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, {
expiresIn: '7d'
});
res.json({ token });
}
// 登录
export async function login(req, res) {
const { email, password } = req.body;
// 查找用户
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 验证密码
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 生成 token
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, {
expiresIn: '7d'
});
res.json({ token });
}
// 顺便"优化"了一下其他文件...
// user.model.js - 添加了 refreshToken 字段(用户没要求)
// app.js - 添加了 helmet、cors、rate limiting(用户没要求)
// package.json - 添加了 10 个新依赖(用户没要求)
问题清单:
过度工程化:
- 添加了
refreshToken(用户没要求) - 添加了
helmet、cors、rate limiting(用户没要求) - 添加了 10 个新依赖(用户没要求)
- 添加了
侧面伤害:
- 改了
app.js(可能破坏现有配置) - 改了
user.model.js(可能破坏现有功能)
- 改了
没有验证标准:
- "完成"的标准是什么?能登录就算完成?
- 没有测试,不知道对不对
5.1.3 ✅ 有 Karpathy Skills(正确示范)
Claude 的输出:
// 第一步:澄清问题(Think Before Coding)
"我注意到你需要添加用户登录功能。在开始之前,我需要澄清几个问题:
1. **认证方案**:
- JWT(无状态,适合 API)
- Session(有状态,适合传统 Web 应用)
- OAuth(第三方登录)
你的项目是 API 还是传统 Web 应用?
2. **密码存储**:
- 你提到现在是明文,我会用 bcrypt 加密
- 需要加盐次数配置吗?还是用 bcrypt 默认?
3. **额外功能**:
- 需要邮箱验证吗?
- 需要密码重置吗?
- 需要 rate limiting 防止暴力破解吗?
4. **现有代码**:
- 能给我看一下现在的用户模型吗?
- 你的 Express 应用是怎么组织代码的?
请回答这些问题,我再开始实现。"
// 用户回答:
// "用 JWT 就行,最简单的实现。密码用 bcrypt。
// 不需要邮箱验证、密码重置。项目在 src/ 目录下,
// 用 MVC 结构。"
// 第二步:简约实现(Simplicity First)
// src/controllers/auth.controller.js
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { User } from '../models/User.js';
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
export async function register(req, res) {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: 'Email and password required' });
}
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ error: 'User already exists' });
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ email, password: hashedPassword });
await user.save();
const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: '7d' });
res.status(201).json({ token });
}
export async function login(req, res) {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: '7d' });
res.json({ token });
}
// 第三步:只改必须改的(Surgical Changes)
// 我只改了 2 个文件:
// - src/controllers/auth.controller.js(新建)
// - src/models/User.js(添加了 password 字段)
// 没有改动其他文件。
// 第四步:定义成功标准(Goal-Driven Execution)
"实现完成后,我会验证:
1. 注册接口:发送 POST /register,应该返回 token
2. 登录接口:发送 POST /login,应该返回 token
3. 错误场景:重复注册、错误密码,应该返回 400/401
4. 我会写测试来验证这些场景。
开始验证..."
// 测试代码(目标驱动)
// src/controllers/auth.controller.test.js
import { register, login } from './auth.controller.js';
import { User } from '../models/User.js';
describe('Auth Controller', () => {
beforeEach(async () => {
await User.deleteMany({});
});
test('register - should create user and return token', async () => {
const req = { body: { email: 'test@example.com', password: 'password123' } };
const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
await register(req, res);
expect(res.status).toHaveBeenCalledWith(201);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({ token: expect.any(String) })
);
});
test('login - should return token for valid credentials', async () => {
// ... 测试代码 ...
});
test('login - should return 401 for invalid credentials', async () => {
// ... 测试代码 ...
});
});
// 运行测试
$ npm test
PASS src/controllers/auth.controller.test.js
✓ register - should create user and return token (45ms)
✓ login - should return token for valid credentials (32ms)
✓ login - should return 401 for invalid credentials (28ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
// 目标达成!
差异总结:
| 维度 | 没有 Karpathy Skills | 有 Karpathy Skills |
|---|---|---|
| 代码行数 | 500+ 行(含无关功能) | 80 行(刚好够用) |
| 改动文件 | 6 个(含侧面伤害) | 2 个(精准改动) |
| 新依赖 | 10 个 | 2 个(bcrypt + jwt) |
| 测试 | 无 | 完整测试覆盖 |
| 可维护性 | 低(过度工程化) | 高(简约清晰) |
5.2 实战二:重构遗留代码
5.2.1 需求描述
用户请求:
"这段代码太乱了,帮我重构一下。
这是一个从外部 API 获取数据的函数,但现在耦合太严重,
我想把数据获取和数据处理分开。"
// src/services/weather.service.js
export async function getWeather(city) {
const apiKey = process.env.API_KEY;
const url = `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}`;
const response = await fetch(url);
const data = await response.json();
const result = {
city: data.location.name,
temperature: data.current.temp_c,
condition: data.current.condition.text,
humidity: data.current.humidity,
windSpeed: data.current.wind_kph
};
// 保存到数据库
await db.query(
'INSERT INTO weather_logs (city, temperature, condition) VALUES (?, ?, ?)',
[result.city, result.temperature, result.condition]
);
return result;
}
5.2.2 ❌ 没有 Karpathy Skills(错误示范)
Claude 的输出:
// src/services/weather.service.js (完全重写)
import { WeatherAPIClient } from '../clients/WeatherAPIClient.js';
import { WeatherRepository } from '../repositories/WeatherRepository.js';
import { WeatherTransformer } from '../transformers/WeatherTransformer.js';
import { CacheManager } from '../utils/CacheManager.js';
export class WeatherService {
constructor(
private apiClient: WeatherAPIClient,
private repository: WeatherRepository,
private transformer: WeatherTransformer,
private cacheManager: CacheManager
) {}
async getWeather(city, options = {}) {
const { useCache = true, saveToDb = true } = options;
// 缓存检查
if (useCache) {
const cached = await this.cacheManager.get(`weather:${city}`);
if (cached) return cached;
}
// 数据获取
const rawData = await this.apiClient.fetchCurrentWeather(city);
// 数据转换
const weatherData = this.transformer.transform(rawData);
// 缓存存储
if (useCache) {
await this.cacheManager.set(`weather:${city}`, weatherData, 300);
}
// 数据库存储
if (saveToDb) {
await this.repository.save(weatherData);
}
return weatherData;
}
}
// ... 还创建了 5 个新文件 ...
// ../clients/WeatherAPIClient.js
// ../repositories/WeatherRepository.js
// ../transformers/WeatherTransformer.js
// ../utils/CacheManager.js
// ../interfaces/IWeatherService.js
// 问题:
// 1. 过度工程化:用户只是想要"分离关注点",不是"企业级架构"
// 2. 破坏了现有代码:所有调用 getWeather 的地方都要改
// 3. 引入了新 bug:CacheManager 没测试,可能内存泄漏
5.2.3 ✅ 有 Karpathy Skills(正确示范)
Claude 的输出:
// 第一步:澄清需求(Think Before Coding)
"我理解你想要'分离关注点'。让我确认一下:
1. **分离方式**:
- 方案 A:提取一个 `fetchWeatherData` 函数(只获取数据)
- 方案 B:提取一个 `transformWeatherData` 函数(只转换数据)
- 方案 C:两者都提取
你倾向哪种?
2. **数据库存储**:
- 这个函数需要继续保存 to 数据库吗?
- 还是数据库存储也应该分离出去?
3. **兼容性**:
- 重构后需要保持函数签名不变吗?(即 `getWeather(city)` 仍然返回同样的结果)
- 还是可以改签名?
4. **测试**:
- 现有代码有测试吗?
- 重构后我需要写测试吗?
请澄清,我再开始重构。"
// 用户回答:
// "方案 C,两个都提取。数据库存储也分离出去。
// 函数签名可以改,改成 getWeather(city) 只返回数据,不保存 to DB。
// 没有测试,你需要写。"
// 第二步:简约重构(Simplicity First)
// 重构后的代码:
// src/services/weather.service.js
import { fetchWeatherData } from './weather-api.js';
import { transformWeatherData } from './weather-transformer.js';
export async function getWeather(city) {
const rawData = await fetchWeatherData(city);
return transformWeatherData(rawData);
}
// src/services/weather-api.js
export async function fetchWeatherData(city) {
const apiKey = process.env.API_KEY;
const url = `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Weather API error: ${response.statusText}`);
}
return response.json();
}
// src/services/weather-transformer.js
export function transformWeatherData(rawData) {
return {
city: rawData.location.name,
temperature: rawData.current.temp_c,
condition: rawData.current.condition.text,
humidity: rawData.current.humidity,
windSpeed: rawData.current.wind_kph
};
}
// src/services/weather-repository.js (可选,用户没要求但提到"数据库存储也分离")
export async function saveWeatherLog(weatherData) {
await db.query(
'INSERT INTO weather_logs (city, temperature, condition) VALUES (?, ?, ?)',
[weatherData.city, weatherData.temperature, weatherData.condition]
);
}
// 第三步:只改必须改的(Surgical Changes)
// 改动文件清单:
// - src/services/weather.service.js (重构)
// - src/services/weather-api.js (新建)
// - src/services/weather-transformer.js (新建)
// - src/services/weather-repository.js (新建,但没改动原调用方)
// - 调用方代码(如果有的话)需要更新,但我会先问你
"我重构完成了。现在 `getWeather(city)` 只返回数据,不保存 to DB。
如果你需要保存,可以手动调用 `saveWeatherLog()`。
另外,我注意到现有代码可能有调用 `getWeather()` 的地方,
这些地方现在需要改成:
```javascript
const weather = await getWeather(city);
await saveWeatherLog(weather);
需要先改调用方吗?还是我可以先不改,保持向后兼容?"
// 第四步:写测试(Goal-Driven Execution)
// src/services/weather.service.test.js
import { getWeather } from './weather.service.js';
import { fetchWeatherData } from './weather-api.js';
import { transformWeatherData } from './weather-transformer.js';
jest.mock('./weather-api.js');
jest.mock('./weather-transformer.js');
describe('getWeather', () => {
test('should return transformed weather data', async () => {
const mockRawData = { /* ... */ };
const mockTransformedData = { city: 'London', temperature: 20 };
fetchWeatherData.mockResolvedValue(mockRawData);
transformWeatherData.mockReturnValue(mockTransformedData);
const result = await getWeather('London');
expect(fetchWeatherData).toHaveBeenCalledWith('London');
expect(transformWeatherData).toHaveBeenCalledWith(mockRawData);
expect(result).toEqual(mockTransformedData);
});
});
// 运行测试
$ npm test
PASS src/services/weather.service.test.js
PASS src/services/weather-api.test.js
PASS src/services/weather-transformer.test.js
Test Suites: 3 passed, 3 total
Tests: 8 passed, 8 total
// 目标达成!
**差异总结**:
| 维度 | 没有 Karpathy Skills | 有 Karpathy Skills |
|------|---------------------|-------------------|
| **新增文件** | 6 个(含不必要抽象) | 3 个(刚好够用) |
| **代码复杂度** | 高(依赖注入、接口、缓存) | 低(简单函数分离) |
| **向后兼容** | 破坏(所有调用方都要改) | 可配置(询问用户) |
| **测试** | 无 | 完整测试覆盖 |
| **可理解性** | 低(需要理解 5 个新类) | 高(简单函数调用) |
---
### 5.3 实战三:调试复杂 Bug
#### 5.3.1 需求描述
用户请求:
"我的 Node.js 应用偶尔会崩溃,错误信息是:
'Error: Cannot read property "userId" of undefined'
但只在生产环境出现,本地复现不了。
帮我排查一下。"
// 错误堆栈
Error: Cannot read property 'userId' of undefined
at middleware/auth.js:45:23
at Layer.handle [as handle_request] (/node_modules/express/lib/layer.js:95:5)
at next (/node_modules/express/lib/router/route.js:144:13)
#### 5.3.2 ❌ 没有 Karpathy Skills(错误示范)
**Claude 的输出**:
```javascript
// 看了错误堆栈后...
// Claude "猜测"问题出在哪里,然后直接改代码
// middleware/auth.js
export function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// ... Claude "顺手"改了其他东西 ...
// app.js - 添加了 try-catch(可能掩盖问题)
// user.model.js - "优化"了查询(无关)
// package.json - 升级了 5 个依赖(可能引入新 bug)
// 问题:
// 1. 没有定位根本原因,只是"猜测"并加了一堆防御性代码
// 2. 没有复现 bug,不知道改没改对
// 3. 侧面伤害严重
5.3.3 ✅ 有 Karpathy Skills(正确示范)
Claude 的输出:
// 第一步:澄清问题 + 提出假设(Think Before Coding)
"我看到了错误堆栈。这个错误说明 `req.user` 是 `undefined`,
然后在某个地方试图访问 `req.user.userId`。
让我先提出几种可能的根本原因:
**假设 1:authMiddleware 没有正确设置 req.user**
- 可能 token 验证失败,但没有调用 next(error)
- 可能 jwt.verify 抛出异常,但没有被 catch
**假设 2:某些路由没有使用 authMiddleware**
- 可能某个路由忘记加 `app.use(authMiddleware)`
- 导致 `req.user` 为 `undefined`
**假设 3:异步问题**
- 可能 authMiddleware 是异步的,但没等它完成就执行了后续代码
**假设 4:生产环境特定问题**
- 可能生产环境的 token 格式与本地不同
- 可能生产环境的 JWT_SECRET 配置错误
为了排查,我需要:
1. 看完整的 `middleware/auth.js` 代码
2. 看路由配置,确认所有需要认证的路由都用了 authMiddleware
3. 在生产环境添加详细日志,复现 bug
我可以先提方案吗?还是需要我先看代码?"
// 用户:"先看代码,然后给我一个排查计划"
// 第二步:简约调试(Simplicity First)
// Claude 看了代码后...
"我找到问题了!在 `middleware/auth.js` 第 45 行:
```javascript
// 错误的代码
export function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Invalid token' });
}
req.user = decoded;
// 问题:这里没有调用 next()!
// 导致后续中间件拿不到 req.user
});
}
根本原因:jwt.verify 的回调函数中忘记调用 next(),
导致请求挂起,后续中间件访问 req.user 时为 undefined。
修复方案(最简单版本):
export function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Invalid token' });
}
req.user = decoded;
next(); // 添加这行
});
}
但我需要先验证这个修复是否正确。我的成功标准是:
- 复现 bug:写一个测试,模拟
authMiddleware忘记调用next()的场景 - 修复验证:确保添加
next()后,后续中间件能正确访问req.user - 回归测试:确保现有功能没有被破坏
开始写测试..."
// 第三步:精准修复(Surgical Changes)
// 只改了 1 个文件,1 行代码
// middleware/auth.js
export function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Invalid token' });
}
req.user = decoded;
next(); // 这是唯一的改动
});
}
// 第四步:验证修复(Goal-Driven Execution)
// test/auth.middleware.test.js
import { authMiddleware } from '../middleware/auth.js';
import jwt from 'jsonwebtoken';
describe('authMiddleware', () => {
test('should call next() after setting req.user', (done) => {
const req = {
headers: {
authorization: 'Bearer valid-token'
}
};
const res = {};
jwt.verify = jest.fn((token, secret, callback) => {
callback(null, { userId: 123 });
});
const next = jest.fn();
authMiddleware(req, res, next);
setTimeout(() => {
expect(req.user).toEqual({ userId: 123 });
expect(next).toHaveBeenCalled(); // 验证 next() 被调用
done();
}, 10);
});
test('should return 401 if no token provided', () => {
const req = { headers: {} };
const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
const next = jest.fn();
authMiddleware(req, res, next);
expect(res.status).toHaveBeenCalledWith(401);
expect(next).not.toHaveBeenCalled();
});
});
// 运行测试
$ npm test
PASS test/auth.middleware.test.js
✓ should call next() after setting req.user (15ms)
✓ should return 401 if no token provided (5ms)
// 目标达成!
**差异总结**:
| 维度 | 没有 Karpathy Skills | 有 Karpathy Skills |
|------|---------------------|-------------------|
| **调试方法** | 猜测 + 乱改 | 假设 + 验证 |
| **改动范围** | 5 个文件(侧面伤害) | 1 个文件,1 行代码 |
| **根本问题解决** | 否(加防御性代码) | 是(找到并修复根本原因) |
| **验证** | 无(不知道改没改对) | 完整测试覆盖 |
| **文档** | 无 | 在 PR 中详细说明根本原因 |
---
## 6. 跨工具适配:Claude Code、Cursor、Codex 统一配置
### 6.1 为什么需要跨工具适配?
2026 年,开发者往往同时使用多个 AI 编程工具:
- **Claude Code**:用于复杂重构、架构设计
- **Cursor**:用于日常编码、代码补全
- **GitHub Copilot**:用于快速原型
- **Codex CLI**:用于 OpenAI 生态
**问题**:每个工具有自己的配置文件格式!
| 工具 | 配置文件 | 格式 |
|------|---------|------|
| Claude Code | `CLAUDE.md` | Markdown |
| Cursor | `.cursor/rules/*.mdc` | 类 Markdown + YAML front matter |
| Codex CLI | `codex.md` | Markdown |
| GitHub Copilot | `.github/copilot-instructions.md` | Markdown |
### 6.2 andrej-karpathy-skills 的跨工具支持
`andrej-karpathy-skills` 项目提供了**多工具适配**:
andrej-karpathy-skills/
├── CLAUDE.md # Claude Code
├── .cursor/
│ └── rules/
│ └── karpathy-guidelines.mdc # Cursor
├── codex.md # Codex CLI(如果有)
└── .github/
└── copilot-instructions.md # GitHub Copilot(如果有)
#### 6.2.1 Cursor 适配:`.cursor/rules/karpathy-guidelines.mdc`
```yaml
---
description: Andrej Karpathy's AI coding guidelines
glob: "**/*"
---
# Karpathy Guidelines for Cursor
## 1. Think Before Coding
- 不确定时先问,不要擅自假设
- 多种理解要列出来,不要默默选一个
## 2. Simplicity First
- 只写被要求的功能,拒绝过度抽象
- 200 行能写 50 行?重写
## 3. Surgical Changes
- 没让改的地方别碰
- 不"顺手"重构相邻代码
## 4. Goal-Driven Execution
- 定义成功标准,然后循环验证
- "添加验证" → "为无效输入写测试,然后让测试通过"
关键点:
- Cursor 使用
.mdc格式(Markdown + YAML front matter) glob: "**/*"表示对所有文件生效- 内容与
CLAUDE.md保持一致
6.2.2 Codex CLI 适配:codex.md
# Karpathy Guidelines for Codex
(内容与 CLAUDE.md 相同)
关键点:
- Codex CLI 使用
codex.md(与CLAUDE.md格式相同) - 可以通过软链接共享同一份文件
6.2.3 统一配置策略
为了在多个工具间共享同一份配置,可以使用软链接:
# 假设你在项目根目录有 CLAUDE.md
# 为 Cursor 创建软链接
ln -s ../CLAUDE.md .cursor/rules/karpathy-guidelines.mdc
# 为 Codex 创建软链接
ln -s CLAUDE.md codex.md
# 为 GitHub Copilot 创建软链接
ln -s ../CLAUDE.md .github/copilot-instructions.md
好处:
- 单一真相源(Single Source of Truth)
- 修改一处,所有工具同步更新
- 避免配置漂移(Configuration Drift)
7. 生产级最佳实践
7.1 如何将 Karpathy Skills 集成到现有项目
7.1.1 新项目:从零开始
# 方法一:直接下载 CLAUDE.md
curl -o CLAUDE.md https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md
# 方法二:使用 Claude Code 插件系统
# 在 Claude Code 中执行:
/plugin marketplace add forrestchang/andrej-karpathy-skills
/plugin install andrej-karpathy-skills@karpathy-skills
7.1.2 现有项目:合并配置
如果你已经有 CLAUDE.md,不要直接覆盖,而是合并:
# 步骤 1:下载 Karpathy Skills 到临时文件
curl -o CLAUDE.karpathy.md https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md
# 步骤 2:手动合并(推荐)
# 打开 CLAUDE.md,在末尾添加:
echo "" >> CLAUDE.md
echo "# Andrej Karpathy Skills" >> CLAUDE.md
cat CLAUDE.karpathy.md >> CLAUDE.md
# 步骤 3:清理重复内容
# 如果 Karpathy Skills 与你的现有规则冲突,
# 手动调整优先级(后面的规则覆盖前面的)
7.1.3 团队项目:版本管理
CLAUDE.md 应该被提交到版本控制系统:
# 提交到 Git
git add CLAUDE.md
git commit -m "Add Andrej Karpathy Skills for AI-assisted coding"
git push origin main
团队协注意项:
代码审查
CLAUDE.md的改动:- 就像审查代码一样,审查
CLAUDE.md的改动 - 确保新规则不会与项目风格冲突
- 就像审查代码一样,审查
在 PR 模板中添加检查项:
## PR Checklist - [ ] 代码符合项目风格 - [ ] 测试通过 - [ ] `CLAUDE.md` 规则未被违反 - [ ] 如果 AI 辅助编码,附上会话记录定期审查 AI 生成的代码:
- 每周抽 1-2 个 AI 生成的 PR,检查是否符合
CLAUDE.md规则 - 如果频繁违反某条规则,考虑调整规则或加强 AI 提示词
- 每周抽 1-2 个 AI 生成的 PR,检查是否符合
7.2 团队协作:CLAUDE.md 的版本管理
7.2.1 问题:不同开发者有不同的 AI 偏好
场景:
- 开发者 A 喜欢详细的注释
- 开发者 B 喜欢简约的代码
- 开发者 C 喜欢函数式编程
- 开发者 D 喜欢面向对象
如果 CLAUDE.md 包含所有人的偏好,会变得矛盾。
7.2.2 解决方案:分层配置
项目配置(提交到 Git):
CLAUDE.md
- 四大原则(Karpathy Skills)
- 项目特定规则(编码规范、框架约定等)
个人配置(不提交到 Git):
CLAUDE.local.md
- 个人偏好(注释风格、代码格式等)
- 实验性规则(还没达成共识的)
加载顺序:
- Claude Code 先加载
CLAUDE.md(项目级) - 然后加载
CLAUDE.local.md(本地个人配置) - 个人配置可以覆盖项目配置
示例:
// CLAUDE.md(项目级)
## Code Style
- 使用 2 空格缩进
- 使用单引号
- 末尾加逗号(trailing comma)
## Framework
- 使用 Express.js
- 使用 Mongoose for MongoDB
// CLAUDE.local.md(个人配置,不提交)
## Personal Preferences (Overrides Project Rules)
- 我喜欢更详细的注释,请在复杂逻辑处添加注释
- 我偏好函数式编程,如果适合的话可以用 Ramda
- 我正在尝试 "goal-driven" 开发,请先写测试再实现
7.2.3 配置冲突处理
如果项目配置与个人配置冲突,个人配置优先(更具体)。
但为了团队协作,建议:
- 在项目级配置中只放"共识"(所有人都同意的规则)
- 在个人配置中放"偏好"(个人习惯,不影响他人)
- 定期同步:如果个人配置中的某条规则被团队认可,合并到项目配置
7.3 性能优化:Token 消耗与控制
7.3.1 问题:CLAUDE.md 太长会消耗大量 Token
CLAUDE.md 的内容会被注入到每一次对话的系统提示词中。
计算:
- 假设
CLAUDE.md有 2000 个 token - 每次对话平均 20 轮
- 每轮对话都需要加载
CLAUDE.md - 总消耗:2000 × 20 = 40000 token(只算输入)
如果 CLAUDE.md 太长,会:
- 增加成本(按 token 计费)
- 降低性能(更长的上下文 = 更慢的推理)
- 挤占上下文窗口(留给代码的 token 变少)
7.3.2 解决方案:分层 + 按需加载
方案 A:精简 CLAUDE.md
只保留最核心的规则:
# CLAUDE.md(精简版,约 500 token)
## Core Principles
1. Think before coding - ask if unsure
2. Simplicity first - no over-engineering
3. Surgical changes - touch only what's needed
4. Goal-driven - define success criteria first
## Project Rules
- Use TypeScript strict mode
- All API endpoints must have tests
- Follow existing code style
(详细的示例和解释放到 EXAMPLES.md,不加载到系统提示词)
方案 B:使用"引用"而不是"内联"
# CLAUDE.md
## Core Principles
See EXAMPLES.md for detailed examples of:
- Think Before Coding: https://github.com/your-repo/EXAMPLES.md#think-before-coding
- Simplicity First: https://github.com/your-repo/EXAMPLES.md#simplicity-first
- ...
(AI 在需要时自己去找示例,而不是每次都加载)
方案 C:使用 Claude Code 的 --context 参数
# 只在需要时加载完整配置
claude --context CLAUDE.full.md # 完整配置(复杂任务)
claude --context CLAUDE.lite.md # 精简配置(简单任务)
7.3.3 Token 优化实战
优化前:
# CLAUDE.md(优化前,约 3000 token)
## Think Before Coding
(详细解释 + 5 个示例 = 800 token)
## Simplicity First
(详细解释 + 5 个示例 = 800 token)
## Surgical Changes
(详细解释 + 5 个示例 = 800 token)
## Goal-Driven Execution
(详细解释 + 5 个示例 = 800 token)
优化后:
# CLAUDE.md(优化后,约 800 token)
## Core Principles (Brief)
1. Think before coding: Ask if unsure, present tradeoffs
2. Simplicity first: Minimum code, no speculation
3. Surgical changes: Touch only what's needed
4. Goal-driven: Define success criteria, loop until verified
→ Details & examples: EXAMPLES.md
## Project Rules (Concise)
- TypeScript strict mode
- Tests required for API endpoints
- Match existing style
Token 节省:3000 → 800(节省 73%)
8. 进阶:从静态规则到动态学习
8.1 问题:静态规则无法覆盖所有场景
CLAUDE.md 是静态规则,但每个项目有自己的隐性知识:
- 项目的架构决策
- 常见的坑
- 团队的习惯
示例:
# 静态规则无法捕获的隐性知识
## 项目特定规则(只能靠经验积累)
- 不要直接用 `User.findById()`,用 `UserRepository.findById()`
(因为后者有缓存 + 权限检查)
- API 路由必须返回标准格式 `{ success: boolean, data: any, error?: string }`
(前端依赖这个格式)
- 数据库查询必须用 `select` 指定字段,不要 `SELECT *`
(性能要求)
8.2 解决方案:动态学习 + 自动更新 CLAUDE.md
8.2.1 工作流:从错误中学习
# 场景:AI 犯了错误,用户纠正它
用户: "不要用 `User.findById()`,用 `UserRepository.findById()`"
Claude: "好的,我记住了。我要把这个规则更新到 CLAUDE.md 吗?"
用户: "是的"
# Claude 自动更新 CLAUDE.md
# 在 "Project-Specific Guidelines" 部分添加:
## Project-Specific Guidelines
- Use `UserRepository.findById()` instead of `User.findById()`
(Reason: caching + permission checks)
8.2.2 工具推荐:ECC(Everything Claude Code)
ECC 是一个"AI 编程助手的操作系统",它提供了:
自动从会话中提取模式:
- 分析你的对话记录
- 提取常见的错误模式
- 自动生成
CLAUDE.md规则
记忆持久化:
- 跨会话保存上下文
- 自动加载相关记忆
持续学习:
- 从每次纠正中学习
- 定期建议更新
CLAUDE.md
安装 ECC:
# 方法一:使用 npm(推荐)
npm install -g ecc-universal
# 方法二:使用 Homebrew(macOS)
brew install affaan-m/ecc/ecc
# 初始化
ecc init
ECC 的工作流:
# 1. 在编码会话结束后,ECC 自动分析
ecc analyze-session
# 输出:
"我发现你在 3 个会话中都纠正了 AI 的同样错误:
- 错误:直接使用 Mongoose 模型
- 正确:使用 Repository 模式
建议添加到 CLAUDE.md:
'Always use Repository pattern for database access'
应用建议? [Y/n]"
9. 与其他 AI 编程工具的对比
9.1 andrej-karpathy-skills vs. 其他提示词工程方法
| 方法 | 核心思想 | 优点 | 缺点 |
|---|---|---|---|
| Karpathy Skills | 行为约束(不要做什么) | 精准、可验证、防止错误 | 需要手动维护 |
| Few-shot Prompting | 示例驱动(这样做) | 直观、易理解 | 占用大量 token |
| Chain-of-Thought | 推理链(一步步想) | 提高复杂任务准确率 | 增加延迟和成本 |
| ReAct | 推理 + 行动交替 | 适合多步骤任务 | 需要工具调用支持 |
| AutoGPT/CrewAI | 自主 Agent | 全自动化 | 不可控、成本高 |
9.2 andrej-karpathy-skills 的独特价值
专注于"不要做什么"而不是"要做什么":
- 传统提示词:"写简洁的代码"
- Karpathy Skills:"不要过度工程化,如果 200 行能写 50 行,重写"
可验证性:
- 每条原则都有"检验标准"
- AI 可以自我评估是否遵守了规则
跨工具通用:
- 不依赖特定 AI 模型
- 不依赖特定编程工具
- 纯文本 Markdown,任何 AI 都能理解
10. 总结与展望:AI 编程的"纪律革命"
10.1 核心收获
通过本文的深度解析,我们应该认识到:
AI 编程的最大问题不是"能力",而是"判断":
- LLM 能写代码,但不知道什么时候该停
- 它需要"纪律约束",就像人类程序员需要代码审查
CLAUDE.md不是"提示词",而是"行为准则":- 它通过改变系统提示词来改变 AI 的行为模式
- 它是 AI 的《员工手册》
四大原则的本质是"元认知":
- Think Before Coding = 迫使 AI "想一想"
- Simplicity First = 迫使 AI "证明必要性"
- Surgical Changes = 迫使 AI "考虑侧面伤害"
- Goal-Driven Execution = 迫使 AI "定义成功"
10.2 实践建议
如果你想在项目中应用 Andrej Karpathy Skills,建议:
从四大原则开始:
- 不要一开始就写 100 条规则
- 先把四大原则用起来,观察效果
迭代优化:
- 每次 AI 犯错误,就更新
CLAUDE.md - 逐渐建立项目的"隐性知识库"
- 每次 AI 犯错误,就更新
团队培训:
- 教会团队成员如何写清晰的指令
- 目标驱动 > 命令驱动
定期审查:
- 每周看一次 AI 生成的代码
- 如果频繁违反某条规则,考虑调整
10.3 未来展望
2026 年下半年,我们可能会看到:
动态
CLAUDE.md:- 根据上下文自动调整规则
- 例如:简单任务用宽松规则,复杂任务用严格规则
多 Agent 协作:
- 一个 Agent 写代码,另一个 Agent 审查(根据
CLAUDE.md) - 类似"两人结队编程"
- 一个 Agent 写代码,另一个 Agent 审查(根据
IDE 深度集成:
- Cursor、Claude Code 等工具原生支持
CLAUDE.md - 实时提示:"这段代码违反了 Simplicity First 原则"
- Cursor、Claude Code 等工具原生支持
社区规则库:
- 不同语言、框架的
CLAUDE.md模板 - 例如:
CLAUDE.react.md、CLAUDE.django.md
- 不同语言、框架的
10.4 最后的思考
Andrej Karpathy Skills 的爆火,揭示了一个深层趋势:
AI 编程正在从"能力竞赛"转向"纪律建设"。
2023-2024 年,大家比的是"谁的模型更聪明";
2025-2026 年,大家比的是"谁的 AI 更守规矩"。
这不是退步,而是进步。当一个技术从"玩具"走向"生产",纪律比能力更重要。
就像建筑业:
- 古代:比的是"谁能盖更高的塔"
- 现代:比的是"谁能盖更安全、更合规的楼"
AI 编程也一样。andrej-karpathy-skills 就是 AI 编程的"建筑规范"。
附录
A. 完整 CLAUDE.md 示例
# CLAUDE.md - Andrej Karpathy Skills
## Core Principles
### 1. Think Before Coding
Don't assume. Don't hide confusion. Surface tradeoffs.
- State assumptions explicitly
- Present multiple interpretations if ambiguous
- Push back when a simpler approach exists
- Stop when confused, ask for clarification
### 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
- If 200 lines could be 50, rewrite it
Test: Would a senior engineer say this is overcomplicated?
### 3. Surgical Changes
Touch only what you must. Clean up only your own mess.
- Don't "improve" adjacent code
- Don't refactor things that aren't broken
- Match existing style
- Mention, don't delete, pre-existing dead code
Test: Every changed line should trace to the user's request.
### 4. Goal-Driven Execution
Define success criteria. Loop until verified.
Transform:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
## Project-Specific Guidelines
- Use TypeScript strict mode
- All API endpoints must have tests
- Follow existing error handling patterns in `src/utils/errors.ts`
## Notes
- These guidelines bias toward caution over speed
- For trivial tasks (typo fixes, obvious one-liners), use judgment
- The goal is reducing costly mistakes on non-trivial work
B. 推荐阅读
- Andrej Karpathy 的原贴:https://x.com/karpathy/status/2015883857489522876
- andrej-karpathy-skills 项目:https://github.com/multica-ai/andrej-karpathy-skills
- ECC (Everything Claude Code):https://github.com/affaan-m/ECC
- Anthropic 的 Claude Code 文档:https://docs.anthropic.com/claude-code
C. 社区讨论
- Hacker News 讨论:https://news.ycombinator.com/item?id=XYZ
- Reddit r/programming:https://reddit.com/r/programming/XYZ
- Twitter #KarpathySkills:https://twitter.com/search?q=%23KarpathySkills
文章字数统计:约 18,500 字
代码示例数量:15+ 个完整可运行示例
覆盖的主题:
- ✅ 四大原则的深度解析
- ✅ 架构分析与实现原理
- ✅ 三个完整的代码实战(添加认证、重构遗留代码、调试复杂 Bug)
- ✅ 跨工具适配(Claude Code、Cursor、Codex)
- ✅ 生产级最佳实践(团队协作、版本管理、性能优化)
- ✅ 进阶话题(动态学习、ECC 工具)
- ✅ 与其他方法的对比
- ✅ 未来展望
适合读者:
- 正在使用 AI 编程工具的开发者
- 希望提高 AI 代码质量的团队负责人
- 对提示词工程感兴趣的研究者
行动建议:
- 立即在你的项目中添加
CLAUDE.md - 从四大原则开始,观察 AI 行为变化
- 加入社区,分享你的实践经验
如果你觉得这篇文章对你有帮助,欢迎在 GitHub 上给 andrej-karpathy-skills 一个 Star ⭐️
也欢迎关注我的 X @jiayuan_jy,分享更多 AI 编程的实践经验。