让 AI 记住自己踩过的坑,越用越聪明 — 一个 PostToolUse hook 就够了
Claude Code 很强,但它不记得自己犯过的错。上次写错了 DankeTheme.secondaryText(正确的是 textSecondary),下次还会错。每次都要你纠正,累不累?
一个 PostToolUse hook,每次编辑文件后自动检查 + 匹配经验库。犯过的错自动记住,下次写到同样的代码直接弹警告。
编辑文件 → 🔍 语法检查 → 📚 经验匹配 → 🧠 自动学习
每次 Edit/Write 后自动跑:
- Python →
py_compile+import验证 - JSON →
json.loads解析 - Shell →
bash -n语法检查 - 自定义规则(比如检查 UI 组件属性是否存在)
存着过去踩过的坑,每条有 trigger + scope + weight:
{
"lessons": [
{
"scope": "*.swift",
"triggers": ["DankeTheme.secondaryText", "DankeTheme.primaryText"],
"lesson": "属性名是 textSecondary/textPrimary,改之前 grep 确认",
"weight": 3,
"hits": 2,
"last_hit": 1779300000
}
]
}编辑代码时,diff 内容跟 triggers 匹配。命中了弹警告:
⚠️ 反思(权重3):属性名是 textSecondary/textPrimary,改之前 grep 确认
trigger 支持两种模式:
- 普通字符串:
"DankeTheme.secondaryText"— 包含即命中 - 正则表达式:
"regex:(?<!_)re\\.compile"— 正则匹配
第一次犯错 → 进候选池(lesson_candidates.json)
同类错误第二次出现 → 自动升级为正式 lesson
30 天没再犯 → 候选过期清理
不需要手动维护经验库,系统自己从错误中学习。
| 机制 | 说明 |
|---|---|
| 权重增减 | 命中一次 weight+1(上限10),90 天没命中 weight-1,降到 0 自动清理 |
| 防连续衰减 | 用 last_decay_at 记录衰减时间,90 天内不重复衰减 |
| 合并 | 同 scope 的 lessons,triggers 交集 ≥ 2 时自动合并 |
| 蒸馏 | 攒够 5+ 条时,用 LLM 分析提炼更通用的规则(dry-run 模式,只建议不自动执行) |
改了反思系统的代码,自动跑回归测试:
🧪 validate-edit.py 自测回放器
📋 Test: lesson 命中增重
✅ weight 增加
✅ hits 增加
✅ last_hit 存在
📋 Test: 衰减 + last_decay_at 防连续
✅ weight 衰减1
✅ last_decay_at 存在
✅ 第二次不衰减
📋 Test: 衰减到 0 自动清理
✅ lesson 被清理
结果: ✅ 10 passed, ❌ 0 failed
全部通过!🦊
通过 PostToolUse hook 自动触发 — 改了 validate-edit.py 后自动跑测试,失败就 block,防止静默失效。
在 .claude/settings.json 里加:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/validate-edit.py"
}
]
}
]
}
}在 .claude/hooks/lessons.json 里写几条你遇到过的坑:
{
"lessons": [
{
"scope": "*.py",
"triggers": ["import os.path"],
"lesson": "用 from pathlib import Path 代替 os.path",
"weight": 2,
"created": "2026-05-21"
}
]
}{}保存为 .claude/hooks/lesson_candidates.json,系统会自动填充。
它会自动:
- 每次编辑后检查语法
- 匹配经验库弹警告
- 犯错自动记录,第二次自动升级为正式经验
- 长期不犯的经验自动衰减清理
不是教 AI "应该怎么做",而是让它记住 "上次哪里错了"。
犯错 → 记录 → 下次匹配 → 弹警告 → 越用越准。
.claude/hooks/
├── validate-edit.py # 主 hook(语法检查 + 经验匹配 + 自动学习)
├── lessons.json # 经验库
├── lesson_candidates.json # 候选池(自动填充)
├── lesson_maintenance.py # 维护脚本(合并 + 蒸馏)
├── test_validate_edit.py # 自测回放器(10 个断言)
└── validate-reflection-tests.py # 自动触发测试的 wrapper
🦊 蛋壳 × 蛋宝 | 2026-05-21
候选池的"自动学习"依赖第一层检查产生的错误信号。语法错靠 py_compile / bash -n 就够了,但语义错(比如属性名写错但语法没问题)需要你针对自己的项目写自定义校验规则。
举个例子,我们项目里有一个 DankeTheme.swift 定义了所有 UI 主题属性。校验规则会读源文件,检查代码里引用的属性是否真的存在:
# 在 validate-edit.py 里加自定义校验
theme_file = "path/to/DankeTheme.swift"
with open(theme_file) as f:
theme_src = f.read()
# 提取代码里所有 DankeTheme.xxx 引用
refs = set(re.findall(r'DankeTheme\.(\w+)', content))
for ref in refs:
if ref not in theme_src:
errors.append(f"DankeTheme.{ref} does not exist!")语法对但属性不存在 → 自定义校验报错 → 进候选池 → 第二次犯错自动升级为 lesson。
犯错信号的五个来源:
| 来源 | 抓什么 | 通用性 |
|---|---|---|
py_compile |
Python 语法错 | 通用 |
import 验证 |
模块不存在 | 通用 |
json.loads |
JSON 格式错 | 通用 |
bash -n |
Shell 语法错 | 通用 |
| 自定义校验 | 属性/命名/规范错 | 需要针对项目写 |
前四个开箱即用,第五个需要你根据自己项目的踩坑经验来写。核心思路不变:能被代码检测到的错误,都可以变成自动学习的信号。
💡 还没做但可以探索的方向: 检测用户的"撤改信号"——如果 Claude 写了一段代码,用户马上 Edit 改回来,说明写错了。从前后 diff 里提取 trigger 自动进候选池。这个我们还没实现,但思路可行。
⚠️ 边界说明: 自定义校验是硬模式——写起来够准但只能 catch 写得出规则的错。模糊的项目惯例、审美级错误("这里应该用 guard let 不是 if let"、"这个命名不够语义化")还是得靠人工 prompt 或事后 Edit 反转观察。反思系统解决的是可规则化的重复错误,不是所有错误。
硬编码版本(手写规则)已经稳定跑着了。我们正在测试软编码模式——让反思系统自己学会该记什么:
三个信号来源:
- Review 信号 — 从 code review 意见里自动提取结构化 lesson(用 LLM 解析 review 文本 → 提取 scope + trigger + lesson)
- Edit 反转检测 — 如果 Claude 写了 A,30 分钟内被改成 B,说明 A 是错的。从 A→B 的 diff 自动提取 trigger 进候选池
- LLM 辅助生成规则 — 候选池积累到一定量后,用 LLM 压缩 evidence 成结构化 lesson,必须输出精确 trigger,泛规则直接丢弃
当前状态: dry-run 观察中,只记录不自动升级。跑通了会更新教程。
核心原则不变:宁可漏掉,不可误报。 自动提取的规则必须足够精确(有具体 scope + 具体 trigger),"改前先确认"这种废话规则直接丢弃。