본문으로 건너뛰기

ADR 0021 — Platform RAG (전 tenant 공용 판례·법령 층)

결정

기존 tenant RAG (ADR 0015/0017 · 자기 사무소 기억) 에 더해 Platform RAG 를 신설한다. 법고을·법제처·국가법령정보센터의 공공 판례·법령·행정규칙·주석publicDocuments 단일 컬렉션 (tenant path ) 에 적재하고, 모든 tenant 의 AI 가 병렬 검색·인용할 수 있도록 한다.

AI 프롬프트는 두 namespace 를 동시에 주입한다:

  • [내 사무실 과거 사건] {snippet} — tenant RAG (사무소 사적 데이터)
  • [공공 판례·법령] {badge} ({date}): {snippet} — Platform RAG (공공 데이터)

이로써 AI 초안은 "이 사무소가 비슷한 사건을 어떻게 처리했는지 × 대법원·하급심이 이 쟁점에서 어떻게 판시했는지" 를 동시에 반영한다. 경쟁사 없는 포지션.

배경·안건

제품 정체성은 "자기 사무소 AI 가 자기 사무소 방식으로 학습". tenant RAG 는 이 축을 완성했지만, 변호사가 실제로 쓰는 자료의 절반은 대법원 판례 다. 판례 없는 자기 사무소 기억만으로는 법리 근거가 취약하다.

법원 판례는 공공 데이터로 무료 제공된다 (법제처 open.law.go.kr). 이를 한 번 수집해 전 tenant 가 공유하면 (a) 법리 근거 보강 (b) 다운스트림 AI 품질 향상 (c) cross-tenant 정보 유출 우려 없음 (공공 데이터).

동시에 tenant 사적 데이터는 여전히 tenants/{tid}/legacyDocuments 경로로 격리 유지. 두 층이 별개 Firestore 컬렉션으로 서로 섞이지 않는다.

대안 검토

A. Platform 레이어 안 만들고 tenant 만 유지

  • 장점: 아키텍처 단순
  • 단점: 판례 없음 → AI 법리 근거 취약 · 경쟁 제품과 미분화

B. tenant RAG 에 판례를 복제 적재

  • 장점: 단일 검색 경로
  • 단점: 모든 사무소가 동일 판례 25만 건 복제 → 용량 250배 낭비 · embedding 재계산 25만×N회

C. Platform RAG 별도 컬렉션 (선택)

  • 장점: 판례는 1회 적재 · 모든 tenant 가 참조 · tenant 격리 원칙 유지
  • 단점: 2개 findNearest 병렬 (소폭 복잡도)

선택: C. tenant 격리의 본질은 "사적 데이터 보호" 이고 공공 판례는 애초 비격리 대상.

결정 내용

1. Firestore 스키마

publicDocuments/{docId}
sourceType: "court_precedent" | "statute" | "admin_rule" | "commentary"
origin: "beopgoeul_lx" | "law_go_kr" | "scourt_openapi" | "aihub_71723" | "manual"
title: string
content: string
sourceUrl?: string
tags: string[]
precedentMeta?: { court, caseNumber, decisionDate, caseName, holdingSummary, citedStatutes }
statuteMeta?: { lawName, articleNumber, effectiveDate, status }
embedding?: { version, dims, vector, generatedAtIso, chunkCount }
embeddedAt: string | null
embeddingError: string | null
uploadedByEmail: string
createdAt: Timestamp
updatedAt: Timestamp

2. Firestore rules

match /publicDocuments/{docId} {
allow read: if isAuthenticated(); // 모든 인증된 tenant 읽기 허용
allow write: if isAuthenticated() &&
exists(/databases/$(database)/documents/masterAdmins/$(request.auth.token.email));
}

3. 업로드 경로

  • ops 콘솔 (apps/ops/app/(ops)/public-rag/): masterAdmin 전용
    • 단건 업로드 폼 — 유형·출처·타이틀·본문·메타 입력
    • 벌크 업로드 (/bulk) — law.go.kr 판례 API JSON 응답 paste → 어댑터가 필드 정규화 + skip 이유 수집 + 일괄 addDoc
    • 어댑터: normalizeDecisionDate (YYYYMMDD · YYYY.MM.DD · YYYY-MM-DD 모두 수용) · parseCitedStatutes · pickContent (판례내용 > 판결요지 > 판시사항 fallback)
  • 배치 수집 스크립트 (scripts/fetch-law-go-kr-precedents.ts): LAW_GO_KR_OC=<key> + 쿼리 → JSON 파일 → ops 벌크 paste

4. 임베딩 파이프라인

Cloud Function onPublicDocumentCreated (functions/src/on-public-document-created.ts):

  • prepareEmbedInput 재활용 (기존 tenant RAG 경로와 동일 정제)
  • Vertex AI text-embedding-004 → 768-dim 벡터
  • 다중 chunk → meanPoolVectors 평균 풀링
  • 실패 시 embeddingError 기록 (재시도 가능)

5. 검색

findRelatedPrecedentsAction (Server Action):

  • tenant-scoped guardedFindNearest 가 아닌 findNearestPublic 사용 (tenant path 밖 의도)
  • distanceResultField: "_dist" 로 cosine distance 회수
  • impl.ts 순수 함수 DI — 쿼리 빌더 + 카드·citation 조립
  • publicCaseRag 플래그 (기본 ON) 로 1-click 차단

6. UI 통합

  • 에디터 사이드바 (RelatedPrecedentsPanel): RelatedMemoriesPanel 옆에 병렬 렌더
    • 수동 검색 박스 (debounce 600ms)
    • 카드: "대법원 2024다12345 · 2024-03-15" 배지 · snippet · relevance %
    • "인용 삽입" 클릭 시 에디터에 blockquote 로 삽입
  • AI 다듬기 프롬프트: [내 사무실 과거 사건] · [공공 판례·법령] 두 라벨로 구분 주입

7. Seed

  • scripts/seed-public-statutes.ts — 민법·상법·민사집행법·민사소송법 주요 조문 22선
  • scripts/seed-public-precedents.ts — 민사 채권 회수 실무 자주 인용되는 판례 12선 (판시사항 요약, 원문 전재 아님)

검증 전략

내부 합성 E2E (ADR 0020 feedback_no_pilot_mock_only 정합):

platform-rag/__tests__/e2e-scenario.test.ts 에서 fixture embedding (n-gram 결정론 벡터) 로 외부 API 없이:

  • 3 개 가상 판례 seed
  • "대여금 소멸시효" 쿼리 → 대여금 판례 #1
  • "공사대금 지연손해금" 쿼리 → 공사대금 판례 #1
  • "보험자 대위" 쿼리 → 구상금 판례 #1
  • plagg OFF · access denied · 빈 결과 경계 경로 커버

Firestore rules 불변조건: firestore-rules-invariants.test.ts 에 Platform RAG 블록 추가 (읽기=auth · 쓰기=masterAdmin · tenants 블록 밖).

후속 과제

  1. 판례 대량 수집: LAW_GO_KR_OC 발급 후 1,000+ 건 초기 시드 (대여금·공사대금·구상금·약정금·임대차·양수금 유형별)
  2. AI Hub 71723 어댑터 — 25만 건 판례 데이터셋 포맷 파서 (Phase C 후속)
  3. Citation 번호 체계 통합 — tenant citation 과 platform citation 번호를 하나의 [1] [2] [3] 로 합쳐 CitationPopover 에서 원본 확인
  4. 판례 요약 품질 — 현재 seed 는 판시사항 요약 기준. 원문 전체가 필요한 경우 법제처 본문 API 또는 수동 업로드

Minority Report

  • P7 (보안): Platform RAG 에 저작권 보호 저작물 (변호사 주석서 등) 을 잘못 적재하면 저작권 이슈 발생. sourceType="commentary" 는 저작권 허용 범위만 허용하도록 업로드 UI 에 경고 추가 권장.
  • P1 (변호사): 판례 요약은 판시사항만이라 때로는 부족. 실제 사건 판단은 판결 이유 전체가 필요. → Phase C 본문 API 수집으로 해결 예정.

성공 지표

  • Platform RAG 활성화 후 에디터 사이드바에 "공공 판례·법령 ≥1건" 표시되는 세션 비율 ≥ 70%
  • AI refine 결과에 "대법원 · 상법 §XXX · 민법 §YYY" 패턴 등장 비율 (품질 모니터링 — 후속)
  • tenant RAG 와 Platform RAG 동시 인용 세션 비율 ≥ 30% (이중 컨텍스트 활용 증거)

구현 이력 (2026-04-24 ~ 2026-04-25)

이 ADR 은 2026-04-24 설계 확정 후 8 phase 로 나눠 구현. 설계에서 변경된 사항만 기록.

PhasePR핵심 산출물
1#712download-law-go-kr-bulk.ts · verify-downloaded-data.ts — 로컬 4,054 건 시드 (민사 채권 21 카테고리)
2#713download-law-go-kr-statutes.ts + law-go-kr-statute-list.ts — 민법·상법·민사소송/집행법 등 3,589 조문
3#721retryPublicDocEmbedding onCall + 30일 누적 그래프 (cumulative 필드)
4a#722opsGenerateTestDoc — masterAdmin AI 초안 생성 시뮬레이션
4b#723opsRagChat — masterAdmin 자연어 법률 리서치 대화
5a#724/library 판례·법령 독립 검색 (structured + keyword) — tenant 변호사 용도
5b#725/library AI 대화 탭 — RAG + Gemini + 인용 카드
6#726scheduledFetchLawGoKr — 매일 02:00 KST Cloud Scheduler 증분 수집
7#727rebuild-public-docs-from-raw.ts · reembed-public-docs.ts — 마이그레이션 대비
8#728문서 정리 — architecture/platform-rag.md 신규 + ADR 이력 정리

설계 대비 변경점

  1. publicDocumentsRaw 컬렉션 추가 (설계 시 미포함)

    • 이유: "원본 보존해서 차후 RAG·LLM 변경 시 마이그레이션이 쉽게" 요구사항 (2026-04-24 사용자)
    • 구조: 법제처 API 응답 JSON 전체를 raw 필드에 그대로 저장. linkedPublicDocIdpublicDocuments 와 양방향 연결
    • 권한: masterAdmin 읽기 · Admin SDK 만 쓰기 (rules allow write: if false)
  2. 법령 조문 수집 경로 변경 — ID= 파라미터 직접 호출

    • 원래 query=상법 검색으로 조회하려 했으나 search=1 이 부분 매칭이라 "특별조치법" 먼저 매칭되는 문제
    • 해결: scripts/law-go-kr-statute-list.tslawId 하드코딩 (civil-law: 001706, commercial-law: 001702 등)
  3. Cloud Scheduler onSchedule 추가 (설계 시 수동 수집만)

    • 이유: 신규 판례 자동 반영 필요
    • 구현: 매일 02:00 KST core 21 카테고리 × page 1 × display 20. 법제처는 선고일 최신순 정렬이라 page 1 만으로 신규 포함
    • 멱등 (court + caseNumber) 기반 skip 으로 기존 embedding 재생성 없음
  4. 마이그레이션 스크립트 추가 (설계 시 미포함)

    • rebuild-public-docs-from-raw.ts: 파싱 규칙 변경 시 원본 재파싱 → publicDocuments update
    • reembed-public-docs.ts: 임베딩 모델 변경·실패 누적 재처리. --only-failed / --only-missing / --model-version-mismatch / --all 4 모드
  5. OC 키 관리 — Firebase Secret Manager

    • 초기 계획: functions/.env 평문
    • 최종: defineSecret("LAW_GO_KR_OC") + defineString("LAW_GO_KR_REFERER") — Secret Manager 버전 관리
  6. 도메인 검증 우회

    • Cloud Functions 의 outbound IP 가 동적 → admin-pro.neohollo.com 등록해도 불일치
    • 해결: 법제처 OPEN API 를 "도메인 없음" 으로 신청 · Referer 헤더만 검증에 활용

후속 과제 업데이트

  • ✅ Phase 1~8 전부 merge
  • ⚙️ AI Hub 71723 대량 판례셋 어댑터 (별도 ADR 0022 후보)
  • ⚙️ Citation 번호 체계 통합 ([1][2] 단일 체계 · UI + 프롬프트 양쪽)
  • ⚙️ 품질 모니터링 — Platform RAG 인용 실제 반영률 샘플링 + 수동 평가 루프
  • ⚙️ commentary 유형 저작권 경고 UI (Minority Report P7)