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。
This commit is contained in:
2026-09-13 16:37:49 +08:00
parent 8f6083f8cf
commit 7a7212f70c
18 changed files with 192 additions and 262 deletions
+18 -26
View File
@@ -225,28 +225,22 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
device=device,
compute_type=compute_type,
)
# language 默认日语;vad_filter 默认开启(用户 2026-08 决定):过滤静音
# 段以提速并减少无语音处幻觉;长静音时 VAD 压缩时间轴可能轻微错位,
# 如需极致对齐可在工作流参数中显式关闭。
# decode_full=true(默认 false):VAD 对呻吟/轻语/BGM 混叠声学切段会
# 把真话当非语音剔除(实测 savr-1054 全片仅召回 115 条),开启后强制
# 无 VAD 整段解码(vad_filter=False 且跳过自动 VAD 分析)以召回弱语音,
# 代价是无语音段会产生长时套话幻觉,由下方日语幻觉清洗兜底移除。
# 另:decode_full 救回的弱语音中混有纯语气词碎片(あ/ん?等),由
# short_moan_max_chars(默认 3,0=关闭)参数控制短呻吟整条删除。
# task 默认 transcribe,中文直出模型可传 translate 直接翻译为目标语言。
# condition_on_previous_text 默认 False:长音频下开启会导致重复/漂移,
# 关闭后每个 30s 窗口独立解码,是 faster-whisper 官方建议的长音频方案。
# 常用参数默认值见各参数的读取处;完整参数手册见
# workflows/learn-translate.json 的 params._node_help。
output_dir = Path(request.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# 分块转写:默认每 1 分钟一块(chunk_seconds=60),切块失败自动回退整段。
chunk_seconds = int(request.params.get("chunk_seconds", 60))
chunks = _split_audio(audio_path, output_dir, chunk_seconds, _ffmpeg_bin())
# decode_full 时强制无 VAD:不传 vad_filter/vad_parameters也不跑自动 VAD
# 分析(分析结果对呻吟/轻语类音频无效,只会把整块切碎/剔除真话)。
# decode_full:忽略 vad_filtervad_parameters整段无 VAD 解码以召回
# 被 VAD 当非语音剔除的弱语音(代价是无语音段产生长时幻觉,由末尾清洗
# 处理)。为 False 时是否启用 VAD 由下方 vad_filter / 自动分析决定。
decode_full = bool(request.params.get("decode_full", False))
vad_parameters = request.params.get("vad_parameters")
# 默认开 VAD:滤掉静音段提速并减少无语音处幻觉;代价是长静音下时间轴
# 会被压缩回映射,需极致对齐的素材可显式关掉。
vad_filter = bool(request.params.get("vad_filter", True))
# 未显式给参且开启 VAD 时,按音频信号分析自动推荐参数(可 WOV_AUTO_VAD=0 关)。
if not decode_full and vad_filter and not vad_parameters and os.getenv("WOV_AUTO_VAD", "1") == "1":
try:
from nodes.vad_profiler import vad_parameters_for_audio
@@ -278,14 +272,18 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
if (Path(request.output_dir).parent.parent / PAUSE_FLAG).exists():
raise RuntimeError(f"whisper 被暂停(run {request.run_id}")
chunk_started = time.monotonic()
# decode_full=true 时 vad_filter 传 False:跳过 VAD 剔除弱语音段。
segments, _info = model.transcribe(
str(chunk),
# 源语言默认日语;中文直出模型配合 task=translate 可直接出中文。
language=str(request.params.get("language", "ja")),
task=str(request.params.get("task", "transcribe")),
# beam_size=1(贪心)足够且最快,提高只对难句有微弱收益。
beam_size=int(request.params.get("beam_size", 1)),
# decode_full 时关闭 VAD,跳过对弱语音段的剔除。
vad_filter=False if decode_full else vad_filter,
vad_parameters=None if decode_full else vad_parameters,
# 默认 False:长音频下开启会累积上下文导致重复/漂移,
# 关闭后每个 30s 窗口独立解码。
condition_on_previous_text=bool(
request.params.get("condition_on_previous_text", False)
),
@@ -302,17 +300,9 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
chunk_seconds / chunk_elapsed if chunk_elapsed > 0 else 0.0,
time.monotonic() - transcribe_started,
)
# decode_full(无 VAD)副作用:无语音/音乐/呻吟段会产生长时寒暄套话幻觉
# (おやすみなさい/ご視聴ありがとうございました 等),在此**连带时间戳整条
# 剔除**(序号/时间轴/文本全删、剩余重编号),不留下 '-' 占位污染下游
# (占位会渲染进 ASS 成减号、翻译/过滤都要额外处理);短时(≤15s)相同词
# 可能是剧情真实道晚安,保留。见 nodes/subtitle_cleanup.py。
# 其次(2026-09 用户决策):decode_full 也会把呻吟/BGM 混叠的弱语音整段
# 救回,其中混有大量**纯语气词碎片**(あ…/ん?/はぁ…/あ!あ!/んふふ 等),
# 这类噪声影响字幕观感;在此按'全部字符∈纯呻吟集合 且 有效假名≤max_chars'
# 判据**整条删除**remove_short_moan_entries),真实短对话(そこ/やばい/
# ねえ/やだ)天然不命中。仅 decode_full 生效,learn-translate 等 VAD 链路不受影响;
# 参数 short_moan_max_chars 可调(默认 3,设 0 关闭)。
# decode_full(无 VAD副作用清理:无语音段的长时寒暄幻觉与纯语气词
# 碎片都是噪声,整条删除(序列号重排,不留 '-' 占位污染下游);判据与
# 细节见 nodes/subtitle_cleanup.py。仅 decode_full 时启用。
if decode_full:
from nodes.subtitle_cleanup import (
clean_japanese_hallucinations,
@@ -320,6 +310,8 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
)
body = clean_japanese_hallucinations("\n".join(lines))
# short_moan_max_chars:有效假名 ≤ 该值的纯呻吟碎片整条删除,
# 设 0 关闭(真实短对话不会命中,判据见 subtitle_cleanup)。
body = remove_short_moan_entries(
body,
max_chars=int(request.params.get("short_moan_max_chars", 3)),