Skip to content

章节梗概 (7/9):T8 plain 源结构化逐段校对实现与接线 - #21

Closed
zj1123581321 wants to merge 3 commits into
stack/chapters-06-wiring-pipelinefrom
stack/chapters-07-plain-structured
Closed

zj1123581321 wants to merge 3 commits into
stack/chapters-06-wiring-pipelinefrom
stack/chapters-07-plain-structured

Conversation

@zj1123581321

@zj1123581321 zj1123581321 commented Jul 20, 2026 •

Copy link
Copy Markdown
Collaborator

范围

Stacked PR 系列第 7 个(依赖 #20)。T8 阶段二 v1 的前两个阶段(S4 llm_ops 接线与 S6 集成测试在下一个 PR #22):

  • S1:确定性段落化工具 paragraphize(长度预算 + 授权断点:句末/停顿,hard_max 与 2×hard_max 收敛,None 时间降级)
  • S3:SpeakerAwareProcessor has_speaker=False 模式(无说话人逐段校对)
  • 文档:方案修订、S3 收工交接(HANDOFF 中 S4/S6 标注为待办,即 章节梗概 (7/8):T8 plain 源结构化逐段校对全链 + T9/T10 验收文档 #22 的内容)与 review 日志建档

注 1:S5(渲染器无 speaker 防御)已随 review 修复提前进入 #20。
注 2:本 PR 中 structured_calibration_for_plain 开关与 has_speaker 参数尚无生产消费方,由 #22 接线(与 #20 配置先行的分层一致)。

配置/Schema 触点

消费 #20 的配置字段,无新增配置。

验证证据

  • 每阶段 TDD,uv run pytest tests/unit 全绿(本地边界点已预验证)
  • review 循环记录见 docs/sessions/260719-1155-t8-implementation/REVIEW-LOG.md

Stacked 结构

  1. 章节梗概 (1/9):统一 segments 带时间片段读取适配器(T3) #15 T3 / 2. 章节梗概 (2/9):字幕时间戳保留(T2)+ ChaptersProcessor 核心(T5) #16 T2+T5 / 3. 章节梗概 (3/9):ChaptersProcessor gate 安全加固 r1–r14 #17 r1–r14 / 4. 章节梗概 (4/9):ChaptersProcessor gate 安全加固 r15–r28 #18 r15–r28 / 5. 章节梗概 (5/9):缓存层 timeline 落盘与读回 #19 缓存层 / 6. 章节梗概 (6/9):协调器/llm_ops 接线 + 前端章节区块 + T8 前段 #20 接线 / 7. 本 PR(stack-06 ← stack-07)S1+S3 / 8. S4+S6+T9+T10 / 9. T11

- has_speaker=None 自动判定(任一原始段可解析出 speaker 即为 True),
  False 走无说话人逐段校对:SpeakerInferencer.infer 整步跳过(零 LLM
  调用),key_info 在校准开启时仍提取
- 不做同说话人合并;speaker/speaker_id 与 None 时间保留缺省,不落
  "unknown"/"00:00:00"
- 落盘前确定性段落化(消费 normalize 前 float 秒时间快照),
  llm_processed dialogs/章节输入/渲染锚点共用同一列表
- prompts 新增无说话人变体(行格式去 [speaker]),DialogSegmenter
  支持按模式覆盖 preferred/max 块长
- 新增 19 个单测锁定契约
上个实施 session 因 provider 2MB 消息上限报废(compaction 亦超限失败),
状态以本 HANDOFF + git 历史为准恢复。
@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Reviewed commit: 03dadb5b710829bbfcc1e55e4bed7b7dcfe4dc2f

🤖 Review 门禁 — 判定: fail(主审: claude-glm)

PR 宣称完成 S1/S3/S4/S6,但 diff 实际只交付 S1(paragraphize)、S3(SpeakerAwareProcessor has_speaker=False 模式)、prompt/segmenter 配套与单测,以及 HANDOFF/REVIEW-LOG 文档。S4(llm_ops 接线:开关门控 + plain_structured provenance 打标 + structured 落盘 gate 扩展)与 S6(plain 结构化校对全链路集成测试)均未实现——这已被本 PR 自带的 HANDOFF.md 明确列为「待办」。经实际读取 src/.../api/services/llm_ops.py 确认:_prepare_llm_content 仍只在 use_speaker_recognition=True 时返回 list,plain 源(use_speaker_recognition=False)返回纯文本字符串,协调器将其路由给 PlainTextProcessor 而非 SpeakerAwareProcessor;config 里的 structured_calibration_for_plain 开关无任何消费方;process() 新增的 has_speaker 参数无任何上游调用方显式传入。因此 has_speaker=False 整条路径在生产环境中不可达,是 dead code——意图与交付不符,构成 blocker。代码层面:上轮两个被撤回的 major(段落化边界裁剪 r > brk、force_break 收敛性)经逐路径独立复核确认算法正确,不再重报;三个 minor(duration 透传未收敛、echo 拒绝正则略宽、快照条数不一致回退)仍然有效,照旧保留。

覆盖:single;1 个审查单元;1928 行 diff。

  • blocker · correctness · scope-missing-s4-s6 docs/sessions/260719-1155-t8-implementation/HANDOFF.md:31 — 意图与交付严重不符:plain 源结构化校对功能虽实现了底层 processor(S3)与段落化工具(S1),但从未被任何生产调用路径触发——开关 structured_calibration_for_plain 没有读取方,has_speaker 参数没有上游传递方,has_speaker=False 整条分支(跳过说话人推断、不合并、确定性段落化)是不可达的 dead code。合入后会误导后续 stacked PR(测试基础设施:把真实打外网的测试移出默认发现范围,修复 contextvar 跨测试污染 #8 T9+T10)的依赖判断,且 PR 描述与仓库内 HANDOFF 自相矛盾。
    • 触发:PR intent body 将 S4(llm_ops 接线:开关门控 + plain_structured provenance 打标)与 S6(plain 结构化校对全链路集成测试,开关开/关双链路)列为本 PR 交付项;PR 标题亦含「与接线」。
    • 证据:HANDOFF.md 第 31-45 行「待办(按序)」明确把 S4(api/services/llm_ops.py,当前未接线,grep 无 plain_structured/has_speaker 痕迹)与 S6(集成测试)列为未完成。经实际读取 llm_ops.py::_prepare_llm_content(第 769-792 行)确认:该函数仅在 use_speaker_recognition=True 且有 transcription_data 时返回 list,plain 源返回 transcript 字符串;structured 落盘 gate 仍只看 use_speaker_recognition;全文无 plain_structured/has_speaker 接线。config.py 第 113 行的 structured_calibration_for_plain 开关在整个 src/ 下无任何消费方。diff 中亦无 llm_ops.py 改动行、无集成测试文件。
    • 验收:二选一:(A) 在 llm_ops.py 中实现 plain 源结构化校对接线——_prepare_llm_content 按 calibrate_requested 且 plain segments 条件返回 list、structured 落盘 gate 扩展到 plain_structured 模式、plain 产物写 provenance mode=plain_structured,并补充开关开/关双链路集成测试(plain 源→llm_processed.json→渲染锚点→章节匹配);或 (B) 更新 PR intent/HANDOFF 明确标注 S4/S6 移出本 PR 范围。验收 grep:开关关时既有 plain 路径行为回归测试通过;开关开时 grep -r 'plain_structured' src/video_transcript_api/api/services/llm_ops.py 能命中真实接线代码。
  • minor · correctness · correctness-coerce-duration-none src/video_transcript_api/llm/processors/speaker_aware_processor.py:331 — 类型收敛点分散,duration 在多条中间路径上以未收敛形态流转;当前下游均有兜底故无实际 bug,但若未来新增消费方可能踩到非数值 duration。
    • 触发:_coerce_dialogs 中 duration 字段直接透传 dialog.get('duration'),无类型收敛(可能是 int/float/str/None)。
    • 证据:第 331 行 'duration': dialog.get('duration') 原样透传。下游 paragraphize._coerce_number 与 _normalize_dialog 的 float() 兜底均能处理脏形态,最终段落 duration 经 _build_paragraph 的 _coerce_number 求和收敛,不构成实际数据错误。
    • 验收:可选:在 _coerce_dialogs 对 duration 做显式数值收敛(float 或 None),或在注释中固化「所有下游消费方必须自行兜底」的契约。不阻塞合入。
  • minor · testing · testing-echo-rejection-over-broad src/video_transcript_api/llm/processors/speaker_aware_processor.py:891 — echo 拒绝正则对两种模式共用,理论上有说话人模式下可能误拒以 [数字]: 开头的合法文本。
    • 触发:_valid_correction_text 的 echo 拒绝正则 ^[\d+]([|:) 同时覆盖有/无说话人两种行格式回吐。
    • 证据:正则匹配任何以 [数字]: 或 [数字][ 开头的校对文本并拒绝。无说话人模式下这是 prompt 行格式,拒绝合理;但有说话人模式下,合法校对文本若恰好以 [数字]: 开头(如引用标注 [1]: ...)也会被误拒。因 ^ 锚定且要求括号内为纯数字,实际触发概率极低。
    • 验收:可选:将 echo 拒绝按 has_speaker 选择不同正则(无说话人模式才拒绝 [数字]: 前缀)。不阻塞合入(实际触发概率极低,且与既有 [数字][ 拒绝行为同源)。
  • minor · reliability · reliability-snapshot-length-mismatch src/video_transcript_api/llm/processors/speaker_aware_processor.py:998 — 超长单条 segment(>4000 字符)被拆分时,时间快照对齐失效,回退路径使用截断后的 HH:MM:SS 且所有子片段共享同一时间,停顿授权精度降级。罕见但可达。
    • 触发:_paragraphize_no_speaker_dialogs 中 use_snapshot 判定 len(time_snapshot)==len(calibrated_dialogs) 不匹配时回退解析 HH:MM:SS。
    • 证据:第 998-1000 行:当 DialogSegmenter._split_long_dialog(segmenter.py 第 108-121 行,单条 text 超过 max_chunk_length=4000 时按句末标点拆分)把一条 normalized dialog 拆成多条 sub_dialog 时,calibrated_dialogs 条数 > time_snapshot 条数,use_snapshot=False,回退用每条自身的 HH:MM:SS。拆分出的 sub_dialog 共享父 dialog 的同一时间(dialog.copy()),回退路径下它们的时间完全相同,停顿授权 gap=0 永不触发,且段落时间精度退化为父段跨度。注释已说明「停顿授权精度降级但不中断」。
    • 验收:可选:确认生产 plain 源 segment 是否可能出现 >4000 字符单条;若可能,考虑在拆分时为子片段分配按文本比例插值的时间,或在段落化入口前合并回原条数。不阻塞合入(plain ASR/字幕 segment 通常远小于 4000 字符)。

@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

📒 Review ledger state

  • Commit: 03dadb5b710829bbfcc1e55e4bed7b7dcfe4dc2f
  • Round: 2
  • Status / findings: fail / 4
  • Comparison: same_head_rerun; stable/missing/appeared = 4/2/0

完整数据保存在 codex-review-ledger artifact;此 sticky comment 仅保存跨 rerun 的连续游标。

@zj1123581321

Copy link
Copy Markdown
Collaborator Author

关闭并并入新 PR:review 指出 S3 的 has_speaker=False 路径在本 PR 头部不可达(dead code),论点成立。S4/S6 接线(原 #22 范围,仅 ~1400 行)并入后整段约 3300 行仍在上限内,功能完整交付。新 PR 马上开。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant