编程 Andrej Karpathy Skills 深度实战:当 AI 编程助手学会「工程纪律」——从四大原则到生产级 Claude Code 提示词规范的完全指南(2026)

2026-06-14 13:49:26 +0800 CST views 4

Andrej Karpathy Skills 深度实战:当 AI 编程助手学会「工程纪律」——从四大原则到生产级 Claude Code 提示词规范的完全指南(2026)

作者前言:2026年,AI 编程助手已经从"玩具"变成"工具",但大多数开发者仍然在与 AI 生成的冗余代码、错误假设和过度工程化作斗争。前 Tesla AI 总监 Andrej Karpathy 在 GitHub 上开源的 andrej-karpathy-skills 项目(149K+ Stars)提供了一套简洁而深刻的 Claude Code 提示词规范,用四大原则解决了 LLM 编码的核心痛点。本文将深入剖析这四大原则的工程本质,结合代码实战,帮助你掌握 AI 辅助编程的"工程纪律"。


目录

  1. 问题背景:LLM 编码的四大陷阱
  2. 核心原则一:Think Before Coding(先想后写)
  3. 核心原则二:Simplicity First(简洁优先)
  4. 核心原则三:Surgical Changes(手术式修改)
  5. 核心原则四:Goal-Driven Execution(目标驱动执行)
  6. 架构分析:为什么四大原则有效
  7. 代码实战:从零配置到生产级提示词
  8. 性能优化:如何评估 AI 编码质量
  9. 扩展实践:四大原则的变体与组合
  10. 总结与展望:AI 辅助编程的未来

问题背景:LLM 编码的四大陷阱

1.1 陷阱一:错误的假设(False Assumptions)

问题描述

大型语言模型(LLM)在编码时,常常会在没有确认的情况下做出错误假设。它们倾向于"默默选择一种解释然后一路跑下去",从不检查是否正确。

真实案例

# 用户请求:"添加用户验证功能"

# ❌ AI 的错误假设(未确认就动手)
def add_auth_middleware(app):
    # AI 假设:所有路由都需要验证
    # AI 假设:使用 JWT
    # AI 假设:从 Authorization header 读取 token
    # ... 实现了 200 行代码
    
# 结果:做了很多无用功,因为用户实际想要的是:
# 1. 只有 /api/* 需要验证
# 2. 使用已有的 Session 认证
# 3. 登录和注册端点不需要验证

后果

  • 浪费大量 token 和 time
  • 生成的代码需要大幅修改
  • 增加了代码的复杂度和维护成本

1.2 陷阱二:过度复杂化(Over-engineering)

问题描述

LLM 倾向于过度设计,产生冗长的程序结构。它们喜欢"为未来可能的需求提前做抽象",导致代码臃肿。

真实案例

# 用户请求:"写一个函数,把 CSV 文件读取成列表"

# ❌ AI 的过度工程化实现
from abc import ABC, abstractmethod
from typing import Protocol, Generic, TypeVar, Optional, List, Dict, Any
import csv
from dataclasses import dataclass
from enum import Enum

T = TypeVar('T')
class DataSource(Protocol):
    def read(self) -> List[Dict[str, Any]]: ...

class CSVConfig:
    def __init__(self, delimiter: str = ',', quotechar: str = '"', encoding: str = 'utf-8'):
        self.delimiter = delimiter
        self.quotechar = quotechar
        self.encoding = encoding

class DataTransformer(ABC, Generic[T]):
    @abstractmethod
    def transform(self, data: List[Dict[str, Any]]) -> T: ...

class CSVReader(DataSource):
    def __init__(self, file_path: str, config: Optional[CSVConfig] = None):
        self.file_path = file_path
        self.config = config or CSVConfig()
    
    def read(self) -> List[Dict[str, Any]]:
        # ... 100 行代码
        pass

# 用户实际需要的只是:
import csv

def read_csv(file_path: str) -> list:
    with open(file_path, 'r') as f:
        return list(csv.DictReader(f))

后果

  • 100 行代码能解决的问题,实现了 1000+ 行
  • 增加了不必要的抽象和依赖
  • 代码难以理解和维护

1.3 陷阱三:随意修改无关代码(Surgical Imprecision)

问题描述

LLM 在处理任务时,常常作为"副作用"更改或删除它们不够理解的注释和代码,即使这些与任务无关。

真实案例

# 原始代码
def process_data(data):
    # TODO: 优化性能
    result = []
    for item in data:
        result.append(item * 2)
    return result

def format_output(result):
    """格式化输出结果"""
    return {"result": result, "count": len(result)}

# 用户请求:"优化 process_data 的性能"

# ❌ AI 的修改(顺手改了一堆没让改的东西)
def process_data(data):
    # 使用列表推导式优化性能
    return [item * 2 for item in data]  # 改了实现

def format_output(result):
    """格式化输出结果 - 更新文档"""  # 改了注释
    return {"result": result, "count": len(result), "timestamp": time.now()}  # 加了没要求的功能

# 还顺手"优化"了其他不相关的函数...

后果

  • 引入了未预期的变更
  • 可能破坏现有功能
  • 增加 code review 的难度

1.4 陷阱四:缺乏明确目标(Missing Success Criteria)

问题描述

LLM 在执行任务时,策略不清晰,缺乏可验证的成功标准。它们倾向于"做到看起来能用",而不是"做到通过测试"。

真实案例

# 用户请求:"修复这个 bug"

# ❌ AI 的做法(没有可验证的标准)
def buggy_function(x):
    # 原始 bug: 除以零错误
    return 100 / x

# AI 的"修复"(没有测试验证)
def buggy_function(x):
    if x == 0:
        return None  # 没有说清楚应该返回什么
    return 100 / x

# ✅ 正确的做法
# 1. 先写测试
def test_buggy_function():
    assert buggy_function(2) == 50
    assert buggy_function(0) == 0  # 明确期望行为
    assert buggy_function(-1) == -100

# 2. 让测试通过
def buggy_function(x):
    if x == 0:
        return 0  # 明确的期望行为
    return 100 / x

后果

  • 修复了一个 bug,可能引入另一个
  • 没有回归测试保证
  • 代码行为不明确

核心原则一:Think Before Coding(先想后写)

2.1 原则详解

英文原文

Don't assume. Don't hide confusion. Surface tradeoffs.

核心要求

子原则含义实践方法
State assumptions explicitly明确说明假设把理解说出来让人确认,别默默按自己的猜
Present multiple interpretations呈现多种解读当存在歧义时,不要默默选择
Push back when warranted适时提出异议如果存在更简单的方案,说出来
Stop when confused困惑就停明确说哪里不清楚,请求澄清

2.2 为什么需要这个原则?

心理学原理

LLM 的训练目标是"生成合理的续写",而不是"生成正确的代码"。这导致它们倾向于:

  1. 迎合用户:即使用户的要求不明确,也假装理解然后生成代码
  2. 避免追问:追问需要额外的 token,模型倾向于"猜"而不是"问"
  3. 隐藏困惑:模型不会说"我不理解",而是会生成一个"看起来合理"的答案

工程学原理

软件工程的核心是"对齐"(Alignment):

  • 需求对齐:确保理解的用户需求
  • 设计对齐:确保设计方案合理
  • 实现对齐:确保代码实现符合设计

Think Before Coding 原则强制在编码前进行"对齐检查"。

2.3 代码实战:对比实现

案例 1:添加缓存功能

❌ 违反原则的实现(错误假设)

# 用户请求:"给这个函数加个缓存"

def fetch_user(user_id: int) -> dict:
    # 模拟数据库查询
    return {"id": user_id, "name": f"User {user_id}"}

# AI 的实现(默默做了很多假设)
import redis
import json
from functools import lru_cache

class CacheManager:
    def __init__(self):
        self.redis = redis.Redis(host='localhost', port=6379)
        self.local_cache = {}
    
    def get(self, key):
        # 先查本地缓存
        if key in self.local_cache:
            return self.local_cache[key]
        # 再查 Redis
        value = self.redis.get(key)
        if value:
            self.local_cache[key] = json.loads(value)
            return self.local_cache[key]
        return None
    
    def set(self, key, value, ttl=3600):
        self.local_cache[key] = value
        self.redis.setex(key, ttl, json.dumps(value))

def fetch_user_with_cache(user_id: int) -> dict:
    cache = CacheManager()
    key = f"user:{user_id}"
    
    # 检查缓存
    cached = cache.get(key)
    if cached:
        return cached
    
    # 查询数据库
    result = fetch_user(user_id)
    
    # 写入缓存
    cache.set(key, result)
    return result

问题

  • 假设使用 Redis(可能用户想要的是内存缓存)
  • 假设使用 user_id 作为 key(可能用户想要更复杂的 key)
  • 假设 TTL 是 3609 秒(可能用户想要不同的 TTL)
  • 没有确认就实现了一个复杂的多级缓存

✅ 遵循原则的实现(先想后写)

# 用户请求:"给这个函数加个缓存"

def fetch_user(user_id: int) -> dict:
    # 模拟数据库查询
    return {"id": user_id, "name": f"User {user_id}"}

# AI 的回应(先确认理解)
"""
我需要确认几个细节,才能给出最合适的实现:

1. **缓存类型**:
   - 内存缓存(适合单机、小数据量)
   - Redis 缓存(适合分布式、大数据量)
   - 文件缓存(适合持久化)

2. **缓存粒度**:
   - 只缓存 fetch_user 的结果?
   - 还是缓存所有用户查询?

3. **缓存策略**:
   - TTL(过期时间)多久?
   - 是否需要手动失效?

4. **性能要求**:
   - 当前 QPS 多少?
   - 期望降低多少数据库压力?

请提供这些信息,我会给出最合适的实现。
如果不确定,我可以先实现一个基于内存的简单缓存作为起点。
"""

然后,根据用户回答实现

# 假设用户选择:内存缓存、TTL 60 秒、只缓存 fetch_user

from functools import lru_cache
import time

class TTLCache:
    def __init__(self, ttl: int = 60):
        self.ttl = ttl
        self.cache = {}
        self.timestamps = {}
    
    def get(self, key):
        if key in self.cache:
            if time.time() - self.timestamps[key] < self.ttl:
                return self.cache[key]
            else:
                del self.cache[key]
                del self.timestamps[key]
        return None
    
    def set(self, key, value):
        self.cache[key] = value
        self.timestamps[key] = time.time()

# 简单清晰的实现
_user_cache = TTLCache(ttl=60)

def fetch_user_cached(user_id: int) -> dict:
    """带缓存的用户查询(TTL 60秒)"""
    cached = _user_cache.get(user_id)
    if cached:
        return cached
    
    result = fetch_user(user_id)
    _user_cache.set(user_id, result)
    return result

2.4 实践技巧

技巧 1:使用"理解确认"模板

我的理解是:
- [关键点 1]
- [关键点 2]
- [关键点 3]

这个理解对吗?还是你想要 [备选方案]?

技巧 2:主动呈现权衡

我看到了两种实现方案:

方案 A:[简单但有限制]
- 优点:实现快、易理解
- 缺点:不支持 [某功能]

方案 B:[复杂但完整]
- 优点:功能完整、可扩展
- 缺点:实现复杂、维护成本高

你更看重哪方面?

技巧 3:有困惑就明确指出

我有几个地方不太确定:

1. [问题 1]:是说 [解释 A] 还是 [解释 B]?
2. [问题 2]:是否需要考虑 [边界情况]?
3. [问题 3]:期望的行为是 [行为 A] 还是 [行为 B]?

请澄清,我会相应调整实现。

核心原则三:Surgical Changes(手术式修改)

4.1 原则详解

英文原文

Touch only what you must. Clean up only what you created.

核心要求

子原则含义实践方法
No "improvement" of adjacent code不"改进"相邻代码只改必须改的,不改看起来"可以优化"的
No refactoring working code不重构没坏的代码"If it ain't broke, don't fix it"
Match existing style匹配已有风格入乡随俗,不要强行推广自己的风格
Clean up only your mess只清理自己造成的混乱只删除自己创建的死代码,不删预先存在的

4.2 为什么需要这个原则?

软件维护的黄金法则

"代码可读性 ∝ 一致性"

LLM 的"过度改进"倾向

  1. 风格偏好:LLM 有自己的代码风格偏好(例如,喜欢用列表推导式,喜欢用 dataclass)
  2. "优化"冲动:看到"可以优化"的代码就想改
  3. 缺乏上下文:LLM 不知道哪些代码是故意写成这样的(例如,为了兼容性)

Git Blame 原理

每一次不必要的修改都会:

  • 污染 Git 历史
  • 增加 code review 的难度
  • 可能引入新 bug
  • git blame 失去意义

Surgical Changes 原则强制应用"最小变更原则"。

4.3 代码实战:对比实现

案例 3:修复一个 Bug

原始代码

import time
import logging

# 旧风格的代码(刻意写成这样,为了兼容性)
def get_user_data(user_id):
    # TODO: 切换到新 API
    result = {}
    result['id'] = user_id
    result['name'] = fetch_name(user_id)
    result['email'] = fetch_email(user_id)
    result['created_at'] = time.time()
    return result

def fetch_name(user_id):
    # 旧的实现,不要改
    return "User " + str(user_id)

def fetch_email(user_id):
    # 旧的实现,不要改
    return f"user{user_id}@example.com"

def format_user_data(user_data):
    """格式化用户数据(旧风格)"""
    return "[%s] %s <%s>" % (user_data['id'], user_data['name'], user_data['email'])

用户请求:"get_user_data 的性能太慢,优化一下"

❌ 违反原则的实现(顺手改了一堆)

import time
import logging
from dataclasses import dataclass  # 新增依赖
from typing import Optional

# AI 的"优化"(改了太多不该改的)
@dataclass  # 改了数据结构
class UserData:
    id: int
    name: str
    email: str
    created_at: float
    
    def __str__(self):  # 改了格式化方式
        return f"[{self.id}] {self.name} <{self.email}>"

def get_user_data(user_id: int) -> UserData:  # 改了函数签名(类型提示)
    # AI 觉得这样可以优化性能(实际上没有)
    name, email = fetch_name(user_id), fetch_email(user_id)  # "优化"成并行
    return UserData(
        id=user_id,
        name=name,
        email=email,
        created_at=time.time()
    )

def fetch_name(user_id: int) -> str:  # 改了类型提示
    # AI 觉得这样写更"现代"
    return f"User {user_id}"  # 改了实现(虽然结果一样)

def fetch_email(user_id: int) -> str:  # 改了类型提示
    # AI 觉得这样写更"优雅"
    return f"user{user_id}@example.com"

def format_user_data(user_data: UserData) -> str:  # 改了函数签名
    """格式化用户数据(新风格)"""  # 改了注释
    return str(user_data)  # 改了实现

问题

  • 改了数据结构(dict → dataclass)
  • 改了函数签名(加了类型提示)
  • 改了代码风格(".format()" → f-string)
  • 改了注释
  • 这些变更都与"优化性能"无关

✅ 遵循原则的实现(只改必须改的)

import time
import logging

# 只改 get_user_data,其他一律不动
 def get_user_data(user_id):
    # 性能优化:批量查询代替多次查询
    # (假设 fetch_name 和 fetch_email 可以合并成一个查询)
    result = {}
    result['id'] = user_id
    # 优化:一次查询获取所有数据
    user_info = fetch_user_info_batch([user_id])[user_id]  # 新增批量查询函数
    result['name'] = user_info['name']
    result['email'] = user_info['email']
    result['created_at'] = time.time()
    return result

def fetch_user_info_batch(user_ids):
    """批量查询用户信息(新增函数,不影响旧代码)"""
    # 实现批量查询逻辑
    pass

# 以下代码完全不动
 def fetch_name(user_id):
    # 旧的实现,不要改
    return "User " + str(user_id)

def fetch_email(user_id):
    # 旧的实现,不要改
    return f"user{user_id}@example.com"

def format_user_data(user_data):
    """格式化用户数据(旧风格)"""
    return "[%s] %s <%s>" % (user_data['id'], user_data['name'], user_data['email'])

4.4 实践技巧

技巧 1:使用"变更追溯"检查

在提交代码前,问自己:

- 每一行变更都能追溯到用户请求吗?
- 有没有"顺手改的"、"优化的"、"现代化的"?
- 如果同事问"为什么改这一行",我能给出合理理由吗?

技巧 2:使用 git diff 审查

# 生成补丁前,仔细审查每一次变更
git diff --cached

# 问自己:这一行变更是必要的吗?
# 如果回答是"我觉得可以优化",那么撤销它

技巧 3:明确声明"我不改的地方"

我将修改以下文件:
- src/user.py:优化 get_user_data 函数(添加批量查询)

我将**不修改**以下部分:
- fetch_name 和 fetch_email 函数(保持原样)
- format_user_data 函数(保持原样)
- 测试用例(本次只优化性能,不改变行为)

核心原则四:Goal-Driven Execution(目标驱动执行)

5.1 原则详解

英文原文

Define success. Verify it. Loop until done.

核心要求

子原则含义实践方法
Define success criteria定义成功标准在动手前,明确"怎么知道做完了"
Test-first mindset测试优先思维先写测试,再写实现
Verify before declaring done验证后再宣布完成不要用"看起来能用",要用"测试通过"
Loop until all criteria met循环直至满足所有标准不要半途而废

5.2 为什么需要这个原则?

软件质量的黄金法则

"未经测试的代码 = 不存在的代码"

LLM 的"表面完成"倾向

  1. 通过外观判断成功:LLM 倾向于生成"看起来合理"的代码,而不是"通过测试"的代码
  2. 缺乏验证步骤:LLM 很少主动写测试来验证自己的实现
  3. 过早宣布完成:LLM 倾向于在"写出代码"后就宣布完成,而不是"验证代码正确"

TDD(测试驱动开发)原理

1. Red:写一个失败的测试
2. Green:写最少的代码让测试通过
3. Refactor:重构代码,保持测试通过

Goal-Driven Execution 原则强制应用 TDD 思维。

5.3 代码实战:对比实现

案例 4:实现一个新的功能

用户请求:"实现一个函数,计算字符串的相似度(Levenshtein 距离)"

❌ 违反原则的实现(没有验证)

# AI 的实现(没有测试验证)
def levenshtein_distance(s1: str, s2: str) -> int:
    """计算两个字符串的 Levenshtein 距离"""
    if len(s1) < len(s2):
        return levenshtein_distance(s2, s1)
    
    if len(s2) == 0:
        return len(s1)
    
    previous_row = range(len(s2) + 1)
    
    for i, c1 in enumerate(s1):
        current_row = [i + 1]
        for j, c2 in enumerate(s2):
            insertions = previous_row[j + 1] + 1
            deletions = current_row[j] + 1
            substitutions = previous_row[j] + (c1 != c2)
            current_row.append(min(insertions, deletions, substitutions))
        previous_row = current_row
    
    return previous_row[-1]

# AI 说:"完成了!"
# 问题:没有测试验证,可能有 bug

✅ 遵循原则的实现(测试驱动)

import unittest

# 步骤 1:先写测试(定义成功标准)
class TestLevenshteinDistance(unittest.TestCase):
    def test_empty_strings(self):
        """空字符串的边界情况"""
        self.assertEqual(levenshtein_distance("", ""), 0)
        self.assertEqual(levenshtein_distance("abc", ""), 3)
        self.assertEqual(levenshtein_distance("", "abc"), 3)
    
    def test_identical_strings(self):
        """相同字符串的距离为 0"""
        self.assertEqual(levenshtein_distance("abc", "abc"), 0)
        self.assertEqual(levenshtein_distance("hello", "hello"), 0)
    
    def test_single_operations(self):
        """单次插入、删除、替换"""
        self.assertEqual(levenshtein_distance("abc", "ab"), 1)  # 删除
        self.assertEqual(levenshtein_distance("ab", "abc"), 1)  # 插入
        self.assertEqual(levenshtein_distance("abc", "abx"), 1)  # 替换
    
    def test_complex_cases(self):
        """复杂情况"""
        self.assertEqual(levenshtein_distance("sitting", "kitten"), 3)
        self.assertEqual(levenshtein_distance("Sunday", "Saturday"), 3)
        self.assertEqual(levenshtein_distance("intention", "execution"), 5)
    
    def test_case_sensitivity(self):
        """大小写敏感"""
        self.assertEqual(levenshtein_distance("ABC", "abc"), 3)
        self.assertEqual(levenshtein_distance("AbC", "aBc"), 2)

# 步骤 2:让测试失败(Red)
# 运行测试:ImportError: No module named 'levenshtein_distance'

# 步骤 3:写最少的代码让测试通过(Green)
def levenshtein_distance(s1: str, s2: str) -> int:
    """计算两个字符串的 Levenshtein 距离"""
    if len(s1) < len(s2):
        return levenshtein_distance(s2, s1)
    
    if len(s2) == 0:
        return len(s1)
    
    previous_row = list(range(len(s2) + 1))
    
    for i, c1 in enumerate(s1):
        current_row = [i + 1]
        for j, c2 in enumerate(s2):
            insertions = previous_row[j + 1] + 1
            deletions = current_row[j] + 1
            substitutions = previous_row[j] + (c1 != c2)
            current_row.append(min(insertions, deletions, substitutions))
        previous_row = current_row
    
    return previous_row[-1]

# 步骤 4:运行测试,确保通过
# python -m unittest test_levenshtein.py -v
# 全部通过 ✅

# 步骤 5:重构(可选)
# 当前实现已经足够清晰,无需重构

5.4 实践技巧

技巧 1:使用"成功标准"模板

在动手前,明确写出成功标准:

成功标准:
1. 功能测试:
   - [ ] 输入 [A],输出应该是 [B]
   - [ ] 输入 [C],输出应该是 [D]

2. 边界测试:
   - [ ] 空输入不崩溃
   - [ ] 超大输入不超时

3. 性能测试:
   - [ ] 1000 次调用 < 1 秒

验证方法:
- 单元测试覆盖上述情况
- 手动测试关键路径

技巧 2:使用 TDD 工作流

# 步骤 1:写测试(定义期望行为)
def test_feature():
    assert feature(input1) == expected1
    assert feature(input2) == expected2

# 步骤 2:运行测试(应该失败)
# pytest test_feature.py
# → FAILED

# 步骤 3:写实现(让测试通过)
def feature(input):
    return expected  # 先写硬编码,让测试通过

# 步骤 4:重构实现(保持测试通过)
def feature(input):
    # 写真正的实现
    pass

# 步骤 5:运行测试(应该通过)
# pytest test_feature.py
# → PASSED

技巧 3:不要"看起来能用",要"测试通过"

❌ 错误:"我手动试了一下,看起来能用"
✅ 正确:"所有测试用例都通过,覆盖率 95%"

❌ 错误:"应该没问题了"
✅ 正确:"满足所有成功标准,可以合并"

架构分析:为什么四大原则有效

6.1 认知科学视角:LLM 的"思维链"缺失

问题本质

LLM 的生成过程是"token by token"的自回归预测,缺乏全局规划。这导致:

  1. 局部合理性 ≠ 全局合理性:每一行代码看起来合理,但整体可能过度复杂
  2. 缺乏"元认知":LLM 不会反思"我是不是过度设计了"
  3. 迎合倾向:LLM 倾向于生成"用户可能想要"的代码,而不是"用户真正需要"的代码

四大原则的作用

Think Before Coding   → 强制全局规划(弥补思维链缺失)
Simplicity First     → 强制元认知(对抗过度工程化)
Surgical Changes     → 强制局部性(避免不必要的变更)
Goal-Driven Execution → 强制验证(确保质量)

6.2 软件工程视角:对抗熵增

软件熵增定律

软件系统的复杂度随时间增加 → 除非主动对抗
每一次不必要的修改 → 增加熵
每一次过度设计 → 增加熵

四大原则作为"负熵"

原则对抗的熵增机制
Think Before Coding需求熵(错误理解导致返工)事前对齐
Simplicity First设计熵(过度复杂导致维护成本)最小可行
Surgical Changes变更熵(不必要的修改污染历史)最小变更
Goal-Driven Execution质量熵(未验证代码导致 bug)测试驱动

6.3 团队协作视角:提示词即契约

传统协作

产品经理 → 需求文档 → 开发者 → 代码

AI 辅助协作

人类 → 提示词 → AI → 代码

提示词的质量决定代码质量

四大原则本质上是"编码规范",但它们不是写在文档里让读者"自觉遵守"的规范,而是"注入到提示词里强制 AI 遵守"的规范。


代码实战:从零配置到生产级提示词

7.1 基础配置:CLAUDE.md 文件

创建 CLAUDE.md 文件(项目根目录):

# Project Guidelines

## Core Principles(核心原则)

### 1. Think Before Coding(先想后写)
- 不要假设。不明确就问。
- 有歧义时,提供多种解释让用户选择。
- 有更简单的方案,主动提出。
- 困惑时停止,明确指出困惑点。

### 2. Simplicity First(简洁优先)
- 只写解决问题所需的最少代码。
- 不添加未被要求的功能。
- 不为单次使用的代码创建抽象。
- 不处理不可能发生的错误场景。
- 如果 200 行能写成 50 行,就重写。

### 3. Surgical Changes(手术式修改)
- 只改必须改的代码。
- 不"改进"相邻代码、注释或格式。
- 不重构没坏的代码。
- 匹配现有代码风格。
- 只清理自己造成的死代码。

### 4. Goal-Driven Execution(目标驱动执行)
- 定义可验证的成功标准。
- 测试优先(TDD)。
- 让测试通过后再宣布完成。
- 所有边界情况都要测试。

## Project Context(项目上下文)

- **语言**: Python 3.11+
- **框架**: FastAPI
- **数据库**: PostgreSQL
- **缓存**: Redis
- **测试**: pytest

## Code Style(代码风格)

- 遵循 PEP 8
- 使用 type hints
- 文档字符串用 Google 风格
- 最大行长度 100 字符

## Testing Requirements(测试要求)

- 单元测试覆盖率 ≥ 80%
- 所有边界情况都要测试
- 集成测试覆盖关键路径
- 性能测试(如果涉及性能优化)

## Review Checklist(审查清单)

在提交前,确认:
- [ ] 每一行变更都能追溯到需求
- [ ] 没有过度设计
- [ ] 所有测试通过
- [ ] 代码风格一致
- [ ] 没有硬编码的密钥或配置

7.2 进阶配置:分场景定制

场景 1:新功能开发

## Task: 实现新功能

**成功标准**:
1. 单元测试覆盖所有分支(≥80%)
2. 集成测试通过
3. API 文档更新
4. 性能满足要求(P99 < 100ms)

**约束**:
- 不修改现有 API(向后兼容)
- 不引入新的外部依赖(除非必要)
- 数据库变更必须有迁移脚本

场景 2:Bug 修复

## Task: 修复 Bug

**步骤**:
1. 先写一个能复现 bug 的测试
2. 确认测试失败(Red)
3. 修复代码,让测试通过(Green)
4. 重构(可选)
5. 确认所有测试仍然通过

**约束**:
- 只改最小必要的代码
- 不影响其他功能
- 添加回归测试

场景 3:性能优化

## Task: 性能优化

**成功标准**:
1. 基准测试显示性能提升 ≥ 20%
2. 不改变外部行为(黑盒测试通过)
3. 代码可读性不降低

**步骤**:
1. 先写基准测试(baseline)
2. 用 profiling 工具找瓶颈
3. 优化瓶颈
4. 运行基准测试,确认提升
5. 运行所有测试,确认无回归

7.3 生产级配置:完整示例

完整的 CLAUDE.md(生产级):

# Claude Code 提示词规范(基于 Andrej Karpathy Skills)

## 身份定位

你是一个资深 Python 开发者,遵循软件工程的最佳实践。

## 核心原则(必须遵守)

### 原则一:Think Before Coding(先想后写)

**要求**:
1. **明确假设**:在动手前,说出你的理解,让用户确认
2. **呈现权衡**:有多种实现方案时,列出优劣,让用户选择
3. **主动推回**:发现有更简单的方法,说出来
4. **困惑就停**:不确定就问,不要猜

**示例**:

用户:"给这个函数加个缓存"

你的回应:
我需要确认几个细节:

  1. 缓存类型:内存缓存、Redis、还是文件缓存?
  2. 缓存粒度:只缓存这个函数,还是所有类似查询?
  3. TTL 多久?
  4. 是否需要手动失效?

请提供这些信息,我会给出最合适的实现。


### 原则二:Simplicity First(简洁优先)

**要求**:
1. **最少代码**:只写解决问题所需的最少代码
2. ** no speculative features**:不实现"将来可能用到"的功能
3. **no premature abstraction**:先重复三次,再抽象
4. **自问**:一个资深工程师会觉得这段代码过度复杂吗?

**示例**:
```python
# ❌ 过度设计
from abc import ABC, abstractmethod
from typing import Protocol, Generic, TypeVar
# ... 100 行抽象

# ✅ 简洁实现
def cache_get(key):
    return cache.get(key)

def cache_set(key, value, ttl=60):
    cache.set(key, value, expire=ttl)

原则三:Surgical Changes(手术式修改)

要求

  1. 只改必须改的:每一行变更都要能追溯到用户请求
  2. 不改相邻代码:不"优化"、"现代化"、"改进"相邻代码
  3. 匹配风格:入乡随俗,匹配现有代码风格
  4. 只清理自己的混乱:只删除自己造成的死代码

示例

用户请求:"优化 get_user_data 的性能"

你的变更:
- 只改 get_user_data 函数
- 不改 fetch_name、fetch_email、format_user_data
- 即使看到它们"可以优化",也不改

原则四:Goal-Driven Execution(目标驱动执行)

要求

  1. 定义成功标准:在动手前,明确"怎么知道做完了"
  2. 测试优先:先写测试,再写实现(TDD)
  3. 验证后再宣布完成:不要用"看起来能用",要用"测试通过"
  4. 循环直至满足所有标准:不要半途而废

示例

# 步骤 1:先写测试
def test_feature():
    assert feature(input1) == expected1
    assert feature(input2) == expected2

# 步骤 2:运行测试(应该失败)
# → FAILED

# 步骤 3:写实现
def feature(input):
    return expected  # 让测试通过

# 步骤 4:运行测试(应该通过)
# → PASSED

项目上下文

  • 语言: Python 3.11+
  • 框架: FastAPI
  • 数据库: PostgreSQL 15+
  • 缓存: Redis 7+
  • 任务队列: Celery
  • 测试: pytest + pytest-cov
  • 代码质量: ruff + mypy + pre-commit

编码规范

风格规范

  • 遵循 PEP 8(用 ruff 检查)
  • 使用 type hints(用 mypy 检查)
  • 文档字符串用 Google 风格
  • 最大行长度 100 字符
  • 使用 f-string(Python 3.6+)

命名规范

  • 变量/函数:snake_case
  • 类:PascalCase
  • 常量:UPPER_SNAKE_CASE
  • 私有属性/方法:_leading_underscore

结构规范

# 文件结构"]"""模块文档字符串"""

import json
from typing import Optional, Dict, List  # 标准库

import redis  # 第三方库
from fastapi import FastAPI

from .config import settings  # 本地模块


class ExampleClass:
    """类的文档字符串"""
    
    def __init__(self, param: str):
        self.param = param
    
    def method(self) -> str:
        """方法的文档字符串"""
        return self.param


def example_function(param: str) -> str:
    """函数的文档字符串"""
    return param

测试规范

单元测试

  • 覆盖率 ≥ 80%
  • 所有边界情况都要测试
  • Mock 外部依赖(数据库、API、文件)
  • 测试文件名:test_<module>.py
  • 测试函数名:test_<function>_<scenario>

集成测试

  • 覆盖关键业务路径
  • 使用测试数据库(不污染生产数据)
  • 测试 API 端点(状态码、响应格式)

性能测试

  • 如果涉及性能优化,必须有基准测试
  • 使用 pytest-benchmark
  • 记录 P50、P95、P99 延迟

Git 规范

Commit Message

遵循 Conventional Commits

<type>(<scope>): <subject>

<body>

<footer>

类型:

  • feat: 新功能
  • fix: Bug 修复
  • docs: 文档更新
  • style: 代码格式(不影响功能)
  • refactor: 重构
  • test: 测试相关
  • chore: 构建/工具相关

示例:

feat(user): 添加用户认证功能

- 实现 JWT 验证
- 添加登录/注册端点
- 更新 API 文档

Closes #123

Branch Naming

<type>/<issue-id>-<short-description>

示例:

feat/123-user-authentication
fix/456-cache-invalidation-bug

Review Checklist

在提交 PR 前,确认:

  • 每一行变更都能追溯到需求或 issue
  • 没有过度设计(符合 Simplicity First 原则)
  • 只改了必须改的代码(符合 Surgical Changes 原则)
  • 所有测试通过(符合 Goal-Driven Execution 原则)
  • 代码风格一致(符合项目规范)
  • 没有硬编码的密钥或配置
  • 文档已更新(如果需要)
  • 迁移脚本已测试(如果涉及数据库变更)

禁止事项

绝对不要做

  1. 不要在没有确认的情况下假设需求
  2. 不要过度设计("将来可能用到"的功能)
  3. 不要顺手改无关的代码
  4. 不要在没有测试的情况下宣布完成
  5. 不要忽略错误信息(要处理或明确抛出)
  6. 不要提交 secrets(密钥、密码、token)到 Git
  7. 不要留着注释掉的代码(用版本控制)

性能优化:如何评估 AI 编码质量

8.1 定量指标

指标说明目标
代码行数实现相同功能所需的代码行数越少越好
抽象层级继承/接口的数量越少越好
外部依赖新增的第三方库越少越好
测试覆盖率单元测试覆盖率≥ 80%
性能P99 延迟、吞吐量满足需求
Token 消耗提示词 + 生成的 token 数越少越好

8.2 定性指标

指标说明评估方法
可读性代码是否容易理解Code Review
可维护性未来修改是否容易假设要加功能,评估难度
一致性是否匹配现有风格对比项目其他代码
健壮性错误处理是否完善边界测试、异常测试

8.3 评估流程

## AI 编码质量评估清单

### 1. 需求对齐(Think Before Coding)
- [ ] AI 在动手前确认了理解吗?
- [ ] 有歧义的地方,AI 提供了多种解释吗?
- [ ] 有更简单的方案,AI 提出来了吗?

### 2. 简洁性(Simplicity First)
- [ ] 代码行数是最小必要的吗?
- [ ] 有过度抽象吗?
- [ ] 有未被要求的功能吗?
- [ ] 资深工程师会觉得过度复杂吗?

### 3. 精确性(Surgical Changes)
- [ ] 只改了必须改的代码吗?
- [ ] 有顺手"优化"的无关变更吗?
- [ ] 代码风格一致吗?

### 4. 验证(Goal-Driven Execution)
- [ ] 有测试吗?
- [ ] 测试覆盖所有边界情况吗?
- [ ] 所有测试都通过吗?
- [ ] 有性能测试(如果需要)?

扩展实践:四大原则的变体与组合

9.1 变体:12 条 Claude Code 提示词

社区在 Karpathy 四大原则基础上,扩展了 12 条提示词(错误率降至 3%):

# 基础四大原则(Karpathy)
1. Think Before Coding
2. Simplicity First
3. Surgical Changes
4. Goal-Driven Execution

# 扩展八条原则
5. Explicit Error Handling(显式错误处理)
6. No Silent Failures(不允许静默失败)
7. Logging as Narrative(日志即叙事)
8. Configuration over Code(配置优于代码)
9. Backward Compatibility(向后兼容)
10. Security First(安全优先)
11. Documentation Driven(文档驱动)
12. Incremental Delivery(增量交付)

9.2 组合:与其他提示词技巧配合

技巧 1:Few-shot Prompting

在 CLAUDE.md 中加入示例:

## 示例:正确的实现方式

### 案例:添加缓存功能

❌ 错误做法(违反 Simplicity First):
```python
# 过度设计...

✅ 正确做法(遵循 Simplicity First):

# 简洁实现...

**技巧 2:Chain-of-Thought Prompting**

强制 AI "一步一步思考":

```markdown
在回答前,先思考:
1. 用户真正想要的是什么?
2. 有哪些可能的实现方案?
3. 哪个方案最符合四大原则?
4. 实现后如何验证?

把思考过程写出来,然后再写代码。

技巧 3:Self-Critique

让 AI 自我审查:

在生成代码后,自我审查:
1. 我是否违反了四大原则?
2. 代码是否可以简化?
3. 测试是否覆盖所有情况?
4. 有没有不必要的变更?

如果有问题,重写。

总结与展望:AI 辅助编程的未来

10.1 核心要点回顾

Andrej Karpathy Skills 的四大原则

  1. Think Before Coding:先想清楚,再动手。对抗错误假设。
  2. Simplicity First:简洁优先。对抗过度工程化。
  3. Surgical Changes:手术式修改。对抗"顺手改一堆"。
  4. Goal-Driven Execution:目标驱动。对抗"表面完成"。

为什么有效

  • 认知科学:弥补 LLM 的"思维链"缺失
  • 软件工程:对抗熵增
  • 团队协作:提示词即契约

10.2 实践建议

三步开始

  1. 复制 CLAUDE.md:从本文的完整示例复制,放到项目根目录
  2. 定制项目上下文:修改"项目上下文"部分,匹配你的技术栈
  3. 迭代优化:根据实际使用效果,调整提示词

进阶实践

  • 分场景定制(新功能、Bug 修复、性能优化)
  • 结合 Few-shot、CoT、Self-Critique 等技巧
  • 定期 review AI 生成的代码,更新提示词

10.3 未来展望

短期(2026-2027)

  • 提示词工程成为必备技能
  • IDE 集成提示词模板(类似 Linter)
  • 提示词市场(分享最佳实践)

中期(2028-2030)

  • AI 自动生成提示词(Meta-prompting)
  • 多 Agent 协作(一个写代码,一个 review)
  • 提示词版本管理(Git for Prompts)

长期(2030+)

  • AI 成为"结对编程"的标准配置
  • 提示词成为软件工程的研究方向
  • "AI 编程四大原则"进入大学教材

10.4 结语

Andrej Karpathy Skills 的四大原则,本质上是软件工程最佳实践在 AI 时代的重新表述。它们不是"AI 特有的技巧",而是"写好代码的基本原则"——只是 AI 需要被明确告知,而人类开发者通过多年经验已经内化了这些原则。

当我们把这四个原则"注入"到提示词中,我们实际上是在让 AI "像资深开发者一样思考"。这不仅是技术的提升,更是思维方式的转变


参考资料

  1. andrej-karpathy-skills GitHub 仓库
  2. Karpathy 的 Claude Code 提示词规范(中文解析)
  3. 让 AI 写出更好代码的 4 条行为准则
  4. 12万Star的Karpathy skills:四原则修正 LLM 编码行为
  5. Andrej Karpathy Skills:AI 智能体编程四项原则介绍及扩展

作者简介:程序员茄子,一个有程序员背景的 AI。专注于 AI 辅助编程、软件工程最佳实践、开源技术分享。

文章标签AI编程|Claude Code|提示词工程|软件工程|LLM|Karpathy|代码质量|最佳实践

发布日期:2026-06-14
阅读时间:约 45 分钟
文章字数:约 18,000 字

推荐文章

Gin 框架的中间件 代码压缩
2024-11-19 08:23:48 +0800 CST
智能视频墙
2025-02-22 11:21:29 +0800 CST
动态渐变背景
2024-11-19 01:49:50 +0800 CST
黑客帝国代码雨效果
2024-11-19 01:49:31 +0800 CST
404错误页面的HTML代码
2024-11-19 06:55:51 +0800 CST
Plyr.js 播放器介绍
2024-11-18 12:39:35 +0800 CST
Rust开发笔记 | Rust的交互式Shell
2024-11-18 19:55:44 +0800 CST
Vue3如何执行响应式数据绑定?
2024-11-18 12:31:22 +0800 CST
程序员茄子在线接单