在交付前 拦住 LLM 的坏代码
guard-skills 是一组反应式二次质量闸门,在代码、测试、文档落地之前,系统性地拦截 LLM 反复犯的失效模式——过度防御、虚假成功、过早抽象、注释污染、参数爆炸。
# npx skills add amElnagdy/guard-skills · MIT · skills.sh
三种触发模式
每个 guard 技能(clean-code-guard / test-guard / docs-guard / wp-guard / woo-guard)都运行在三种模式之一。由用户请求决定激活哪一种。
反应式复查
代码已生成 / 编辑 / 重构 / 修复之后运行。在呈现、提交或合并前,把 diff 或目标文件对照所有常驻准则检查一遍。
- 编码 agent 写完或改完实现之后
- 重构之后——必须严格保持行为不变
- PR 呈现前、commit 落地前
触发语:"review this PR"、"is this safe to merge?"、"check this diff"、"should I commit this?"
约束式写作
用户在一次有风险的编辑前显式调用 guard,则在写作时就套用同一批准则,写完后再跑交付前自检才展示结果。
- 用户说 "use clean-code-guard while implementing this"
- 有风险的结构性变更(抽取、抽象、重写)
- 希望约束在生成过程中持续生效时
触发语:"use $clean-code-guard while you write"、"implement this endpoint using clean-code-guard"
结构化审计
用户要求 review / audit / critique / rate 代码时,对目标文件走完整评审模式清单,产出结构化发现报告。未被要求则不改代码。
- 用户说 "review this PR" 或 "audit this code"
- 面向正确性的代码评审——行为变更在范围内
- 带严重度评级的结构化输出(Critical / Important / Nit)
触发语:"review"、"audit"、"critique"、"rate this code"、"assess this"
14 种系统性失效模式
这是杠杆最高的检查项。14 项中有 8 项追溯到同一根因:模型偏向于多写代码、多加参数、多设防御、多搞抽象——任何东西,就是不肯只写规格要求的最小实现。每次交付前都要走一遍。
| # | 模式 | 规则(自检) | 来源 |
|---|---|---|---|
| 1 | 吞掉失败的 catch-all 错误处理 | 只 catch 你能恢复的那个具体错误类型。除非契约写明,绝不用宽泛的 catch any,也不从 handler 返回 null/空成功。 | Karpathy;LLM 错误抑制实地报告 |
| 2 | 为不可能情况加防御性守卫 | 不要为类型注解或调用方契约已排除的值再加 null 检查或运行时类型检查。相信契约。 | arXiv 2409.19182 |
| 3 | 过早抽象 | 除非今天就存在两个及以上具体实现,否则不要引入 interface / 抽象类 / factory / registry / strategy / plugin 模式。只有一个实现 = 内联。 | Fowler, "Patterns for Reducing Friction" |
| 4 | 注释污染 | 注释解释 why,绝不解释 what。删掉复述代码的注释、步骤编号脚手架、复述签名的 docstring。 | HN #43929768;arXiv 2402.13013 |
| 5 | 复制代码而非复用 | 写 ≥5 行的块之前,先在代码库里搜相似的现成函数。复制知识是违规;复制能匹配现有 helper 的文本也是违规。 | GitClear 2025 — 2.11 亿行,2021–2024 增长 8× |
| 6 | 幻觉 API 与依赖包 | 每个 import 与外部 API 调用都必须对照实际安装的版本核实。读包、查 lockfile,或 import 后 inspect。绝不凭"应该有"去调方法。 | USENIX Security '25 — 16 模型,平均 19.6% 幻觉率 |
| 7 | 泛化、无意图的命名 | 禁用 data result item temp value obj info helper manager handle_* process_* do_*(除非有限定词)。名字必须揭示意图。 | arXiv 2512.01141;arXiv 2510.03178 |
| 8 | 长函数干多件事 | 一个函数只做一件事。硬上限:≤50 行、≤4 参数、圈复杂度 ≤8。规格要求 N 件事就产出 N 个函数加一个小 composer。 | GitClear 2025 — 文件 142→267 行;arXiv 2512.11922 |
| 9 | 参数爆炸 | 函数参数达到 5 个就停下,引入一个有类型的 request/config 对象(record、struct、DTO)。不要继续堆位置参数。 | arXiv 2304.10778;Fowler 过度热心模式 |
| 10 | 与周围代码不一致 | 在文件里写之前,先读这个文件和至少一个邻居。对齐大小写、import 风格、错误处理模式、日志风格。复用既有工具。 | Pullflow "The Context Illusion";Honeycomb 实地报告 |
| 11 | 死代码、未用 import、半成品 | 跑 linter 或 grep,扫未用 import、未用符号、不可达分支、"以防万一"的导出。交付前删掉。 | arXiv 2411.01414;GitClear "新增码远多于删除码" |
| 12 | 宣告成功——生产代码里的 mock 兜底 | 规格说要做真实工作的函数,绝不返回硬编码"成功"值或夹具数据。实现不了就显式失败并说明缺什么。 | Fowler "Reducing Friction";Claude Code #6984 |
| 13 | 看似合理实则错误的代码 | 任何边界、范围、差一、null 语义的问题,先在注释里写出 case 枚举。绝不复制相似函数再改——从规格重新推导。 | arXiv 2411.01414;Katanaquant "Plausible Code" |
| 14 | YAGNI 违规——投机式可配置 | 没有当下调用方就别加可选参数、config flag、env var、feature toggle。发现自己在加 enable_* use_* *_mode 就删掉,发布具体行为。 | Fowler "YAGNI";HN 防御代码讨论 |
23 条强制准则
在 guard-pass 与 live 模式下,这些规则适用于每一次代码变更。它们是命令式的,不是建议。
绝不在无限定词时用 data result item temp value obj info helper manager utils 或 handle_*/process_*/do_*。名字必须回答它为何存在、做什么。(Clean Code Ch.2)
目标 ≤20 行、一个抽象层级、一件事。若能抽出一个名字不复述函数体的子函数,说明父函数干了不止一件事。(Clean Code Ch.3)
到第五个就停下,引入 request/config 对象(record、struct、DTO)。绝不用布尔 flag 参数——拆成两个函数。
函数要么返回值(query),要么有副作用(command),绝不两者兼有。command 用动词,query 用名词或 getter 式名字。(CQS)
删掉任何复述下一行的注释。删掉步骤编号脚手架。删掉注释掉的代码——版本控制就是干这个的。(Clean Code Ch.4)
写之前读你要改的文件和至少一个邻居。对齐大小写、import 顺序、错误处理、日志、HTTP/DB client 选型。不要引入第二套模式。
一个类只对一个利益相关方群体(Accounting、Auth、Reporting)负责。两个不相关子系统都伸手进同一个类,就拆开。(SRP, Uncle Bob 2014)
若加新变体要在既有函数里再加一个 type-tag 分支,先重构成 registry、strategy 或多态分派。(OCP)
绝不为发"未实现/不支持"信号而 override 方法。绝不在 override 里强化前置条件或弱化后置条件。要这么做,说明继承用错了。(LSP)
引入 interface、protocol 或抽象契约时,放在消费它的包里,而不是紧挨着具体类。(DIP)
两个长得像但编码不同规则的函数不是 DRY 违规。一条规则同时出现在 代码+文档+schema 才是。(Pragmatic Programmer, "DRY")
抽象若为每个调用方的特例堆满了分支,把它重新内联回调用方,删掉死分支,再重新抽象。(Sandi Metz, "The Wrong Abstraction")
圈复杂度 ≤10,嵌套深度 ≤5。超限前先重构。(McCabe 1976)
没有当下调用方就别加可选参数、config flag、env var、feature toggle、interface、factory 或基类。发现自己加 enable_* use_*_v2 *_mode 就删掉,发布具体行为。(Fowler, "YAGNI")
只 catch 你能恢复的具体错误类型。无法恢复就让错误传播。除非函数契约写明,从 catch handler 返回 null/none/空成功是禁止的。(Karpathy)
不要为声明类型或调用方契约已排除的值再加 null 检查或运行时类型检查。相信契约。(arXiv 2409.19182)
调库方法前,确认它在已安装的版本里存在(读包、查 lockfile,或 import 后 inspect)。不要凭 API "应该长什么样"生成代码。(USENIX Security '25)
规格说做真实工作的函数,绝不返回 {"status":"ok",...} 或罐头数据。实现不了就用语言的 unimplemented/unsupported 机制显式失败并说明。绝不为让测试通过而禁用、跳过或弱化它。(Fowler, Claude Code #6984)
想复制一个函数改改时,停下。从规格重新推导。差一和错 null 语义的 bug 几乎都从"复制相似"溜进来。(arXiv 2411.01414)
任何范围、差一、null/空/一个/多个、奇偶、unicode/byte 边界,先在注释里写出 case 清单。逐个在代码里覆盖再往下。
跑 linter 或 grep,扫未用 import、未用符号、不可达分支、"以防万一"的导出,删掉。今天没人调的函数不许为"将来"活着。
在陌生仓库写之前,读你要改的文件、一个邻居,以及任何项目规则文件(CLAUDE.md、AGENTS.md、README 的 conventions 段)。用项目既有的 helper、错误类型与日志。
相同输入产出相同输出、抛相同异常、相同副作用、相同顺序保证。重构时发现 bug,单独标记并先问再改。重构定义为改内部结构使其更易理解而不改变可观察行为(Fowler, Refactoring)。修 bug 与重构是两个操作——绝不打包。
交付前 8 步自检
在向用户展示你写或改的任何代码之前,跑这份清单。每一步都答"是"才能交付,否则先修。
常驻准则里的每一条都适用于每次变更。扫完整个 diff,确认 23 条无一被违反。逐个修掉。
行数 ≤20?参数 ≤4?复杂度感觉 ≤10?名字揭示意图?套用准则 1–3 的硬上限。超限的函数几乎一定多做了一件事或藏了抽象。
它解释 why 吗?若解释 what,删掉。准则 5 是过滤器:复述下一行的注释只加噪声不加信号;残留的步骤编号脚手架也删。
catch 的错误类型够具体吗?handler 除了静默返回还做别的吗?准则 15 与 18:无文档化恢复路径却返回 null/空成功就是失败。只 catch 你能行动的。
interface / factory / 基类 / registry:今天有第二个具体使用方吗?没有就内联。准则 3 与 12:单实现的抽象是过早;分支按调用方堆积的抽象是错抽象。
你读了要改的文件和至少一个邻居吗?风格对齐了吗?准则 6 与 22:风格不一致是 LLM 代码评审三大高频发现之一。写前先读。
有没有硬编码 "ok" 返回或夹具数据?有就换成真实实现,或显式的 unimplemented/unsupported 失败。准则 18:这是最危险的 AI 模式——让代码看似能跑却交付虚构,必须交付前抓住。
你改了可观察行为吗?改了就说明你打包了 bug 修复——拆出来问用户。准则 23:重构与修 bug 是两个独立操作,混在一个 diff 里两者都没法干净评审。
评审模式输出模板
当用户要求 review / audit / critique 代码时,产出这一确切格式。评审模式下未被要求不改代码。交付发现前走完全部五个小节。
# Code review: <file or scope>
## Summary
<2–3 sentence verdict: ship / needs work / rewrite>
## Critical findings
<must-fix before merge>
- **<short title>** — `<file>:<line>`
Evidence: <quoted code or behavior>
Principle violated: <e.g., LSP, catch-all error swallowing>
Suggested fix: <concrete>
## Important findings
<should fix but not blocking>
- ...
## Nits
<style, naming, minor structure>
- ...
## What's good
<at least 2–3 specific positives — naming, structure, test coverage>
## Self-check coverage
- [ ] Walked Section A (naming & functions)
- [ ] Walked Section B (comments & formatting)
- [ ] Walked Section C (SOLID)
- [ ] Walked Section D (DRY/KISS/YAGNI)
- [ ] Walked Section E (AI failure modes)
| 严重度 | 示例 |
|---|---|
| Critical | 安全漏洞、正确性 bug、数据丢失、吞掉的异常、硬编码 "success" 返回 |
| Important | SOLID 违规、过早抽象、参数爆炸、泛化命名、DRY 违规 |
| Nit | 风格不一致、循环外的单字母命名、公共 API 缺少 docstring 契约 |
| 小节 | 检查 |
|---|---|
| A — 命名与函数 | 泛化命名、函数大小、参数数量、CQS 违规、布尔 flag |
| B — 注释与格式 | 复述型注释、步骤编号脚手架、注释掉的代码、风格不一致 |
| C — SOLID | SRP 多 actor 模块;OCP type-tag 分派膨胀;LSP 不支持-操作 override;ISP 胖接口;DIP 错层 import |
| D — DRY/KISS/YAGNI | 知识重复、错抽象分支、圈复杂度 >10、嵌套 >5、投机式可配置 |
| E — AI 失效模式 | 全部 14 种模式。与 A–D 交叉核对。杠杆最高的小节。 |
技能参考
每个 guard 技能针对一类特定的 agent 产出。它们层叠:平台专属 guard(wp-guard、woo-guard)在 clean-code-guard 之后运行。
任意语言
LLM 代码异味、SOLID/DRY/KISS/YAGNI、14 种 AI 失效模式。
pytest · PHPUnit · Jest · Go · WP/Woo
mock 滥用、重复测试、断言实现细节。
README · docstring · API 文档 · changelog
幻觉符号、坏掉的示例、文档与代码漂移。
WordPress 插件 · 主题 · REST · AJAX
转义、消毒、nonce、capability、预处理查询、i18n。
WooCommerce 扩展 · 结账 · 订单
HPOS 安全访问、CRUD 优于 meta、金额错误、服务端校验。
| Guard | 能抓什么 | 配对 |
|---|---|---|
clean-code-guard | LLM 代码异味、过度抽象、宽泛错误吞噬、坏命名、SOLID/DRY 违规 | 平台专属 guard |
test-guard | mock 滥用、重复测试、断言实现细节、什么都抓不到的测试 | 测试 helper 含真实逻辑时配 clean-code-guard |
docs-guard | 幻觉符号、坏示例、文档与代码漂移、不可验证声明 | 任何行为被写进文档的 guard |
wp-guard | 转义、消毒、nonce、capability、预处理查询、i18n、查询/缓存错误 | clean-code-guard;出现 Woo API 时配 woo-guard |
woo-guard | 直接读订单 meta、HPOS 破坏、缺兼容声明、绕过结账、金额错误 | WordPress 层配 wp-guard |
正反例速查
最常见的失效模式,配上具体的 before / after。
失效模式 1 — catch-all 错误吞噬
getEmail(userId):
attempt:
user = userStore.get(userId)
return user.email
catch anyError:
return null
getEmail(userId):
user = userStore.get(userId) # errors propagate
return user.email
失效模式 2 — 为不可能情况加防御守卫
total(orderItems):
if orderItems is null: return 0
if orderItems is not a collection: return 0
return sum(order.amount for each non-null order in orderItems)
total(orderItems):
return sum(order.amount for each order in orderItems)
失效模式 3 — 过早抽象
PaymentProcessor interface
charge(amount)
CardPaymentProcessor implements PaymentProcessor
charge(amount):
return paymentProvider.createCharge(amount).id
PaymentProcessorFactory
create():
return new CardPaymentProcessor()
charge(amount):
return paymentProvider.createCharge(amount).id
失效模式 7 — 泛化、无意图命名
processData(data):
result = []
for item in data:
temp = item.value * 2
result.add(temp)
return result
doublePrices(orders):
return orders.map(order -> order.priceCents * 2)
失效模式 9 — 参数爆炸
sendEmail(to, subject, body, retry=true, backoff="exp",
html=false, fromAddress=null, encoding="utf-8",
internal=false, verbose=false)
EmailRequest
to
subject
body
html = false
sendEmail(request: EmailRequest)
失效模式 12 — 宣告成功,mock 兜底
getUserBalance(userId):
return 1000 # TODO: actual provider call
getUserBalance(userId):
raise NotImplemented("Wire to billing.getBalance() after auth is available")
失效模式 13 — 看似合理实则错误(复制相似)
median = (values[values.length / 2] + values[values.length / 2 + 1]) / 2
# BAD — silently drops items[0]
for index from 1 to items.length - 1:
...
# cases: empty / one / even / odd
median(values):
sorted = values.sort()
mid = sorted.length / 2
return sorted.length % 2 == 0
? (sorted[mid-1] + sorted[mid]) / 2
: sorted[mid]
来源与证据
每条规则背后的研究与实地报告。下表把每个来源与它支撑的声明对应起来。
| 来源 | 支撑的声明 |
|---|---|
| GitClear 2025 — AI Copilot Code Quality | 2021–2024 代码重复增长 8×;函数大小 142→267 行;复杂度 4.2→8.1 |
| arXiv 2409.19182 — AI-Generated Code Considered Harmful | 防御代码过度使用;为不可能情况加运行时类型检查 |
| USENIX Security '25 — Package Hallucinations | 16 模型,平均 19.6% 包幻觉率;商业 ~5%,开源 ~21% |
| Fowler — Patterns for Reducing Friction | 过度热心、测试未过却宣告成功、暴力修复 |
| Fowler — YAGNI | 投机式可配置的成本;无调用方的可选参数 |
| Metz — The Wrong Abstraction | 重新内联规则;共享抽象中按调用方堆积的分支 |
| arXiv 2411.01414 — LLM Code Generation Mistakes | 7 类分类法;复制相似的语义错误;半成品 |
| arXiv 2402.13013 — Code Needs Comments | LLM 注释平均 ~5 词 vs 作者 19 词;回答 "what" 而非 "why" |
| arXiv 2512.01141 — Neural Variable Name Repair | 泛化标识符是命名修复模型的明确目标;LLM 代码过度产出它们 |
| arXiv 2510.03178 — When Names Disappear | 语义名在生成时充当锚点;泛化名劣化代码质量 |
| arXiv 2512.11922 — Vibe Coding in Practice | 典型症状:300 行函数处理四个不相关关注点 |
| arXiv 2304.10778 — Copilot/CodeWhisperer/ChatGPT 对比 | 参数爆炸、LLM 辅助提交中的函数复杂度增长 |
| HN #43929768 | 注释污染是让 agentic 代码变丑的最常见因素 |
| Claude Code #6984 — Systematic Mock Data Generation Bias | 生产代码中硬编码 "success" 返回与夹具数据 |