| name | code-standards |
| description | 代码规范与完整开发工作流,涵盖 Python、PyTorch/深度学习、Shell 脚本、配置文件。贯穿始终的四条设计原则:易读直观、拒绝过度抽象、单一职责、命名自解释。强制三段式流程:(1)写代码前——在已有项目上开发时必须先了解项目全貌避免重复实现,然后先出 plan(思路+实现步骤)并获得用户批准才能开工;(2)写代码中——遵循命名/类型/docstring/张量操作/异常处理等规范,同时时刻守住四条设计原则;(3)写代码后——必须先补齐对应单元测试并本地跑通,再主动调用 simplify 简化新代码,最后调用 review 做审查。触发条件:用户要求写/生成/重构 Python 或 PyTorch 代码;用户要求在已有项目/代码库上开发;用户创建新模块、脚本或训练循环;用户说"按代码规范来"或引用 /code-standards。 |
代码规范(Code Standards)
本项目通用的编码规范 + 完整开发工作流。不仅规定写出来的代码长什么样,还规定写之前要做什么、写之后要做什么。
使用方式(三段式工作流)
本 skill 强制以下三个阶段,缺一不可:
阶段 A ── 写代码前
- 项目全貌认知(若在已有项目上开发):必须先完成"§0 项目探索"中的检查,避免重复造轮子。
- 规范预热:快速浏览将要涉及的语言/领域对应章节(§1–§4),心里有数再动手。
- 先 plan,再实施(强制 · 需用户批准):
- 禁止未出 plan 直接开写。除非任务是极小的改动(单行修复、typo、显而易见的调参)。
- 动手前必须先向用户呈交一份 plan,内容见"§0.5 Plan 格式"。
- 必须等到用户明确批准("可以"、"同意"、"开始吧"、"OK" 等)后才开始写代码。用户未回复前一律等待,不要擅自推进。
- 若实施中发现 plan 与现实不符需要偏离,暂停,说明偏差并重新请求批准。
阶段 B ── 写代码中
- 遵循 §1–§4 的规范。
- 时刻牢记 §0.9 的四条设计原则:易读直观、反对过度抽象、单一职责、命名自解释。
- 优先复用阶段 A 中识别到的项目已有工具/函数/基类,不要另起炉灶。
- 按已批准的 plan 推进;若需要临时改动范围,回阶段 A.3 重新请求批准。
阶段 C ── 写代码后(强制)
完成代码后主动执行,无需用户催促(见 §5 详细流程):
- 自检本次变更,对照审查清单。
- 补齐单元测试并跑通(除非命中 §4.5.2 免测清单,且已明说)——没单测不能进下一步。
- 调用
simplify skill 对本次新增/修改的代码做简化与复用检查。
- 调用
review skill 做代码审查。
- 按本文件定义的"必改 / 建议改 / 可考虑"三级汇总审查结论:
- 必改(Must-fix)——违反硬性规则(如可变默认参数、裸
except、硬编码设备、公共 API 缺少类型注解、功能缺失单测)。
- 建议改(Should-fix)——违反强推荐(命名、docstring 缺失、魔法数字、测试覆盖不足)。
- 可考虑(Consider)——依上下文而定的软建议。
- 同步项目文档(强制)——向用户汇报完本次结果后,把开发进展与"下次可 resume 的状态"写回项目文档目录(见 §5.1 第 6 步)。没这一步视为未完成。
审查时不要静默改写代码——先暴露问题,再修复用户确认的部分(或用户明确要求一并清理的必改项)。
0. 项目全貌认知(在已有项目上开发时强制执行)
目标:在已有代码库上动手前,先摸清项目结构,确保新代码与现有组件风格一致,并优先复用已有函数/类而非重新实现。
0.1 何时必须执行
- 在已有仓库中新增模块、函数、脚本。
- 修改涉及多个文件的跨模块功能。
- 用户未明确告知项目结构时。
0.2 必做检查清单
动手前按以下顺序完成,并把发现简要汇报给用户:
-
顶层结构扫一遍
- 读
README*、CLAUDE.md(若存在)、pyproject.toml / setup.py / requirements*.txt。
ls 或 Glob 看顶层目录划分(src/、scripts/、configs/、tests/ 等)。
- 明确项目的包名、入口、主要依赖。
-
定位相关领域代码
- 按关键词 Grep 搜索将要实现的功能名、相关动词/名词(如要写"采样器"就搜
sampler、sample、Sampler)。
- 检查是否已有功能相近的实现——可能叫不同的名字。
- 看
__init__.py 暴露了哪些公开 API。
-
识别现有工具层
- 配置加载、日志、checkpoint、设备管理、分布式、数据加载——这些"基础设施"类功能几乎必然已有实现。不要重写。
- 典型目录:
utils/、common/、core/、base/。
-
识别现有风格
- 抽样读 2–3 个同类型文件,注意:类型注解风格、docstring 风格、命名风格、错误处理方式、日志方式。
- 你的新代码必须与现有风格一致,即使现有风格与 §1–§4 默认推荐略有差异(以项目内一致性优先)。
-
识别扩展点
- 是否有基类/抽象接口/插件注册机制需要遵循?(如
BaseSampler、register_model 装饰器等。)
- 是否有约定好的配置 schema?
0.3 开工前的最小汇报
动手前,用一两段话简要告诉用户:
- 找到的相关现有代码(文件路径 + 函数名)。
- 计划复用什么、新增什么。
- 若发现可能重复实现的风险,先询问用户是复用已有实现、扩展它、还是确实需要新实现。
0.4 常见反模式(禁止)
- 未检索就新写一个
load_config、set_seed、get_logger、save_checkpoint——这些几乎都已存在。
- 新建一个
utils.py 或 helpers.py 放散装函数——先查现有 utils 目录。
- 在新文件里复制粘贴已有函数改个名字。
- 新增与现有风格不一致的模块(如项目全用 dataclass 你却写 Pydantic;项目全用
logging 你却用 print)。
0.5 Plan 格式(强制 · 动手前呈交,等待批准)
完成 §0.1–§0.3 的项目探索后,必须先向用户呈交一份 plan,获得明确批准后才能写代码。plan 用如下结构,保持简洁(通常 < 200 行):
## 任务理解
<用一两句话复述需求,表明你理解的目标。>
## 项目现状(复用机会)
- 已有可复用组件:<文件:符号> —— <用途>
- 可扩展的基类/接口:<...>
- 参考的同类实现:<...>
## 实现思路
<核心设计/算法思路 2–5 句话,必要时画个数据流或调用链。>
## 实现步骤
1. <第一步:改/新增哪个文件,做什么>
2. <第二步:...>
3. ...
## 涉及文件
- 新增:<path/to/new_file.py>
- 修改:<path/to/existing.py>(修改点概述)
## 风险与取舍
- <列出与现有代码可能冲突的点、性能/内存假设、未决问题>
- <若有两种实现路径,给出推荐并说明理由>
## 待确认的问题(如有)
- <若有需要用户裁决的设计分歧,列在这里等回复>
批准前禁止的动作:
- 禁止调用 Edit / Write / NotebookEdit 类改写文件的工具。
- 禁止
git add/git commit 等修改仓库状态的操作。
- 只允许只读探索(Read / Glob / Grep / Bash 的只读命令)。
何时可省略 plan:
- 单行/几行的 typo 修复、显而易见的 bug 修复、参数微调。
- 用户已在本轮对话中给出具体到文件/函数级的指令(相当于他已经给了 plan)。
- 纯研究/探索类问题(不改代码)。
即使可省略正式 plan,仍应用一两句话简述你要做的事再动手。
0.9 核心设计原则(高于一切具体规则)
本节的四条是第一性原则——当具体规则与它们冲突时,以这四条为准。实施阶段(阶段 B)的每一行代码都要同时满足这四条;写代码后的 simplify/review 也围绕它们展开。
0.9.1 易读、直观
代码是写给人读的,顺便能被机器执行。
- 直线胜过曲线:能用顺序/条件/循环表达的逻辑,不要用回调、装饰器链、元编程。
- 显式胜过隐式:关键行为写在代码里,不要藏进
__init_subclass__、monkey patch、全局注册表里——除非项目已有此模式。
- 局部性:读一个函数时所需的上下文应尽量都在它附近。参数显式传入比从
self 深处挖好;常量在模块顶部比散落各处好。
- 可扫描:一眼扫过去能看到主流程。提前返回(early return)让嵌套变浅,胜过层层
if/else。
- 评判标准:一个不熟悉这段代码的同事,能否在 30 秒内讲清它做了什么?不能就重写。
0.9.2 有简单方案时,禁止引入复杂抽象(YAGNI)
- 禁止提前抽象:"以后可能要支持 X" 不是理由——等真的要支持 X 时再抽象。
- 禁止为"对称/优雅"新建一堆基类、接口、工厂、策略——除非当下已有 ≥ 2 个真实实现需要共用。
- 禁止为唯一一次使用而建配置/插件系统。
- 一个 if/else 的策略选择,不需要策略模式;一个参数化的函数,不需要工厂类。
- 抽象的代价是实际存在的:多一层跳转、多一份文档、多一个测试表面、多一处未来 bug 的藏身处。
- 判断公式:抽象带来的收益 > 引入的阅读/维护成本——不成立就不要抽。
- 如果你发现自己在写
class AbstractBaseManagerFactory,停下重新想一下。
0.9.3 函数/类的职责要单一(SRP)
- 一个函数只做一件事:输入 → 一个清晰的输出/副作用。动词能描述完整行为就行。
- 一个类只有一个变化维度:它的字段、它的方法应当围绕同一个概念。一个类既管数据加载又管日志格式化,是两个类。
- 副作用要集中:读写文件、打日志、改全局状态的代码集中在明显位置,其他函数保持纯粹。训练循环这类天然副作用重的地方除外。
- 描述函数时必须用"and"才能说清 → 拆。"它加载数据并归一化并写缓存"——三个函数。
- 判断信号:
- 函数参数超过 6 个且彼此无关 → 可能在做多件事。
- 函数里有大段互不引用的代码 → 它们应该是独立函数。
- 类里的方法明显分成互不共享字段的两组 → 拆成两个类。
0.9.4 命名自解释
- 名字即文档。读到一个名字就应知道它是什么、干什么,不必跳到定义。
- 具体 > 抽象:
max_batch_size 胜过 limit;user_email_by_id 胜过 data;normalize_per_channel 胜过 process。
- 命名要反映用途,不是类型或结构:
users(list of User)胜过 user_list;is_ready(bool)胜过 ready_flag。
- 禁止出现的名字:
data、info、item、obj、tmp、result、helper、util、handle、do_stuff、process、manager、handler——除非上下文已锁死含义。
- 布尔名用谓词:
is_ready、has_children、should_retry、can_load —— 不要用 flag、status。
- 函数名用动词:
load_checkpoint、compute_loss、normalize_image;类名用名词:TrajectorySampler、CheckpointManager。
- 缩写只用公认的:
cfg、ctx、msg、df、idx、lr、bs 可以;usrmgr、calcval、procimg 不行。
- 避免同义词漂移:一个概念全项目只用一个名字。不要一会儿
user_id、一会儿 uid、一会儿 account_id。
- 测试一下你的名字:把它念给另一个开发听,如果对方会问"这是什么意思?",就换个名字。
0.9.5 冲突裁决
这四条原则偶尔互相拉扯,顺序如下:
- 正确性 > 一切。
- 易读直观(0.9.1) > 抽象层级。
- 单一职责(0.9.3) 与 YAGNI(0.9.2) 同级:拆分是为了当下的清晰,不是为了未来的扩展。
- 命名(0.9.4) 贯穿所有层级——再好的结构配上差名字也会变坏。
1. Python
1.1 命名
snake_case:函数、方法、变量、模块。
PascalCase:类名。
UPPER_SNAKE_CASE:模块级常量。
_前导下划线:内部/私有名称;__双下划线__:仅用于协议方法。
- 避免单字母变量,除了循环索引(
i、j)、数学约定(短作用域内的 x、y 表示张量可以接受)、或公认缩写(df 表示 dataframe)。
- 不要使用非通用缩写(
cfg、ctx、msg 可以;usrmgr、calcval 不行)。
1.2 布局与格式
- 4 空格缩进,禁止 Tab。
- 行长度:软限制 100 字符;能提升可读性就提前换行。
- 顶层定义间隔两个空行;方法间隔一个空行。
- Import 顺序,每组之间空一行:
- 标准库
- 第三方库
- 本地/第一方模块
- 一行一个 import。常用名称优先用
from x import y。
- 禁止通配导入(
from x import *)。
1.3 类型注解
- 所有公共函数签名必须标注(参数 + 返回值)。
- 内部辅助函数可选,但推荐。
- 使用现代语法:
list[int] 而非 List[int],int | None 而非 Optional[int](Python 3.10+)。若项目目标是旧版本,跟随现有代码风格。
Any 是兜底方案——优先用 object、协议(Protocol)或联合类型。
- 会导致循环导入的类型,用
TYPE_CHECKING 分支导入。
1.4 Docstring
- 公共函数、类、模块必须有 docstring。内部辅助函数仅在签名无法体现行为时才需要。
- 使用三双引号。首行用祈使语气的单句概括("返回……"、"计算……"),不要用陈述式。
- 非平凡函数需包含 Args / Returns / Raises 段落。除非项目另有约定,使用 Google 风格(跟随现有风格)。
- 不要重复类型注解已表达的内容——解释含义和为什么。
示例:
def compute_advantage(rewards: torch.Tensor, values: torch.Tensor, gamma: float) -> torch.Tensor:
"""计算轨迹的 GAE 优势值。
Args:
rewards: 形状 (T,),每步的奖励。
values: 形状 (T+1,),价值估计(含 T 处的 bootstrap)。
gamma: 折扣因子,范围 [0, 1]。
Returns:
形状 (T,) 的优势值,未做归一化。
"""
1.5 函数与类
1.6 异常处理
1.7 日志与打印
1.8 通用风格
- 用
is None / is not None,不要用 == None。
- 判空用真值:
if not items: 而非 if len(items) == 0:。
- 单行能写下时,列表/字典/集合推导式优先于
map/filter。
- 大序列用生成器;不要构造一个只遍历一次的临时列表。
- 文件路径用
pathlib.Path,不要拼字符串。
- 字符串格式化用 f-string。除日志外,避免
% 和 .format()。
- 禁止魔法数字。要么提到模块顶部命名常量,要么走配置。
2. PyTorch / 深度学习
2.1 设备管理
- 禁止硬编码
.cuda() 或 .to("cuda")。device 必须可配置:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
device 通过函数签名或配置传递,不要依赖全局变量。
- 能直接在目标设备创建张量就直接创建:
x = torch.zeros(N, device=device)
x = torch.zeros(N).to(device)
2.2 dtype 与精度
- 在关键位置显式指定 dtype(
dtype=torch.float32、索引用 torch.long)。
- 不要依赖跨操作的隐式类型提升——明确 cast。
- 混合精度用
torch.autocast + GradScaler,不要手动 cast。
2.3 张量形状纪律
- 在 docstring 里用
(B, C, H, W) 风格标注预期形状,复杂 reshape 加行内注释。
- 非平凡的 reshape 用
einops(rearrange / repeat / reduce),自解释。
- 优先
.reshape(),除非明确需要 view(依赖连续性)。
- 在函数边界对依赖的形状加断言:
assert x.ndim == 4 and x.shape[1] == 3, f"期望 (B,3,H,W),实际 {tuple(x.shape)}"
- 避免不带
dim= 参数的 squeeze() / unsqueeze()——行为依赖形状,容易踩坑。
2.4 梯度与 autograd
- 推理/评估路径用
with torch.no_grad(): 或 @torch.inference_mode()——后者略快。
- 评估前
model.eval(),训练前 model.train(),两边都要做。
- 转 CPU/NumPy 前先 detach:
.detach().cpu().numpy()。
- 不要对非预期张量调用
.backward()——检查 requires_grad。
optimizer.zero_grad(set_to_none=True) 优于默认行为,更快更合理。
2.5 训练循环结构
2.6 数据
Dataset 子类只实现 __len__ 和 __getitem__;batch 整合放到 collate_fn。
__getitem__ 里不要做能向量化的重 CPU 工作——但也别为 1000 条数据过度优化。
DataLoader 配好:num_workers、GPU 场景下 pin_memory=True、长训练加 persistent_workers=True。
- 不要把完整数据集常驻 GPU,除非确实很小。
2.7 模型
nn.Module 子类:__init__ 注册子模块,forward 做计算。不要在 forward 里定义层。
- 非平凡层显式初始化权重,不要盲目相信库默认。
- 模型结构通过
__init__ 参数暴露,不要靠全局变量或环境变量。
- 多输出模型的
forward 返回结构化结果(dict 或 dataclass),比裸 tuple 更易演进。
2.8 可复现性与配置
- 每次实验能被配置(YAML / JSON / dataclass)完整描述,不要把可调参数埋成硬编码字面量。
- 启动时把配置 + git SHA + seed 记录到运行目录。
- Checkpoint 旁边存一份配置,让 checkpoint 自包含。
3. Shell 脚本与配置文件
3.1 Shell(bash)
3.2 JSON 配置
- 不支持注释。需要说明就配一个 README 或改用 JSON5/YAML。
- 2 空格缩进。
- 文件末尾留换行。
- 键按逻辑顺序组织——相关设置放一起;顺序有意义就别按字母排。
- 不要留尾逗号(非法 JSON)。
3.3 YAML 配置
- 2 空格缩进,绝不混用 Tab。
- 可能被误解析的字符串加引号:版本号(
"1.0")、布尔样字符串("yes"、"no")、以特殊字符开头的值。
- 长配置用锚点(
&)和别名(*)去重。
- 行长度控制在 120 字符内,长列表换行。
3.4 配置卫生(通用)
- 禁止在提交的配置里放 secret。用环境变量或密钥管理器,只引用名字。
- 任何需要本地创建的配置,都提供带注释的
*.example 样例文件。
- 必填字段缺失/格式错误要大声报错,不要用默认值悄悄兜底。
4. 通用约定
4.1 文件与目录组织
- 一个模块只做一件清晰的事。文件超过约 500 行就考虑拆。
- 测试文件对应源码结构:
src/foo/bar.py ↔ tests/foo/test_bar.py。
- 入口脚本放
scripts/ 目录或做成命令行 entry point,不要一堆 run_*.py 散落顶层。
__init__.py 定义包的公开 API。不要把所有东西都 re-export,选择性暴露。
4.2 注释
- 注释写为什么,不写是什么。代码本身说明"是什么"。
- 刻意绕行的地方标
# HACK: 或 # FIXME: 加原因。
# TODO: 带条件/时机(如 # TODO(v2 迁移后): 移除此分支)。
- 删掉注释掉的代码。版本控制会记住它。
4.3 依赖
- 生产 requirements 锁版本(
torch==2.3.1);库的依赖上界放宽。
- 开发依赖(lint、测试工具)与运行时依赖分开。
- 标准库能一行解决的事,不要引依赖。
4.4 Git 提交
4.5 测试(功能完成即补单测 · 强制)
硬性规则:每完成一项功能开发(新函数、新类、新模块、新 endpoint、新配置逻辑),必须在同一个交付内补齐对应的单元测试。没有单测的功能视为未完成——不能进入阶段 C 的 simplify/review,也不能向用户汇报"完成"。
4.5.1 什么算"一项功能开发"(必须补单测)
- 新增的公共函数 / 方法 / 类。
- 新增的公共模块(至少覆盖其主 API)。
- Bug 修复(加回归测试,锁住不再复现)。
- 有纯函数逻辑的数据变换(tokenizer、归一化、advantage 计算、reward shaping 等)。
- 配置解析 / 校验逻辑。
4.5.2 什么可以不写单测
- 一次性脚本(
scripts/ 下的临时任务脚本)。
- 纯 I/O 粘合代码(文件读写、简单的 CLI argparse),且无分支逻辑。
- 明显的打印语句、日志消息调整。
- 训练主循环这类难以在 CI 里跑的代码——但其中可抽出的纯函数必须抽出并测试(例如学习率调度、损失计算、advantage 估计)。
- 这些例外要在 plan 或汇报里明说,别默默跳过。
4.5.3 测什么(一个功能对应的单测应覆盖)
- 正常路径(happy path):1 条主要用例,典型输入 → 期望输出。
- 边界条件:空输入、0、1、极大值、单元素、重复元素、NaN/Inf(若相关)。
- 错误路径:非法输入是否抛出预期异常(用
pytest.raises 验证异常类型 + 消息关键词)。
- 形状 & dtype(PyTorch 场景):显式断言输出 tensor 的
.shape 和 .dtype,避免"看似跑通"。
- 不变量:概率之和为 1、归一化后均值 0 方差 1、梯度可回传等。
不需要把每种组合穷举——用 pytest.mark.parametrize 覆盖关键维度即可。
4.5.4 写法规范
- 框架:优先
pytest;若项目已用 unittest,跟随项目。
- 测试文件位置镜像源码:
src/foo/bar.py ↔ tests/foo/test_bar.py(没有 tests/ 时就在源文件旁建 test_*.py,跟随项目约定)。
- 测试名描述场景:
test_compute_advantage_handles_zero_gamma,不是 test_1 / test_compute_advantage。
- 一个测试一个行为。名字里出现"and"就该拆。
- 用
fixture / parametrize 复用 setup,不要复制粘贴。
- 测试本身要快:单个用例 < 1s;需要 GPU / 网络 / 大模型的,标记
@pytest.mark.slow 或 @pytest.mark.gpu 并说明如何跳过。
- Mock 外部依赖(网络、文件系统、时间),避免测试环境耦合。
- 不要在测试里测私有实现细节——测行为、不测实现,否则重构必改测试。
4.5.5 测试的结构(AAA)
每个测试用 Arrange-Act-Assert 三段式组织,可用空行分隔:
def test_compute_advantage_handles_zero_gamma():
rewards = torch.tensor([1.0, 2.0, 3.0])
values = torch.tensor([0.5, 0.5, 0.5, 0.0])
adv = compute_advantage(rewards, values, gamma=0.0)
assert adv.shape == (3,)
torch.testing.assert_close(adv, rewards - values[:-1])
4.5.6 运行与验证(交付前必做)
4.6 文档
- 项目根目录的 README 回答四件事:这是什么、怎么装、怎么跑、下一步去哪。
- 非显然的算法加注释指向论文/参考。
- 文档与代码就近放,长篇设计文档放
docs/。
5. 完成后流程(强制 · 主动执行)
写完代码后不要等用户催,按顺序执行以下步骤,并把结果以结构化方式汇报。
5.1 步骤
-
自检清单(先自己扫一遍)
- 本次变更的文件 & 新增/修改的符号清单。
- 对照"审查清单(快速参考)"自查一轮——明显的必改项先自己修掉。
-
补齐单元测试并跑通(强制 · 在 simplify/review 之前完成)
- 对本次新增/修改的每项功能,按 §4.5 补齐对应单元测试。
- 测试位置镜像源码目录(
src/foo/bar.py ↔ tests/foo/test_bar.py)。
- 覆盖:正常路径 + 边界条件 + 错误路径;PyTorch 场景还需断言 shape/dtype。
- 本地跑通测试:
pytest <新增测试文件> -v
- 测试未通过不得进入步骤 3。若遇到无法在本地跑通的情况(需 GPU / 大数据等),明确标注
@pytest.mark.slow 或 @pytest.mark.gpu,并在汇报里说明未执行的原因和跳过方式。
- 按 §4.5.2 的例外条款可省略单测时,必须在此步骤明说哪些文件、为什么省略。
-
调用 simplify skill
- 目的:让 simplify 针对本次新代码做复用性、质量、效率检查并修复。
- 明确告知 simplify 审查的范围(只看本次变更,不要扩散到全库)。测试文件一并纳入范围。
- simplify 提出的改动若属于"必改/建议改"级别,直接采纳;属于"可考虑"级别,汇报给用户决定。
- 若 simplify 修改了被测代码,重新跑一次相关测试确认没破坏行为。
-
调用 review skill
- 目的:完整 review 本次新增/修改的代码 + 新增测试。
- 若 simplify 阶段已做修改,review 的是修改后的版本。
-
汇总报告
按以下格式给用户最终报告:
## 完成情况
- 新增:<文件清单>
- 修改:<文件清单>
## 单元测试
- 测试文件:<路径>
- 用例数:<N>
- 运行结果:<passed / failed / skipped>
- 省略单测的部分(若有):<文件 + 理由>
## simplify 结果
- 已自动修复:<...>
- 待确认:<...>
## review 结果
### 必改(Must-fix)
- <文件:行号> <问题> → <建议>
### 建议改(Should-fix)
- ...
### 可考虑(Consider)
- ...
## 下一步
<等待用户确认 / 继续修复 / 已全部完成>
-
同步项目文档(强制 · 在汇报之后执行)
目的:让任何一个新 session 读完 docs/ 就能 resume 到当前状态,不用靠对话历史。汇报完不同步文档等于未完成交付。
- 落点:项目内的开发文档目录(本项目是
docs/)。新项目无此目录时,先在项目根建 docs/ 再写。
- 必更的两类文件:
- 开发日志(本项目
docs/DEVELOPMENT_LOG.md)——在末尾追加本次迭代章节,至少包含:
- 时间线与本次任务目标(用户原话 + 关键决策理由)
- plan 演进(如有多轮修订,记录原因)
- 文件改动清单(新增 / 修改)
- 测试状态(数量 + passed/failed/skipped)
- simplify / review 结论摘要(必改/建议改/可考虑)
- 踩到的坑与未完成事项(留给下次)
- Resume 入口(本项目在
DEVELOPMENT_LOG.md 最后一节"Resume 入口"小节 + 可选的独立 RESUME_PROMPT.md)——更新为本次结束后的最新状态:
- 当前功能/测试/环境的简短快照
- "下次进来先做的事"优先级列表
- "绝对不要做的事"若有新增项也要补
- 按需更新的文件:
docs/ARCHITECTURE.md:只有本次改动影响架构/扩展点/数据流时才改;否则不动。
CHANGELOG.md(若项目维护):在 [Unreleased] 段追加条目。
README.md:只有对外接口/安装/使用方式变化时才改。
- 风格要求:跟随现有文档风格(本项目用中文 + 编号章节 + 代码块示例),不要新造格式。
- 防止静默废弃:如果本次把某个旧做法替换了,旧章节保留历史记录,但在 Resume 入口明确标注"以新做法为准"。
汇报用户"已完成"之前,最后一步是贴出同步的文档段落(或 diff 摘要),让用户知道文档已经跟上。
5.2 例外
- 用户明确说"只写不审"时,跳过步骤 3–4,但仍需完成 §5.1 步骤 1(自检)、步骤 2(单测)与步骤 6(同步文档)。
- 单次改动极小(< 5 行、纯 typo 或参数调整)时,可合并步骤 3–4 为一次轻量 review,不强制调用 skill;单测按 §4.5.2 判断是否仍需补;文档同步仍要做——至少在 CHANGELOG 或日志末尾留一行记录。
- 例外不适用于"跳过单测"与"跳过文档同步":功能开发必须有对应单测(除非命中 §4.5.2 的免测清单),同时必须把本次进展同步到
docs/。
5.3 反模式(禁止)
- 写完代码直接交差,不做 simplify/review。
- 功能完成却没补单测——除非命中 §4.5.2 且已明示。
- 单测没跑通就上 simplify/review。
- 静默地在 simplify/review 阶段改写代码而不汇报。
- 跳过 simplify 直接上 review,导致 review 在未清理的代码上反馈。
- 为应付流程写空壳测试(只 assert True、不覆盖任何真实行为)——等于没写。
- 汇报完就收尾,不同步
docs/——新 session 无法 resume,视为未交付。
- Resume 入口陈旧:只追加开发日志但没更新"下次进来先做的事",等于文档在撒谎。
审查清单(快速参考)
审查代码时按以下顺序扫描:
- 核心设计原则(§0.9,先扫这一层)
- 易读直观:主流程能 30 秒读懂吗?嵌套是否过深?
- 是否存在不必要的抽象(基类/工厂/策略但只有一个实现)?
- 函数/类是否单一职责?描述时要不要用"and"?
- 命名是否自解释?有没有
data/info/tmp/helper/process 这类空壳名?
0.5. 单元测试(§4.5)
- 本次每个新增/修改的功能都有对应测试吗?
- 测试覆盖了正常 + 边界 + 错误路径吗?
- PyTorch 场景是否断言了 shape/dtype?
- 测试都本地跑通了吗?
-
正确性隐患
- 可变默认参数
- 裸
except 或吞异常
- 硬编码设备/dtype
- 无断言的形状假设
- 评估路径缺
model.eval() / no_grad
- Shell 变量未加引号
- 配置里有 secret
-
公共 API 卫生
- 签名有类型注解
- 公共函数/类有 docstring
- 命名合理(不是
data、foo、helper)
-
结构
- 函数长度与单一职责
- Import 有序且干净
- 无死代码/注释代码
- 无魔法数字
-
风格打磨
- 行长度
- f-string 该用就用
pathlib 替代 os.path
- 真值判断
审查结论按必改 / 建议改 / 可考虑分组,格式见本文件开头。