Skip to content

音频管线:LLM 结论列 null 语义修复 + status 列落实行不丢弃约定 - #21

Merged
FANNG1 merged 3 commits into
mainfrom
audio-status-null-semantics
Jul 15, 2026
Merged

FANNG1 merged 3 commits into
mainfrom
audio-status-null-semantics

Conversation

@FANNG1

@FANNG1 FANNG1 commented Jul 15, 2026 •

Copy link
Copy Markdown
Owner

背景

端到端验证音频管线时发现三个关联问题:

  1. LLM 结论列用 fill_null 填缺省值:没配 DEEPSEEK_API_KEY 或调用失败时,downgrade_related 被填成 False、primary_reason 填 '其他'、confidence 填 0.0。合规场景无法区分"LLM 判定不涉及降档"和"根本没打标"——真实降档通话会被静默漏报。
  2. 音频管线没有 status 列,且直接丢行:下载失败、时长过滤的行被 where 直接丢掉,违背"manifest 每条对应输出一行、失败用 status 标记"的项目约定(图片管线已落实,音频没有)。
  3. 音频 LLM 打标自 Wire up Daft execution/S3 tuning and fix Ray parallelism collapse #18 起被破坏:Wire up Daft execution/S3 tuning and fix Ray parallelism collapse #18(8fd7f35)给 llm_prompt 新增了 concurrency / on_error 参数,而 Daft 0.7.15 会把这些 UDF 选项泄漏进 OpenAI 请求 kwargs,SDK 对未知参数直接抛 TypeError,每一行都失败(Wire up Daft execution/S3 tuning and fix Ray parallelism collapse #18 之前的调用不传这些参数,工作正常)。fill_null 把失败伪装成"判定为否",所以 Wire up Daft execution/S3 tuning and fix Ray parallelism collapse #18 合入后这个回归完全不可见;修复问题 1 之后它才第一次暴露(全部行变成 llm_failed)。图片 VLM 路径在 Add optional VLM image analysis #17 已为同一 Daft 问题写过适配器,音频路径缺失。

改动

null 语义(analyze.py)

  • 删除 8 个结论列的 fill_null,null 就是"没打标"。
  • 配了 key 但没拿到可解析结果的 ok 行标 llm_failed(区别于"没配 key 整批跳过",后者 status 保持 ok)。

行不丢弃 + status 列(analyze.py / udfs.py / ingest.py / schema.py)

  • 删除两处丢行的 where,改为 status 标记:ok / download_failed / decode_failed / duration_filtered / llm_failed。
  • duration_udf 对坏字节返回 null(decode_failed 的判定依据,0.0 会和合法空录音混淆);ASR/prompt/embedding 对非 ok 行走 null 传播,不花模型和 API 成本。
  • ingest 镜像图片侧两个机制:blob 下载失败的行保留并标 blob_download_failed;全 null 列显式 cast 回规范类型,防止"没配 key 的批次打标列全 null → JSONL 推断成 null 类型 → 毒化 Lance 表 schema"。
  • AUDIO_ASSET_SCHEMA 增加 status 字段,README 同步更新。

LLM 打标修复(新增 audio/llm.py)

  • 与 image/vlm.py 同一模式的 provider 适配层(按媒体隔离约定各自持有一份):实例化 prompter 前剔除泄漏的 UDF 选项;null prompt(非 ok 行)直接返回 null,不发请求。

验证

  • 全量 pytest:104 passed(新增 8 个测试:UDF null 语义 2 个、ingest 台账语义 2 个、LLM 适配层 2 个,及原有测试全绿)。
  • 真实管线端到端(本机 MinIO,2 条正常通话 + 1 个损坏文件):
    • 无 key:正常行结论列全 null、status=ok;损坏文件 decode_failed 全 null;JSONL 和 Lance staging 两种输出都验证。
    • 真实 DeepSeek key:两条通话正确打标 downgrade_related=true、primary_reason=价格敏感,call_006 判出 bad_tone=true;--where "downgrade_related = true AND bad_tone = true" 精确命中。
    • 含 null embedding 的表建索引(IVF_FLAT)和 ANN 查询正常,坏行被正确排除。
    • 全 null 打标批次先建表、正常批次再 append:schema 不冲突。

fanng added 3 commits July 15, 2026 15:53
… tagging

- Stop fill_null'ing LLM verdict columns: null now means "not judged",
  never disguised as "judged no". Compliance queries can distinguish
  unprocessed rows from negative verdicts.
- Preserve every manifest row through analyze/ingest with a status
  column (ok / download_failed / decode_failed / duration_filtered /
  llm_failed / blob_download_failed), mirroring the image pipeline.
- duration_udf returns null for undecodable bytes; ASR/prompt/embed
  UDFs skip non-ok rows via null propagation.
- Audio ingest keeps null-blob rows (blob_download_failed) and casts
  all-null JSONL columns to canonical dtypes so a keyless batch cannot
  poison the Lance schema.
- Add audio/llm.py provider adapter: Daft 0.7.15 leaks UDF options
  (on_error/concurrency) into OpenAI request kwargs, so every LLM call
  raised TypeError; previously invisible because fill_null masked the
  failures as negative verdicts.
@FANNG1
FANNG1 merged commit 62e2b1a into main Jul 15, 2026
1 check passed
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