Commit 89d9d72b 89d9d72b3e78ab27e1ef574538d69cba6b3aa598 by cnb.bofCdSsphPA

Keep ACR entity layers simple without collapsing recording assets

Constraint: Preserve the Phase-1 minimal schema story while clarifying when simplification is safe and when it creates future refactor risk.
Rejected: Merge recording and recording_asset in the formal schema now | Copyright-scale catalogs will quickly need multi-file and multi-source recording support.
Confidence: high
Scope-risk: narrow
Directive: Use 'song -> recording -> asset -> window -> feature' as the default communication shorthand, but keep recording and asset split in the persisted model.
Tested: git diff --check on touched docs; /usr/local/miniconda3/bin/python scripts/check_markdown_links.py --root docs returned OK for 31 markdown files
Not-tested: Rendered markdown preview in external viewers
1 parent 6d4f8c1c
......@@ -6,6 +6,8 @@
- 清理重复或过期文档:删除 `docs/acr-design.md``docs/open-dataset-plan.md``docs/external-manifest-template.md``docs/roadmap.md``docs/changelist-2026-06-02.md``docs/delivery-handoff-2026-06-02.md`
- 历史记录仍保留在 `docs/CHANGELOG.md`,当前有效入口以上述主链为准。
-`docs/postgresql-data-model.md``docs/acr-architecture.md` 补充“为什么层多、Phase-1 如何简化、`recording``asset` 是否能合并”的明确口径:推荐保留 `song -> recording -> asset -> window -> feature` 作为最小可用骨架,不建议在正式 schema 中合并 `recording``recording_asset`
## 2026-06-04
- 更新 `docs/README.md` 顶部为与 `session-handoff` 一致的“最短启动路径”,并再次用该入口命令重跑 `run_planner_validation_commands_live.py`,确认 fresh 结果仍为 `executed_count=4``all_passed=true`
......
......@@ -93,12 +93,18 @@ cd /workspace/acr-engine
- semantic baseline:`MERT-v1-95M`
- semantic challenger:`MuQ`
- `ECAPA` 保留为 historical baseline,不再作为长期主底座。
- PostgreSQL 主链固定为:
- 可演进完整版主链为:
```text
canonical_song -> work -> recording -> recording_asset -> audio_window
```
- 如果只看 Phase-1 最小骨架,可以先按下面理解:
```text
song -> recording -> asset -> window -> fingerprint / embedding
```
- 模型/特征主链固定为:
```text
......@@ -116,7 +122,17 @@ model_registry -> feature_set_registry -> audio_embedding / audio_fingerprint ->
---
## 5. 补充但不建议作为第一入口
## 5. 文档维护命令
```bash
/usr/local/miniconda3/bin/python scripts/check_markdown_links.py --root docs
```
用途:在清理或重组文档后,快速发现 `docs/` 下的相对链接断链。默认会跳过 `CHANGELOG.md` 这类历史归档文档。
---
## 6. 补充但不建议作为第一入口
以下文档保留用于专题补充,不建议新同学第一轮就读:
- [dataset-spec.md](./dataset-spec.md)
......
......@@ -53,6 +53,50 @@ flowchart TD
---
## 2.1 为什么现在会显得“层很多”
因为当前蓝图同时覆盖了 3 个维度:
1. **业务归属**`song/work/recording`
2. **音频实体**`asset/window`
3. **检索计算**`feature/index/candidate/decision`
把这三类问题放在一张总图中,会看起来像一条很长的链。
但在工程上,它们其实是不同职责:
- 业务归属层回答:**最后该归谁**
- 音频实体层回答:**命中的是哪段音频**
- 检索计算层回答:**这段音频是怎么被召回出来的**
---
## 2.2 当前最小可用架构可以收敛到什么程度
如果当前阶段只追求:
> 快速稳定地把 query 命中到正确 `song_id`
那 Phase-1 完全可以按下面这套最小骨架推进:
```text
song -> recording -> asset -> window -> fingerprint / embedding
```
保留原因:
- `recording` 不能删:同一首歌会有多个版本
- `window` 不能删:它是 offset/evidence/多段投票的最小单元
- `feature_set_registry` 不能删:否则未来换 MERT/MuQ 会把 schema 写死
可以延后:
- `work`
- 更重的 `retrieval_index_registry`
- 更细的全链路审计表
因此推荐口径不是“把所有层都砍掉”,而是:
> **Phase-1 先上最小可用层;未来版本归属/cover/work 治理再继续加层。**
---
## 3. 角色视图
## 3.1 产品 / 架构角色
......
# PostgreSQL 数据模型与 DDL 设计说明
> 更新:2026-06-04
> 关联 SQL:[`acr-engine/sql/acr_pg_schema_v2.sql`](../acr-engine/sql/acr_pg_schema_v2.sql)
> 更新:2026-06-04
> 关联 SQL:[`acr-engine/sql/acr_pg_schema_v2.sql`](../acr-engine/sql/acr_pg_schema_v2.sql)
> 目标:给出面向版权保护 / 大规模曲库 / 可替换 encoder 的 PostgreSQL 数据字典、DDL 设计意图、流程图与典型使用路径。
## 一页结论
......@@ -23,6 +23,188 @@ canonical_song -> work -> recording -> recording_asset -> audio_window
---
## 0.1 为什么会感觉链路很多
本质上当前文档把 **3 类问题** 放在同一个总图里,所以看起来链路很长:
1. **业务归属层**`canonical_song / work / recording`
- 解决“最终归哪个 song_id / work_id / recording_id”
2. **物理音频层**`recording_asset / audio_window`
- 解决“实际文件是什么、切成了哪些检索窗口”
3. **检索计算层**`model_registry / feature_set_registry / audio_embedding / audio_fingerprint / retrieval_index_registry`
- 解决“用了哪个模型、哪套特征、哪套索引”
所以这不是一条单链,而是:
- 一条 **归属回溯链**
- 一条 **音频资产链**
- 一条 **特征/索引链**
把三者混看,就会误以为每次查询都要手工经过所有表。实际上在线检索真正关心的是:
```text
window -> candidate -> recording -> song
```
---
## 0.2 当前是否可以简化
可以。
如果当前阶段的目标是:
> 先服务版权保护场景,让 query 能快速稳定地命中正确 `song_id`
那么 Phase-1 完全可以收敛为下面这套 **最小可用骨架**
```text
song -> recording -> recording_asset -> audio_window
-> audio_fingerprint
-> audio_embedding
```
为了支持模型替换,再保留一个轻量版本登记层:
```text
feature_set_registry
```
也就是说,Phase-1 最小主链可以压缩成:
```text
song -> recording -> asset -> window -> feature
```
其中 `feature` 可具体落成:
- `audio_fingerprint`
- `audio_embedding`
---
## 0.3 哪些层建议 Phase-1 保留,哪些层可以弱化
### 建议保留
| 层 | 是否保留 | 原因 |
|---|---|---|
| `song` | 保留 | 最终业务返回对象 |
| `recording` | 保留 | 同一 song 下会有多个版本/录音 |
| `recording_asset` | 保留 | 一个 recording 可能有多个真实文件 |
| `audio_window` | 保留 | 检索和 evidence 的最小计算单元 |
| `feature_set_registry` | 保留 | 避免把 embedding/fingerprint 固化成表列 |
| `audio_embedding` / `audio_fingerprint` | 保留 | 真正的检索特征事实表 |
### 可以弱化或延期
| 层 | 当前建议 | 原因 |
|---|---|---|
| `work` | 可延期 | 如果当前只需稳定返回 `song_id`,可先不显式拆作品层 |
| `canonical_song` | 可与 `song` 合并理解 | 当前重点不是权利层深治理,而是先完成可用归属主键 |
| `retrieval_index_registry` | 可先弱化 | Phase-1 可先把索引治理做轻,不必一开始做太重 |
| `match_decision` 全量审计 | 可逐步补齐 | 先保证召回闭环,再加强审计/解释性 |
---
## 0.3.1 `recording` 和 `recording_asset` 能不能合并
可以合并,但**只适合非常早期、非常受控的数据集**
### 什么时候可以临时合并
只有当下面条件基本都成立时,才可以把二者临时看成一个对象:
1. 每个 `song` 只有一个可用录音版本
2. 每个录音只有一个音频文件
3. 不区分 master / distribution / captured / query_sample
4. 不需要追踪同一录音的多个来源文件
5. 不需要后续补高码率、补母带、补平台版本
在这种情况下,可以暂时把模型理解成:
```text
song -> recording_asset -> audio_window
```
也就是让 `recording_asset` 同时承担“版本对象 + 物理文件对象”的职责。
### 为什么长期不建议合并
因为 `recording``recording_asset` 回答的是两个不同问题:
- `recording` 回答:**这是哪个录音版本**
- `recording_asset` 回答:**这个录音版本对应哪个具体文件**
一旦进入真实版权保护场景,下面几类情况会非常常见:
1. **同一录音有多个文件版本**
例如 wav/flac/mp3、不同码率、不同平台导出件。
2. **同一 song 有多个录音版本**
例如 official/live/remaster/short/bgm cut。
3. **同一录音要接多个来源**
例如平台抓取、业务导出、人工补档。
4. **query 命中的是 asset,但归属要落到 recording/song**
如果不拆层,后面聚合和去重会比较乱。
### 当前最推荐的判断
对于你现在这个目标:
-`100w` 音频
-`30w` 歌曲
- 面向版权保护 / 听歌识曲 / 版本归属
**不建议把 `recording` 和 `recording_asset` 合并进正式 schema。**
原因很直接:
- 数据量已经不小
- 后续大概率会遇到多版本、多来源、多文件问题
- 现在省掉一层,后面重构成本会更高
### 更务实的折中方案
如果你觉得当前实现心智负担太高,可以不在产品/算法讨论里反复强调 `recording_asset`,而是采用下面口径:
```text
song -> recording -> asset -> window -> feature
```
也就是说:
- **概念上保留** `recording``asset` 两层
- **沟通上简写**`recording -> asset`
- **实现上继续分表**,避免未来返工
这通常是 Phase-1 最稳妥的折中。
---
## 0.4 一个更容易理解的口径
建议把当前体系理解为下面两条核心链:
### 归属链
```text
window -> asset -> recording -> song
```
作用:
- 检索命中后,回溯最终归属到哪个 `song_id`
### 特征链
```text
window -> fingerprint / embedding -> candidate -> aggregate
```
作用:
- 真正完成召回、打分、聚合与排序
这样看时,整个设计就不再是“很多层没必要”,而是:
- **归属层负责回答是谁**
- **窗口层负责回答命中了哪一段**
- **特征层负责回答怎么检索出来**
---
## 1. 设计意图
## 1.1 这套设计想解决什么
......@@ -63,6 +245,15 @@ canonical_song -> work -> recording -> recording_asset -> audio_window
所以原型版 SQL 适合 demo,不适合你现在的 100w 音频目标。
### 当前最建议的简化口径
如果团队正在进入 Phase-1 实施,不必把所有表同时视为“首批必须上线的复杂系统”。
更推荐按下面顺序理解和落库:
1. `song -> recording -> recording_asset -> audio_window`
2. `feature_set_registry -> audio_fingerprint / audio_embedding`
3. `reference_set_registry` 与更重的索引治理随后补齐
---
## 2. 数据主链
......