MyStory/Consensus_Verifiers

MeshProof: 두 모델이 합의할 때까지 수렴하는 QA 시스템

LupyLaon 2025. 8. 24. 16:14

MeshProof: 두 모델이 합의할 때까지 수렴하는 QA 시스템

consensus-verifiers로 만드는 신뢰형 LLM 파이프라인

(1/3) 왜 "두 모델 합의(Consensus)"인가 — 문제 정의 → 설계


🎯 한 문장 요약

MeshProof는 GPT와 Claude가 서로의 답을 검증하고 합의할 때까지 수렴시켜 신뢰 가능한 최종 답을 만드는 QA 시스템입니다.


🔍 문제 정의

현재 LLM의 한계

문제점 설명 예시

틀린 확신 Hallucination으로 잘못된 정보를 확신있게 전달 "2024년 노벨물리학상은 김철수가 수상했습니다"
형식 오류 요청한 형태와 다른 응답 형식 JSON 요청했는데 일반 텍스트로 응답
검증 불가 답변의 근거나 신뢰도를 알 수 없음 "이 정보가 정확한가?" → 별도 확인 필요
출처 부족 중요한 사실 확인이 필요한데 링크나 날짜 없음 "한국 수출 통계" → 출처나 기준일 미표기

사용자가 원하는 것

  • 정확성: 검증된 올바른 정보
  • 설명가능성: 단순한 답이 아닌 검증 가능한 이유
  • 신뢰도: 답변에 대한 정량적 신뢰 지표
  • 추적가능성: 출처와 근거가 명확한 답변

💡 핵심 가설

1. 중복성 (Redundancy)

// 동시에 두 모델 호출
const [gptAnswer, claudeAnswer] = await Promise.all([
    askOpenAI(question),
    askAnthropic(question)
]);

// 결과가 같으면 신뢰도 ⬆
if (isConsensus(gptAnswer, claudeAnswer)) {
    return { confidence: 0.95, agreed: true };
}

이질적인 두 모델같은 결론이면 신뢰도가 오른다.

2. 규칙 검증 (Verification)

const registry = new VerifierRegistry()
    .use(MathSumVerifier())           // 수학 계산 정확성
    .use(FactCitationVerifier({       // 출처 검증
        minLinks: 2,                  // 최소 2개 링크
        requireDeepLinks: true,       // 상세 페이지 필요
        requireDatesInReasoning: true // 날짜 표기 필수
    }))
    .use(BasicQualityVerifier())      // 기본 품질
    .use(FormatRegexVerifier());      // 형식 검증

// 점수 산출: 0.6 * 기본통과 + 0.2 * 깊은링크비율 + 0.2 * 도메인가중치

**도메인 전용 검증기(Verifier)**로 답을 정량 평가하면 품질이 오른다.

3. 합의 (Consensus)

// 수학 문제: 정수쌍 정규화로 세트 동치 비교
function canonicalizePairs(text: string): string {
    // "(1,4), (2,3)" → "[[1,4],[2,3]]"
    // "(2,3), (1,4)" → "[[1,4],[2,3]]"  (동일)
}

// 일반 문제: OpenAI Judge 활용
const verdict = await judgeOpenAI(question, gptAnswer, claudeAnswer);
// → { relation: "equivalent"|"different", winner: "A"|"B"|"tie" }

다르면 정규화 비교 또는 Judge AI 판정으로 최종 답 결정.


🏗️ 시스템 아키텍처

graph TB
    A[React Frontend<br/>Vite + TypeScript] --> B[Express.js API Server<br/>Node.js + TypeScript]
    
    B --> C[Authentication<br/>JWT Cookie + CSRF]
    B --> D[Key Vault<br/>AES-256-GCM Encryption]
    B --> E[Orchestrator Engine]
    
    E --> F[OpenAI Provider<br/>gpt-5 + Responses API]
    E --> G[Anthropic Provider<br/>claude-sonnet-4]
    E --> H[OpenAI Judge<br/>답변 동치성 판정]
    
    B --> I[Verifier Registry<br/>플러그인 시스템]
    I --> J[math.sum<br/>정수쌍 합계 검증]
    I --> K[fact.citation<br/>출처 링크 검증]
    I --> L[basic.quality<br/>답변 품질 검증]
    I --> M[format.regex<br/>형식 검증]
    
    B --> N[(SQLite Database<br/>사용자/암호화키)]
    
    style E fill:#fff3e0
    style I fill:#e8f5e8
    style F fill:#e3f2fd
    style G fill:#fce4ec

핵심 데이터 타입

// 표준화된 답변 스키마
type Answer = {
    answer: string;              // 최종 답변
    reasoning?: string;          // 근거 (출처 링크 포함)
    confidence?: number;         // 0~1 자체 신뢰도
    assumptions?: string[];      // 전제 조건들
};

// 검증 결과
type VerifyResult = {
    ok: boolean;                 // 통과 여부
    score?: number;              // 0~1 정량 점수
    notes?: string[];            // 상세 메모
    evidence?: string[];         // 추출된 링크들
    category?: string;           // 검증기 분류
};

⚖️ 합의 전략 (도메인별 실제 구현)

📊 수학 문제: 정수쌍 정규화

// 질문: "더해서 5가 나오는 정수 2개의 쌍은?"
function canonicalizePairs(text: string): string {
    const pairs: Array<[number, number]> = [];
    
    // (x,y) 패턴 추출
    for (const m of text.matchAll(/\((-?\d+)\s*[,;]\s*(-?\d+)\)/g)) {
        const a = Number(m[1]), b = Number(m[2]);
        pairs.push([Math.min(a, b), Math.max(a, b)]);
    }
    
    // "x + y" 패턴 추출  
    for (const m of text.matchAll(/(-?\d+)\s*\+\s*(-?\d+)/g)) {
        const a = Number(m[1]), b = Number(m[2]);
        pairs.push([Math.min(a, b), Math.max(a, b)]);
    }
    
    // 중복 제거 후 정렬
    const uniq = Array.from(new Set(pairs.map(([a,b]) => `${a},${b}`)))
        .map(s => s.split(",").map(Number) as [number, number]);
    uniq.sort((p, q) => (p[0] - q[0]) || (p[1] - q[1]));
    
    return JSON.stringify(uniq);
}

// 예시 비교
GPT: "(1,4), (2,3)"     → "[[1,4],[2,3]]"
Claude: "(2,3), (1,4)"  → "[[1,4],[2,3]]"  ✅ 동치!

🧠 일반 지식: Judge AI 판정

const judgePrompt = `You are a strict judge. Output a JSON object with keys:
relation: "equivalent"|"different"  
winner: "A"|"B"|"tie"
rationale: short string`;

// 실제 판정 예시
{
  "relation": "different",
  "winner": "B", 
  "rationale": "Answer B provides more comprehensive analysis with concrete examples"
}

🔗 출처 검증: 실제 링크 품질 평가

const FactCitationVerifier = ({
    minLinks: 2,                    // 최소 2개 링크 필요
    requireDeepLinks: true,         // 루트 도메인 금지
    minDeepLinks: 1,               // 깊은 링크 최소 1개
    requireDatesInReasoning: true,  // YYYY-MM-DD 날짜 필수
    weights: {
        whitelistBoost: {
            "kostat.go.kr": 0.25,     // 통계청 가산점
            "bok.or.kr": 0.2,         // 한국은행 가산점
            "kita.net": 0.15          // 무역협회 가산점
        },
        blacklistPenalty: {
            "medium.com": 0.2,        // 개인 블로그 감점
            "tistory.com": 0.15       // 개인 블로그 감점
        }
    }
});

// 점수 계산: 0.6 * 기본통과 + 0.2 * 깊은링크비율 + 0.2 * 도메인가중치

🔄 오케스트레이션 플로우

flowchart TD
    A[질문 입력] --> B[병렬 모델 호출]
    B --> C[GPT 답변] 
    B --> D[Claude 답변]
    
    C --> E[검증기 실행]
    D --> F[검증기 실행]
    
    E --> G{수학 문제?}
    F --> G
    
    G -->|Yes| H[정수쌍 정규화 비교]
    G -->|No| I[Judge AI 판정]
    
    H --> J{세트 동치?}
    I --> K{동치 판정?}
    
    J -->|Yes| L[✅ 합의 달성<br/>즉시 반환]
    K -->|equivalent| L
    
    J -->|No| M[재시도 필요?]
    K -->|different| N[승자 선택]
    
    M -->|Yes| O[출처 보강 재요청]
    M -->|No| N
    
    O --> B
    N --> P[최종 답변 반환]
    L --> P
    
    style L fill:#c8e6c9
    style P fill:#fff3e0

실제 정지 조건 (우선순위)

순위 조건 행동 신뢰도

1순위 🎯 수학 세트 동치<br/>canonicalizePairs(A) === canonicalizePairs(B) 즉시 정지 최고 (95%+)
2순위 🤝 Judge: equivalent<br/>verdict.relation === "equivalent" 검증점수 높은 쪽 높음 (85-95%)
3순위 🏆 Judge: 명확한 승자<br/>verdict.winner !== "tie" 승자 선택 중간 (70-85%)
4순위 🔄 재시도 조건<br/>출처 부족 시 1회 재요청 보강 후 재판정 개선됨
5순위 ⚖️ 점수 기반 선택<br/>verifierScore + confidence 높은 점수 선택 낮음 (50-70%)

🎪 실제 동작 예시

Case 1: 수학 문제 (세트 동치 합의)

📝 질문: "더해서 5가 나오는 정수 2개의 쌍은?"

🤖 GPT: {
  answer: "(1,4), (2,3)",
  reasoning: "1+4=5, 2+3=5 입니다.",
  confidence: 0.95
}

🤖 Claude: {
  answer: "(2,3), (1,4)", 
  reasoning: "기본적인 정수쌍입니다. 2+3=5, 1+4=5",
  confidence: 0.90
}

🔍 정규화:
GPT   → "[[1,4],[2,3]]"
Claude → "[[1,4],[2,3]]"  ✅ 동치!

🎯 결과: 즉시 합의 달성 (신뢰도: 95%)

Case 2: 출처 부족으로 재시도

📝 질문: "2024년 한국 수출 통계는?"

🤖 GPT (1차): {
  answer: "2024년 한국 수출은 전년 대비 증가했습니다",
  reasoning: "무역 관련 데이터 분석 결과입니다"  ❌ 출처 없음
}

🔍 검증 결과: minLinks 미달, 날짜 표기 없음

🔄 재시도 요청: 
"[중요 지시]
- reasoning에 최신 출처를 최소 2개 포함하세요
- 홈페이지 루트 링크 금지, 상세 페이지(Deep link) URL 사용
- 발표/게시 날짜(YYYY-MM-DD) 표기하세요"

🤖 GPT (2차): {
  answer: "2024년 한국 수출은 6,835억 달러로 전년 대비 8.9% 증가",
  reasoning: "관세청 수출입 통계(2024-12-01): https://unipass.customs.go.kr/stats/2024/export-stats, 
             KOTRA 보고서(2024-11-28): https://kita.net/trade-report/2024-export-analysis",
  confidence: 0.88
}

✅ 검증 통과: 깊은 링크 2개, 날짜 표기 완료

Case 3: Judge 개입 (서로 다른 관점)

📝 질문: "블록체인 기술의 미래 전망은?"

🤖 GPT: "기술적 혁신과 확장성 개선이 핵심..."
🤖 Claude: "규제 환경과 사회적 수용성이 더 중요..."

⚖️ Judge 판정: {
  "relation": "different",
  "winner": "B",
  "rationale": "Claude's answer covers broader perspective including regulatory aspects"
}

🏆 결과: Claude 답변 선택 (신뢰도: 78%)

🛠️ 기술적 특징

플러그인형 검증 시스템

// 새로운 검증기 추가가 간단
const registry = new VerifierRegistry()
    .use(MathSumVerifier())
    .use(FactCitationVerifier({ 
        weights: {
            whitelistBoost: { "stat.kita.net": 0.25 }  // 도메인별 가중치
        }
    }))
    .use(BasicQualityVerifier())
    .use(FormatRegexVerifier(/^\d{4}-\d{2}-\d{2}$/));  // 날짜 형식 강제

// 모든 검증기 실행 후 집계
const result = await registry.verifyAndAggregate(question, answer);

안전한 키 관리 시스템

// AES-256-GCM 암호화로 API 키 저장
app.put("/api/auth/me/keys", authRequired, async (req, res) => {
    const { openaiKey, anthropicKey } = req.body;
    const encryptedKeys = await encryptKeys({ openaiKey, anthropicKey });
    await saveUserKeys(req.user.id, encryptedKeys);
    res.json({ success: true });
});

// BYOK (Bring Your Own Key) 또는 저장된 키 자동 사용
app.post("/api/orchestrate", async (req, res) => {
    const keys = req.body.keys || await loadUserKeys(req.user?.id);
    const result = await orchestrate(req.body.q, req.body.debug, keys);
    res.json(result);
});

🔮 다음 편 예고

2편에서는 MeshProof의 핵심 검증기와 오케스트레이션 엔진 구현을 깊이 파헤칩니다:

  • 🧮 MathSumVerifier: 정수쌍 추출과 정규화 알고리즘
  • 🔗 FactCitationVerifier: URL 추출과 도메인 가중치 시스템
  • 🤖 Judge System: OpenAI 기반 답변 동치성 판정 로직
  • 🔄 Orchestration Engine: 재시도와 합의 수렴 메커니즘
  • 🛡️ Security: JWT + CSRF + 키 암호화 완전 구현

핵심 질문: "어떻게 서로 다른 AI 모델들을 정교하게 검증하고 안전하게 조율할 수 있을까?"


MeshProof는 단순한 AI 답변 비교가 아닙니다.
정량적 검증과 체계적 합의를 통해 더 신뢰할 수 있는 지식을 만들어내는 엔지니어링 시스템입니다.