Commit 44222971 442229713987b3aaf38ba840ea1e81b36b800e5e by cnb.bofCdSsphPA

Favor typed unified tables for the Phase-1 ACR storage model

Constraint: Reduce schema reading cost for new engineers while preserving the logical distinctions needed for copyright-scale retrieval and attribution.
Rejected: Keep adding highly specialized tables for every layer in Phase-1 | It increases join cost in the mental model faster than it improves first-stage delivery.
Confidence: high
Scope-risk: narrow
Directive: Prefer a fused physical model (media_entity/audio_object/feature_fact/set_membership) with type fields, while keeping song/recording/asset/window as logical semantics.
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; rg confirmed fused-model sections are present in docs
Not-tested: concrete SQL DDL for the fused physical model
1 parent 7ada6f21
......@@ -10,6 +10,8 @@
-`docs/postgresql-data-model.md` 补充 `Phase-1` 极简 schema 视图,明确首批应优先落稳的表集合:`song/recording/recording_asset/audio_window``feature_set_registry/audio_fingerprint/audio_embedding``reference_set_registry/reference_set_member`
- 根据“尽量融合、用多 type 关联”的新约束,在 `docs/postgresql-data-model.md` 补充“融合优先”建模视图:推荐以 `media_entity``audio_object``feature_fact``set_membership` 这 4 类通用表承载 Phase-1 物理实现,同时保留 `song/recording/asset/window/feature` 的逻辑分层。
## 2026-06-04
- 更新 `docs/README.md` 顶部为与 `session-handoff` 一致的“最短启动路径”,并再次用该入口命令重跑 `run_planner_validation_commands_live.py`,确认 fresh 结果仍为 `executed_count=4``all_passed=true`
......
......@@ -45,7 +45,7 @@ cd /workspace/acr-engine
### C. 第一个阶段怎么落地
- [phase1-implementation-checklist.md](./phase1-implementation-checklist.md) — Phase-1 执行清单
- [postgresql-data-model.md](./postgresql-data-model.md) — 含 Phase-1 极简 schema 视图
- [postgresql-data-model.md](./postgresql-data-model.md) — 含 Phase-1 极简 schema 与融合优先视图
- [model-feature-registry-bootstrap.md](./model-feature-registry-bootstrap.md) — model/feature/reference set 初始化
- [phase1-worker-contract.md](./phase1-worker-contract.md) — worker、job、失败语义合同
- [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) — PostgreSQL 存储样例
......
......@@ -256,24 +256,160 @@ window -> fingerprint / embedding -> candidate -> aggregate
---
## 1.2.1 融合优先:逻辑分层保留,物理表尽量收敛
如果你的核心诉求是:
> **尽量减少表数量,用 `type` + 通用关联表达多种对象,而不是一路拆很多表再 join**
那么推荐采用下面这个口径:
- **逻辑层** 仍然保留 `song / recording / asset / window / feature`
- **物理层** 尽量融合成少数几张通用表
也就是说:
> **概念上分层,落库上收敛。**
### 推荐的融合优先物理视图
| 物理表 | 主要 type | 作用 |
|---|---|---|
| `media_entity` | `song`, `work`, `recording` | 承载业务归属对象 |
| `audio_object` | `asset`, `window` | 承载真实音频文件与切片对象 |
| `feature_fact` | `fingerprint`, `embedding` | 承载检索特征事实 |
| `set_membership` | `reference_set`, `hot_set`, `eval_set` | 承载集合归属关系 |
这样,Phase-1 在物理表层面可以被收敛成:
```text
media_entity -> audio_object -> feature_fact -> set_membership
```
而不是新同学第一眼就看到很多高度专用表。
### 对应的逻辑语义
#### `media_entity`
`entity_type` 区分:
- `song`
- `work`
- `recording`
公共字段可统一为:
- `entity_id`
- `entity_type`
- `parent_entity_id`
- `root_song_id`
- `title`
- `artist_name`
- `entity_status`
- `metadata_json`
#### `audio_object`
`object_type` 区分:
- `asset`
- `window`
公共字段可统一为:
- `object_id`
- `object_type`
- `recording_entity_id`
- `parent_object_id`
- `storage_uri`
- `codec`
- `sample_rate`
- `start_ms` / `end_ms`
- `duration_ms`
- `checksum`
- `metadata_json`
解释:
- `asset` 行表示真实音频文件
- `window` 行表示由某个 `asset` 切出来的检索窗口
#### `feature_fact`
`feature_type` 区分:
- `fingerprint`
- `embedding`
公共字段可统一为:
- `feature_id`
- `feature_type`
- `object_id`
- `model_name`
- `model_version`
- `feature_set_name`
- `embedding_dim`
- `fingerprint_value` / `embedding_uri`
- `vector_table_name`
- `metadata_json`
这样可以避免:
- 一套模型一张表
- 一类特征一张表
- 后续换模型就改 schema
### 为什么这比“纯拆表”更适合当前 Phase-1
优点:
1. **新同学更容易理解**:看到的是 3~4 张核心表,而不是十几张专用表
2. **多 type 复用更自然**`song/work/recording``asset/window` 都能用 type 统一表达
3. **模型演进更平滑**`feature_fact` 可以同时容纳不同模型与不同特征
4. **更符合当前目标**:先把识别闭环跑通,而不是先把治理模型拆到很细
### 但不要融合过头
虽然推荐物理收敛,但仍然不建议极端融合成一张大全表。
例如下面这种仍然过度扁平:
```text
song_everything
```
原因是它会把:
- 归属对象
- 音频对象
- 检索特征
- 集合关系
全部揉在一起,导致:
- 空字段过多
- 约束难写
- 批量写入难做
- 查询语义不清晰
因此更推荐的边界是:
```text
实体一张表 + 音频对象一张表 + 特征事实一张表 + 集合关系一张表
```
这是“融合优先但不过度融合”的平衡点。
---
## 1.3 Phase-1 极简 schema 视图
如果只从“第一阶段必须落哪些表”来理解,推荐把正式设计压缩成下面这组最小表集合:
如果只从“第一阶段必须落哪些表”来理解,推荐优先采用“融合优先”的最小表集合:
| 层 | 推荐保留表 | 当前作用 |
| 层 | 融合优先推荐表 | 当前作用 |
|---|---|---|
| 归属层 | `song`(或当前 `canonical_song` 的等价口径), `recording` | 最终归属到 song,区分不同录音版本 |
| 资产层 | `recording_asset` | 管理真实音频文件、来源与编码版本 |
| 窗口层 | `audio_window` | 支撑 offset / evidence / 多段投票 |
| 特征层 | `feature_set_registry`, `audio_fingerprint`, `audio_embedding` | 管理 fingerprint / embedding 的生成事实 |
| reference 层 | `reference_set_registry`, `reference_set_member` | 管理当前线上 reference 集 |
| 实体层 | `media_entity` | 统一承载 `song/work/recording` |
| 音频对象层 | `audio_object` | 统一承载 `asset/window` |
| 特征层 | `feature_fact` | 统一承载 `fingerprint/embedding` |
| 集合层 | `set_membership` | 统一承载 `reference/hot/eval` 等集合关系 |
也就是说,Phase-1 真正应该优先落稳的是:
也就是说,Phase-1 如果按物理实现优先,真正应该先落稳的是:
```text
song -> recording -> recording_asset -> audio_window
feature_set_registry -> audio_fingerprint / audio_embedding
reference_set_registry -> reference_set_member
media_entity -> audio_object -> feature_fact -> set_membership
```
如果按逻辑语义理解,则仍然对应:
```text
song/work/recording -> asset/window -> fingerprint/embedding -> reference membership
```
### 这版极简 schema 明确不要求第一天就重投入的内容
......