AI 협업 개발

모델 합의를 사용하여 LLM의 수치적 판단을 안전하게 만드는 방법

단일 모델은 그럴듯해 보이지만 틀린 숫자를 생성할 수 있습니다. 결과를 신뢰하기 전에 두 개의 독립적인 모델을 교차 확인하고 코드에서 다시 계산하십시오.

이 글은 영어 원문을 AI 모델이 번역한 것입니다. 표현이 원문과 다를 수 있습니다. 영어 원문 보기

단락을 요약하는 언어 모델은 약간 틀려도 여전히 유용할 수 있습니다. 임계값을 계산하거나, 금액을 검증하거나, 두 수치가 일치하는지 확인하는 모델은 그런 관용을 누릴 수 없습니다. 숫자는 맞거나 틀리거나 둘 중 하나이며, 그럴듯하게 틀린 숫자는 명백하게 틀린 숫자보다 더 나쁩니다. 왜냐하면 검토를 통과하기 때문입니다. 이 게시물은 모델의 숫자 출력을 진실의 원천이 아닌, 확인해야 할 주장으로 취급하는 패턴을 설명합니다. 즉, 동일한 판단을 두 개의 독립적인 모델을 통해 실행하고, 두 모델이 정확히 동의할 때만 숫자를 수락하며, 중요한 것에 도달하기 전에 일반 코드로 결과를 재계산하는 것입니다.

언어는 관대하지만, 숫자는 그렇지 않습니다

LLM이 잘하는 대부분의 작업은 허용 오차가 있는 공간에 존재합니다. 요약에서 뉘앙스가 빠지거나 재작성 시 약간 어색한 단어를 선택하더라도, 독자는 여전히 의미를 파악할 수 있으며 후속 작업에 문제가 생기지 않습니다. 자연어에는 중복성이 내재되어 있어 작은 오류는 희석됩니다.

수치적 판단에는 그러한 여유가 전혀 없습니다. 모델이 1,050,000의 이체가 한도 1,000,000 이내라고 판단하거나, 할인된 가격이 다른 값으로 반올림되어야 하는데 특정 값으로 반올림된다고 판단할 때, 부분 점수란 없습니다. 출력은 게이트, 원장 또는 제어 흐름 분기로 이어지며, 잘못된 숫자 하나가 결과를 완전히 바꿔버립니다.

이를 위험하게 만드는 실패 모드는 구체적이고 반복 가능합니다.

  • 환각에 기반한 확신. 모델은 수치를 올바르게 도출했든 지어냈든 상관없이 동일한 유창한 확신을 가지고 제시합니다. 어조만으로는 어느 쪽인지 알 수 없습니다.
  • 숫자 및 자릿수 오류. 모델은 주변 문장이 완벽하게 읽히는 와중에도 0을 빠뜨리거나, 두 숫자의 위치를 바꾸거나, 한 자릿수만큼 차이 나는 답을 내놓을 수 있습니다.
  • 단위 및 통화 혼동. 백분율을 분수로, 분을 초로 읽거나, 한 통화를 다른 통화로 취급합니다. 단위가 틀렸기 때문에 계산은 내부적으로 일관되더라도 여전히 틀립니다.
  • 경계 실수. 임계값에서의 off-by-one 동작이나, 포함해야 할 한계를 배타적인 것으로 처리하여 거부되어야 할 값이 통과됩니다.

이것들은 바로 유창한 단일 답변이 가장 잘 숨기는 오류들입니다. 문장은 잘 구성되어 있고, 추론은 그럴듯하게 들리지만, 숫자는 틀렸습니다.

모델 출력을 답변이 아닌 주장으로 취급하기

핵심적인 변화는 모델에게 답변을 요청하는 것을 멈추고, 그 대신 여러분이 검증할 주장을 요청하기 시작하는 것입니다. 네 가지 규칙은 확률적 생성기를 안전한 게이트로 바꿉니다.

  1. 두 개의 독립적인 모델을 교차 확인하고, 두 모델이 동의할 때만 수용합니다.
  2. 결정론적 코드에서 숫자를 재계산하고, 불일치가 있으면 거부합니다.
  3. 구조화된 출력을 강제하여 답변을 정확하게 비교할 수 있도록 합니다.
  4. 도구나 외부 상태 없이, 모델을 순수한 판단만 내리도록 격리합니다.

이 중 어떤 것도 모델 자체를 신뢰하지 않습니다. 각각의 규칙은 출력이 틀릴 수 있다고 가정하고 그 가정을 기반으로 검증 체계를 구축합니다. 이 글의 나머지 부분에서는 이 규칙들을 하나씩 살펴봅니다.

합의 게이트: 동의 또는 중지

동일한 판단이 두 개의 독립적인 모델로 전달됩니다. 정확히 일치하면 결정론적 코드에서 숫자가 재계산되어 수락되고, 불일치가 발생하면 보류 또는 사람에게 전달되도록 닫힙니다.

첫 번째 규칙은 투표가 아닌 만장일치 게이트입니다. 서로 공유된 컨텍스트가 없는 두 개의 독립적인 모델에 동일한 판단을 보내고 그 답을 비교합니다. 두 모델 모두 동일한 숫자와 동일한 단위를 생성하면 잠정적으로 수락한 것으로 처리합니다. 조금이라도 불일치하면 중지하고 해당 사례를 사람이 처리하거나 대체 경로로 보류합니다.

만장일치라는 단어가 중요합니다. 이는 가장 일반적인 답이 이기는 다수결 규칙이 아닙니다. 숫자 안전성 측면에서 불일치는 전체 프로세스에서 가장 가치 있는 결과물입니다. 이는 적어도 하나의 모델이 여기서 틀렸다는 것을 의미하며, 아직 어느 쪽이 틀렸는지 알 수 없습니다. 다수결에 따라 그 불일치를 해결하는 것은 비용을 지불하고 얻은 바로 그 신호를 버리는 것입니다. 게이트는 닫힌 상태로 실패해야 합니다. 즉, 의심스러울 때는 숫자를 통과시키지 마십시오.

독립성은 이 게이트에 힘을 부여하는 요소입니다. 동일한 계열의 두 모델이나 동일한 모델에 두 번 쿼리하는 것은 같은 위치에서 같은 실수를 하는 경향이 있으므로, 이들 간의 합의는 거의 아무것도 증명하지 못합니다. 서로 다른 데이터로 훈련된 진정으로 다른 두 모델은 서로 다른 맹점을 가지고 있으므로, 두 모델이 우연히 동일한 오답을 생성할 가능성은 훨씬 낮습니다. 여러분은 투표 수를 세는 것이 아니라, 두 개의 개별 추정기가 동일한 지점에 도달하는지 확인하는 것입니다.

def consensus_gate(task, model_a, model_b):
    a = model_a.judge(task)   # structured: {"value": ..., "unit": ...}
    b = model_b.judge(task)   # same task, no shared context

    if a is None or b is None:
        return HOLD           # a model failed to answer, fail closed

    if a.value != b.value or a.unit != b.unit:
        return HOLD           # disagreement is a risk signal, escalate

    return Accept(a.value, a.unit)   # both agree, provisional accept

여기서 합의는 최종적인 것이 아니라 잠정적인 수락일 뿐이라는 점에 유의하세요. 두 모델은 사각지대를 공유하고 함께 틀릴 수 있습니다. 게이트는 그 확률을 낮추지만 제거하지는 않으며, 이것이 다음 규칙이 존재하는 이유입니다.

결정적 코드에서 숫자 재계산

합의(Consensus)는 대부분의 단일 모델 오류를 걸러내지만, 여전히 모델이 생성한 숫자를 신뢰하게 만듭니다. 두 번째 규칙은 그 신뢰를 제거합니다. 즉, 일반적인 계산으로 답을 도출할 수 있을 때는 언제나 직접 도출하고, 진정으로 판단이 필요한 부분에만 모델을 사용합니다.

이를 분업으로 생각하는 것이 유용한 방법입니다. 모델은 문제의 모호한 앞부분, 즉 지저분한 입력을 읽고, 어떤 행이 관련 있는지 결정하고, 모호한 필드를 분류하는 데 능숙합니다. 총합이 정확하다고 보장하는 데는 약합니다. 따라서 모델이 읽기와 선택을 하도록 하고, 산술 계산은 직접 수행하여 모델의 주장을 자신의 결과와 비교하여 확인하십시오.

결정적 계층은 세 가지 종류의 검사를 시행해야 합니다. 값을 정확한 타입으로 파싱하고, 돈에 대해서는 절대 부동소수점(float)을 사용하지 마십시오. 알려진 경계값에 대해 범위 검사를 하십시오. 그리고 어떤 모델이 뭐라고 말했든 상관없이 반드시 유지되어야 하는 불변 조건(invariant)을 단언(assert)하십시오. 예를 들어, 부분의 합이 전체와 같아야 한다는 것과 같은 조건입니다.

from decimal import Decimal

def verify(value, unit, source_rows):
    # 1. parse into an exact type, never a float for money
    amount = Decimal(value)

    # 2. range check against known bounds
    if not (MIN_AMOUNT <= amount <= MAX_AMOUNT):
        raise Reject("value is outside the allowed range")

    # 3. invariant: the parts must sum to the whole
    computed = sum(Decimal(r.amount) for r in source_rows)
    if amount != computed:
        raise Reject(f"sum mismatch: model said {amount}, code computed {computed}")

    # 4. unit must match what the pipeline expects
    if unit != EXPECTED_UNIT:
        raise Reject(f"unexpected unit {unit}")

    return amount

불일치 분기가 중요한 부분입니다. 재계산된 총계가 모델의 숫자와 일치하지 않을 때, 코드는 이 둘을 평균 내거나 하나를 선호하지 않고 거부합니다. 불일치는 모델이 잘못되었거나 모델이 읽은 입력이 코드가 읽은 입력이 아니라는 것을 의미하며, 두 경우 모두 어떤 숫자가 전달되기 전에 사람의 개입이 필요합니다. 이것이 두 모델이 우연히 공유하게 된 규모의 오류를 잡아내는 계층입니다.

숫자를 비교할 수 있도록 구조화된 출력 강제하기

모델이 문장 안에 숫자를 숨기면 두 답변을 비교하거나 하나를 재계산할 수 없습니다. 세 번째 규칙은 고정된 스키마를 가진 구조화된 출력을 요구하고, 값을 담는 필드에 자유로운 산문을 금지하는 것입니다.

스키마는 한 번에 세 가지 일을 합니다. 합의 게이트(consensus gate)에서의 비교를 깨지기 쉬운 영어 구문 분석이 아닌, 타입이 지정된 필드에 대한 정확한 일치로 만듭니다. 결정론적 계층(deterministic layer)에 해석해야 할 구문 대신 확인할 깔끔한 값을 제공합니다. 그리고 모델이 창의성을 발휘할 수 있는 여지를 좁히는데, 바로 이 지점에서 숫자 드리프트(numeric drift)가 슬그머니 들어옵니다.

다음과 같은 형태의 출력을 요청하고, 맞지 않는 것은 모두 거부하세요:

{
  "value": "1050000",
  "unit": "KRW",
  "basis": "sum_of_line_items",
  "confidence": "high"
}

전송 중에는 숫자 값을 문자열로 유지하고 도착 시 정확한 10진수 유형으로 파싱하여 모델과 검사 사이에 부동 소수점 반올림이 발생하지 않도록 하세요. 필드가 누락되거나, 주석을 추가하거나, 숫자를 설명으로 감싸는 응답은 불일치와 마찬가지로 실패한 응답으로 처리하세요. 필요한 형식으로 답변하지 않는 모델은 답변하지 않은 것입니다.

모델을 도구와 상태로부터 격리

마지막 규칙은 모델이 무엇을 건드릴 수 있는지에 대한 것입니다. 수치적 판단을 내릴 때는 입력과 질문 외에는 아무것도 주지 마십시오. 도구도, 다른 시스템을 호출할 능력도, 다른 검토자와의 공유 메모리도, 어떤 것에 대한 쓰기 접근 권한도 없어야 합니다.

격리는 두 가지 이점을 제공합니다. 이는 두 모델을 진정으로 독립적으로 유지하는데, 어느 쪽도 다른 쪽의 추론을 보거나 공유된 중간 결과에 의존할 수 없기 때문이며, 바로 이 점이 두 모델의 합의를 의미 있게 만듭니다. 그리고 폭발 반경을 0으로 유지합니다. 즉, 구조화된 주장만 반환할 수 있는 모델은 잘못된 숫자에 따라 행동할 수 없으며, 단지 하나를 제안할 수 있을 뿐이고, 모든 제안은 어떤 일이 일어나기 전에 게이트와 재확인을 거칩니다. 모델은 액추에이터가 아니라 센서입니다. 모델은 측정값을 보고하고, 코드가 그 값을 가지고 무엇을 할지 결정합니다.

단일 모델 대 합의 게이트

이 패턴은 움직이는 부품을 추가하므로, 단일 호출에 비해 이러한 부품이 어떤 이점을 제공하는지 명확히 확인하는 데 도움이 됩니다.

차원 단일 모델 두 모델 합의 게이트
소리 없는 오답 하류로 전달됨 두 모델이 모두 동의하고 재확인을 통과하지 않는 한 차단됨
확신에 찬 환각 종종 수용됨 두 모델이 정확히 동일한 오류를 공유하지 않는 한 포착됨
판단당 비용 모델 호출 1회 모델 호출 2회 이상
지연 시간 모델 1개 두 모델 중 더 느린 쪽
불일치 시 주목할 사항 없음 플래그 지정 후 사람의 검토를 위해 보류됨
최적 활용 사례 되돌릴 수 있고, 위험도가 낮은 텍스트 금전, 임계값, 조정

단일 모델 방식이 모든 작업에 잘못된 것은 아닙니다. 그럴듯한 오답이 실제 피해를 주는 작업에 잘못된 것입니다.

비용과 건너뛰어야 할 때

게이트는 무료가 아닙니다. 판단 하나당 한 번이 아닌 두 번 이상의 모델 호출 비용을 지불하고, 병렬로 실행되지만 가장 마지막에 끝나는 모델이 전체 속도를 결정하므로 그중 가장 느린 것을 기다려야 합니다. 엔지니어링 비용도 있습니다. 유지 관리할 스키마, 작성해야 할 결정론적 검사기, 그리고 사람이나 폴백(fallback)이 처리해야 하는 불일치에 대한 보류 경로가 그것입니다. 처리량은 많지만 가치가 낮은 판단 스트림의 경우, 이러한 오버헤드가 이점보다 클 수 있습니다.

따라서 패턴을 중요도에 맞게 적용해야 합니다. 전체 게이트는 잘못된 숫자로 인한 비용이 비싸고 되돌리기 어려울 때 그 비용의 가치를 합니다.

  • 금전 경로. 금액, 한도, 환불 등 가치를 이동시키거나 거래를 결정하는 모든 것.
  • 임계값 및 제어. 어떤 것이 허용되는지 여부를 결정하는 계산된 컷오프.
  • 조정. 두 개의 독립적인 수치가 일치하는지 확인하는 것으로, 거짓 통과는 실제 불일치를 숨길 수 있습니다.
  • 단방향 출력. 원장에 기록되거나 파트너에게 전송되어 나중에 조용히 수정할 수 없는 숫자.

반대의 경우에는 건너뛰십시오. 사람이 훑어볼 대략적인 추정치를 제안하는 모델, 보고서 요약, 어차피 사람이 편집할 초안 등은 두 개의 모델과 소수점 검사기가 필요하지 않습니다. 일회성 추정치에 전체 게이트를 실행하는 것은 지연 시간과 비용의 낭비입니다.

솔직한 한계는 공유된 사각지대입니다. 합의는 잘못된 숫자가 나올 확률을 낮추지만 0으로 만들지는 못합니다. 두 모델이 동일한 결함 있는 패턴으로 학습되어 함께 그 패턴을 반복할 수 있기 때문입니다. 이것이 결정론적 재검사가 게이트 뒤에 위치하고 사람이 불일치 경로에 남아 있는 이유입니다. 모델은 후보를 생성하는 데는 강하지만 그것이 정확하다고 보장하는 데는 약합니다. 합의, 결정론적 재계산, 그리고 페일 클로즈드(fail-closed) 처리는 그 약한 보증을 반드시 정확해야 하는 숫자 앞에 놓을 수 있는 게이트로 바꿔줍니다.

관련 글