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 @@ ...@@ -6,6 +6,8 @@
6 - 清理重复或过期文档:删除 `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` 6 - 清理重复或过期文档:删除 `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`
7 - 历史记录仍保留在 `docs/CHANGELOG.md`,当前有效入口以上述主链为准。 7 - 历史记录仍保留在 `docs/CHANGELOG.md`,当前有效入口以上述主链为准。
8 8
9 -`docs/postgresql-data-model.md``docs/acr-architecture.md` 补充“为什么层多、Phase-1 如何简化、`recording``asset` 是否能合并”的明确口径:推荐保留 `song -> recording -> asset -> window -> feature` 作为最小可用骨架,不建议在正式 schema 中合并 `recording``recording_asset`
10
9 ## 2026-06-04 11 ## 2026-06-04
10 12
11 - 更新 `docs/README.md` 顶部为与 `session-handoff` 一致的“最短启动路径”,并再次用该入口命令重跑 `run_planner_validation_commands_live.py`,确认 fresh 结果仍为 `executed_count=4``all_passed=true` 13 - 更新 `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 ...@@ -93,12 +93,18 @@ cd /workspace/acr-engine
93 - semantic baseline:`MERT-v1-95M` 93 - semantic baseline:`MERT-v1-95M`
94 - semantic challenger:`MuQ` 94 - semantic challenger:`MuQ`
95 - `ECAPA` 保留为 historical baseline,不再作为长期主底座。 95 - `ECAPA` 保留为 historical baseline,不再作为长期主底座。
96 - PostgreSQL 主链固定为: 96 - 可演进完整版主链为:
97 97
98 ```text 98 ```text
99 canonical_song -> work -> recording -> recording_asset -> audio_window 99 canonical_song -> work -> recording -> recording_asset -> audio_window
100 ``` 100 ```
101 101
102 - 如果只看 Phase-1 最小骨架,可以先按下面理解:
103
104 ```text
105 song -> recording -> asset -> window -> fingerprint / embedding
106 ```
107
102 - 模型/特征主链固定为: 108 - 模型/特征主链固定为:
103 109
104 ```text 110 ```text
...@@ -116,7 +122,17 @@ model_registry -> feature_set_registry -> audio_embedding / audio_fingerprint -> ...@@ -116,7 +122,17 @@ model_registry -> feature_set_registry -> audio_embedding / audio_fingerprint ->
116 122
117 --- 123 ---
118 124
119 ## 5. 补充但不建议作为第一入口 125 ## 5. 文档维护命令
126
127 ```bash
128 /usr/local/miniconda3/bin/python scripts/check_markdown_links.py --root docs
129 ```
130
131 用途:在清理或重组文档后,快速发现 `docs/` 下的相对链接断链。默认会跳过 `CHANGELOG.md` 这类历史归档文档。
132
133 ---
134
135 ## 6. 补充但不建议作为第一入口
120 136
121 以下文档保留用于专题补充,不建议新同学第一轮就读: 137 以下文档保留用于专题补充,不建议新同学第一轮就读:
122 - [dataset-spec.md](./dataset-spec.md) 138 - [dataset-spec.md](./dataset-spec.md)
......
...@@ -53,6 +53,50 @@ flowchart TD ...@@ -53,6 +53,50 @@ flowchart TD
53 53
54 --- 54 ---
55 55
56 ## 2.1 为什么现在会显得“层很多”
57
58 因为当前蓝图同时覆盖了 3 个维度:
59
60 1. **业务归属**`song/work/recording`
61 2. **音频实体**`asset/window`
62 3. **检索计算**`feature/index/candidate/decision`
63
64 把这三类问题放在一张总图中,会看起来像一条很长的链。
65 但在工程上,它们其实是不同职责:
66
67 - 业务归属层回答:**最后该归谁**
68 - 音频实体层回答:**命中的是哪段音频**
69 - 检索计算层回答:**这段音频是怎么被召回出来的**
70
71 ---
72
73 ## 2.2 当前最小可用架构可以收敛到什么程度
74
75 如果当前阶段只追求:
76
77 > 快速稳定地把 query 命中到正确 `song_id`
78
79 那 Phase-1 完全可以按下面这套最小骨架推进:
80
81 ```text
82 song -> recording -> asset -> window -> fingerprint / embedding
83 ```
84
85 保留原因:
86 - `recording` 不能删:同一首歌会有多个版本
87 - `window` 不能删:它是 offset/evidence/多段投票的最小单元
88 - `feature_set_registry` 不能删:否则未来换 MERT/MuQ 会把 schema 写死
89
90 可以延后:
91 - `work`
92 - 更重的 `retrieval_index_registry`
93 - 更细的全链路审计表
94
95 因此推荐口径不是“把所有层都砍掉”,而是:
96 > **Phase-1 先上最小可用层;未来版本归属/cover/work 治理再继续加层。**
97
98 ---
99
56 ## 3. 角色视图 100 ## 3. 角色视图
57 101
58 ## 3.1 产品 / 架构角色 102 ## 3.1 产品 / 架构角色
......
1 # PostgreSQL 数据模型与 DDL 设计说明 1 # PostgreSQL 数据模型与 DDL 设计说明
2 2
3 > 更新:2026-06-04 3 > 更新:2026-06-04
4 > 关联 SQL:[`acr-engine/sql/acr_pg_schema_v2.sql`](../acr-engine/sql/acr_pg_schema_v2.sql) 4 > 关联 SQL:[`acr-engine/sql/acr_pg_schema_v2.sql`](../acr-engine/sql/acr_pg_schema_v2.sql)
5 > 目标:给出面向版权保护 / 大规模曲库 / 可替换 encoder 的 PostgreSQL 数据字典、DDL 设计意图、流程图与典型使用路径。 5 > 目标:给出面向版权保护 / 大规模曲库 / 可替换 encoder 的 PostgreSQL 数据字典、DDL 设计意图、流程图与典型使用路径。
6 6
7 ## 一页结论 7 ## 一页结论
...@@ -23,6 +23,188 @@ canonical_song -> work -> recording -> recording_asset -> audio_window ...@@ -23,6 +23,188 @@ canonical_song -> work -> recording -> recording_asset -> audio_window
23 23
24 --- 24 ---
25 25
26 ## 0.1 为什么会感觉链路很多
27
28 本质上当前文档把 **3 类问题** 放在同一个总图里,所以看起来链路很长:
29
30 1. **业务归属层**`canonical_song / work / recording`
31 - 解决“最终归哪个 song_id / work_id / recording_id”
32 2. **物理音频层**`recording_asset / audio_window`
33 - 解决“实际文件是什么、切成了哪些检索窗口”
34 3. **检索计算层**`model_registry / feature_set_registry / audio_embedding / audio_fingerprint / retrieval_index_registry`
35 - 解决“用了哪个模型、哪套特征、哪套索引”
36
37 所以这不是一条单链,而是:
38 - 一条 **归属回溯链**
39 - 一条 **音频资产链**
40 - 一条 **特征/索引链**
41
42 把三者混看,就会误以为每次查询都要手工经过所有表。实际上在线检索真正关心的是:
43
44 ```text
45 window -> candidate -> recording -> song
46 ```
47
48 ---
49
50 ## 0.2 当前是否可以简化
51
52 可以。
53
54 如果当前阶段的目标是:
55
56 > 先服务版权保护场景,让 query 能快速稳定地命中正确 `song_id`
57
58 那么 Phase-1 完全可以收敛为下面这套 **最小可用骨架**
59
60 ```text
61 song -> recording -> recording_asset -> audio_window
62 -> audio_fingerprint
63 -> audio_embedding
64 ```
65
66 为了支持模型替换,再保留一个轻量版本登记层:
67
68 ```text
69 feature_set_registry
70 ```
71
72 也就是说,Phase-1 最小主链可以压缩成:
73
74 ```text
75 song -> recording -> asset -> window -> feature
76 ```
77
78 其中 `feature` 可具体落成:
79 - `audio_fingerprint`
80 - `audio_embedding`
81
82 ---
83
84 ## 0.3 哪些层建议 Phase-1 保留,哪些层可以弱化
85
86 ### 建议保留
87
88 | 层 | 是否保留 | 原因 |
89 |---|---|---|
90 | `song` | 保留 | 最终业务返回对象 |
91 | `recording` | 保留 | 同一 song 下会有多个版本/录音 |
92 | `recording_asset` | 保留 | 一个 recording 可能有多个真实文件 |
93 | `audio_window` | 保留 | 检索和 evidence 的最小计算单元 |
94 | `feature_set_registry` | 保留 | 避免把 embedding/fingerprint 固化成表列 |
95 | `audio_embedding` / `audio_fingerprint` | 保留 | 真正的检索特征事实表 |
96
97 ### 可以弱化或延期
98
99 | 层 | 当前建议 | 原因 |
100 |---|---|---|
101 | `work` | 可延期 | 如果当前只需稳定返回 `song_id`,可先不显式拆作品层 |
102 | `canonical_song` | 可与 `song` 合并理解 | 当前重点不是权利层深治理,而是先完成可用归属主键 |
103 | `retrieval_index_registry` | 可先弱化 | Phase-1 可先把索引治理做轻,不必一开始做太重 |
104 | `match_decision` 全量审计 | 可逐步补齐 | 先保证召回闭环,再加强审计/解释性 |
105
106 ---
107
108 ## 0.3.1 `recording` 和 `recording_asset` 能不能合并
109
110 可以合并,但**只适合非常早期、非常受控的数据集**
111
112 ### 什么时候可以临时合并
113
114 只有当下面条件基本都成立时,才可以把二者临时看成一个对象:
115
116 1. 每个 `song` 只有一个可用录音版本
117 2. 每个录音只有一个音频文件
118 3. 不区分 master / distribution / captured / query_sample
119 4. 不需要追踪同一录音的多个来源文件
120 5. 不需要后续补高码率、补母带、补平台版本
121
122 在这种情况下,可以暂时把模型理解成:
123
124 ```text
125 song -> recording_asset -> audio_window
126 ```
127
128 也就是让 `recording_asset` 同时承担“版本对象 + 物理文件对象”的职责。
129
130 ### 为什么长期不建议合并
131
132 因为 `recording``recording_asset` 回答的是两个不同问题:
133
134 - `recording` 回答:**这是哪个录音版本**
135 - `recording_asset` 回答:**这个录音版本对应哪个具体文件**
136
137 一旦进入真实版权保护场景,下面几类情况会非常常见:
138
139 1. **同一录音有多个文件版本**
140 例如 wav/flac/mp3、不同码率、不同平台导出件。
141 2. **同一 song 有多个录音版本**
142 例如 official/live/remaster/short/bgm cut。
143 3. **同一录音要接多个来源**
144 例如平台抓取、业务导出、人工补档。
145 4. **query 命中的是 asset,但归属要落到 recording/song**
146 如果不拆层,后面聚合和去重会比较乱。
147
148 ### 当前最推荐的判断
149
150 对于你现在这个目标:
151 -`100w` 音频
152 -`30w` 歌曲
153 - 面向版权保护 / 听歌识曲 / 版本归属
154
155 **不建议把 `recording` 和 `recording_asset` 合并进正式 schema。**
156
157 原因很直接:
158 - 数据量已经不小
159 - 后续大概率会遇到多版本、多来源、多文件问题
160 - 现在省掉一层,后面重构成本会更高
161
162 ### 更务实的折中方案
163
164 如果你觉得当前实现心智负担太高,可以不在产品/算法讨论里反复强调 `recording_asset`,而是采用下面口径:
165
166 ```text
167 song -> recording -> asset -> window -> feature
168 ```
169
170 也就是说:
171 - **概念上保留** `recording``asset` 两层
172 - **沟通上简写**`recording -> asset`
173 - **实现上继续分表**,避免未来返工
174
175 这通常是 Phase-1 最稳妥的折中。
176
177 ---
178
179 ## 0.4 一个更容易理解的口径
180
181 建议把当前体系理解为下面两条核心链:
182
183 ### 归属链
184
185 ```text
186 window -> asset -> recording -> song
187 ```
188
189 作用:
190 - 检索命中后,回溯最终归属到哪个 `song_id`
191
192 ### 特征链
193
194 ```text
195 window -> fingerprint / embedding -> candidate -> aggregate
196 ```
197
198 作用:
199 - 真正完成召回、打分、聚合与排序
200
201 这样看时,整个设计就不再是“很多层没必要”,而是:
202 - **归属层负责回答是谁**
203 - **窗口层负责回答命中了哪一段**
204 - **特征层负责回答怎么检索出来**
205
206 ---
207
26 ## 1. 设计意图 208 ## 1. 设计意图
27 209
28 ## 1.1 这套设计想解决什么 210 ## 1.1 这套设计想解决什么
...@@ -63,6 +245,15 @@ canonical_song -> work -> recording -> recording_asset -> audio_window ...@@ -63,6 +245,15 @@ canonical_song -> work -> recording -> recording_asset -> audio_window
63 245
64 所以原型版 SQL 适合 demo,不适合你现在的 100w 音频目标。 246 所以原型版 SQL 适合 demo,不适合你现在的 100w 音频目标。
65 247
248 ### 当前最建议的简化口径
249
250 如果团队正在进入 Phase-1 实施,不必把所有表同时视为“首批必须上线的复杂系统”。
251 更推荐按下面顺序理解和落库:
252
253 1. `song -> recording -> recording_asset -> audio_window`
254 2. `feature_set_registry -> audio_fingerprint / audio_embedding`
255 3. `reference_set_registry` 与更重的索引治理随后补齐
256
66 --- 257 ---
67 258
68 ## 2. 数据主链 259 ## 2. 数据主链
......