Skip to content

[CCIP] media_regions / ccip_embeddings とPostgres vector storeを実装する #615

Description

@hmjn023

Parent: #612

Depends on: #613, #614

背景

現行CCIPはLanceDBの media_ccip にmedia単位で768次元vectorを保存している。crop対応とmodel更新を見据えると、media 直カラムでは1 media対複数領域・複数embedding versionを表現できない。既存の ICcipVectorStore 境界を維持しつつ、PostgreSQL/pgvectorを正本にする。

設計方針

  • 全体画像とcropを media_regions で統一する
  • 全体画像は kind=full かつbboxなし、cropはbbox付きregionとする
  • ccip_embeddings はregion、model、embedding version、vector(768)、入力revision、抽出時刻を保持する
  • 最初はexact cosine検索で現行意味論を維持し、既存Rust CCIP metricで再rankingする
  • HNSW/IVFFlatは移行後のbenchmark・recall検証まで非スコープとする

スコープ

  • domain/Zod schema、Drizzle schema、migration、明示的repository mapper
  • PostgresCcipVectorStore の実装とDI切替点
  • get/upsert/delete/list/search、source filter、stale判定
  • FK cascadeによるmedia/region削除時の整合性
  • PostgreSQL 18とPGlite双方のCRUD・cosine検索テスト

非スコープ

  • LanceDB既存データのbackfill
  • crop保存API/UI
  • ANN indexの本番tuning

受け入れ条件

  • 768次元以外を拒否する
  • region・model・version単位で冪等upsertできる
  • cosine候補抽出後のRust再rankingを維持する
  • source filter、削除cascade、stale判定がテストされる
  • PG18とPGliteで同じmigration/queryが動作する
  • DB行からdomain型への変換を明示的mapperへ集約する

主な参照

  • packages/application/src/ports/ccip-vector-store.ts:1
  • packages/application/src/services/ccip-vector-service.ts:168
  • apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts:11
  • apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts:266
  • packages/db/src/schema.ts:1

評価反映(2026-07-18)

以下を追加の必須条件とする。既存記述と競合する場合は本節を優先する。

DB制約

media_regions / ccip_embeddings の不変条件はapplication validationだけでなくDB制約でも保証する。

  • kind=full はmediaごとに1件だけとし、UNIQUE (media_id) WHERE kind = 'full' 相当のpartial unique indexを設ける
  • kind=full はbboxをすべてNULLにする
  • kind=person / manual 等のcrop regionはbboxをすべて必須とし、次をCHECKする
    • x >= 0y >= 0
    • width > 0height > 0
    • x + width <= 1y + height <= 1
  • 元画像のwidth/heightは正数とする
  • detector scoreはNULLまたは定義済み範囲内とする
  • embeddingはvector(768)に加え、NaN、Infinity、zero normをruntime境界で拒否する
  • (region_id, model, embedding_version) の一意性をDBで保証する

Port・検索契約

現行のmedia ID中心のICcipVectorStoreでは、複数region・model・versionを安全に扱えないため、次を明示した契約へ更新する。

  • get/upsert/deleteはregion ID、model、embedding versionを指定できる
  • searchはregion kind、source、model、embedding versionを明示する
  • 異なるmodel/versionのvectorを同じ検索空間へ混在させない
  • source・region kind・model/versionのfilterは候補LIMITより前に適用する
  • 初期runtimeはkind=fullだけを抽出・検索対象とする。person/manual regionのembeddingは#618まで作成しない
  • DB行からdomain型への変換は明示的mapperへ集約する

Stale判定

fresh/staleの正本はextracted_atとmedia更新日時の大小比較ではなく、永続化したinput_revisionの一致判定とする。input_revisionには少なくとも元画像revision、model、embedding version、前処理条件を含め、service・batch・再処理判定で同じ生成規則を共有する。

Backend切替

本Issue完了時点ではLanceDBをdefault backendのまま維持する。Postgres adapterは設定flagまたはshadow verification経由で有効化し、read/writeの本切替は#616のbackfill・parity検証・rollback準備完了後に行う。

追加受け入れ条件

  • full regionのmedia単位partial uniqueとbbox CHECKがPG18/PGlite双方で検証される
  • crop bbox、元画像寸法、scoreの不正値をDBまたはschema境界で拒否できる
  • 非有限値・zero norm・768次元以外のvectorを拒否できる
  • region/model/versionを明示したportでCRUDと検索ができる
  • filterが候補LIMIT前に適用され、model/versionの検索空間が混在しない
  • すべてのproducerが同一のinput_revision生成・stale判定を使用する
  • #616完了前はPostgres adapterを導入してもdefault backendがLanceDBから変わらない

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions