Karpathy 的四条黄金法则:当 AI 开始「过度设计」时,如何用 CLAUDE.md 拯救代码质量

CLAUDE.md 激活瞬间:四个图标对应四条原则,手绘插画风格,marker pen 草图质感

凌晨两点,我盯着屏幕,陷入了一种诡异的沮丧。

我让 Claude Code 帮我写一个日志解析工具,需求很简单——从 nginx 日志里提取 IP 地址和请求路径,输出一个 CSV。Claude 很乐意地接受了任务,五分钟后,我得到了一个模块化的、抽象的、支持插件的、可配置的日志处理框架。它有接口抽象、策略模式、三种输出格式的适配器,还有一个用 decorator 实现的中间件管道。

我只需要解析两列文本。

这个框架有 23 个文件。

更糟糕的是,当我试图运行它时,因为某个抽象层的类型不兼容,程序崩溃了。我花了半小时去理解这个框架的结构,然后花了二十分钟删掉了其中 80% 的代码,最后用 15 行 Python 完成了最初的需求。

这不是 Claude 的问题。这是我的问题——我没有告诉它我想要什么。

或者说,我以为我告诉了它,但它其实根本没有听懂。

# 一个 GitHub 仓库与一个老问题

2024 年末,Andrej Karpathy 在 GitHub 上传了一个名为 andrej-karpathy-skills 的仓库,目前已经斩获了 149K+ stars。这个仓库的 README 非常简短,但其中的内容——一份 CLAUDE.md 模板文件——却在 AI 编程社区引发了广泛讨论。

对于不熟悉 Andrej Karpathy 的读者,这里做一个简要介绍:他是斯坦福大学 CS231n 课程的创始人之一,曾担任 OpenAI 的创始成员和 Tesla AI 部门的高级总监,负责 Autopilot 的视觉感知系统。现在,他全职运营 YouTube 频道,专注于 AI 教育和内容创作。

简单来说,这个人不是一个理论家,而是一个从实验室走向生产线的人。他的建议不是书斋里的空谈,而是工业级 AI 系统开发的经验沉淀。

Karpathy 在这个仓库里提供了一份简短的 CLAUDE.md 模板,其中核心内容就是四条编码原则。这份文件没有华丽的排版,没有引用学术论文,没有任何「提示词工程」的繁复技巧——它只有四条直白的规则。

但正是这四条规则,击中了我在日常 AI 编程中反复遇到的核心痛点。

# LLM 编码的四大陷阱:Karpathy 为什么要写这四条原则

在我深入分析这四条原则之前,我想先谈一个根本问题:为什么我们需要这些原则?

使用 LLM 进行编程辅助已经成为了无数开发者的日常。根据我的观察和自我反省,当我们让 LLM 帮助写代码时,主要会遇到四类典型问题:

第一类:猜测式编码(Guesswork Coding)

LLM 遇到需求不明确时,不会说「我不确定这里该怎么处理」,而是会直接选择一个看起来合理的方向继续前进。它不会暴露歧义,不会询问用户偏好,不会展示权衡过程。在 Karpathy 的语境里,这就是「先编码,后思考」——代码先行,思考滞后。

第二类:过度工程(Over-Engineering)

这正是我在文章开头遇到的场景。LLM 倾向于「做得更多」而非「做得刚好」——它会创建抽象层来应对「将来可能的需求」,编写插件系统来支持「将来可能扩展的功能」,添加配置选项来处理「将来可能不同的场景」。结果是,一个简单需求被包裹在一层又一层的设计模式中。

第三类:编辑污染(Edit Pollution)

当我们让 LLM 修改现有代码时,它常常会对「附近但无关」的代码进行「改进」——调整格式、更新注释、优化变量名、甚至重构相邻的逻辑。这看起来是出于好意,但会让代码变更的 diff 变得不可控,增加了 code review 的难度,也增加了引入新 bug 的风险。

第四类:目标模糊(Goal Ambiguity)

这是最隐蔽的一类问题。我们通常以命令式的语言给出指令——「添加验证」「修复错误」「优化性能」——但这些指令没有给出可验证的目标状态。LLM 收到这样的指令后,会在黑暗中摸索,而它的「努力」可能与我们的真实意图南辕北辙。

这四类问题——猜测式编码、过度工程、编辑污染、目标模糊——正是 Karpathy 四条原则要直击的核心。

# 原则一:Think Before Coding——先思考,后编码

不要假设。不要隐藏困惑。要呈现权衡取舍。不确定时主动提问。不要默默选择一种解释。

这条原则听起来像是对人类程序员的建议,但它对 LLM 的指导意义同样深刻——甚至可以说更加深刻。

# LLM 为什么倾向于「先编码后思考」

人类程序员在接到一个模糊需求时,通常会有意识地停下来思考:「这里有歧义」「这个边界条件我没考虑」「我需要和需求方确认一下」。但 LLM 在生成模式下,会倾向于「流畅地」完成整个任务——它会利用训练数据中见过的相似模式来填补空白,让代码看起来完整、连贯。

问题在于,这种「流畅」可能掩盖了深层的理解缺失。

举个例子。我曾经让 Claude 帮我写一个函数,用于计算一组用户的平均活跃时长。我的需求描述中提到了「活跃时长」,但没有定义什么是「活跃」。Claude 直接假设「活跃时长」等于「用户在线时间」,于是写了一个基于 session 的计算逻辑。

而实际上,我们的业务定义是「活跃时长 = 有效操作时间(排除空闲超过 5 分钟的时段)」。

Claude 没有问。它直接假设了。它生成了看起来完全合理、功能完整的代码。如果我没有仔细 review,这个 bug 就会进入生产环境。

# 反例:沉默的假设

# 需求:写一个函数计算购物车总价
# 反例:Claude 在假设中完成编码

def calculate_cart_total(cart):
    """Calculate total price of cart items."""
    total = 0
    for item in cart:
        # 假设 item 有 'price' 字段,假设 price 是数值
        total += item['price']
    return total

# 问题:
# 1. 没有考虑 discount 字段的存在(实际业务中 discount 是必需的)
# 2. 没有考虑 currency 字段(可能导致不同货币直接相加)
# 3. 没有处理 None/缺失值(实际数据中很常见)
# 4. 没有验证 cart 的类型(可能传入 None 或非 list)

在这个例子里,Claude 没有问任何一个问题就完成了编码。它做了一个「合理」的假设,但这个假设可能完全不符合实际业务场景。

# 正例:暴露困惑,主动澄清

# CLAUDE.md 中添加的原则触发词

## 当你遇到以下情况时,必须停下来提问,不要继续编码:

1. 需求中存在技术术语但未定义具体含义
2. 数据结构/类型未明确指定
3. 边界条件未说明(如空输入、异常输入、极限值)
4. 存在多种合理的实现路径但未指明偏好
5. 引用了外部系统/API 但未提供接口规范

## 提问模板

当你需要澄清时,请使用以下格式:

我注意到 [具体歧义点] 可能导致不同的实现方式:

方案 A:[描述 + 适用场景]
方案 B:[描述 + 适用场景]
默认选择:[如果你必须选一个,描述你的选择和原因]

你倾向于哪种方案?

这样做的价值在于:它把「沉默的错误假设」变成了「公开的设计决策」,让人类有机会在编码之前纠正方向。

# 我的真实经验

我在使用 Claude Code 做一个内部工具时,遇到了一个典型场景:我需要实现一个数据导出功能。Claude 在开始编码之前,主动发了这样一条消息:

我注意到需求中提到'导出'但没有说明格式。当前项目中有两处导出逻辑——一个是 CSV,一个是 Excel。我应该遵循哪种模式?另外,导出内容的字段是否需要用户选择,还是导出所有字段?

这正是 Think Before Coding 的完美体现。如果没有这个「暂停」,Claude 可能会基于训练数据中的常见模式选择一个格式,结果可能与现有代码的风格不一致,或者遗漏了我们需要的字段选择功能。

而有了这个暂停,我只需要回复两句话,整个方向就被纠正了。这比事后发现「导出格式不对」然后返工要高效得多。

# 原则二:Simplicity First——简单优先,不做预言家

用最少的代码解决问题。避免投机性功能(不要为「将来可能用到」写代码)。不为单次使用的代码创建抽象。

这条原则是四原则中我认为对我影响最大的。它不仅仅是一种编码风格,更是一种关于「未来」的哲学立场。

# 为什么 LLM 如此热衷于过度设计

这里有一个有趣的认知现象:当我们(无论是人类还是 AI)被要求「写一段代码」时,脑子里会自动激活一套「好代码」的模板。这套模板包含设计模式、抽象层次、可扩展性——这些在教科书里被反复强调的概念。

结果是,即使需求只是「打印斐波那契数列前 20 项」,LLM 也可能写出一个支持任意起始位置、任意终止条件、支持多种输出格式的「斐波那契生成器框架」。

这不是恶意行为。这是训练数据的副作用——大量的开源代码库推崇可扩展性,LLM 学到了这些模式,却没有学会在什么场景下使用它们。

过度工程的「城堡」:一个简单需求被层层抽象包裹,手绘插画风格,幽默对比

# 反例:为一个需求建造了一座城堡

# 需求:写一个函数,判断字符串是否是回文
# 反例:LLM 的「过度工程」版本

from abc import ABC, abstractmethod
from typing import Protocol, TypeVar, Generic
from enum import Enum, auto

class NormalizationStrategy(ABC):
    """抽象基类:支持不同的规范化策略"""
    @abstractmethod
    def normalize(self, text: str) -> str:
        pass

class LowercaseStrategy(NormalizationStrategy):
    def normalize(self, text: str) -> str:
        return text.lower()

# ...(省略大量抽象层)

# 实际只需要:
# return s == s[::-1]
# 3 行代码 vs 45 行代码

这个例子当然是极端的,但类似的情况在日常使用中并不罕见。我见过 Claude 为一个简单的 CRUD API 创建完整的 DTO 层、转换层、异常处理链,而实际上那个 API 只有两个端点。

# 正例:最小化实现,只满足当前需求

# 需求:判断字符串是否是回文(忽略大小写和空格)
# 正例:最小化代码

def is_palindrome(s: str) -> bool:
    """Check if string is a palindrome (case-insensitive, ignoring spaces)."""
    cleaned = ''.join(c.lower() for c in s if not c.isspace())
    return cleaned == cleaned[::-1]


# 需求:添加用户到列表
# 正例:不做提前抽象

def add_user(name: str, email: str):
    """Add a user to the in-memory user list."""
    users.append({'name': name, 'email': email})

# 不需要 User 类,不需要 Repository 接口,不需要数据库抽象
# 如果将来需要持久化,到时候再加

关键判断标准:这段代码有没有「如果将来需要」的想法? 如果有,就先不要写。

# 「简单」的反直觉价值

这里有一个反直觉的事实:简单代码通常比复杂代码更 robust

复杂代码引入了更多的抽象层,每个抽象层都有边界条件和异常路径。当你添加一个「可扩展的设计」时,你也添加了一个「需要维护的接口」和一个「需要理解的逻辑」。这些东西在需求不变时是负担,在需求变化时——很可能——这些「扩展点」根本不是你需要的那个方向。

最简单的代码不需要理解抽象就能理解逻辑,不需要追踪调用链就能定位 bug,不需要文档就能维护(因为代码本身就是文档)。

我自己在使用 Claude Code 时学会的一个技巧是:明确告诉它「不要使用设计模式,除非我明确要求」。这句话在我每次新建 CLAUDE.md 时都会写进去。

# 原则三:Surgical Changes——外科手术式的代码变更

编辑现有代码时,不「改进」相邻代码/注释/格式。不重构没坏的东西。匹配已有风格。只清理自己制造的混乱。

这条原则是我在 code review 中踩过最多坑的。

# LLM 的「好意」是最大的风险

当要求 LLM 修改一段代码时,它常常会「顺便」做一些「改进」:

  • 更新相邻函数的命名风格使其「更一致」
  • 重写注释使其「更清晰」
  • 调整代码格式使其「更规范」
  • 重构一个「看起来可以优化」的数据结构

这些「改进」单独看都没有问题,但它们带来的问题是:你不知道哪些变更是你要求的,哪些是 LLM「自作主张」加的

这在实践中意味着:当你试图理解一个 diff 时,你面对的不再是「一个 bug fix」或「一个功能变更」,而是「一个 bug fix + 风格调整 + 重构 + 格式变更」的混合体。任何一点引入新 bug 的风险,都会被放大。

外科手术式变更:左侧混乱的多余修改 vs 右侧精准的单行修改,手绘插画风格

# 反例:一个 bug fix 引入了三个新 bug

# 原始代码
def process_payment(amount: float, method: str) -> bool:
    if amount <= 0:
        return False
    if method == "credit_card":
        return charge_credit_card(amount)
    return False


# Claude 修改后的代码
def process_payment(amount: float, method: str) -> dict:
    if amount < 0:  # ❌ 修改了边界条件(<= 0 变成 < 0)
        return {"success": False, "error": "Invalid amount"}
    if method == "credit_card":
        return {"success": True, "transaction_id": charge_credit_card(amount)}
    return {"success": False, "error": "Unsupported method"}

用户的需求可能只是「当金额为负时抛出异常而不是静默返回 False」,但 Claude 因为接收到了一段代码,就觉得应该「整体改进」它。结果是:

  1. 返回类型从 bool 变成了 dict,所有调用方需要修改
  2. 边界条件从 <=0 变成了 <0,丢失了对「零金额」的处理
  3. 返回值的结构变了,调用方需要适配新的 dict 格式

一个简单的 bug fix 变成了一次破坏性重构。

# 正例:手术刀式的精准修改

# Claude 的手术刀式修改(只改需要改的)
def process_payment(amount: float, method: str) -> bool:
    if amount <= 0:
        raise ValueError(f"Invalid payment amount: {amount}")  # 只改这一行
    if method == "credit_card":
        return charge_credit_card(amount)
    return False

差异最小化。只添加了 raise ValueError,其他所有内容保持原样。

# Surgical Changes 的实操要求

在 CLAUDE.md 中,我可以这样描述 Surgical Changes 的具体要求:

## 编辑现有代码时的规则

1. **变更范围最小化**:只修改直接相关的代码行,不触碰相邻代码
2. **不变动注释**:除非注释本身就是 bug 的来源,否则不修改注释
3. **不改变样式**:如果现有代码使用 tabs,使用 tabs;如果使用 camelCase,保持 camelCase
4. **不添加新功能**:编辑请求不是重构或改进请求,专注于当前任务
5. **先展示 diff 再执行**:在修改前展示你计划的变更,让用户确认

我自己在实践中发现,「先展示 diff 再执行」这一条特别重要。即使 Claude 理解了这个原则,它的判断也可能与我的预期不符。通过让它先展示计划,我可以及时纠正偏差,而不是在代码已经写入文件后才发现问题。

# 原则四:Goal-Driven Execution——目标驱动执行

将命令式任务转化为可验证目标。「添加验证」 → 「为无效输入写测试,然后让测试通过」。

这条原则是四原则中最具「方法论」色彩的一条。它实际上是在说:把「做什么」变成「做到什么」

# 命令式 vs 目标导向

这两种表述方式的差异非常微妙,但影响巨大:

命令式表述 目标导向表述
「添加输入验证」 「当前无效输入会导致程序崩溃。编写一个测试,确认 is_valid_input() 对无效输入返回 False,然后让该测试通过」
「优化性能」 「当前处理 10000 条记录耗时 12 秒。目标是将耗时降低到 2 秒以内。性能瓶颈在第 3 层循环」
「添加日志」 「当 process_payment 失败时,日志中应包含 amountmethod 和错误原因,以便调试」
「修复 bug」 「当 amount = -10 时,当前函数静默返回 False。修复后应抛出 ValueError

命令式表述告诉 LLM 一个动作,目标导向表述告诉 LLM 一个期望状态。

# 为什么这个区别如此重要

因为 LLM 在执行「动作」时,路径是模糊的——它可以「添加验证」,但验证放在哪里?验证哪些值?验证失败后如何处理?每个选择都可能与你的意图不符。

而当你描述「目标状态」时,你给 LLM 的是一个可验证的终点。LLM 可以围绕这个终点反向工作:「我需要让无效输入导致测试失败,然后让测试通过,这意味着我需要实现某个验证逻辑」。

更深层地说,目标导向的表述将「质量标准」外化给了 LLM。LLM 不再只是执行一系列指令,它需要理解「什么是成功」——这要求它对问题域有更深的理解。

# 测试驱动:目标导向的最强形式

在这四条原则中,Goal-Driven Execution 与 TDD(测试驱动开发)有着天然的亲和力。Karpathy 提到的「为无效输入写测试,然后让测试通过」本质上就是 TDD 的核心流程。

当你让 LLM 用 TDD 的方式工作时,你给了它三个明确的阶段:

  1. Red:写一个会失败的测试
  2. Green:写最少的代码让测试通过
  3. Refactor:(可选)只重构刚写的代码

这个循环把「编码」变成了「解题」——每个步骤都有明确的目标和可验证的反馈。

我个人的经验是,这个方法在我进行重构时尤其有效。例如,当我要重构一个复杂函数时,我会先让 Claude 为现有行为写测试用例(确保我不会破坏任何东西),然后再进行重构。测试用例就是「目标」的具体化——只要测试通过,就说明重构是正确的。

# 为什么这四条原则能解决 80% 的 LLM 编码错误

现在,我想从一个更宏观的角度来分析这四条原则的有效性。

# 从信息论角度看四条原则

如果我们把「LLM 编码错误」看作一个信息传递问题,那么错误的核心来源是信息不对称

  • LLM 不完全理解你的意图(歧义)
  • 你不完全理解 LLM 的方案(黑盒)
  • 变更的范围超出了你的预期(scope creep)
  • 「完成」的定义不清晰(目标模糊)

这四条原则恰好对应了这四个信息不对称问题:

信息不对称 对应原则 解决方案
LLM 不知道你的隐含假设 Think Before Coding 强制 LLM 暴露歧义,主动提问
你不知道 LLM 选择了什么方案 Surgical Changes 限制变更范围,先展示 diff
LLM 做了超出需求的事 Simplicity First 约束代码量,只满足当前需求
双方对「完成」的理解不同 Goal-Driven Execution 定义可验证的目标状态

从根本上说,这四条原则不是在「优化 LLM 的输出」,而是在建立一个人机协作的沟通协议。它们把模糊的「帮我写代码」变成了一个有结构的对话。

# 四原则之间的协同关系

这四条原则不是孤立的,它们之间有一个隐含的递进逻辑:

Think Before Coding
    ↓
澄清需求 → 确定方向
    ↓
Simplicity First
    ↓
选择最小化方案 → 避免过度设计
    ↓
Goal-Driven Execution
    ↓
定义验收标准 → 明确完成条件
    ↓
Surgical Changes
    ↓
精准执行变更 → 交付预期结果

Think Before Coding 解决的是「方向」问题(做什么);Simplicity First 解决的是「方案」问题(怎么做最简);Goal-Driven Execution 解决的是「标准」问题(做到什么程度);Surgical Changes 解决的是「执行」问题(最后怎么落地)。

这四个阶段覆盖了一个完整的人机协作编码流程。

# 实操指南:如何把这四条原则融入你自己的 CLAUDE.md

好,道理懂了,但怎么落地?这是我自己在多个项目中反复迭代后打磨出的 CLAUDE.md 模板。直接拿去用,改成你自己的场景即可。

# Project Context

我是 [你的名字],主要做 [你的工作类型]。
主要技术栈:[列出核心技术]。
当前阶段:[项目当前所处阶段]。

# Working Principles

## Think Before Coding
- 需求有歧义时主动提问,不要默默做假设
- 不确定时停下来列出来可能的方向,问我偏向哪个
- 如果有更简单的方案,必须先提出来再问是否采用

## Simplicity First
- 不实现未被要求的功能
- 不为「将来可能用到」创建抽象
- 不为不可能发生的场景写错误处理
- 遇到复杂代码主动问:「需要我重构吗?」

## Surgical Changes
- 编辑现有代码时只改需要改的部分
- 不修改相邻代码、注释、格式
- 匹配已有代码风格(缩进、命名、模式)
- 修改前先展示 diff,等用户确认再写文件
- 只清理自己造成的孤立代码,不动原有的死代码

## Goal-Driven Execution
- 把命令式任务转成可验证目标
- 「添加 X」 → 「写测试验证 X 工作正常,然后让测试通过」
- 「修复 bug」 → 「写一个能复现的测试,然后让测试通过」
- 完成功能后说明你是怎么验证的

# Code Style

- [具体写明的命名规则、缩进风格、注释风格]
- [你项目的架构约定,例如 feature-based / layer-based]

# 当前优先事项

1. [本阶段最重要的目标]
2. [次要目标]
3. ...

# 明确禁止

- 不要在代码中留下 TODO
- 不要添加不必要的依赖
- 不要改动 [某些配置文件],除非我明确要求

如何使用:

把这份模板复制到项目根目录,命名为 CLAUDE.md。几点说明:

Working Principles 部分 是核心——这四条不是随便写的,而是针对 Karpathy 原则的具体化。比如「自我检查」那条,我加了一句「如果你发现了自己的错误,直接改正」,因为这是我在使用中发现最影响效率的细节——模型有时候会坚持一个错误的实现,直到我指出来。

Code Style 部分 要写得具体。我看过很多 CLAUDE.md 把「遵循最佳实践」写在 Code Style 里,这等于没写——最佳实践有一百种解释。写得越具体,模型输出的一致性越高。

当前优先事项 是动态的。每次开始新任务之前,我会更新这一节。这帮我自己在和 AI 对话前理清思路,也帮 AI 理解我现在最在意什么。

明确禁止 也很重要。告诉 AI 不要做什么,往往比告诉它要做什么更有效——因为负面规则更具体,更不容易被「善意地误读」。

# 反思:这是约束 AI 还是赋能 AI?

写到这里,我停下来问了自己一个问题:这份 CLAUDE.md,本质上是在「驯服」AI,还是在给 AI 一个更好的发挥空间?

我倾向于后者。

很多人把 prompt 工程理解为「让 AI 按我的方式做事」。但更准确的描述是:在给定的约束下,给 AI 更大的自由度

Karpathy 的四条原则,每一条都是在划边界:不要只给我片段、发现错误要改、代码复杂了要说、做完功能要验证。在这些边界之内,AI 可以充分发挥——它可以选它认为最好的实现方式,可以在遇到歧义时做出合理假设,可以在感觉不对的时候提出来。

没有这些边界会怎样?AI 会变得更「保守」。因为它不确定你会接受什么输出,于是它倾向于输出最少、最模糊的内容——反正你说哪里不对它改哪里就是了。这其实是效率最低的协作方式。

约束 vs 赋能:左侧被杂乱绳子束缚的木偶 vs 右侧被清晰光环环绕的同款木偶,手绘插画风格

把这套思路放到 Claude Code 的生态里看,会更清晰。

Harness(配置文件、~/.claude.json 等)是全局层面的约束——你希望 Claude Code 在所有项目中的默认行为是什么。这是基础设施。

MCP(Model Context Protocol)是工具层面的扩展——你的 AI 能访问什么信息、能操作什么系统。这是能力边界。

Skill 是任务层面的指导——针对特定类型的工作,AI 应该遵循什么流程。这是工作方法论。

而 CLAUDE.md,恰好落在 Skill 和 Harness 之间的那个交叉点——它是项目级别的指导原则,定义了「在这个特定项目中,AI 应该怎么做」。

这就是为什么 CLAUDE.md 不是「又一个配置文件」。配置文件告诉 AI「你能做什么」,CLAUDE.md 告诉 AI「你应该怎么做」。


现在让我们把视野拉大。

AI Agent 时代正在发生一件根本性的变化:程序员的角色正在从执行者变成协调者

这句话已经被说了无数遍,但它的含义值得我们仔细拆解。

执行者的工作模式是:需求清晰,产出明确,你交付的是代码。AI 出现后,这种工作模式受到冲击——AI 能以 10 倍的速度完成同样的代码交付。于是执行者开始焦虑:我的价值在哪里?

协调者的工作模式完全不同。需求可能模糊,产出是决策和判断,你交付的是方向。AI 负责在方向之下执行,你负责在执行之上导航。

CLAUDE.md 是这个转变的缩影。写一份好的 CLAUDE.md,需要你对自己正在做的事有清晰的理解——不只是「我要做什么功能」,而是「我重视什么、我不接受什么、我当前阶段的核心矛盾是什么」。这些东西 AI 无法替你写,因为它们来自你对自己工作的判断。

换句话说:你越清楚自己要什么,AI 就能帮你越多

这不是「程序员被 AI 替代」的叙事,这是一个更微妙的转变:AI 把执行层的事情接管了,人类被推到了真正需要判断力的位置。多数时候,判断力比执行能力更稀缺,也更难培养。

开发者站在仪表盘前看向远方城市天际线,仪表盘上有四个发光图标对应四条原则,手绘插画风格,思考氛围

# 结语

四条原则,一篇长文,最后归结为一句话:

和 AI 协作的本质,不是让它替你工作,而是让你学会提出更好的问题。

Karpathy 的四条原则,表面上在约束 AI 的输出行为,实际上在训练你——训练你对「好代码」的直觉,训练你对「这段逻辑是否正确」的敏感度,训练你对「这个实现是否过度设计」的判断力。

CLAUDE.md 只是一个开始。真正的技能,是你有一天不再需要这份文件——因为你已经在无数次对话中,把这些原则内化成了自己的思维方式。

到那时,AI 不再是你的拐杖,而是一面镜子:它忠实地反映你的思路,也放大了你的盲区。

这是最好的时代,还是最坏的时代?取决于你愿不愿意回答那个最难的问题:

你到底想要构建什么?


# 参考资料