关于 code-humanizer
code-humanizer 是用于审查和清理 AI 生成代码结构性问题的 Skill,也可在实现改动时预防此类问题。它以测试套件验证行为保持,并要求先扫描和报告既有代码问题。
主要功能
- 01扫描代码结构性问题
- 02按模式分类并评估严重程度
- 03先报告再清理既有代码
- 04通过测试验证行为保持
- 05实现期间预防新增问题
适用场景
- 01审查仓库或变更中的重复实现
- 02在维护者批准后清理特定模式
- 03编写功能时避免不必要的抽象和防御代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ leonardnju/ code-humanizer --skill code-humanizer安装后在 Claude Code 中输入/code-humanizer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.zh.md.gitignoreLICENSEREADME.md详细介绍
Code Humanizer
从代码仓库中移除 AI 生成代码的痕迹,或使用 Guard 模式防止这些模式出现在当前更改中。用于处理文本的 humanizer 会移除 AI 写作模式;本工具处理 AI 编码模式——当 Agent 优先考虑“测试通过”而非“代码库保持健康”时留下的结构性债务。
**核心原则:**AI 代码垃圾并非难看的代码,而是那些能够运行却会损害代码仓库的代码:它们重复实现已有功能,添加无人要求的抽象,吞掉本应暴露的错误,并为不可能发生的情况做防范。与文本不同,代码有判定标准:由测试套件决定什么叫“保持语义不变”。请始终使用它。
铁律
规则 1–4 适用于模式 A/B 中对既有代码的清理。在 Guard 模式中,这些规则适用于实现完成后的任何清理,不适用于所请求的功能本身。规则 5 适用于所有模式。
- **绝对要保持行为不变。**会改变行为的“清理”就是披着好意外衣的 bug。这包括错误类型和错误发生时机——把意外的
AttributeError换成“更好”的ValueError,会改变所有捕获该错误的调用方所观察到的行为。如果发现潜在 bug,或难看但不可或缺的行为:在报告中指出;绝不要把它作为清理的一部分悄悄修复。 - 没有测试 → 不做清理改动。先运行测试套件。如果测试套件不存在、未通过,或未覆盖要清理的既有代码,你只能报告该债务(模式 A)。可以先提议编写特征测试。这并不禁止在测试不完整的项目中实现所请求的更改;但要报告验证方面的缺口。
- **重写既有代码前先报告。**默认使用模式 A(扫描 → 报告)。只有维护者批准,或明确要求修复时,才进入模式 B(修复)。Guard 模式可以直接避免或移除当前更改引入的代码垃圾。
- **每个清理提交只处理一类模式。**每个移除既有代码垃圾的提交只处理一种类型,并通过完整测试套件。测试失败就回退该提交——不要通过修改无关代码来“向前修复”。在 Guard 模式下,普通功能提交无需按模式拆分,因为预防措施与实现不可分割。
- **判断重复前先搜索。**在扫描或实现之前,先在脑中建立一份代码仓库现有辅助工具的索引(搜索
utils、helpers、common、验证器和现有基类)。不了解已有内容,就无法识别重复实现。
模式目录
每项发现的严重程度:0 不存在 · 1 存在但有充分理由(→ 豁免,不要改动)· 2 轻微债务 · 3 明显债务,会增加未来的维护成本 · 4 严重,会破坏语义或架构边界。
第 1 级 — 重复与重新发明
1. 重复实现已有辅助工具 (AI 代码的典型特征)
**迹象:**新增私有函数的函数体与 utils/helpers/同级模块中的代码相似;代码仓库已有对应实现的正则表达式/验证/解析/格式化代码块;新增代码没有从其所属的软件包导入任何内容。
**问题:**Agent 从提示词出发编写代码,而不是从代码仓库出发。每个重复实现都会让未来的修复分道扬镳。
# Before — repo already has utils.validate_email
def _is_valid_email(s):
pattern = r"^[\w.+-]+@[\w-]+\.[\w.]+$"
if re.match(pattern, s):
return True
return False
# After
from .utils import validate_email
2. _v2 / _new / _impl 克隆
迹象:foo 和 foo_v2 同时存在;使用 enhanced_、improved_、new_ 前缀;复制粘贴的函数体只改动了一个分支。
**问题:**Agent 不敢修改原有实现,于是现在出现了两个事实来源。应合并两者(使用参数或更新后的函数体),并只保留一个名称,除非正在进行有文档说明的迁移。
3. 重新发明标准库或已安装的依赖
**迹象:**手动实现 groupby、通过 JSON 深拷贝、手动解析 URL、自行编写日期计算;而 stdlib 或已安装的依赖中已有等效实现。
第 2 级 — 推测性架构
4. 单一实现的抽象
信号: ABC / 接口 / Protocol 只有一个具体类;注册表或工厂只有一项注册;“可插拔后端”只在一个调用点使用。
问题: 为从未提出的未来需求增加灵活性。将其内联;等出现第二种实现时再恢复抽象。
# Before: AbstractReportBackend + BackendRegistry + TextReportBackend (3 classes, 40 lines)
# After:
_BACKENDS = {"text": lambda rows: "\n".join(map(str, rows))}
5. “留待将来使用”的死代码
信号: 没有调用点的辅助函数;没有调用方传入的参数;声称“为便于扩展而提供”的 export_* / *_to_dict;写着灵活、可扩展、无缝的文档字符串。
问题: 不可达代码仍会增加审查负担、搜索结果和虚假的信心。删除它——git 会记住。先在整个仓库(以及脚本和模板)中搜索相关用法。
6. 毫无增益的封装
信号: 函数体只有一次参数完全相同的调用;类继承后只把所有内容交给 super()。
7. 针对本地情况的配置/API 泛滥 信号: 新增全局标志、配置字段、CLI 参数或公共参数,却只在一个地方读取;模块级常量只有一个函数会读取。 问题: 每个全局开关都会永久扩大状态空间。将其设为局部变量,或设为唯一需要它的函数的参数。
第 3 级 — 防御性冗余
8. 宽泛地吞掉异常
信号: except Exception: / 裸 except 返回默认值、不记录日志或直接 pass;.get() 链把错误悄悄转换成错误输出。
问题: 将崩溃(明显、可调试)变成数据损坏(隐蔽)。将捕获范围缩小到实际可能发生的异常,或让异常继续抛出。注意——这是行为变更;可能需要维护者批准(规则 1)。
9. 缺乏依据的 try-import 回退
信号: try: import fast_x except ImportError: import x,但没有基准测试、打包元数据中没有 extras 条目,也没有测试覆盖回退路径;对并非可选的依赖使用可选依赖处理逻辑。
问题: 两条代码路径,却只测试了一条。如果依赖很重要,就声明它;如果不重要,就移除它。
10. 属性探测链
信号: 使用 hasattr / getattr / isinstance 层层判断,以接受“字典或对象,或者也许是 None”;每次访问字段时都复制粘贴相同的探测表达式。
问题: Agent 没有检查这里实际传递的类型,所以什么都接受。找出真实类型(测试会告诉你),然后针对它编写代码。
11. 多余的谨慎式重新校验
信号: 对刚刚构造的值检查 if x is not None;重复检查类型系统或上游关卡已经保证的不变量。
第 4 级 — 噪声
12. 复述代码的注释
信号: 注释只是复述下一行(# 用换行符连接各行);写给审查者看的注释(# 此更改确保了正确性);Agent 在同一个 PR 中编写并解决的 TODO。
规则: 注释只有在说明代码无法表达的内容(约束、不变量、为何不采用显而易见的做法)时,才值得占用一行。
13. 模板化文档字符串
信号: 文档字符串只是函数名加空格(def get_user(): """获取用户。""");营销式形容词(稳健、全面、无缝)。
14. 无用导入、未使用变量、装饰性横幅
信号: 删除旧尝试后遗留的导入;# ===== SECTION ===== 横幅;标识符 / 日志行中使用 emoji;遗留的 print / 调试日志。
第 5 级 — 测试冗余(默认仅报告)
15. 断言模拟对象的测试 — 每个协作者都被模拟,断言检查模拟对象是否被调用;测试永远不会因真实原因而失败。 16. 琐碎或重复的断言 — 断言字面量,或用三个名称重复测试同一个用例以虚增覆盖率。
不应标记的情况(误报防护)
严重程度为 1 = 有正当理由。问题不在于模式的形式,而在于缺乏正当理由。标记之前,先查找是否有合理解释:
- 有理由的回退方案/兼容垫片 — 有文档记录的平台差异、打包的额外功能(
pip install pkg[fast])、迁移期间的版本门控。关键在于缺乏正当理由,而非回退方案。 - 信任边界处的防御性代码 — 解析用户输入、网络负载、插件提供的对象。数据不可信时,探测和宽泛捕获错误完全合理(但仍应记录日志,而不是使用
pass)。 - 实际插件系统中的注册表/抽象基类 — 如果入口点会动态加载实现,那么仓库内只有一个实现并不意味着系统只有一种实现。
- 有经过衡量的理由而重复代码 — 热路径上的代码副本附有基准测试说明;有意保持同步的 vendored 代码。
- 文档记录的迁移期间使用
_v2— 合并前检查 git 历史/CHANGELOG。 - 会记录日志且附有注释的吞掉错误 — 有意作出的弹性决策应由维护者日后重新审视,而不是由你处理。
- 你单纯不喜欢的风格。 此技能消除的是结构性债务,而非格式偏好——那是 linter 的职责。
拿不准时:按严重程度 1–2 报告,不要修复。留意问题是否成簇出现——一条解释性注释不算什么;同一个 PR 中同时出现解释性注释、仅有单一实现的注册表、一个 _v2 和一个未使用的导出,那就是自白。
工作流
模式 A — 扫描(默认):
- **Oracle 检查:**运行测试套件;记录通过情况以及目标文件的大致覆盖率。
- **索引:**梳理仓库现有的辅助工具和抽象(为模式 1 提供依据)。
- **扫描:**对照目录检查目标范围(差异、PR、模块或整个仓库)。对每项发现记录:
模式编号 · 文件:行号 · 严重性 · 证据(一行) · 建议的修复 · 行为风险(无 / 错误类型 / 需要维护者处理)。 - **报告:**按模式归类发现,汇总每个文件的严重性,并列出豁免及其理由。最后给出建议的修复顺序(优先处理严重性最高、行为风险最低的项目)。
模式 B — 修复(获准后):
5. 按获准的顺序逐个处理模式类别:修复所有实例 → 运行完整测试套件 → 提交(deslop: <pattern> (#N) — <n> instances)。测试失败则撤销提交。
6. 任何存在行为风险的项目(错误类型、吞掉异常、未知键路径)都要单独作为明确标注的提交提出,或保留为报告项——绝不能与安全清理混在一起。
7. **审计:**重新扫描结果,并问自己:“还有什么会让审查者觉得这是 AI 写的吗?”修复或报告剩余问题。
8. **总结:**按模式统计移除数量、代码行数变化、已遵守的豁免,以及待维护者处理的行为风险项目。
模式 C — 防护(实现期间):
当用户要求在实现变更时防止 AI 编码潦草问题,就进入防护模式。这是一层精简的预防措施,不是通用的编码方法论。它将同一目录应用于当前任务的实现决策和最终差异。除非用户提出要求,否则不要将其扩展为全仓库清理。
编码前 — 从仓库现状出发:
- 创建新工具之前,先搜索现有的辅助工具、实用程序、同类实现、基类、验证器和已安装依赖项。
- 沿着变更路径追踪实际的类型、不变量和错误行为。不要用探测链或宽泛的回退逻辑来弥补未知情况。
- 明确范围:探索性代码、内部模块,还是稳定的公共边界。评估其形态之前,先查找已记录的迁移、兼容性要求和实际的插件加载情况。
编码期间 — 遵循五条决策规则:
- 优先复用,而非重造(模式 1–3):扩展或调用仓库已有的实现;不要另起炉灶编写辅助工具,也不要重复实现标准库行为。
- 优先具体,而非抽象(模式 4–7):局部需求就留在局部;在出现真正的第二个实现之前,不要添加抽象、注册表、全局开关或公共 API。
- 先了解,再防御(模式 8–11):根据实际契约编写代码;防御性灵活性只适用于真正的信任边界,而不是类型尚未查明的任意位置。
- 解释原因,而非描述内容(模式 12–14):注释记录约束和不明显的原因;删掉叙述性文字、样板内容和实现残留。
- 测试行为,而非脚手架(模式 15–16):验证对外可见的重要行为和失败情形,而不只是断言已调用模拟对象。
编码后 — 审查差异,索引仓库:
- 运行相关测试,并仅对照目录检查当前差异。重复代码检查仍需搜索整个仓库。
- 修复当前变更引入的安全潦草实现。不要顺手重构无关的既有问题;应另行报告。
- 简要记录有充分理由的豁免。只有在仍存在有意义的技术债或行为风险时,才展开为完整的发现列表。
- 最后给出简明的防护审计,例如:复用了现有辅助工具;未新增推测性 API;有一项探索性豁免;测试通过。
探索不是生产环境的技术债
探索阶段可以合理地暂时重复代码、硬编码值、并行保留多个变体,或使用粗略脚本,尤其是在 experiments/、scratch/、notebooks/ 和 prototypes/ 目录下。如果这些做法范围受限且是有意为之,应将其视为严重性 1 级问题。当代码进入核心包、稳定 API、共享模块或准备合并的路径时,这种豁免便不再适用。Guard 模式应为探索保留空间;在硬化阶段,临时分支才会合并,并开始适用常规目录。
范围: 对于 PR/差异,只扫描已更改的文件,但要对照整个代码库检查重复内容。对于整个代码库,逐个模块检查;提出检查顺序,并由维护者决定是否删减。在 Guard 模式下,除非请求了更广泛的清理,否则应将修改和事后检查限制在当前任务的差异范围内。
常见错误(真实案例)
| 错误 | 实际情况 |
|---|---|
| “这个错误类型显然是意外的——我来改进一下” | 这是行为变更。调用方会捕获特定异常。请报告此问题。 |
| 删除被“闲置”的辅助函数,而脚本/模板/反射机制可能会用到它 | 删除模式 5 相关内容前,先用 grep 搜索所有内容,包括非代码文件。 |
| 一次大型提交修复 9 种模式 | 无法审查,也无法回滚。每次提交只处理一类模式。 |
| 只运行一次测试套件就开始修复 | 你无法判断“是我弄坏了”还是“它本来就坏了”。先运行预检。 |
| 在信任边界处重写有效的防御性代码 | 不可信输入是唯一应该保持高度谨慎的地方。 |
| 把问题目录当作尽可能多找问题的清单 | 目标是让代码库更健康,而不是追求问题数量。严重性 1 级的问题应保留。 |