본문으로 건너뛰기

인용 실재성 검증 — 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단 체크리스트 미완료 제출 시도 횟수 (감사 대응)

관련 기능

관련 ADR