Skip to content

feat(ai-server): Cody Stage 3 catalog match — per-role embedding + pgvector retrieval (Phase 0-4) #646

Description

@cocoyoon

배경

raf 의 2026-06-07 결정 — browser/scrape spike 전면 폐기 후 제휴업체 catalog DB + pgvector cosine retrieval path 로 전환. Stage 1+2 (cody_describe / style_formula / cody_embedding) 는 Phase A-E 로 완료된 상태. 본 issue 는 그 위에 Stage 3 (catalog match) 의 ai-server 측 핵심 path 를 lock-in.

직전 대화 결정:

  • Items DB 위치 = assets supabase 의 items 테이블 (raf 명시, feat(affiliate): Rakuten SFTP 기반 아이템 카탈로그 DB 구축 #625 의 본문 operation DB 기본 과 다름 — 본 issue 가 SoT)
  • Embedding 단위 = cody 측 per-role (cody_role_embeddings 별 테이블, jsonb 안 vector 의 ivfflat 불가 회피)
  • Axis = Exact (legacy solutions) + Similar (items vector, per role) — Mood axis 폐기
  • Retailer 정보는 내부 적재만, UI 비노출

작업 정리 (Phase 0-4 완료)

Phase 0 — Cleanup ✅

  • spike 27 .py + ~75 probes + `spike-browser-agent-results.md` 영구 폐기 (`spike_cody_search_criteria.py` 만 Stage 1.5 데이터 pipeline 용 유지)
  • ADR-0005 (`.planning/cody-stage3-catalog-match.md`) 의 appendix 에 "2026-06-07 — Browser path 폐기, catalog DB 전환" 추가

Phase 1 — Migration ✅

  • `supabase-assets/migrations/20260608000000_cody_stage3_matching.sql` — `items` 테이블 (sku_id PK + retailer + brand + title + category_path jsonb + attributes jsonb + item_embedding vector(768) + 8 indexes + RLS + updated_at trigger) + `cody_role_embeddings` (raw_post_id FK + role_index UNIQUE + embedding vector(768) + ivfflat + RLS) + pg_trgm extension
  • `supabase/migrations/20260608000000_solutions_catalog_link.sql` — `solutions.match_type` CHECK 확장 ("perfect" | "close" | "exact" | "embedding" | NULL) + `solutions.catalog_item_id text` (logical FK, cross-DB)
  • local 의 assets / postgres DB 둘 다 적용 검증

Phase 2 — catalog team 공유 spec ✅

  • `.planning/cody-stage3-items-spec.md` — items 테이블 schema + attributes jsonb 권장 shape + cody axis ↔ items.attributes mapping table + `embedding_text` prose format (vector space alignment 의 SoT) + 모델 lock-in (`gemini-embedding-2 @ 768 + task_type=RETRIEVAL_DOCUMENT`) + first batch 권장 분포 (100 SKU: clothing 41 + footwear 20 + accessory 30 + dress/set 5/4)

Phase 3 — per-role embedding 적재 ✅

  • `cody_role_embedder.py` (신규) — `build_role_embedding_text(item, style, formula)` formatter (LLM 호출 0, prose serialize, items.embedding_text 와 1:1 match)
  • `processor.py` step 10 (`STEP_CODY_ROLE_EMBEDDING`) — cody_analysis.items[i] 마다 embed → cody_role_embeddings row 적재 (per-item soft-fail)
  • `repository.upsert_cody_role_embeddings` + `CodyRoleEmbeddingRow` dataclass — atomic DELETE + INSERT (UNIQUE (raw_post_id, role_index))
  • `scheduler.py` passthrough — `upsert_cody_role_embeddings` 의 별 트랜잭션 호출 (soft-fail)
  • 12 신규 unit test (formatter 6 + processor 6)

Phase 4 — PgvectorCatalogItemRepository + matcher rewrite ✅

  • `catalog_repository_pgvector.py` (신규) — `NullCatalogItemRepository` 의 실 구현. `find_exact` (brand + title pg_trgm 유사도 ≥ 0.85 + category_path.group filter) + `find_similar` (cosine `<=>` ORDER BY + min_score filter + group filter)
  • `catalog_matcher.match()` signature 변경 — `role_embeddings` (Phase 3 산출) 받아 cached vec 사용 → re-embed 호출 skip. cached 없는 idx 는 `build_item_query_text` fallback (Phase 1 inert 환경 + soft-fail 회복)
  • `processor.py` step 11 — `role_embeddings_list` 전달
  • `_container.py` — env `CATALOG_MATCH_REPOSITORY` (default "null") switch 의 factory dispatch
  • `_environment.py` — `CATALOG_MATCH_REPOSITORY` / `CATALOG_MATCH_EXACT_THRESHOLD` 추가
  • 15 신규 unit test (helper 4 + early-return 7 + happy 4)

Total 60 tests green (회귀 0).

산출물

  • Plan: `~/.claude/plans/sleepy-napping-quasar.md` (full Phase 0-8)
  • ADR: `.planning/cody-stage3-catalog-match.md` + 의사결정 appendix (Browser path 폐기 사유)
  • Catalog team spec: `.planning/cody-stage3-items-spec.md`
  • Migrations: 2개 (assets + public DB)
  • ai-server LOC: +1,150 (production + test)

남은 phase

Phase 영역 catalog 의존
5 mock seed ai-server raf skip 결정 — "raf 가 필요 시 별 요청"
6 solutions adopt endpoint api-server (Rust) 코드 자체 가능, e2e 만 적재 후
7 admin verify UI (CatalogMatchesGrid) web (TS) 코드 자체 가능, 검증만 적재 후
8 public UI (match_type badge) web (TS) Phase 6 완료 의존

Catalog 적재 시점에 enable

```bash
export CATALOG_MATCH_REPOSITORY=pgvector
export CATALOG_MATCH_ENABLED=true

ai-server 재기동 → reparse 1건 → catalog_matches jsonb 적재 확인

```

코드 변경 0 — env flag flip 만으로 동작 (adapter-first 의 SoT 가치).

의존성

Metadata

Metadata

Assignees

No one assigned

    Labels

    aiAI/자동화backend백엔드/APIcodyCody Recreation Engine MVPenhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions