Platform RAG 일상 운영 (ADR 0021)
전 tenant 공용 판례·법령 RAG (publicDocuments) 의 수집·임베딩·재처리·장애 대응 절차. ADR 0021 + architecture/platform-rag.md 가 SSoT, 본 문서는 운영 측 절차.
1. 데이터 흐름 (한 그림)
법제처 OPEN API
│
├── 매일 02:00 KST: scheduledFetchLawGoKr (Cloud Scheduler)
├── ops 수동: fetchAndIngestLawGoKr (단건 / 벌크)
└── 스크립트: download-law-go-kr-bulk.ts → upload-public-raw-to-firestore.ts
│
▼
publicDocumentsRaw (원본 보존, masterAdmin 만 read, Admin SDK only write)
│ (linkedPublicDocId 양방향)
▼
publicDocuments (인증 tenant read, masterAdmin write)
│
▼
onPublicDocumentCreated trigger (Cloud Function)
│
├── prepareEmbedInput (정제, tenant RAG 와 동일)
├── Vertex AI text-embedding-004 (768-dim)
└── meanPoolVectors (다중 chunk 평균)
│
▼
publicDocuments.embedding {version, dims, vector, generatedAtIso, chunkCount}
2. 일상 모니터링
2.1 매일 09:00 KST
Firebase Console → Firestore → publicDocuments:
- 어제(02:00 KST 기준) 신규 문서 +30 ± 변동 = 정상 (core 21 카테고리 × page 1 × display 20 = 최대 420 후보, 멱등 skip 후 30 내외)
- 0 건 = scheduler 실패 의심 (§4 절차)
2.2 임베딩 누락 점검
# 임베딩 안 된 문서 (embeddedAt: null)
firebase firestore:export \
--project <projectId> \
--collection-ids publicDocuments \
| jq '[.[] | select(.embeddedAt == null)] | length'
- 누락 0~5 = 일시적 (재시도 trigger 자동 처리)
- 누락 10+ =
embeddingError누적 → §5 절차
2.3 Sentry alert (TODO)
onPublicDocumentCreated임베딩 실패 임계값scheduledFetchLawGoKr실패
3. 수집 경로 3 종
3.1 Cloud Scheduler (자동, 매일 02:00 KST)
- 함수:
scheduledFetchLawGoKr - 범위: core 21 카테고리 (대여금·공사대금·구상금·약정금·임대차·양수금·이혼·부동산·상속·계약 등)
- 페이지: page 1 × display 20
- 멱등:
(court + caseNumber)키, 기존 문서 skip - secret:
LAW_GO_KR_OC(Firebase Secret Manager)
3.2 Ops 수동 (UI)
- 위치:
apps/ops/app/(ops)/public-rag/ - 단건: 유형·출처·타이틀·본문·메타 입력 → addDoc
- 벌크:
law.go.krAPI JSON 응답 paste → 어댑터가 정규화 + skip 이유 수집 + 일괄 addDoc - 어댑터:
normalizeDecisionDate·parseCitedStatutes·pickContent(판례내용 > 판결요지 > 판시사항)
3.3 스크립트 bulk
# 다운로드 (로컬 JSON)
NODE_PATH=apps/web/node_modules \
npx tsx scripts/download-law-go-kr-bulk.ts \
--query "대여금" --pages 5 --display 50
# 검증
npx tsx scripts/verify-downloaded-data.ts <output-dir>
# 업로드 (publicDocumentsRaw → publicDocuments cascade)
NODE_PATH=apps/web/node_modules \
npx tsx scripts/upload-public-raw-to-firestore.ts <output-dir>
→ 임베딩은 onPublicDocumentCreated trigger 가 자동.
4. 장애 대응 — Cloud Scheduler 실패
증상: 어제 신규 0 건
Step 1: scheduler 로그
Firebase Console → Functions → scheduledFetchLawGoKr → Logs
Error: 401→ secret 만료 (LAW_GO_KR_OC 재발급)Error: 429→ rate limit (자동 재시도 다음 날까지 대기)Error: domain check failed→ Referer 헤더 검증 실패 (settings 변경 여부 확인)
Step 2: 재실행
# 수동 트리거 (gcloud)
gcloud scheduler jobs run firebase-schedule-scheduledFetchLawGoKr-asia-northeast3 \
--location=asia-northeast3 \
--project=<projectId>
Step 3: 멱등성 확인
publicDocuments 에 동일 (court + caseNumber) 중복 없는지 firestore-rules-invariants test 통과 확인.
5. 임베딩 누락 복구
5.1 단건 재시도
# ops 콘솔 → public-rag → 해당 문서 → "재시도" 버튼
# 또는 onCall 함수 직접 호출
firebase functions:shell
> retryPublicDocEmbedding({docId: "<id>"})
5.2 대량 재처리
scripts/reembed-public-docs.ts 4 모드:
# (A) 임베딩 실패한 문서만
NODE_PATH=apps/web/node_modules \
npx tsx scripts/reembed-public-docs.ts --only-failed
# (B) 임베딩 누락 (embeddedAt: null) 만
npx tsx scripts/reembed-public-docs.ts --only-missing
# (C) 모델 버전 불일치 (예: text-embedding-004 → 005 마이그레이션)
npx tsx scripts/reembed-public-docs.ts --model-version-mismatch
# (D) 전체 재임베딩 (low priority, 비용 주의)
npx tsx scripts/reembed-public-docs.ts --all
6. 재파싱 (HTML 정제·스키마 확장)
원본이 publicDocumentsRaw.raw 에 보존되어 있어 네트워크 재호출 없이 publicDocuments 재구축 가능.
# 어댑터 변경 후
NODE_PATH=apps/web/node_modules \
npx tsx scripts/rebuild-public-docs-from-raw.ts
→ 변환 로직은 scripts/lib/law-go-kr-convert.ts (scripts 경로) + functions/src/law-go-kr-adapter.ts (Cloud Function 경로) 양쪽에 복제. 의미 변경 시 양쪽 동시 수정 필수. 회귀 가드:
apps/web/__tests__/scripts-law-go-kr-convert.test.tsfunctions/__tests__/law-go-kr-adapter.test.ts
7. Secret Manager
# OC 키 갱신
firebase functions:secrets:set LAW_GO_KR_OC --project <projectId>
# Referer 변경
firebase apphosting:secrets:set LAW_GO_KR_REFERER --project <projectId>
→ Secret 변경 후 functions 재배포 필요.
8. 도메인 검증 우회 (참고)
Cloud Functions outbound IP 가 동적이므로 법제처 OPEN API 도메인 등록 불가. 대신:
- 법제처 OPEN API 신청 시 "도메인 없음" 으로 신청
- Referer 헤더만 검증
- IP 화이트리스트 불필요
9. 권한·격리 모델
publicDocuments (tenant path 밖)
├── allow read: if isAuthenticated() # 모든 tenant 읽기 OK
└── allow write: if isAuthenticated() &&
exists(/databases/$(database)/documents/masterAdmins/$(request.auth.token.email))
publicDocumentsRaw (원본 보존)
├── allow read: if masterAdmin
└── allow write: if false # Admin SDK only
cross-tenant 유출 우려 없음 — 공공 데이터.
10. 관련 PR (구현 이력)
| Phase | PR | 핵심 산출물 |
|---|---|---|
| 1 | #712 | download-law-go-kr-bulk.ts · verify-downloaded-data.ts — 4,054 건 시드 |
| 2 | #713 | 민법·상법·민사소송/집행법 등 3,589 조문 |
| 3 | #721 | retryPublicDocEmbedding onCall + 30일 누적 그래프 |
| 4a | #722 | opsGenerateTestDoc |
| 4b | #723 | opsRagChat |
| 5a | #724 | /library 판례·법령 검색 |
| 5b | #725 | /library AI 대화 |
| 6 | #726 | scheduledFetchLawGoKr Cloud Scheduler |
| 7 | #727 | rebuild-public-docs-from-raw.ts · reembed-public-docs.ts |
| 8 | #728 | architecture/platform-rag.md SSoT 정리 |
관련 문서
- Platform RAG 아키텍처 — SSoT
- ADR 0021 Platform RAG
- Public RAG 벌크 수집 — 스크립트 운영
- 공용 템플릿 playbook