인용 실재성 검증 — Hallucination 방어 3단
기반 ADR: 0005 §5 Hallucination 방어 · 0006 §10 승소 판단 경계
개요
AI 생성 서류에 존재하지 않는 판례 인용 이 들어가면 변호사 과실 배상 · 징계 직결. Mata v. Avianca (2023, SDNY) 사건 (ChatGPT 가짜 판례 인용 → 변호사 제재) 교훈 반영. 3단 자동 검증 + 변호사 서명 게이트.
왜 중요한가
- Citation Precision 0.95+ hard gate (Phase 0 기준, ADR 0005)
- 99.9% 목표 (Phase 1 aspirational)
- P1 변호사 10 조건 #1: "판례 인용 100% 실재"
- 1건 오류 → 변호사 신뢰 영구 붕괴 (P1 "3회 연속 오류 시 영구 이탈")
3단 파이프라인
1단 — 사건번호 재조회
대상: AI 생성문 내 판례 인용 (정규식 매칭)
정규식 패턴:
\d{4}(가|다|나|고|도|드|다재|나재|고재)(단|합|정|집|구|조)\d+
예: 2023다12345, 2024가단98765, 2022도5678
검증 방법:
- 대법원 종합법률정보 (glaw.scourt.go.kr) HEAD 요청 병렬
- 10건 이하 동시 fetch
- Timeout 2초
결과:
- 실재 확인 ✅ → 인용 유지
- 실재 확인 실패 ❌ → 자동 삭제 + inline 경고 배지 "AI 생성 사건번호 미확인 — 검토 필요"
Latency: +200~400ms (병렬)
False positive 대응:
- 대법원 사이트 일시 장애 시 3초 재시도 1회
- 재시도 실패 → 경고만 (자동 삭제 X, 변호사 수동 확인)
2단 — 조문 실재 확인
대상: AI 생성문 내 법령 조문 인용
정규식 패턴:
(민법|형법|상법|민사소송법|소송촉진등에관한특례법|변호사법|개인정보보호법|...) 제\d+조(의\d+)?(제\d+항)?
예: 민법 제477조, 민법 제479조, 소송촉진등에관한특례법 제3조 제1항
검증 방법:
- 국가법령정보센터 OpenAPI (
law.go.kr/DRF) 쿼리 - 24시간 Firestore 캐시 (hit rate 95%+)
- API 무료, 쿼터 일 10,000건
결과:
- 실재 확인 + 현행 ✅ → 인용 유지
- 실재 확인 + 개정됨 ⚠️ → 하이라이트 경고 ("2021.7.7 개정, 현재 이자제한법 한도 20%")
- 실재 확인 + 폐지됨 ⚠️ →
status=repealed배지, 생성 허용 (과거 사건 인용 가능) - 실재 확인 실패 ❌ → 하이라이트 경고 (자동 삭제 ✗ — API 누락 false positive 대비)
Latency: +50~100ms (캐시 hit)
관리자 화이트리스트: config/citationOverrides/{citationKey} Firestore 수동 승인 (API 누락 조문 예외 처리)
3단 — 변호사 서명 전 체크리스트 (DocSignGate)
위치: 서류 제출 전 고정 모달·인라인 컴포넌트
구성:
┌─ 서류 제출 전 확인 ──────────────────────────┐
│ │
│ ⚠️ AI 초안 · 변호사 검토 필수 │
│ │
│ □ 판례 인용 10건 모두 원문 확인 완료 │
│ (1건 미확인 인용 존재 — 자동 강조) │
│ □ 조문 인용 3건 모두 current 버전 확인 │
│ (민법 제162조 2021 개정 — 경고) │
│ □ 당사자·금액·기일 정확성 검증 │
│ │
│ [ 서명 및 제출 ] ← 3개 체크 전까지 disabled│
└──────────────────────────────────────────────┘
제약:
- 변호사 계정 (tenant owner) 2FA 통과 필수 (사무장·사무원 권한 차단)
- 3개 체크 미완료 시 외부 송출 (이메일·의뢰인 포털·PDF 다운로드) 전면 차단
- 체크 로그 Firestore
tenants/{tid}/auditLog/docSign/{draftId}5년 보존
로그 스키마:
{
draftId: string;
signerId: string; // 변호사 user id
signedAt: Timestamp;
citationsVerified: boolean;
statutesVerified: boolean;
partiesVerified: boolean;
unverifiedCitations: string[]; // 1·2단계에서 미확인 목록
ipAddress: string;
userAgent: string;
}
4. 결정론 계산 결과에도 적용 (ADR 0006 §10)
승소 판단 통계 집계 (weight 0) 결과에도:
- "유사 판례 10건 중 원고 승소 8건" 표기 시 10건 모두 실재 판례 검증
- 원본 링크 필수 제공 (변호사 직접 확인 경로)
Citation Traceability UI
span-level citation
AI 생성 응답 구조:
{
text: "민법 제477조에 따라 [1] 변제충당은 비용→이자→원본 순이며, 대법원 2023다12345 판결 [2] 에서도...",
citations: [
{
start: 0,
end: 12, // "민법 제477조"
refId: "statute:civil-477",
verified: true,
source: "국가법령정보"
},
{
start: 45,
end: 58, // "대법원 2023다12345"
refId: "case:2023da12345",
verified: true,
source: "대법원 종합법률정보",
url: "https://glaw.scourt.go.kr/..."
}
]
}
CitationPopover
- 문장 hover 2초 후 표시
- 원문 요지·참조조문·판결 결과
- 1-click 원문 새 탭
수치 금지 린트 (변호사법 §109 방어)
위치: apps/web/lib/ai/compliance-filter.ts
차단 대상 정규식:
const BANNED_PATTERNS = [
/\d+%\s*(?:확률|가능성)/, // "65% 확률"
/승소\s*(?:확률|가능성|전망)/, // "승소 확률"
/이길\s*(?:것|수\s*있)/, // "이길 것", "이길 수 있"
/패소\s*(?:확률|가능성)/, // "패소 확률"
/(?:소송을?\s*)?포기(?:하|할)/, // "포기하라", "포기할"
/소송\s*하지\s*마/, // "소송 하지 마"
];
동작:
- AI 응답 파싱 시 정규식 매칭
- 매칭 시 500 에러 +
classifyRefund발동 (crediting back) - 로그:
auditLog/complianceFilter/{draftId}5년 보존 (변호사법 형사책임 기간)
ESLint 커스텀 룰
위치: 루트 eslint.config.mjs 의 apps/web no-restricted-syntax 통합 가드
// predictWinProbability · estimateWinChance · winProbability ·
// calculateWinRate · guessVerdict 식별자를 5개 AST 셀렉터로 차단:
// FunctionDeclaration · VariableDeclarator (함수·변수 선언)
// Property · TSPropertySignature (객체/Zod 스키마 필드 · 인터페이스 필드)
// MemberExpression (필드 접근 — 구 문서 잔여 필드 읽기 차단)
Build 시 위반 발견 → PR 차단. 선언만 잡던 초기 룰을 Zod 스키마 필드
winProbability: 가 우회했던 사고 (2026-06-10 ADR 0006 §10 시행 PR) 후
프로퍼티 키·멤버 접근까지 확장. 구 Firestore 문서 호환 테스트 픽스처만
따옴표 키 ("winProbability": …) 로 예외 작성.
허용 구현 (결정론 통계)
// apps/web/app/(workspace)/cases/[caseId]/_lib/similar-case-stats.ts
export interface SimilarCaseStats {
total: number;
won: number;
partialWon: number;
lost: number;
settled: number;
caseRefs: Array<{
caseNumber: string;
outcome: Outcome;
url: string;
}>;
}
export async function computeSimilarCaseStats(
caseId: string,
ragResults: RagResult[]
): Promise<SimilarCaseStats> {
// AI 호출 없음 (weight 0)
// 결정론 집계만
}
UI 표시:
유사 판례 10건 중:
🏆 원고 승소: 8건
⚖️ 일부 승소: 1건
❌ 패소: 1건
(확률 ·가능성 등 단어 사용 금지)
각 판례 → 원본 링크 필수.
포털 격리 (의뢰인 노출 방지)
- Server Action 에서 승소 관련 필드 스트리핑
app/(portal)/라우트는strategyReport·유사 판례 통계 렌더 금지- 변호사가 "의뢰인 공유" 명시 토글 + 서명 후만 공유 (변호사 최종 검토본)
API
// 1단 검증
export async function verifyCaseNumbers(
citations: string[]
): Promise<Array<{ citation: string; verified: boolean; url?: string }>>;
// 2단 검증
export async function verifyStatutes(
citations: string[]
): Promise<Array<{ citation: string; verified: boolean; status: "current" | "amended" | "repealed" | "unknown" }>>;
// 3단 체크리스트 서명
export async function signDocument(
draftId: string,
checklist: {
citationsVerified: boolean;
statutesVerified: boolean;
partiesVerified: boolean;
}
): Promise<ActionResult<{ released: boolean }>>;
테스트
Phase 0 필수:
- Mata 재현 테스트: 가짜 사건번호 10건 삽입 → 모두 자동 삭제 확인
- 개정 조문 시나리오:
민법 제750조(손해배상) →current확인 - 폐지 조문 시나리오: 구 상법 조문 →
repealed배지 - API 장애 시나리오: 3초 재시도 · 재시도 실패 시 경고만
- 수치 금지 린트: "승소 확률 65%" 생성 시도 → 500 에러
- ESLint:
predictWinProbability함수 선언 시 build fail
지표
- Citation Precision 일 단위 모니터링 → Grafana 대시보드
- 1단 자동 삭제 비율 (AI 환각 빈도 신호)
- 2단
status=amended/repealed빈도 (개정 법령 추적) - 3단 체크리스트 미완료 제출 시도 횟수 (감사 대응)