본문으로 건너뛰기

ADR 0017 — v2 사무실 기억 검색 파이프라인 완성 (tenantEmbedding)

  • 일자: 2026-04-23
  • 상태: Accepted
  • 연관: ADR 0015 (tenant-isolated AI learning + Phase 1b), ADR 0012 (RelatedMemoriesPanel S1 B)

배경

ADR 0015 로 도입한 Phase 1b S2 (scan 업로드 · 임베딩 생성 · guardedFindNearest · v2 core ranking) 가 S3 (자동 인용 + UI 노출) 까지 도달하지 못한 상태로 남아 있었다. 구체적 gap:

  1. features.ai.tenantEmbedding 플래그를 실제로 소비하는 Server Action 부재 — impl 은 있어도 라우팅 경로 없음.
  2. buildRelatedMemoryQuery 결과로 얻은 랭킹을 UI 가 쓸 수 있는 카드 shape 로 조립하는 결선 작업 미완.
  3. "자동 인용" (Phase 2) — ranked 메모리에서 법리 키워드 anchored 본문 발췌는 기획만 존재.
  4. legacyDocuments.embedding.meta 필드가 writer 단에서 비어 있어 v2 의 메타 보너스 랭킹이 항상 cosine 100% 로 작동.

결정

한 번의 집중 세션으로 v2 루프 전체를 닫고 플래그 ON 상태로 기본 출시 (2026-04-23 갱신: 파일럿 실측 전제 제거, mock/fixture 검증 정책 하에 기본 활성). 원칙:

  1. 뒤에서 앞으로 (bottom-up): pure 유틸 → impl → Server Action → UI.
  2. 단일 kill switchfeatures.ai.tenantEmbedding Firestore 플래그 하나. 기본 true. 비용·품질 사고 시 false 로 1-click 차단. 이전 LEGACY_EMBEDDING_ENABLED env 2단 gate 는 파일럿 전제였고 제거됨.
  3. v1 (Vertex AI Search) 은 유지 — dispatch action 이 플래그 OFF 또는 v2 skipped 시 v1 fallback.
  4. PII 무전파 — citations 도 redactedText 기반이므로 1단 마스킹 후 텍스트만 본다.

결정 근거

  • 전 사용자 무료 + 파일럿 실측 금지 (출시 우선 · 레이어 적층) 맥락에서, 코드를 전부 배포하되 플래그로 잠궈 두는 것이 가장 빠른 검증 가능성 + 최저 리스크 조합.
  • fixture 기반 테스트가 unit + integration 양쪽을 커버하므로 파일럿 실측 없이도 알고리즘 정확성을 보장한다.
  • auto-citation 은 순수 함수로 분리해 UI 변경 없이 rollout 가능 — 기존 redactedText 위에 얹는 thin layer.

아키텍처 (완성본)

[UI] RelatedMemoriesPanel
│ dispatchRelatedMemoriesAction

[Action] dispatch-related-memories-action (v2 우선 → v1 fallback)

├── flag ON → findRelatedMemoriesV2Action
│ │
│ └── findRelatedMemoriesV2Impl (DI)
│ ├── embedQueryForMemorySearch (Vertex AI text-embedding-004)
│ ├── guardedFindNearest (Firestore · COSINE)
│ ├── rankMemories (cosine × 0.7 + meta × 0.3)
│ ├── fetchDocMetas (getAll 조인)
│ └── extractAutoCitations (Phase 2)

└── flag OFF 또는 v2 skipped → findRelatedMemoriesForDocGenAction (Vertex AI Search)

[Cloud Fn] generateEmbeddingForLegacyDoc
├── embedText (Vertex AI REST)
├── meanPoolVectors (다중 chunk)
└── write { vector, meta: deriveEmbeddingMeta(sourceType, scannedMeta, caseData) }

주요 코드 포인트

  • apps/web/app/(workspace)/legacy/_lib/find-related-memories-v2-impl.ts — DI 순수 로직
  • apps/web/app/(workspace)/legacy/_lib/auto-citation-extractor.ts — 키워드 anchored 본문 발췌
  • apps/web/app/(workspace)/legacy/_lib/related-memories-dispatch-impl.ts — v2/v1 라우팅
  • apps/web/app/(workspace)/legacy/_lib/embedding-meta-derivation.ts — meta 파생 (+ functions 미러)
  • apps/web/lib/ai/embed-query.ts — Vertex AI 쿼리 임베딩 REST
  • apps/web/lib/firebase/find-nearest.tsguardedFindNearest (tenant 경계 assert)
  • functions/src/embedding-meta.ts — SSoT 미러
  • functions/src/legacy-pipeline.ts — embedding 작성 시 meta 주입

알고리즘 세부

랭킹

finalScore = cosineScore × 0.7 + metaBonus × 0.3
cosineScore = 1 - distance / 2 (COSINE)
metaBonus 최대 1.0:
recoveryType 일치 +0.4
docType 일치 +0.3
isCommercial +0.15
principal bucket (소액/중액/고액) +0.15

자동 인용 키워드 맵 (일부)

  • debt:complaint: 민법 제479조 · 민법 제477조 · 변제충당 · 소멸시효
  • debt:payment-order: 지급명령 · 변제충당 · 이자의 기산
  • construction:complaint: 공사대금 · 도급계약 · 하자보수
  • *:complaint fallback: 청구취지 · 청구원인

매칭된 키워드 주변 ±80자 윈도우 → contextSnippet. 매칭 없으면 선두 120자 generic excerpt.

플래그 & 롤아웃 (2026-04-23 단순화)

현재 단일 Firestore 플래그로 통일됨. 기본값은 defaults.ts SSoT.

플래그기본용도
features.ai.tenantEmbeddingtrue임베딩 생성 + v2 검색 kill switch
features.ai.autoCitationtruecitations UI 섹션 kill switch (검색 유지)
features.ai.ragSearchtruedispatch action 이 v1·v2 모두 게이트
features.infraHardening.relatedMemoriesPaneltruePanel 자체 노출

비상 차단: 문제 발생 시 Firestore 콘솔에서 해당 플래그 false 로 직접 수정. 배포 불필요.

대안 검토

  1. v2 단독 (v1 제거) — 거부. 신규 임베딩 생성까지 v2 는 쓸 카드 없음 → 빈 패널 UX 퇴보. v1 fallback 유지가 가드레일.
  2. citations 를 v1/v2 양쪽 지원 — 거부. v1 은 Vertex AI Search 검색 결과의 snippet 에 의존하는데 caseNumber/closedAt 조인 없이 citation 을 만들기엔 부족. v2 한정이 정직.
  3. UI 대신 Server Action 반환값만 확장 — 거부. 변호사 가치는 "붙여넣기 가능한 인용"을 보는 것.

Minority Report

  • P7 (보안): auto-citation 은 redactedText 기반이지만 키워드 매칭 주변 ±80자가 여전히 민감 맥락을 드러낼 가능성. 후속 3단 재식별 스캐너 적용 전까지 내부 dogfood 만 권장.
  • P1 (변호사): "민법 제479조" 같은 키워드 우선순위 순서가 실제 쟁점 분포와 어긋날 수 있음 — 로그/텔레메트리 수집 후 재조정 필요.

후속

  • citations "이 쟁점 서면에 반영" 버튼 · 복사는 #567 로 완료
  • 키워드 맵 텔레메트리 학습 (shown/clicked 기반 재조정)
  • NER 2단 (Phase 1b S4) — Gemini 로 PII leakage 재검증