Commit e6c2e0a1 e6c2e0a1a1197c259da620c4c3b2b5be02ca4e30 by cnb.bofCdSsphPA

Why the docs need an explicit vector payload storage contract

Constraint: Phase-1 embedding storage must explain where vectors live, how feature_fact points to them, and how hot/cold migration works at scale
Rejected: Leave vector payload placement implicit | does not give operators a stable contract for ANN loading, backfill, or cleanup
Confidence: high
Scope-risk: narrow
Directive: Keep embedding payload guidance split between feature_fact metadata and vector-side storage unless the physical schema default changes
Tested: markdown link check on /workspace/docs after adding vector payload storage and lifecycle guidance
Not-tested: No live database rerun; this is a documentation-only clarification over the current schema path
1 parent df241d20
......@@ -9,6 +9,7 @@
- 继续补充数据绑定与模型落库说明:在 `docs/postgresql-data-model.md``docs/postgres_db_schema_samples.md` 明确 `media_entity -> audio_object(asset/window) -> feature_fact` 的绑定字段关系,并给出 `chromaprint / mert-v1-95m / muq-base / local_wavehash_embed / ecapa-tdnn` 的 Phase-1 存储口径与 SQL 样例。
-`docs/postgres_db_schema_samples.md` 继续补充一个完整的 `同一 song -> 多 asset -> 多 window -> 多 model` 样例,附带缺模型扫描 SQL 与 asset 级特征完备性检查 SQL,方便 Phase-1 批量补算与巡检。
- 继续补充规模化实施口径:在 `docs/postgresql-data-model.md``docs/postgres_db_schema_samples.md` 新增 `100w 音频 / 30w song` 的批量入库顺序、索引建设顺序、冷热分层策略与主链/模型缺口巡检 SQL,明确 Phase-1 先保主链、exact 先铺满、semantic 再批量补齐。
- 继续补充 embedding 落盘规范:在 `docs/postgresql-data-model.md``docs/postgres_db_schema_samples.md` 新增 `embedding_uri / vector_table_name / embedding_dim` 的字段语义、向量侧表命名规范、冷热迁移与回收策略,以及 `feature_fact -> vector table` 的关联样例 SQL。
## 2026-06-04
......
......@@ -821,3 +821,105 @@ hot_set -> 高频版权曲
reference_set -> 主 reference catalog
cold -> 长尾曲库,先保主链 + exact
```
---
## 16. vector table / embedding 文件存储样例
### 16.1 `feature_fact` 中怎么记录 embedding 位置
```sql
insert into feature_fact (
feature_type,
object_id,
song_id,
model_name,
model_version,
feature_set_name,
feature_schema_ver,
embedding_dim,
embedding_uri,
vector_table_name,
checksum,
metadata_json
) values (
'embedding',
:window_id,
:song_id,
'mert-v1-95m',
'hf-main',
'mert_5s_hop2.5_v1',
'v1',
768,
's3://acr-emb/phase1/mert-v1-95m/song_1001/asset_2001/window_3001_mert_5s_hop2.5_v1.npy',
'audio_embedding_vector_768',
'sha256:emb-demo-001',
'{"storage_tier":"hot"}'::jsonb
);
```
### 16.2 一个推荐的向量侧表样例
> 这里是逻辑样例,真实向量类型可按你的 pgvector 版本落地。
```sql
create table if not exists audio_embedding_vector_768 (
embedding_row_id bigserial primary key,
feature_id bigint not null references feature_fact(feature_id),
vector_dim integer not null default 768,
embedding_vector vector(768),
created_at timestamptz not null default now()
);
```
### 16.3 feature_fact 与 vector table 的关联查询
```sql
select ff.feature_id,
ff.song_id,
ff.object_id as window_id,
ff.model_name,
ff.feature_set_name,
ff.embedding_dim,
ff.embedding_uri,
ff.vector_table_name,
v.embedding_row_id
from feature_fact ff
left join audio_embedding_vector_768 v
on v.feature_id = ff.feature_id
where ff.feature_type = 'embedding'
and ff.model_name = 'mert-v1-95m'
and ff.feature_set_name = 'mert_5s_hop2.5_v1';
```
### 16.4 查哪些 embedding 还没进入 vector table
```sql
select ff.feature_id,
ff.song_id,
ff.object_id as window_id,
ff.embedding_uri
from feature_fact ff
left join audio_embedding_vector_768 v
on v.feature_id = ff.feature_id
where ff.feature_type = 'embedding'
and ff.embedding_dim = 768
and ff.vector_table_name = 'audio_embedding_vector_768'
and v.feature_id is null
order by ff.song_id, ff.object_id;
```
### 16.5 查哪些冷层 embedding 可以先不进热索引
```sql
select ff.feature_id,
ff.song_id,
ff.object_id,
ff.embedding_uri,
ff.metadata_json
from feature_fact ff
where ff.feature_type = 'embedding'
and coalesce(ff.metadata_json->>'storage_tier', 'cold') = 'cold'
and ff.vector_table_name is null
order by ff.song_id, ff.object_id;
```
......
......@@ -854,3 +854,154 @@ order by w.song_id, w.parent_object_id, w.start_ms;
### 17.8 一句话策略
> 大规模阶段不要先追求“所有模型都齐”,而要先保证 **对象主链完整、exact 先可用、semantic 可持续补齐、集合可分层治理**。
---
## 18. vector table / embedding 文件存储规范
当前 `feature_fact` 对 embedding 采用的是 **元数据 + 外部载荷位置** 的设计:
- `feature_fact` 负责记录:
- 这个 embedding 属于哪个 `window`
- 属于哪个 `song`
- 由哪个 `model_name/model_version/feature_set_name` 产生
- 维度是多少
- 向量实际放在哪
也就是说:
```text
feature_fact = 向量事实索引卡
embedding_uri / vector_table_name = 向量载荷位置
```
### 18.1 为什么不把大向量直接塞进 `feature_fact`
因为当前目标是 100w 音频规模:
- `feature_fact` 应该先承担可检索、可聚合、可审计的“事实表”职责
- 大向量 payload 适合放到:
- 外部文件(`embedding_uri`
- 专门的 vector table(`vector_table_name`
这样做的好处:
- 主链表更轻
- 便于冷热迁移
- 便于不同维度分开治理
- 便于不同 ANN 索引策略独立演化
### 18.2 当前推荐字段语义
| 字段 | 含义 | 何时必填 |
|---|---|---|
| `embedding_dim` | 向量维度 | `feature_type='embedding'` 时必填 |
| `embedding_uri` | 向量文件或对象存储地址 | 外部文件存储时必填 |
| `vector_table_name` | 向量物理表名 | 落 pgvector / vector side table 时必填 |
| `checksum` | 向量载荷摘要 | 建议填写 |
### 18.3 推荐的 table naming
建议按维度分表:
```text
audio_embedding_vector_8_placeholder
audio_embedding_vector_192
audio_embedding_vector_768
audio_embedding_vector_1024
```
原因:
- 不同维度的 ANN 索引通常不应混表
- 便于控制索引构建成本
- 便于模型演进时独立扩容
### 18.4 推荐的 embedding_uri 规范
建议统一成下面几种 scheme 之一:
```text
s3://bucket/path/to/embedding.npy
oss://bucket/path/to/embedding.npy
file:///data/embeddings/song_xxx/window_xxx.npy
```
建议路径里至少编码:
- `song_id`
- `asset_id`
- `window_id`
- `model_name`
- `feature_set_name`
例如:
```text
s3://acr-emb/phase1/mert-v1-95m/song_1001/asset_2001/window_3001_mert_5s_hop2.5_v1.npy
```
### 18.5 Phase-1 推荐策略
#### 小规模 / 先打通链路
优先:
- `embedding_uri` 必填
- `vector_table_name` 可先写占位或后补
#### 中规模 / 要做 ANN 检索
优先:
- `embedding_uri` 保留
- `vector_table_name` 同步填写
- 向量侧表开始按维度维护
#### 大规模 / 热冷分层
优先:
- 热层 embedding 同时保留 `embedding_uri + vector_table_name`
- 冷层可只保留 `embedding_uri`
- 只给热层/主 reference 建近邻索引
### 18.6 推荐的冷热迁移策略
#### 热层
- `hot_set` 成员
- 主 reference 热门版权曲
- 保留:`feature_fact + vector_table + embedding_uri`
#### 温层
- 主 reference 全量库
- 保留:`feature_fact + embedding_uri`
- vector table 可按需部分加载
#### 冷层
- 长尾 song
- 最低要求:`feature_fact + embedding_uri`
- vector table 可不常驻
### 18.7 回收策略
不建议先删 `feature_fact`,而是按下面顺序回收:
1. 先删或迁移冷 embedding payload 文件
2. 再回收冷层 vector table 中对应 row / index 分片
3. 最后如果确认整批数据弃用,才删 `feature_fact`
因为:
- `feature_fact` 是主审计事实
- 一旦删掉,很难追溯“这个 window 是否算过、用哪个模型算过”
### 18.8 一个推荐的向量侧表逻辑结构
逻辑上建议至少有:
```text
embedding_row_id
feature_id
vector_dim
embedding_vector
created_at
```
关键点:
- `feature_id` 回指 `feature_fact.feature_id`
- 向量侧表只存向量 payload 与最少索引字段
- song/window/model 相关语义仍以 `feature_fact` 为准
### 18.9 一句话规范
> `feature_fact` 负责“谁算的、算的是什么、归属到谁”,`embedding_uri / vector_table_name` 负责“向量 payload 实际放哪”。
......