postgres_db_schema_samples.md 27.5 KB

PostgreSQL DB Schema Samples / 落库样例与 live 测试链路

更新:2026-06-04
目标:给后续开发一个可直接照着做的 PostgreSQL 落库样例,同时保留一次真实 pgvector live 测试的证据。


一页结论

这次已经在用户提供的 PostgreSQL 上完成了下面几件事:

  1. 真实连接 PostgreSQL 成功

    • DSN:postgres://d2:***@127.0.0.1:5432/d2
    • PostgreSQL:17.5
    • 已确认扩展 vector 存在
  2. 真实应用 schema v2 成功

    • 使用隔离 schema:acr_test
    • DDL 来源:acr-engine/sql/acr_pg_schema_v2.sql
    • 已成功创建主数据、registry、embedding、candidate、decision 等表
  3. 真实插入了一套完整的样例数据链

    • canonical_song -> work -> recording -> recording_asset -> audio_window
    • model_registry -> feature_set_registry -> audio_embedding -> retrieval_index_registry
    • reference_set_registry -> reference_set_member
  4. 真实跑通了一轮 PostgreSQL + pgvector 检索评测

    • 输入:acr-engine/data/pgvector_eval/music20/*.jsonl
    • 输出:acr-engine/data/pgvector_eval/music20/live_pgvector_report.json
    • live pgvector 指标和现有 FAISS stand-in 指标一致
      • overall top1=0.9091
      • overall top3=0.9545
      • query_type=1: top1=1.0
      • query_type=7: top1=0.0, top3=0.5
  5. lineage trigger 已被验证有效

    • 脚本主动构造了三类错误 lineage:
      • recording
      • audio_window
      • audio_embedding
    • PostgreSQL 都正确拒绝插入

本次使用的 live 测试资产

数据库

项目
Host 127.0.0.1
Port 5432
DB d2
User d2
PostgreSQL 17.5
扩展 vector, pg_trgm, ltree, hstore
本次测试 schema acr_test

代码与产物

类型 路径
推荐 DDL acr-engine/sql/acr_pg_schema_v2.sql
live 测试脚本 acr-engine/scripts/live_pgvector_music20_eval.py
registry bootstrap 脚本 acr-engine/scripts/bootstrap_phase1_model_registry_live.py
live 报告 acr-engine/data/pgvector_eval/music20/live_pgvector_report.json
FAISS 对照报告 acr-engine/data/pgvector_eval/music20/songid_eval_report_fresh.json
registry bootstrap 报告 acr-engine/data/pgvector_eval/music20/phase1_registry_bootstrap_report.json
registry bootstrap 幂等性报告 acr-engine/data/pgvector_eval/music20/phase1_registry_bootstrap_idempotency_report.json
extraction job bootstrap 报告 acr-engine/data/pgvector_eval/music20/phase1_extraction_jobs_report.json
extraction plan 报告 acr-engine/data/pgvector_eval/music20/phase1_extraction_plan_report.json
reference member bootstrap 报告 acr-engine/data/pgvector_eval/music20/phase1_reference_member_bootstrap_report.json
chromaprint worker dry-run 报告 acr-engine/data/pgvector_eval/music20/phase1_worker_chromaprint_dry_run.json
embedding worker dry-run 报告 acr-engine/data/pgvector_eval/music20/phase1_worker_embedding_dry_run.json
job status 手工回写报告 acr-engine/data/pgvector_eval/music20/phase1_worker_mark_pending_report.json
double-claim guard 报告 acr-engine/data/pgvector_eval/music20/phase1_worker_double_claim_guard_report.json
历史对照报告 acr-engine/data/pgvector_eval/music20/songid_eval_report.json

这次实际落进去的数据链

flowchart LR
    A[reference_embeddings.jsonl] --> B[canonical_song]
    B --> C[work]
    C --> D[recording]
    D --> E[recording_asset]
    E --> F[audio_window]
    F --> G[audio_embedding]
    G --> H[audio_embedding_vector_192]

    I[model_registry] --> J[feature_set_registry]
    J --> G

    K[reference_set_registry] --> L[reference_set_member]
    D --> L

    M[query_embeddings.jsonl] --> N[SQL pgvector search]
    H --> N
    N --> O[retrieval_candidate]
    O --> P[match_decision]

为什么这次 live 测试要把 24 维 embedding pad 到 192 维

当前 schema v2 里提供了:

  • audio_embedding_vector_192
  • audio_embedding_vector_768

而这次本地 music20 样例 embedding 是 24 维 chroma 特征

所以本次 live 测试采用的策略是:

  • 逻辑维度24
  • 物理落盘维度192
  • 做法:后面补 0,写入 vector(192)

这样做的原因:

  • 不需要临时改 schema
  • 仍然可以验证 schema v2 + pgvector + retrieval 链路
  • 对这批样例的余弦相似度排序不会产生方向性错误(所有向量都以同样方式补零)

这只是验证链路用法。

生产里应按真实 encoder 维度选择:

  • MERT / MuQ 之类高维 embedding:直接落合适物理表
  • 如果后续维度更多,建议继续扩成 audio_embedding_vector_<dim> 分桶策略

本次实际落盘样例

以下内容来自 acr_test schema 的真实查询结果。

1. canonical_song

{"canonical_song_id":1,"biz_song_code":"100","title":"Song 100","primary_artist":"Artist 100","rights_status":"protected"}
{"canonical_song_id":2,"biz_song_code":"101","title":"Song 101","primary_artist":"Artist 101","rights_status":"protected"}

2. work

{"work_id":1,"canonical_song_id":1,"work_code":"work-100","work_title":"Song 100","composer":"Composer 100"}
{"work_id":2,"canonical_song_id":2,"work_code":"work-101","work_title":"Song 101","composer":"Composer 101"}

3. recording

{"recording_id":1,"work_id":1,"canonical_song_id":1,"recording_code":"rec-100","version_type":"master_reference","is_reference":true,"reference_priority":100}
{"recording_id":2,"work_id":2,"canonical_song_id":2,"recording_code":"rec-101","version_type":"master_reference","is_reference":true,"reference_priority":101}

4. recording_asset

{"asset_id":1,"recording_id":1,"asset_role":"reference_audio","storage_uri":"/workspace/downloads/100/type_11/93dfdeb0-7da5-42a8-9c71-cf12af57dd191650256918.wav","storage_scheme":"file","duration_sec":8.0,"ingest_status":"ready"}
{"asset_id":2,"recording_id":2,"asset_role":"reference_audio","storage_uri":"/workspace/downloads/101/type_11/83c0c07f-4f96-4ff4-998c-58db910f3cfa1650256915.wav","storage_scheme":"file","duration_sec":8.0,"ingest_status":"ready"}

5. audio_window

{"window_id":1,"asset_id":1,"recording_id":1,"work_id":1,"canonical_song_id":1,"window_index":0,"start_sec":0.0,"end_sec":8.0,"segment_role":"reference","segment_type":"full_clip"}
{"window_id":2,"asset_id":2,"recording_id":2,"work_id":2,"canonical_song_id":2,"window_index":0,"start_sec":0.0,"end_sec":8.0,"segment_role":"reference","segment_type":"full_clip"}

6. model_registry / feature_set_registry

{"model_id":1,"model_name":"local_chroma24","model_family":"chroma_baseline","model_version":"v1","output_embedding_dim":24,"default_window_sec":8.0}
{"feature_set_id":1,"model_id":1,"feature_name":"chroma24_songid_eval","embedding_dim":24,"distance_metric":"cosine","feature_schema_version":"v1"}

7. audio_embedding

{"embedding_id":1,"feature_set_id":1,"asset_id":1,"window_id":1,"recording_id":1,"canonical_song_id":1,"embedding_storage_mode":"pgvector_inline_192_padded","is_indexed":true}
{"embedding_id":2,"feature_set_id":1,"asset_id":2,"window_id":2,"recording_id":2,"canonical_song_id":2,"embedding_storage_mode":"pgvector_inline_192_padded","is_indexed":true}

8. reference_set_registry / retrieval_index_registry

{"reference_set_id":1,"set_name":"music20_live_reference","encoder_scope":"local_chroma24","status":"active"}
{"retrieval_index_id":1,"feature_set_id":1,"index_name":"music20_live_pgvector_hnsw","index_backend":"pgvector","index_type":"hnsw_cosine","row_count":20,"index_status":"active"}

9. retrieval_candidate / match_decision

{"retrieval_candidate_id":1,"query_id":"music20-q0000-t1-song100","source_lane":"semantic","candidate_level":"canonical_song","candidate_id":1,"raw_score":0.99998549,"normalized_score":0.90998694,"rank_no":1}
{"retrieval_candidate_id":2,"query_id":"music20-q0000-t1-song100","source_lane":"semantic","candidate_level":"canonical_song","candidate_id":17,"raw_score":0.9527432,"normalized_score":0.86746888,"rank_no":2}
{"match_decision_id":1,"query_id":"music20-q0000-t1-song100","canonical_song_id":1,"decision_status":"matched","decision_score":0.90998694}

本次 live 测试的表规模

行数
canonical_song 20
work 20
recording 20
recording_asset 20
audio_window 20
audio_embedding 20
retrieval_candidate 220
match_decision 22

说明:

  • 20 条 reference song
  • 22 条 query
  • 每条 query 写入 top10 candidate,因此 22 * 10 = 220

本次测试链路与逻辑

A. schema / 数据完整性测试

  1. 连接 PostgreSQL
  2. 创建隔离 schema:acr_test
  3. 执行 acr_pg_schema_v2.sql
  4. 初始化:
    • model_registry
    • feature_set_registry
    • reference_set_registry
    • retrieval_index_registry
  5. 导入 20 条 reference 样例
  6. 验证表计数是否正确
  7. 主动插入三类错误 lineage:
    • recording.canonical_song_idwork.canonical_song_id 不一致
    • audio_window.recording_idrecording_asset.recording_id 不一致
    • audio_embeddingcanonical_song_id 与父 audio_window 不一致
  8. 预期 PostgreSQL trigger 拒绝这些坏写入

B. live 检索评测测试

  1. reference_embeddings.jsonl 读 20 条 reference embedding
  2. 写入 audio_embedding + audio_embedding_vector_192
  3. query_embeddings.jsonl 读 22 条 query embedding
  4. 每条 query 用 SQL 执行 pgvector cosine 检索
  5. 在应用层做 song-level aggregation:
    • max_sim
    • top3_avg
    • vote
    • combined = 0.6 * max_sim + 0.3 * top3_avg + 0.1 * vote_factor
  6. 将 top10 候选落表到 retrieval_candidate
  7. 将 top1 决策落表到 match_decision
  8. 计算:
    • overall top1/top3/top10/mrr
    • by_query_type
    • confusion_focus

C. confusion test 口径

当前这次 live 样例里只实际包含:

  • type_1

Phase-1 worker dry-run 测试链路(新增)

这一步解决的是:

planner 虽然已经能输出可复制命令,但之前仓库里没有真正的 worker 可以消费这些命令。

现在已经补上最小真实 worker:

  • acr-engine/workers/mark_job_status.py
  • acr-engine/workers/run_chromaprint_job.py
  • acr-engine/workers/run_embedding_job.py

测试目标

验证下面这条链是真实可走通的:

flowchart TD
    A[feature_extraction_job pending] --> B[planner 生成命令模板]
    B --> C[worker 读取 extraction_job_id]
    C --> D[worker 解析 feature/model/scope]
    D --> E[worker 回写 running/completed]
    E --> F[bootstrap 脚本可再次恢复 pending]

当前验证口径

这轮先不跑真实模型推理,而是先验证工业执行面:

  1. run_chromaprint_job.py

    • 真实连接 PostgreSQL
    • 读取 feature_extraction_job=1
    • 解析 reference_set:phase1_hot_reference_v1
    • 回写 running -> completed
  2. run_embedding_job.py

    • 真实连接 PostgreSQL
    • 读取 feature_extraction_job=2
    • 解析 mert v1-95m
    • 回写 running -> completed
  3. 再次执行 bootstrap_phase1_extraction_jobs_live.py

    • 把 job 状态恢复为 pending
    • 保证后续 session 可以从同一批 jobs 继续推进
  4. plan_phase1_extraction_jobs_live.py

    • 当前生成的主命令模板已显式带:
      • cd /workspace/acr-engine &&
      • PG_DSN="${PG_DSN:?set PG_DSN}"
      • --complete-dry-run
    • 因此 primary_command 已经可以直接复现当前 dry-run 状态流转

为什么先做 dry-run

因为当前第一优先级是把下面这些东西固定住:

  • job contract
  • status transitions
  • scope 解析
  • planner -> worker 命令兼容性

等这个骨架稳定后,再把真实的:

  • chromaprint 提取
  • MERT / MuQ embedding 提取

接进去,整体风险更低。

当前 live 结果的关键更新

本轮已经新增:

  • acr-engine/scripts/bootstrap_phase1_reference_members_live.py

并已把 acr_test.phase1_hot_reference_v1 真实挂上 20 条 reference recordings,因此当前 worker dry-run 看到的 scope 已变成:

  • recording_count=20
  • ready_asset_count=20
  • active_window_count=20

这说明当前验证已经从“空 scope 状态机演示”推进到:

  • planner -> worker 命令兼容
  • worker -> PostgreSQL 状态流转可用
  • reference_set -> recording/asset/window scope 解析可用

仍然要注意:

  • 这依然是 dry-run
  • 不是真实特征抽取吞吐验证

当前并发/重试保护验证

本轮还额外做了一个故意的重复执行测试:

  1. 先让 feature_extraction_job=1pending -> running -> completed
  2. 不做 reset,直接再次执行同一个 chromaprint dry-run worker
  3. 预期第二次执行失败,因为 worker 认领 job 时要求:
    • expected_status = pending

实际结果见:

  • phase1_worker_double_claim_guard_report.json

关键证据:

  • double_claim_exit_code = 1
  • stderr = failed to update feature_extraction_job=1 with expected_status=pending

这证明当前最小 worker contract 已经具备:

  • 基础 claim guard
  • 基础重复执行保护

exact lane 非 dry-run 写入尝试(新增)

这轮又继续向前推进了一步:

run_chromaprint_job.py 已经不再只是 dry-run。

当前行为:

  1. 如果 reference asset 对应音频文件可读:

    • 提取 repo-local chromaprint-style hash
    • 写 artifact JSON
    • audio_fingerprint
    • job 标记为 completed
  2. 如果 reference asset 对应音频文件不可读:

    • job 标记为 failed
    • metadata_json 里写入:
      • failure_reason
      • missing_asset_count
      • missing_asset_samples

本轮 live 结果

报告:

  • acr-engine/data/pgvector_eval/music20/phase1_worker_chromaprint_write_attempt.json
  • acr-engine/data/pgvector_eval/music20/phase1_worker_chromaprint_write_guard_report.json

关键结果:

  • scope_asset_count = 20
  • processed_assets = 0
  • missing_assets = 20
  • job_status = failed
  • failure_reason = unreadable_audio_assets
  • audio_fingerprint_count = 0

这说明什么

说明当前 exact lane 的 PostgreSQL worker contract 已经具备:

  • 非 dry-run 的真实写入路径
  • 明确的失败落盘
  • 环境缺失时的可审计错误证据
  • “全量成功 / 否则失败”的批次语义
  • audio_fingerprint(feature_set_id, asset_id) 的原子 upsert 约束基础

但当前容器仍然缺:

  • /workspace/downloads/... 实际音频文件

因此这轮证明的是:

  • worker 写入路径已经接上
  • 当前被环境数据挂载阻塞

而不是 exact lane 逻辑本身还没落地。

  • type_7

因此:

  • type_7 可以作为 当前 live confusion check
  • type_8 / type_16 这次 live JSONL 没覆盖到,只能结合历史业务样本结果一起看

live pgvector 结果

1. overall

指标
query 数 22
top1 0.9091
top3 0.9545
top10 0.9545
MRR 0.9343
mean rank 1.8182

2. by query type

query_type count top1 top3 top10 解释
1 20 1.0 1.0 1.0 clean / near-clean
7 2 0.0 0.5 0.5 当前 live confusion 样例
8 0 N/A N/A N/A 本次 live JSONL 未覆盖
16 0 N/A N/A N/A 本次 live JSONL 未覆盖

3. 和现有 FAISS stand-in 的一致性

路径 overall top1 overall top3 type_1 top1 type_7 top1 type_7 top3
live PostgreSQL + pgvector 0.9091 0.9545 1.0 0.0 0.5
FAISS stand-in 0.9091 0.9545 1.0 0.0 0.5

结论:

当前 acr_test 上的 live pgvector 路径,已经和现有 stand-in 检索逻辑对齐。
问题不在“PostgreSQL 落盘导致召回变坏”,而在当前样例 embedding 对混淆类 query 本身就不够强。


本轮补充:完整 lineage trigger 负例覆盖

本轮重新执行 live 脚本后,live_pgvector_report.json 中的 lineage_negative_test 已从“单条 audio_window 验证”升级为“三类坏写入全部验证”:

case 结果 PostgreSQL 返回
recording_lineage_mismatch 拒绝成功 recording.canonical_song_id ... mismatches work.canonical_song_id ...
audio_window_lineage_mismatch 拒绝成功 Invalid asset_id=... or recording_id=... for audio_window
audio_embedding_lineage_mismatch 拒绝成功 audio_embedding lineage mismatch

这意味着:

当前 schema v2 的三条核心 lineage trigger,已经都有真实负例证据,而不只是“理论上存在”。

同时,本轮还补了两条机械验证证据:

  • py_compile 通过:live_pgvector_music20_eval.py
  • git diff --check 通过:本轮脚本、报告、文档变更无格式问题

混淆测试补充视图

1. 当前 live 样例视图

query_type 数据来源 top1 top3 结论
7 live_pgvector_report.json 0.0 0.5 已明显偏弱

2. 历史本地 20-song 小样本视图

来自:acr-engine/data/local_eval/music20_summary.json

query_type top1 top3
1 1.0 1.0
7 0.45 0.65
8 0.4667 0.7333
16 0.4167 0.4167

说明:

  • 这是本地小样本 chroma/FAISS sanity flow 的结果
  • 它比当前 live JSONL 的 type_7 好,是因为样本构成不同
  • 不能把这个结果直接当作生产效果,但可以当作“当前特征在小样本内并非完全不可用”的旁证

3. 历史业务语料 voice correctness 视图

query_type 文件 top1 top3 结论
7 voice_workspace20_type7_eval.json 0.0 0.05 极弱
8 voice_workspace20_type8_eval.json 0.0 0.0 极弱
16 voice_workspace20_type16_eval.json 0.0 0.0 极弱

结论:

只要 query 进入更真实、更混淆的业务样本,当前这条 baseline 仍然远远不够。
PostgreSQL 落库没问题,真正的问题还是 embedding lane 对 hard case 的判别力不足


这次验证了什么,没验证什么

已验证

  • PostgreSQL 真实连通可用
  • vector 扩展可用
  • schema v2 可以真实 apply
  • main lineage trigger 可以真实拦截坏数据
  • 样例数据链可以按 song -> work -> recording -> asset -> window -> embedding 落盘
  • live pgvector 检索和现有 stand-in 逻辑一致
  • retrieval_candidate / match_decision 可以真实承载在线结果
  • semantic worker 已真实验证 preflight failure 语义:既能识别 /workspace/downloads 缺失,也能识别 torch/torchaudio/transformers 缺失
  • audio_embedding 已补上 window / asset 双路幂等唯一键,为后续 encoder 真实 upsert 预留稳定主键

未验证

  • 还没把 MERT / MuQ 真正接进这套 live 路径
  • 这次 live 样例没有覆盖 type_8 / type_16 的 JSONL embedding
  • 这次只验证了 20-song 级别,不代表 30w song 的索引性能
  • 还没做多 recording / 多 version / cover lane 的聚合测试

推荐的下一步

本轮新增:Phase-1 registry 已可 live bootstrap

除了 live 检索脚本外,本轮还新增了:

  • acr-engine/scripts/bootstrap_phase1_model_registry_live.py

它已经在 acr_test schema 上真实写入了:

  • chromaprint
  • mert
  • muq
  • ecapa
  • 对应 feature sets
  • phase1_hot_reference_v1

对应 live 报告:

  • acr-engine/data/pgvector_eval/music20/phase1_registry_bootstrap_report.json

本轮继续新增:Phase-1 extraction jobs 已可 live bootstrap

在 registry bootstrap 之后,本轮又新增:

  • acr-engine/scripts/bootstrap_phase1_extraction_jobs_live.py

它已经在 acr_test schema 上真实创建了 5 条 feature_extraction_job

  • chromaprint
  • mert 5s/2.5s
  • mert 10s/5s
  • muq 5s/2.5s
  • ecapa 5s/2.5s

对应 live 报告:

  • acr-engine/data/pgvector_eval/music20/phase1_extraction_jobs_report.json

本轮继续新增:pending jobs 已可生成 live execution plan

在 extraction jobs 之后,本轮又新增:

  • acr-engine/scripts/plan_phase1_extraction_jobs_live.py

它已经在 acr_test schema 上真实读取 5 条 pending jobs,并生成按执行顺序排列的 plan:

  • chromaprint exact lane 优先
  • 然后是 mert / muq / ecapa 的 semantic lanes

对应 live 报告:

  • acr-engine/data/pgvector_eval/music20/phase1_extraction_plan_report.json

本轮补充后,plan 里还会真实给出:

  • command_suggestions
  • primary_command

也就是从 PostgreSQL 的 pending jobs 已经可以直接走到“可复制的执行命令模板”。

路线 1:继续做 PostgreSQL 工程化

  1. live_pgvector_music20_eval.py 泛化成:
    • 可导入任意 manifest/reference set
    • 可选择 encoder / feature set
    • 可直接生成 retrieval_candidate / match_decision 报告
  2. 增加:
    • audio_embedding_vector_1024 / 其他常见维度表
    • bulk COPY / batched insert
    • HNSW 参数管理

路线 2:继续做混淆类效果验证

  1. 构造真正覆盖 type_8 / type_16 的 query embedding JSONL
  2. 用同一条 live script 重跑 PostgreSQL 评测
  3. 对比:
    • Chromaprint only
    • semantic only
    • fusion
  4. 输出 confusion bucket 报告

当前环境补充说明:

  • 本轮继续尝试从 /workspace/downloads 直接补 type_8 / type_16 live 样本时,发现该目录在当前容器里不存在
  • 因此,下一轮若要继续这条支线,需要先恢复/挂载业务样本目录,或把对应 query 音频与 reference 清单重新落到仓库可见路径

路线 3:切到 Phase-1 encoder-only 主线

  1. 保留当前 PostgreSQL 结构不变
  2. local_chroma24 替换成:
    • MERT-v1-95M
    • MuQ
  3. 继续复用:
    • model_registry
    • feature_set_registry
    • reference_set_registry
    • retrieval_index_registry
  4. 重新测:
    • clean
    • type_7
    • type_8
    • type_16
    • 业务 voice bucket

复现命令

1. live PostgreSQL + pgvector 测试

cd /workspace/acr-engine
/usr/local/miniconda3/bin/python scripts/live_pgvector_music20_eval.py \
  --dsn 'postgres://d2:d2pass@127.0.0.1:5432/d2' \
  --schema acr_test \
  --reset-schema \
  --output data/pgvector_eval/music20/live_pgvector_report.json

2. FAISS stand-in 对照测试

cd /workspace/acr-engine
/usr/local/miniconda3/bin/python scripts/evaluate_songid_pgvector_path.py \
  --reference-embeddings-jsonl data/pgvector_eval/music20/reference_embeddings.jsonl \
  --query-embeddings-jsonl data/pgvector_eval/music20/query_embeddings.jsonl \
  --output data/pgvector_eval/music20/songid_eval_report_fresh.json

一句话结论

PostgreSQL 这条路已经可以真实落 schema、落样例、落 candidate、落 decision,也能真实跑 pgvector 检索。
当前最大的短板不再是“怎么存”,而是 当前 baseline embedding 对混淆 query 的召回仍然明显不够

新增:Phase-1 semantic worker live 证据

本轮继续对 run_embedding_job.py 做 live PostgreSQL 验证,目标不是伪造 embedding,而是把 失败语义先固定住

结果摘要

extraction_job_id=2mert v1-95m, 5s/2.5s)执行非 dry-run worker 后:

结果
scope_window_count 20
job_status failed
output_count 0
failure_reason preflight_failed
preflight_blockers ['unreadable_audio_assets', 'model_runtime_unavailable']
vector_table_report.resolved true
audio_embedding_vector_768_count 0

说明:

  • 当前语义 lane 不是“没做事”,而是已经真实走到了 PostgreSQL job scope / runtime / vector table / asset 路径检查
  • 只是当前容器同时被两个外部条件挡住:
    1. /workspace/downloads/... 未挂载
    2. torch / torchaudio / transformers 未安装

证据文件

  • acr-engine/data/pgvector_eval/music20/phase1_worker_embedding_write_attempt.json
  • acr-engine/data/pgvector_eval/music20/phase1_worker_embedding_write_guard_report.json
  • acr-engine/data/pgvector_eval/music20/phase1_worker_embedding_post_state.json

为什么要先补唯一键

当前 audio_embedding 已新增:

  • uq_audio_embedding_feature_window
  • uq_audio_embedding_feature_asset

设计意图是:

  1. 同一 feature_set_id + window_id 的 embedding 重跑时可以稳定 upsert
  2. 将来如果有 asset-level embedding,也能独立幂等
  3. 不把幂等职责留给应用层“先查再写”

这一步对后续的 MERT / MuQ / ECAPA 都通用。

新增:Semantic preflight blocker matrix

为了避免下次 session 继续手工逐个试,本轮又新增:

  • acr-engine/scripts/run_phase1_embedding_preflight_matrix_live.py
  • acr-engine/data/pgvector_eval/music20/phase1_embedding_preflight_matrix_report.json

它会:

  1. 先把 feature_extraction_job 重置回 pending
  2. 顺序执行全部 semantic jobs(当前是 mert 5smert 10smuq 5secapa 5s
  3. 归并输出每个 job 的:
    • failure_reason
    • preflight_blockers
    • runtime_missing_dependencies
    • vector_table_report

当前矩阵结果

job model vector table blockers runtime missing
2 mert v1-95m audio_embedding_vector_768 unreadable_audio_assets, model_runtime_unavailable torch, torchaudio, transformers
3 mert v1-95m audio_embedding_vector_768 unreadable_audio_assets, model_runtime_unavailable torch, torchaudio, transformers
4 muq large-msd-iter audio_embedding_vector_768 unreadable_audio_assets, model_runtime_unavailable torch, torchaudio, transformers
5 ecapa acr-baseline-v1 audio_embedding_vector_192 unreadable_audio_assets, model_runtime_unavailable torch, torchaudio, speechbrain

结论:

  • 当前 semantic lane 的失败已经具有稳定矩阵特征,不是某一个 job 独有的偶发问题
  • vector_table 路径已全部通过
  • 当前真正阻塞 Phase-1 encoder-only 落地的是:
    1. /workspace/downloads 音频挂载
    2. 模型 runtime 依赖安装

新增:asset-level embedding upsert live 验证

为了把 uq_audio_embedding_feature_asset 从“DDL 声明”推进到“真实证据”,本轮新增:

  • acr-engine/scripts/validate_audio_embedding_asset_upsert_live.py
  • acr-engine/data/pgvector_eval/music20/audio_embedding_asset_upsert_live_report.json

验证动作

脚本会在隔离 schema acr_asset_upsert_test 中:

  1. 落最小主数据图:song -> work -> recording -> asset
  2. 插入第一条 window_id IS NULL 的 asset-level embedding
  3. 再做一次普通重复 INSERT
  4. 预期被 uq_audio_embedding_feature_asset 拒绝
  5. 再做一次 ON CONFLICT ... DO UPDATE
  6. 验证最终仍只有 1audio_embedding1audio_embedding_vector_192

当前结果

结果
首次 embedding_id 1
重复普通 INSERT UniqueViolation
唯一键名 uq_audio_embedding_feature_asset
upsert 后 embedding_id 1
same_embedding_id_reused true
audio_embedding 行数 1
audio_embedding_vector_192 行数 1
最终 checksum checksum-v2

结论:

  • asset-level 唯一键不是“纸面存在”,而是已经在 live PostgreSQL 上真实生效
  • 后续如果补 asset-level semantic writer,可以直接沿用同一个 ON CONFLICT (feature_set_id, asset_id) ... 合同