name: karpathy-coding-discipline description: Karpathy 式编程纪律——四条铁律 + 完整代码案例注入(源自 multica-ai/andrej-karpathy-skills 全量吸收)。涉及 coding 时 100% 严格执行。 version: 2.0.0
来源: Andrej Karpathy 对 LLM 编程 Agent 常见陷阱的观察,经 multica-ai/andrej-karpathy-skills(14.3k ⭐)提炼为 CLAUDE.md + Cursor 规则 + Claude Code 插件 + 完整代码案例集,再全量转换为 Hermes SKILL 内部格式。
仓库目录(本地镜像:~/andrej-karpathy-skills/):
├── .claude-plugin/ # Claude Code 插件打包
│ ├── plugin.json # 插件声明(指向 skills/karpathy-guidelines)
│ └── marketplace.json # 插件市场元数据(/plugin install 用)
├── .cursor/rules/
│ └── karpathy-guidelines.mdc # Cursor 规则(alwaysApply: true)
├── skills/karpathy-guidelines/
│ └── SKILL.md # 他们自己的 Hermes 风格 skill 格式
├── CLAUDE.md # 核心行为准则(70行)
├── CURSOR.md # Cursor vs Claude Code 平台引导
├── EXAMPLES.md # 14.8KB —— 8 个真实代码反例,最值钱的部分
├── README.md
└── README.zh.md
⚠️ 研究外部仓库的标准流程(本仓库研究的教训):
1. 不要只看 README/主页 — 完整 clone 或下载全量目录
2. 检查所有文件,包括隐藏目录(.claude-plugin/、.cursor/、skills/ 等)
3. 特别关注 examples/use-cases 文件(往往最有价值,如 EXAMPLES.md 14.8KB 的 diff 对比)
4. 研究插件/平台适配层的结构(了解它是如何在多种工具之间复用的)
5. 再提炼核心价值注入到 Hermes 体系
适用范围: 所有涉及代码编写、修改、审查的任务(forge / chief / tester / aide / default 均适用)
强制等级: ⚠️ 涉及 coding 时 100% 强制执行,不得跳过。 这不是可选的风格建议。
引用材料路径(全量注入): ~/server-config/skills/references/karpathy-original-repo/(已 git commit 推至 CNB sieta/server-config commit 58ff5eb)。包含完整 EXAMPLES.md(14.8KB 8 个反例)、.claude-plugin/ 插件定义、.cursor/rules/ Cursor 规则、skills/karpathy-guidelines/ 原始 SKILL.md。
仓库源起的三个观察是理解全部原则的基础:
1. "模型会代你做错误假设,不假思索就执行。它们不管理困惑、不寻求澄清、不暴露矛盾、不展示权衡、不反驳。" 2. "它们真的很喜欢把代码和 API 搞复杂……明明 100 行能搞定的事,非要 1000 行。" 3. "它们仍然会改动或删除自己没充分理解的代码和注释——即使与任务无关。"
这三句话分别对应了原则一/三/二/四。
「不要假设。不要隐藏困惑。暴露权衡。」
在开始实现之前:
✅ 自检: 实现前是否清楚所有接口的输入输出?不确定的地方确认过了吗?
场景:用户说"加个导出功能"
❌ LLM 的错误做法:
def export_users(format='json'):
users = User.query.all() # 假设导出全部用户(没问范围/隐私)
if format == 'json':
with open('users.json', 'w') as f: # 假设存本地文件(没问方式)
json.dump(...)
elif format == 'csv':
# 假设字段名(没问数据结构)
✅ 正确的做法——先列清模糊点:
在实现之前我需要确认:
1. 全部用户还是子集?(隐私影响)
2. "导出"是指浏览器下载、后台发邮件还是 API 端点?
3. 哪些字段?(有些可能敏感)
4. 数据量多大?(影响实现方式)
「最少代码解决问题。绝不写投机性代码。」
✅ 自检: 自问"一个 senior engineer 看了会觉得过度复杂吗?"如果会,简化。
场景:用户说"加个折扣计算函数"
❌ LLM 的错误做法(过度抽象):
# 策略模式 + 抽象基类 + 配置类 + 30行 setup
class DiscountStrategy(ABC):
@abstractmethod
def calculate(self, amount: float) -> float: pass
class PercentageDiscount(DiscountStrategy): ...
class FixedDiscount(DiscountStrategy): ...
class DiscountCalculator: ...
# → 30+行 setup 才能算个折扣
✅ 正确做法:
def calculate_discount(amount: float, percent: float) -> float:
return amount * (percent / 100)
原则:当复杂性被需要时才加,而不是提前预判。
其他的过度复杂反例(来自 EXAMPLES.md): - ❌ PreferenceManager 类带了 cache/validation/notification/merge 四个没人要的功能 - ✅ 一个 UPDATE SQL 就搞定
「只动必须改的东西。只清理自己造成的混乱。」
编辑已有代码时: - 不要"顺手改进"相邻代码、注释或格式 - 不要重构没有坏的东西 - 匹配现有风格,即使你个人更喜欢另一种写法 - 发现无关的死代码 → 提一句,但不删(除非用户明确让删)
自己改动造成孤儿代码时: - 只清理你的修改导致的未使用 import/变量/函数 - 不要删已有的死代码,除非用户要求
✅ 自检: 每一行修改是否能直接追踪到用户的需求? 问自己:这一行改变是修 bug 的必要步骤,还是我的"锦上添花"?
场景:用户说"修 bug:空邮箱会让验证器崩溃"
❌ LLM 的错误做法——顺手把半个文件改了一遍:
- # Check email format
- if not user_data.get('email'):
+ """Validate user data."""
+ # 改了注释
+ # 改了 email 校验逻辑(加了 @ 和 . 的完整校验)
+ # 加了 username 校验(三行没人要的校验)
+ # 加了 docstring
✅ 正确做法——只改引起 bug 的那两行:
- if not user_data.get('email'):
+ email = user_data.get('email', '')
+ if not email or not email.strip():
raise ValueError("Email required")
- if '@' not in user_data['email']:
+ if '@' not in email:
raise ValueError("Invalid email")
# username 部分:不动
场景:用户说"在上传函数里加日志"
❌ LLM 改了单引号为双引号 + 加了类型注解 + 加了 docstring + 改了 return 逻辑:
- def upload_file(file_path, destination):
+ def upload_file(file_path: str, destination: str) -> bool:
+ """Upload file..."""
+ logger.info(f"Uploading {file_path} to {destination}")
- with open(file_path, 'rb') as f:
- with open(file_path, "rb") as f: # ' → "
- if response.status_code == 200:
- return True
- else:
- return False
+ success = response.status_code == 200 # 改了 return 逻辑
✅ 正确做法——匹配现有风格,只加那几行 logger:
+ import logging
+ logger = logging.getLogger(__name__)
def upload_file(file_path, destination):
+ logger.info(f'Starting upload: {file_path}')
try:
with open(file_path, 'rb') as f:
data = f.read()
response = requests.post(destination, files={'file': data})
if response.status_code == 200:
+ logger.info(f'Upload successful: {file_path}')
return True
else:
+ logger.error(f'Upload failed: {file_path}, status={response.status_code}')
return False
except Exception as e:
- print(f"Error: {e}")
+ logger.exception(f'Upload error: {file_path}')
return False
「定义成功的标准。循环直到验证通过。」
把命令式任务转换成可验证的目标:
| 不要说... | 改成... |
|---|---|
| "加验证" | "写测试让无效输入失败,然后让它们通过" |
| "修这个 bug" | "写一个能复现 bug 的测试,然后让它通过" |
| "重构 X" | "确保测试在重构前后都通过" |
多步骤任务的结构化计划格式(必须用):
1. [步骤] → 验证:[检查项]
2. [步骤] → 验证:[检查项]
3. [步骤] → 验证:[检查项]
✅ 自检: 验收标准是否明确、可验证?能明确判断"做完了"吗?
场景:用户说"修复排序在有重复分数时出问题"
❌ LLM 的错误做法——直接改 sort 逻辑,不先复现 bug:
def sort_scores(scores):
return sorted(scores, key=lambda x: (-x['score'], x['name']))
✅ 正确做法——先写复现测试,再修:
# 1. 先写测试复现 bug
def test_sort_with_duplicate_scores():
scores = [
{'name': 'Alice', 'score': 100},
{'name': 'Bob', 'score': 100},
{'name': 'Charlie', 'score': 90},
]
result = sort_scores(scores)
# 跑 10 次 → 顺序不稳定 → 测试失败 ✓ 确认 bug
# 2. 再修
def sort_scores(scores):
return sorted(scores, key=lambda x: (-x['score'], x['name']))
# 测试现在稳定通过 ✓
| 原则 | 反模式 | 正确做法 |
|---|---|---|
| Think Before Coding | 默认假设文件格式/范围/字段 | 列出假设,逐个问清楚 |
| Simplicity First | 折扣计算用例策略模式 | 一个函数解决问题,复杂度等到真需要再加 |
| Surgical Changes | 修 bug 时顺手改引号/类型注解/return 逻辑 | 只改修复 bug 的必要行 |
| Goal-Driven | "我来审查改进代码" | "先写复现 bug 的测试 → 让它通过 → 验证无回归" |
那些"过度复杂"的例子并不是明显错误的——它们遵循了设计模式和最佳实践。问题出在时机:它们在需要之前就加了复杂度。
好的代码是用简单方式解决今天的问题,而不是过早预测明天的问题。
每次 coding 任务完成后,过一遍这个清单:
如果以上任何一项回答"不确定"或"可能超标",stop and clarify。
⚠️ 特别警告:Surgical Changes(原则三)是 Karpathy 认为 LLM 最容易犯、也最让人沮丧的错。修 Bug 时每次 check 一下 diff 里有没有不属于任务的行——任何与用户需求无关的改动都是问题**。
references/chartjs-cdn-china.md — 在中国服务器上使用 Chart.js 的 CDN 选择方案(cdnjs 优于 jsdelivr)、自定义 afterDraw 标注插件(零外部依赖,替代 chartjs-plugin-datalabels)、以及环形图标注前 N 项的技术细节。当需要在静态页面中使用 Chart.js 时,始终优先引用此文件。**