본문으로 건너뛰기

ADR 0048 — 기억·판례 두 메뉴 목적 재정의 + 사무소 지능 자동 회상 통일

날짜: 2026-06-03 상태: Accepted Tier: A (Architecture · 제품 정보구조 + AI 검색 코어) 적용 범위: apps/web/app/(workspace)/legacy/* (기억) · apps/web/app/(workspace)/library/* (판례) · apps/web/app/(workspace)/cases/[caseId]/_components/CaseDetailClient.tsx · apps/web/app/(workspace)/legacy/_lib/related-memory-query.ts

결정문

너홀로프로의 두 AI 자산 메뉴 — 기억(/legacy)판례(/library) — 를 데이터 소유권을 축으로 명확히 분리한다. 두 메뉴는 변호사 옆의 성격이 다른 두 AI 동료다:

기억 /legacy판례 /library
정체우리 사무소를 다 기억하는 고참 사무장공공 법률 도서관 사서·리서처
데이터Tenant RAG 전용 (legacyDocuments) — 우리 것만Platform RAG 전용 (publicDocuments) — 공공만
질문"우리 예전에 이런 사건 어떻게 풀었지?""이 쟁점의 대법원 입장은?"
신뢰 경계100% 우리 사무소 경험100% 검증 가능한 공공 출처

평소엔 각자 특화된 AI 로 동작하고, 서류 생성 시에만 두 RAG 가 자동 결합된다(ADR 0041 유지). 이 결정은 그 경계를 코드·UI·문서에 일관 적용하고, 그 첫 구현으로 사무소 지능의 자동 회상(automatic recall)을 단일 V2 의미검색 엔진으로 통일한다.

배경

비대칭 — 기억은 "쌓기만", 판례는 "쓰기까지"

코드 전수 분석(2026-06-03) 결과 두 메뉴는 비대칭 상태였다:

  • 판례 메뉴: 능동 검색(조건 검색 탭) + AI 채팅(AI 법률 리서치 탭) 모두 존재. 변호사가 공공 판례·법령에 능동적으로 질문할 수 있다.
  • 기억 메뉴: /legacy 는 업로드·OCR 검수·칸반·연대기 등 관리/열람 화면뿐. 변호사가 자기 사무소 기억에 능동적으로 질문·검색할 입구가 없다. 실제 활용(서류 생성 시 자동 참조)은 백그라운드로만 일어나 변호사 눈에 보이지 않는다.

역설적으로 한 줄 본질("자기 데이터로 자기 AI 학습·활용")이 가장 약하게 구현된 곳이 기억 메뉴 자신이었다.

경계 흐림 — 사무소 기억과의 대화가 판례 메뉴에 숨어 있었다

searchLibraryRagAction(판례 AI 채팅)은 Platform RAG 6건 + Tenant RAG 4건을 암묵 결합(ADR 0041 Phase H2)했다. 그래서 "우리 예전에 이런 사건 어떻게?"의 답이 기억 메뉴가 아니라 판례 메뉴에서, 출처 라벨 없이 섞여 나왔다. 변호사 멘탈모델이 모호해진다.

분열된 자동 회상 — 엔진은 멀쩡한데 변호사가 보는 곳엔 가짜가 꽂혀 있었다

가장 결정적인 발견. "비슷한 과거 기억"을 찾아주는 자동 회상이 세 갈래로 분열돼 있었다:

경로호출처엔진상태
V2 의미검색 (find-related-memories-v2-impl.ts)문서 생성·편집 모달 (cases/[caseId]/_modals/RelatedMemoriesPanel) → dispatchRelatedMemoriesAction임베딩 findNearest + cosine·meta 결합 랭킹 + rerank + auto-citation✅ 진짜
v1 fallback (findRelatedMemoriesForDocGenAction)dispatch 가 v2 skip 시Vertex AI Search 폐기 후 항상 빈 결과⚰️ dead
가짜 검색 (findRelatedMemoriesAction)사건 상세(CaseDetailClient:887) · 기억 상세(LegacyDocDetail:496) 의 legacy/_components/RelatedMemoriesPanellegacyDocsRef.where("caseId","==",caseId).limit(10) + relevance: 0.5 하드코딩❌ 가짜

변호사 동선의 핵심(사건 상세)에서 "비슷한 과거 기억"이라 써 붙인 패널이, 실제로는 "지금 이 사건의 다른 문서들"을 가짜 관련도 0.5 로 나열하고 있었다. 의미 검색이 아니라 caseId 동일 필터다. 게다가 이 가짜를 전략 보고서 컨텍스트(ai-strategy-context-actions.ts)도 끌어다 썼다.

V2 엔진은 cosineScore·metaBonus·"왜 이 기억이 상단인지" matchReasons 까지 분해해 보여줄 만큼 완성도가 높은데, 가장 눈에 띄는 곳에는 연결되지 않았다.

production 플래그 검증 (2026-06-03)

config/appMetadata 직접 조회 결과 V2 의미검색의 3중 gate 가 production 에서 모두 ON:

features.ai.tenantEmbedding: true ✅
features.ai.ragSearch: true ✅
infraHardening.relatedMemoriesPanel: true ✅

즉 V2 엔진은 production 에서 이미 활성이며, 플래그를 켤 필요 없이 가짜 패널을 V2 로 결선하는 순간 진짜 의미검색이 즉시 작동한다. 효과가 즉각적이다.

부수 발견: features.ai.publicCaseRag·casePrefill 필드가 Firestore 문서에 부재 — Zod default 에 의존 중. Phase 3(판례 공공 정제) 착수 시 점검 대상.

결정 — Phase 1: 자동 회상 통일 (이 ADR 의 구현 범위)

1. 가짜 패널을 V2 의미검색으로 결선

legacy/_components/RelatedMemoriesPanel.tsx(사건 상세·기억 상세에 렌더)의 검색 백엔드를 가짜 findRelatedMemoriesAction → 진짜 V2 dispatchRelatedMemoriesAction 로 교체한다.

  • relevance: 0.5 하드코딩 제거 → 실제 cosineScore 기반 finalScore
  • "왜 이 기억이 상단인지"(의미 N% · 메타 +M% · matchReasons) 분해 노출 — 검색 투명성
  • Gemini 분석(닮은점·차이점·활용 포인트)·history 영속화 레이어는 유지하되 입력을 V2 카드 snippet 으로 교체

2. V2 엔진 docType optional 확장

V2 의 쿼리 빌더 buildRelatedMemoryQuery 는 현재 docType필수(서류 생성 맥락 전제). 사건 상세에는 특정 서류 유형이 없으므로, docTypeoptional 로 확장한다:

// 변경 전
export interface RelatedMemoryQueryInput {
recoveryType?: CaseDomainType;
docType: DocKind; // 필수
principal?: number;
isCommercial?: boolean;
}

// 변경 후 (하위 호환)
export interface RelatedMemoryQueryInput {
recoveryType?: CaseDomainType;
docType?: DocKind; // optional — 없으면 토큰 생략
principal?: number;
isCommercial?: boolean;
}
  • docType 있으면 기존과 100% 동일(서류 생성 호출처 영향 0)
  • docType 없으면 recoveryType + 금액 bucket + 상사 토큰만으로 쿼리 — "이 사건과 비슷한 과거 사건들"
  • rankMemories 의 메타 보너스는 이미 candidate.docType 존재 조건이라 자연 처리(docType 없으면 +0.3 보너스만 생략, cosine 주축)
  • find-related-memories-v2-core/impl/action, dispatch-related-memories-actiondocType 도 optional 전파

3. 흐름 (변경 후)

사건 상세 / 기억 상세

legacy/_components/RelatedMemoriesPanel (caseId)

dispatchRelatedMemoriesAction({ caseId }) ← docType 없이
├─ tenantEmbedding ON → findRelatedMemoriesV2Action
│ ├─ buildRelatedMemoryQuery({ recoveryType, principal, isCommercial }) ← docType 생략
│ ├─ embedQuery → guardedFindNearest(legacyDocs, COSINE)
│ └─ rankMemories → cards[{ docId, finalScore, cosineScore, metaBonus, matchReasons }]
└─ skipped → 빈 상태 (조용히)

(선택) Gemini 분석 — V2 cards snippet 으로 닮은점·차이점 분석 + history 영속화

후속 — Phase 2~4 (별도 PR, 이 ADR 의 방향성)

Phase작업한 줄 본질 기여
2사무소 지능 대화/legacy 에 Tenant RAG 전용 채팅 추가. LibraryChatWorkspace 패턴 재활용하되 publicDocuments 결합 제거, legacyDocuments 만.정면 구현
3판례 공공 정제searchLibraryRagAction 에서 buildCitationsFromTenantDocs·tenantPromise(Tenant 4건) 제거. 사무소 기억 대화는 Phase 2 기억 메뉴로 완전 이관. publicCaseRag 플래그 Firestore 부재 정정.경계 확정
4집단 인사이트 — 연대기(Chronicle)를 정적 타임라인에서 살아있는 통계로(회수율·인용 Top·평균 처리기간).메타 지능

firm-memory.md 드리프트 정정 (이 ADR 동반)

apps/docs/content/product/firm-memory.md 는 ADR 0002·0015·0017 이전 표현이 잔존해 정정한다:

  • "Pro 전용"(line 37·65) → ADR 0002(전 사용자 무료)로 폐기. tier 차별 표현 제거.
  • "Vertex AI Search"(line 85·94·120) → ADR 0015/0017 V2 임베딩(text-embedding-004 + findNearest)으로 대체됨.
  • findRelatedMemoriesAction 을 "기억 검색 SSoT"로 지칭한 부분 → 가짜(caseId 동일필터)임을 명시하고 V2 dispatch 로 갱신.

위험·완화

위험완화
buildRelatedMemoryQuery docType optional 화가 서류 생성 경로를 깸서류 생성 호출처는 항상 docType 전달 → 분기 미진입, 동작 100% 동일. 단위테스트로 양 분기 고정
사건 상세 패널 UX 변경(버튼 클릭형 → 자동 로드형) 혼란기존 "기억에서 찾기 + AI 분석" 버튼 트리거·Gemini 분석·history 유지. 검색 백엔드만 교체 → 가시적 UX 변화 최소
V2 가 빈 사무소·임베딩 미생성에서 0 결과skipped → 조용한 빈 상태(기존과 동일). caseId equality 가짜보다 "정직하게 비어있음"이 옳음
전략 보고서가 가짜 findRelatedMemoriesAction 의존본 PR 범위 밖. Phase 2~3 에서 V2 경로로 일원화 검토(별도 평가). 이번엔 패널만 우선
Tenant 결합 제거(Phase 3)가 판례 채팅 답변 품질 저하Phase 2(기억 채팅)가 ship 된 뒤 이관 — 사무소 기억 대화 경험이 사라지지 않도록 순서 보장

Non-goal

  • 라우트·컬렉션 개명 금지 — /legacy·/library·legacyDocuments·publicDocuments 유지(ADR 0015 일관)
  • find-related-memories-v2-action.ts 등 기존 엔진 폐기 ❌ — 결선만
  • generateDocxAction·getRagContextForDocGenAction(서류 생성 두 RAG 결합) 영향 0 — ADR 0041 그대로
  • 새 임베딩 모델·검색 엔진 도입 ❌
  • Phase 2~4 는 이 ADR 의 방향성만 — 구현은 별도 PR

구현 순서 (이 ADR = Phase 1, PR 단위)

PR산출물
1본 ADR 0048 + firm-memory.md 드리프트 정정 + CLAUDE.md ADR 목록 갱신
1buildRelatedMemoryQuery docType optional + V2 core/impl/action/dispatch 전파 + 단위테스트
1legacy/_components/RelatedMemoriesPanel V2 결선(가짜 제거) + Gemini 분석 입력 교체

(ADR 0048 + Phase 1 을 한 흐름 PR 로 — "ADR 0045[0048] + Phase 1 함께" 결정 정합)

변경 이력

버전날짜변경
1.02026-06-03초안 — 두 메뉴 목적 재정의(데이터 소유권 축) + Phase 1 자동 회상 통일(가짜→V2 의미검색) 결정. production 플래그 V2 ON 검증. firm-memory.md 드리프트 정정 동반
1.12026-06-03Phase 2 (기억 메뉴 Tenant RAG 채팅 MVP, #2580) + Phase 2b (memoryChatThreads 영속화, #2581) + Phase 3 (판례 searchLibraryRagAction Tenant 결합 제거 → 공공 전용) ship. "두 AI 동료" 경계 완성 — 판례=공공 데이터, 기억=우리 사무소 데이터, 서류 생성 시에만 자동 결합(ADR 0041)
1.22026-06-03Phase 4 (연대기 집단 인사이트 — 회수율·처리기간·유형 분포, 추가 fetch 0) ship. ADR 0048 전체 완성 — 기억(①대화 ②자동회상 ③집단패턴) + 판례(공공 전용) 4 축 모두 production