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:
+25
-36
@@ -1,31 +1,23 @@
|
||||
"""LLM 字幕过滤节点。
|
||||
"""LLM 字幕过滤节点:对 OCR 识别出的 SRT 做二次过滤。
|
||||
|
||||
对 OCR 识别出的 SRT 字幕做二次过滤,两级判断:
|
||||
两级判断,**默认只跑规则层**(use_llm=0;LLM 层误删真实对话的代价过高,
|
||||
依据见 docs/decisions.md):
|
||||
|
||||
1. **确定性规则层**(不调用 LLM):横线装饰、HTML/水印 token、URL/邮箱、
|
||||
单双 ASCII 字符等 OCR 噪声直接删除——这些模式是稳定可判的,走规则
|
||||
既省 token 又保证结果确定(真实数据中占删除量的 65%+)。
|
||||
1. **确定性规则层**(不调 LLM):横线装饰、HTML/水印 token、URL/邮箱、
|
||||
单双 ASCII 字符等 OCR 噪声直接删除。模式稳定可判,省 token 且结果确定。
|
||||
|
||||
2. **LLM 五类分类层**:把目标字幕连同前后各 context_size 条纯文本分批
|
||||
提供给 LLM,模型输出五个类别之一:
|
||||
- garbage:垃圾字符(乱码、残缺、装饰性符号)→ 删除
|
||||
- overlay:水印/网页/播放器等覆盖层文本 → 删除
|
||||
- noise:与上下文无关、无实际语义的杂项 → 删除
|
||||
- repeat:内容性重复(语气词、呻吟、重复感叹,属于内容本身)→ 保留
|
||||
- dialogue:正常对话 → 保留
|
||||
未识别输出一律回退 dialogue(宁滥勿缺,避免误删真实对话)。
|
||||
2. **LLM 五类分类层**(use_llm=1 时启用):把目标字幕连同前后各
|
||||
context_size 条纯文本交给 LLM,输出五个类别之一:
|
||||
- garbage(乱码/残缺/装饰)、overlay(水印/播放器覆盖层)、
|
||||
noise(无实义杂项)→ 删除;
|
||||
- repeat(内容性重复,如语气词/呻吟)、dialogue(正常对话)→ 保留。
|
||||
未识别输出一律回退 dialogue(宁滥勿缺)。
|
||||
|
||||
3. **文本去重**:相同文本(忽略全部空白与大小写差异)只调一次 LLM,
|
||||
上下文取首次出现位置,结果缓存复用——修复"同一句字幕 5 留 6 删"的
|
||||
判定不一致,同时把长视频的 LLM 调用量降到唯一文本数。
|
||||
|
||||
4. **上下文净化**:喂给 LLM 的上下文是**过滤后的字幕**——规则层确定性的
|
||||
垃圾(横线/HTML/网址/水印等)从上下文中剔除,只留下有意义的对白,
|
||||
避免覆盖层垃圾污染 LLM 的场景判断导致误删真实对话。
|
||||
|
||||
参考真实任务 run_ac7f480a3ccb(2026-08,OCR 1666 条):旧实现把 394 条
|
||||
真实对话当噪声删掉(占删除 35%)、同文本判定不一致;新实现按上述机制
|
||||
回归测试已固化在 tests/test_llm_filter.py。
|
||||
该层的两个配套机制:
|
||||
- **文本去重**:相同文本(忽略空白与大小写)只调一次 LLM,结果复用,
|
||||
保证同一句话判定一致并减少调用量;
|
||||
- **上下文净化**:喂给 LLM 的上下文先剔除规则层已判定的垃圾,避免覆盖层
|
||||
噪声污染场景判断而误删真实对话。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -120,7 +112,7 @@ _HTML_MARK_RE = re.compile(
|
||||
re.IGNORECASE,
|
||||
)
|
||||
# 规则层正则:播放器/作品编号水印(VLM 常把画面角落的编号识别成短串)。
|
||||
# 实测真实数据命中:SPHO-1 / PHO一号馆 / NO.1专用 / PHD-手術 / SP10-1型。
|
||||
# 覆盖水印编号形态,如 SPHO-1 / PHO一号馆 / NO.1专用 / PHD-手術 / SP10-1型。
|
||||
# 限定为**不含汉字的编号形态**(或纯形态串),避免误伤正常英文对白。
|
||||
_SERIAL_MARK_RE = re.compile(
|
||||
r"^(?:SPH|SPHO|SPIO|SPNO|SP10|PHO|PH0|PHD|P10|NO\.|SP\s*\d)"
|
||||
@@ -142,14 +134,14 @@ _CAST_MARK_RE = re.compile(r"^[((]\s*(?:出演|主演|配役|监督|スタッ
|
||||
DEFAULT_OVERLAY_TOKENS = frozenset(
|
||||
{"html", "background", "___", "cleaning", "buffering", "loading", "marketing"}
|
||||
)
|
||||
# 长文本保护阈值:≥ 该长度的文本,noise 类别不构成删除依据。
|
||||
# LLM 判定不稳定(实测把完整对话句误判 noise),长度是必要兜底而非删除依据。
|
||||
# 长文本保护阈值:≥ 该长度的文本,noise 类别不构成删除依据(LLM 会把完整
|
||||
# 对话句误判为 noise,长度是兜底手段,只用于保护不用于删除)。
|
||||
DEFAULT_MIN_KEEP_LEN = 12
|
||||
|
||||
# 纯数字/小数点组合("4.0"/"2.0"/"10-1期B" 中的纯数值形态)。
|
||||
_NUMBER_ONLY_RE = re.compile(r"^\d{1,3}[.,]\d{1,2}$")
|
||||
|
||||
# LLM 分类层开关(2026-09 默认关闭):见模块与 nodes/llm_filter.py 说明。
|
||||
# LLM 分类层开关:默认关闭,只跑确定性规则层(原因见模块 docstring)。
|
||||
DEFAULT_USE_LLM = False
|
||||
|
||||
|
||||
@@ -232,9 +224,8 @@ def _should_delete(category: str, text: str, min_keep_len: int) -> bool:
|
||||
"""按 LLM 类别与长文本保护决定是否删除。
|
||||
|
||||
repeat/dialogue 一律保留;garbage/overlay 一律删除(明确的垃圾信号);
|
||||
noise 对短文本删除,但 ≥min_keep_len 的长文本不删——LLM 判定不稳定,
|
||||
完整对话句常被误判 noise,长度保护是必要兜底(实测移除后新增误删
|
||||
124 条真实长对话)。长度只用于"保护",不用于"删除"。
|
||||
noise 仅对短文本删除:LLM 会把完整对话句误判为 noise,≥min_keep_len 的
|
||||
长文本一律保留。长度只用于保护,不用于删除。
|
||||
"""
|
||||
if category not in DELETE_CATEGORIES:
|
||||
return False
|
||||
@@ -362,10 +353,8 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
|
||||
}
|
||||
# 去重开关:默认开;关掉时每个条目独立调用 LLM(不省调用,判定各自独立)。
|
||||
dedupe = str(request.params.get("dedupe", "1")) not in ("0", "false", "False")
|
||||
# LLM 分类层开关(2026-09 默认关闭):实测该层额外删除的 131 条中 56% 是
|
||||
# 真实对话(run_ac7f480a3ccb 逐类人工审查),真正抓到而规则层抓不到的仅 58
|
||||
# 条(已大部分下沉为规则)。默认只跑确定性规则层,宁多留不漏删;
|
||||
# 需要旧行为时用 params.use_llm=1 显式打开。
|
||||
# LLM 分类层开关:默认关闭,只跑确定性规则层(宁多留不漏删);关闭原因
|
||||
# 见模块 docstring 与 docs/decisions.md。需要该层时用 params.use_llm=1。
|
||||
use_llm = str(request.params.get("use_llm", "1" if DEFAULT_USE_LLM else "0")) not in (
|
||||
"0", "false", "False",
|
||||
)
|
||||
@@ -414,7 +403,7 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
|
||||
done, total, rate, avg_time, workers, pool.max_workers,
|
||||
)
|
||||
|
||||
# 自适应并发调用 LLM:按实测负载弹性伸缩,避免压垮 LLM 接口。
|
||||
# 自适应并发调用 LLM:按响应快慢弹性伸缩,避免压垮接口。
|
||||
pool = AdaptiveThreadPool(
|
||||
worker=judge_one,
|
||||
on_progress=log_progress,
|
||||
|
||||
Reference in New Issue
Block a user