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 블록 밖).
후속 과제
- 판례 대량 수집:
LAW_GO_KR_OC발급 후 1,000+ 건 초기 시드 (대여금·공사대금·구상금·약정금·임대차·양수금 유형별) - AI Hub 71723 어댑터 — 25만 건 판례 데이터셋 포맷 파서 (Phase C 후속)
- Citation 번호 체계 통합 — tenant citation 과 platform citation 번호를 하나의 [1] [2] [3] 로 합쳐 CitationPopover 에서 원본 확인
- 판례 요약 품질 — 현재 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 로 나눠 구현. 설계에서 변경된 사항만 기록.
| Phase | PR | 핵심 산출물 |
|---|---|---|
| 1 | #712 | download-law-go-kr-bulk.ts · verify-downloaded-data.ts — 로컬 4,054 건 시드 (민사 채권 21 카테고리) |
| 2 | #713 | download-law-go-kr-statutes.ts + law-go-kr-statute-list.ts — 민법·상법·민사소송/집행법 등 3,589 조문 |
| 3 | #721 | retryPublicDocEmbedding onCall + 30일 누적 그래프 (cumulative 필드) |
| 4a | #722 | opsGenerateTestDoc — masterAdmin AI 초안 생성 시뮬레이션 |
| 4b | #723 | opsRagChat — masterAdmin 자연어 법률 리서치 대화 |
| 5a | #724 | /library 판례·법령 독립 검색 (structured + keyword) — tenant 변호사 용도 |
| 5b | #725 | /library AI 대화 탭 — RAG + Gemini + 인용 카드 |
| 6 | #726 | scheduledFetchLawGoKr — 매일 02:00 KST Cloud Scheduler 증분 수집 |
| 7 | #727 | rebuild-public-docs-from-raw.ts · reembed-public-docs.ts — 마이그레이션 대비 |
| 8 | #728 | 문서 정리 — architecture/platform-rag.md 신규 + ADR 이력 정리 |
설계 대비 변경점
-
publicDocumentsRaw컬렉션 추가 (설계 시 미포함)- 이유: "원본 보존해서 차후 RAG·LLM 변경 시 마이그레이션이 쉽게" 요구사항 (2026-04-24 사용자)
- 구조: 법제처 API 응답 JSON 전체를
raw필드에 그대로 저장.linkedPublicDocId로publicDocuments와 양방향 연결 - 권한: masterAdmin 읽기 · Admin SDK 만 쓰기 (rules
allow write: if false)
-
법령 조문 수집 경로 변경 —
ID=파라미터 직접 호출- 원래
query=상법검색으로 조회하려 했으나search=1이 부분 매칭이라 "특별조치법" 먼저 매칭되는 문제 - 해결:
scripts/law-go-kr-statute-list.ts에lawId하드코딩 (civil-law:001706,commercial-law:001702등)
- 원래
-
Cloud Scheduler
onSchedule추가 (설계 시 수동 수집만)- 이유: 신규 판례 자동 반영 필요
- 구현: 매일 02:00 KST core 21 카테고리 × page 1 × display 20. 법제처는 선고일 최신순 정렬이라 page 1 만으로 신규 포함
- 멱등 (
court + caseNumber) 기반 skip 으로 기존 embedding 재생성 없음
-
마이그레이션 스크립트 추가 (설계 시 미포함)
rebuild-public-docs-from-raw.ts: 파싱 규칙 변경 시 원본 재파싱 →publicDocumentsupdatereembed-public-docs.ts: 임베딩 모델 변경·실패 누적 재처리.--only-failed/--only-missing/--model-version-mismatch/--all4 모드
-
OC 키 관리 — Firebase Secret Manager
- 초기 계획:
functions/.env평문 - 최종:
defineSecret("LAW_GO_KR_OC")+defineString("LAW_GO_KR_REFERER")— Secret Manager 버전 관리
- 초기 계획:
-
도메인 검증 우회
- 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)