Files
vrsub/AGENTS.md
cat-shark 7a7212f70c docs: 注释规范要求精简可读,并清理生产代码中的历史叙事
AGENTS.md 的注释规范新增三节可执行约束:

- 只写代码真实逻辑:注释只回答"做什么"与"为什么必须这么做",禁止写决策/
  修改时间、历史版本对比、实测数据与实验结论、事故与缺陷编号(run_xxxx /
  batch_xxxx / R01 等)——这些属 docs/decisions.md 与审查跟踪文件;当前生效
  的约束可以写,但不附带它何时因何变成这样。
- 精简可读:单段连续注释不超过 3 行;docstring 一句话概括职责,不重复函数名
  已表达的信息;不写逐行翻译代码的废话注释,只在非显然处(业务规则、边界、
  易错点、外部约束)加注。
- 覆盖范围:测试注释只说明验证什么行为,回归用例可保留一句溯源;并明确
  参数说明应写在**参数读取处**附近,而不是把多个参数的解释堆在离使用位置
  很远的注释块里。

按此清理生产代码(注释净减 70 行,18 个文件),典型处理:

- nodes/whisper.py:删掉堆在一起、含"用户 2026-08 决定 / 实测 savr-1054"
  等叙事的参数块,把各参数说明移到各自的读取处与 model.transcribe 调用处;
- nodes/llm_filter.py、nodes/subtitle_cleanup.py:模块 docstring 去掉英文
  背景叙事与条数统计,保留"默认只跑规则层""整条删除而非 '-' 占位"等当前
  行为;
- src/wov_app/{batch,db,scheduler}.py 与 routers:去掉 batch_xxx/run_xxx 事故
  编号与"修复前……"对比,改为一句"否则会出现什么问题";
- nodes/ass.py、frame_extract.py:去掉废弃值对比与日期,保留判据本身。

安全验证:用 AST 对比(剥离 docstring 后比较语法树)确认 18 个文件**零逻辑
变更**;`nodes/proper_nouns.py` 的规则表 reason 字段会注入 LLM 提示词,属于
数据而非注释,已恢复原值。全量测试 476 passed。
2026-09-13 16:37:49 +08:00

209 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — VRSub 协作规则
本文件只写**对 AI 编码代理的要求**(行为规则、流程约束、执行环境)。
项目信息(架构是什么、有哪些节点、怎么配置)不写在这里,按需阅读:
| 想了解 | 阅读 |
| --- | --- |
| 项目定位、快速开始、目录结构、文档导航 | [README.md](./README.md) |
| 架构、核心机制、北极星不变式、单体化说明 | [docs/architecture.md](./docs/architecture.md) |
| 节点输入输出协议、模型权重解析、长音频、OCR 并发 | [docs/node-protocol.md](./docs/node-protocol.md) |
| 内置工作流、参数标注约定、切换模型、产物命名 | [docs/workflows.md](./docs/workflows.md) |
| 环境变量全表、启动方式、uv 依赖管理 | [docs/configuration.md](./docs/configuration.md) |
| 批量处理、暂停/继续、孤儿清理、进度日志 | [docs/operations.md](./docs/operations.md) |
| 测试资产清单、真实模型集成测试 | [docs/testing.md](./docs/testing.md) |
| 设计决策与踩坑记录(含实验结论) | [docs/decisions.md](./docs/decisions.md) |
| 代码缺陷跟踪与修复验收标准 | [docs/代码审查问题跟踪.md](./docs/代码审查问题跟踪.md) |
**开工前先做两件事**:读 [README.md](./README.md) 建立项目认知,
再读 [docs/architecture.md](./docs/architecture.md#关键设计约束北极星不变式)
确认不能破坏的硬约束。
## 提交规则(强制,2026-08
- **未经用户明确许可,禁止执行 `git commit` / `git push`**,包括"小步提交"。
- 完成改动后只汇报改动内容与验证结果,等待用户指示;用户明确说出
"提交 / push / 推上去" 等指令时才可执行提交。
- 需要用户确认的场景:新增功能、修复、文档改动、工作流/清单数据改动等
一切涉及 git 写操作的行为。
## 等待规则(强制,2026-09
- **单次等待时间不得超过 10 秒**。需要长时间运行时,把它放到后台
`setsid ... &` / `nohup ... &`)写日志,然后**每 10 秒轮询一次结果**
`for i in $(seq 1 6); do sleep 10; ...; done`),而不是用一个长 `sleep`
或长 `timeout` 把用户的对话阻塞住。
- 反例(禁止):`sleep 600``timeout 1800 uv run pytest`——即使命令本身
很快,把 timeout 设成 30 分钟也是错的:它无法预判耗时,只会在出问题时
让用户白等半小时。应当先用 10 秒轮询确认它是否结束。
- 正例:把任务丢后台 → `for i in $(seq 1 N); do sleep 10; <检查是否完成 && break>; done`
耗时任务写日志到 `data/experiments/...`,事后读日志汇报。
- 测试与构建也遵循此规则:已知 `uv run pytest` 全量约 70 秒,用后台+轮询,
不要写 `timeout 1800`
## 开发流程:TDD(红-绿-重构)
- 任何新功能/修复必须先写失败测试(红),再实现最小代码让其通过(绿),
最后重构保持整洁;不允许先写实现后补测试。
- 小改动只跑相关测试,避免每次都完整跑全量测试;改动影响面大时才跑全量。
- 测试运行方式与资产说明见 [docs/testing.md](./docs/testing.md)。
## 测试规则
### 模块的边界
「模块」指**由独立功能、在程序运行时会真实被使用到的代码块构成的代码上下文**:
它可独立运行,也可和其他代码上下文组合运行。
例如 `nodes/whisper.py``nodes/llm_filter.py``src/wov_app/batch.py`
`src/wov_sdk/models.py` 各是一个模块;`nodes/subtitle_ocr.py` 与其
`nodes/adaptive_pool.py` 是两个模块(后者可独立使用)。
### 按模块组织测试代码
- **测试代码必须按模块编写**:一个模块对应测试目录下的**一个模块目录**,
不得把同一模块的测试拆成多个互不相干的平铺文件。
- **单文件过长时**,在同一模块目录内拆成多个文件(按行为分组,如
`test_rules.py` / `test_llm_layer.py`),而**不是**拆到模块目录之外。
- 目录布局固定为 `tests/<层级>/<模块>/``<层级>` 对应被测代码位置
`nodes/``tests/nodes/``src/wov_app/``tests/app/`
`src/wov_sdk/``tests/sdk/`),模块目录名即模块名。
- 每个模块目录必须有 `__init__.py`,保证不同模块目录下的同名测试文件可共存。
- 具体目录示例见 [docs/testing.md](./docs/testing.md#目录结构与模块对应)。
### 数据 / 测试过程 / 验证结果
- 每个测试必须写成**数据 → 测试过程 → 验证结果**三段结构:先准备输入数据,
再调用真实生产代码,最后断言输出结果;顺序清晰、一读即懂。
- **测试必须可独立运行**:单个测试文件或单个模块目录被单独执行时结果不变。
**不依赖全局 `conftest.py`(不保留全局 conftest**,不依赖仓库其他测试的
配置、执行顺序或共享状态。
- **数据与产物目录就是测试代码所在目录**:模块专用数据(JSON/期望输出等)
写在测试代码内的常量或模块目录下;视频、音频等无法写进代码的大体积素材,
放**同一个模块目录内**(如 `data/`)与其他测试区分,不使用仓库根级中央数据目录。
- 测试过程**必须调用真实的生产代码**(导入 `nodes/``wov_app``wov_sdk`
的真实实现),我们只构建测试数据、验证输出结果;
不得在测试里重写业务逻辑来模拟被测功能,也不得用伪造结构冒充被测数据。
- 需要磁盘或环境隔离时,由**模块自己的 fixture**(模块目录下的 `conftest.py`
或测试文件内部)创建临时目录并设置变量,不依赖外部预先存在的配置。
### 功能覆盖优先,不追求代码覆盖率
- **不追求测试的代码覆盖率 100%,追求功能性代码覆盖率 100%**。
- 一个大模块的功能正常,就无须对这个大模块中的辅助函数单独测试:
私有工具函数、内部转换、仅供主流程调用的分支,由主功能用例顺带覆盖即可,
**不为了凑行覆盖率给辅助函数补无意义用例**
- 但**每个独立功能模块必须被测试覆盖**(即上文“模块的边界”中定义的模块),
不得出现“完全无测试的模块”。模块清单与当前覆盖状态见
[docs/testing.md](./docs/testing.md#功能模块覆盖清单)。
- 新增独立功能模块时,必须同时补上覆盖其功能的测试,并同步更新上述清单;
只写生产代码不补测试的改动视为未完成。
### 测试质量与运行
- 测试以保证功能可用为目标,**不强制 100% 行覆盖率**pytest 已移除
`--cov-fail-under=100` 门槛);需要查看覆盖率时可手动追加
`uv run pytest --cov=src --cov=nodes`
- **测试必须使用真实数据**:真实音频(合法 WAV/PCM)、真实 JSON/数据库/文件;
禁止用占位字节(如 `b"x"`)或伪造结构冒充被测数据——假数据测试只能凑覆盖率,
无法验证真实行为,视为无意义测试。
- 只允许在 I/O 边界使用 mock/stub:文件系统、网络、子进程、环境变量、时间、
**模型推理**(重模型不进入单元测试;注入的假模型必须返回结构真实的分段)。
- **真实模型集成测试**必须配套:真实 faster-whisper 模型 + 真实音频素材验证
端到端转写,本地缺模型/素材时跳过,有则必须执行(清单见
[docs/testing.md](./docs/testing.md#真实模型集成测试))。
- 测试素材必须**一次性准备后随模块目录入库**(放模块目录内),禁止在测试执行时
现场生成;缺失时跳过而非生成。
- **外部环境状态缺失时应跳过而不是判失败**:未配置的密钥、账户余额/配额、
限流、模型/服务不可达、缺失的真实素材都属于环境状态,不是被测代码的行为;
这类用例用 `pytest.skip()` 并说明原因(如 `pytest.mark.integration` 用例)。
但代码抛异常、返回结构错误、断言不成立仍必须失败,不得用跳过掩盖回归。
- 测试只保证代码路径被执行,不覆盖端口占用、防火墙、权限等外部环境状态;
端口问题用启动检查、端口检查与 uvicorn 冒烟测试补充。
- 本地出现 `WinError 10013` / `WinError 10048` 时,先用
`netstat -ano | findstr :<port>` 确认是否有残留监听进程。
## 代码注释规范
### 只写代码真实逻辑
- 注释只回答两个问题:**这段代码做什么**、**为什么必须这么做**(不这样写会出
什么错)。读代码的人需要的是当前逻辑,不是它的来历。
- **禁止写进代码注释**(这些属于 [docs/decisions.md](./docs/decisions.md) 与
[docs/代码审查问题跟踪.md](./docs/代码审查问题跟踪.md)):
- 修改/决策时间(“用户 2026-08 决定”、“2026-09 起”);
- 历史版本对比(“旧版是 X,现在改为 Y”、“修复前……”);
- 实测数据与实验结论(具体条数、耗时、模型名、实验目录);
- 事故与缺陷编号(`run_xxxx``batch_xxxx`、R01/R02 等):改用一句
“否则会出现什么问题” 描述后果即可;
- 变更原因的长篇叙述、将来计划、TODO 式背景;需要时在 docs 记录并链接。
- 例外:**当前生效的约束**可以写(如“默认 60 秒一块,切块失败回退整段”),
但不能附带它何时、因何改为如此。
### 精简可读
- **单段连续注释不超过 3 行**(含行)。超过说明它很可能在讲历史或设计辩论,
应压缩为 1~3 行;确实需要展开的写进 docs/ 并在此链接一句。
- 函数/类 docstring 用一句话概括职责,必要时补 1~2 句关键行为或参数语义;
不重复函数名已表达的信息(`def parse_srt` 不必再写“解析 SRT”)。
- 不写“废话注释”:逐行翻译代码、`# 返回结果``# 循环处理` 这类无信息量的
句子;只在**非显然处**加注释(业务规则、边界、易错点、外部约束)。
- 中文说明,术语与代码标识符保持英文;保持注释与代码同步,改代码必须
同步改注释(不留过期注释),但**不要为了“补充说明”把注释越写越长**。
### 覆盖范围
- 模块/文件职责、公开类与函数的职责与非显然行为必须有注释;私有工具函数
仅在逻辑非显然时加简注。
- 测试代码同样配中文注释,但只说明**验证什么行为**(一句话),不重复测试
步骤的机械描述。例外:回归用例可保留**简要的溯源**(如"曾因 XX 导致
时间轴错位"一句),因为它解释了"为什么必须有这条用例";但仍不写长叙事、
不贴大段实测数据,详细经过放 [docs/decisions.md](./docs/decisions.md)。
- JSON 数据文件(如 `manifests/*.json``workflows/*.json`)按 JSON 规范不支持
注释,字段语义以 `src/wov_sdk/models.py` 的模型注释和 [docs/](./docs/) 文档为准;
修改 JSON 字段时须同步更新文档。
## 目标运行环境
- 本服务的最终部署目标是 Linux,通常以 Docker/Kubernetes 容器运行。
- 当前 Windows 只作为本地开发环境,不允许在业务代码中写死 Windows 路径、
盘符或 Windows 专用命令。
- 路径处理统一使用 `pathlib`
- ffmpeg 在 Linux 上可使用系统包,也允许通过 `imageio-ffmpeg` 使用内置
二进制,节点代码不能假设 ffmpeg 一定在 PATH。
- 测试必须可以在 Windows 和 Linux 上运行;涉及平台分支的代码应同时覆盖
两种路径解析。
## Windows / PowerShell 执行规则
- 默认 shell 视为 Windows PowerShell 5.1;不要假设 Bash、zsh 或 PowerShell 7。
必要时先查 `$PSVersionTable.PSVersion`
- 禁止把 Bash 语法交给 PowerShell`python - <<'PY'``cat <<EOF``export`
`source``rm -rf`、Bash 后台 `&` 等。
- PowerShell 中 `&` 是调用运算符;URL 或参数含 `&` 时整体单引号引用。
- 避免 PowerShell 5.1 下使用 Bash 风格 `&&` / `||`;顺序步骤用多行 PowerShell。
- 参数含空格、括号、中文、`&|;><$` 或引号时默认用单引号。
- 外部程序路径可能有空格时,用 `& 'C:\path with spaces\tool.exe' arg1`
- 文件操作优先 PowerShell 原生命令和 `-LiteralPath`
- 复杂 Python 不用 `python -c`;涉及 SQL、JSON、中文、反斜杠、换行或
多层引号时,用仓库脚本或临时 `.py` 文件。
- 禁止在 PowerShell 用 Bash here-doc。临时传 Python 源码只允许 PowerShell
here-string,且尽量保持 ASCII。
- Python 源码含中文常量时,不通过 PowerShell 管道传给 `python -`;用 UTF-8
脚本文件、仓库脚本或 `\uXXXX`
- 搜索文本/文件优先 `rg` / `rg --files`
- 数据库或生产内容写操作前先查询当前数据;写入必须有明确筛选条件,禁止
无条件 `DELETE` / `UPDATE`
- 同一 PowerShell 命令连续失败两次后,停止微调长命令;改短命令、脚本文件、
数组 splatting 或分步验证。
## 文档维护规则
- 本文件**只放对代理的要求**。新增项目知识(架构、协议、参数默认值、运维语义、
实验结果)写进 [README.md](./README.md) 或 [docs/](./docs/)
不得写进本文件。
- 新增/移动文档时同步更新 README 的文档导航表与本文件顶部的索引表,
保证链接可达;文档中的相对链接指向真实文件。
- 决策与踩坑("为什么这样做")写 [docs/decisions.md](./docs/decisions.md)
缺陷跟踪写 [docs/代码审查问题跟踪.md](./docs/代码审查问题跟踪.md)
专题文档只保留结论并链接过去,避免同一内容多处维护。