패스노트
도메인 4. 프롬프트 엔지니어링과 구조화된 출력

4.4검증과 재시도 루프

예상 학습 시간 35

모델 출력은 확률적이므로 실패를 전제로 시스템을 설계해야 합니다. 이 수업에서는 오류 피드백을 포함한 재시도 루프 설계, 재시도 횟수와 비용의 균형, temperature 조정 전략, 그리고 반복 실패를 처리하는 폴백(fallback) 경로를 다룹니다.

오류 피드백 재시도의 기본 패턴

검증에 실패한 출력을 버리고 같은 요청을 그대로 다시 보내는 것은 비효율적입니다. 같은 입력과 같은 프롬프트는 같은 실패를 재생산할 확률이 높기 때문입니다. 올바른 패턴은 실패한 출력과 구체적 오류 내용을 대화에 추가해 수정을 요청하는 것입니다. Claude는 자기 출력의 어디가 틀렸는지 알려 주면 높은 확률로 고칩니다.

def call_with_retry(client, messages, validate, max_retries=3):
    conversation = list(messages)
    for attempt in range(max_retries):
        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            messages=conversation,
        )
        raw = response.content[0].text
        result, errors = validate(raw)
        if not errors:
            return result
        # 실패한 출력과 오류를 대화에 추가해 수정 요청
        conversation.append({"role": "assistant", "content": raw})
        conversation.append({
            "role": "user",
            "content": "출력이 검증에 실패했습니다. 오류를 수정해 "
                       f"전체 JSON을 다시 출력하세요:\n{errors}",
        })
    raise ExtractionFailed(errors)

핵심 설계 포인트는 세 가지입니다. 첫째, 오류 메시지는 "형식이 잘못됨"이 아니라 "total 필드가 누락됨, date가 YYYY-MM-DD 형식이 아님"처럼 구체적이어야 합니다. 둘째, 수정 요청 시 "전체를 다시 출력하라"고 명시해야 부분 수정문("total을 추가하면 됩니다")이 오는 것을 막습니다. 셋째, 재시도 대화가 길어지면 그 자체가 비용이므로 최대 횟수를 제한합니다.

재시도 횟수, 비용, 그리고 에스컬레이션

재시도는 공짜가 아닙니다. 매 재시도는 지연 시간과 토큰 비용을 추가하며, 재시도 대화에는 이전 실패 출력이 포함되어 입력 토큰이 누적됩니다. 실무 기준은 다음과 같습니다.

최대 2~3회로 제한합니다. 경험적으로 오류 피드백 재시도는 1~2회 안에 성공하거나 계속 실패합니다. 3회 실패한 입력은 프롬프트나 입력 자체에 구조적 문제가 있다는 신호입니다.

실패 유형을 기록합니다. 어떤 입력이, 어떤 오류로, 몇 번째에 성공/실패했는지 로깅하면 프롬프트 개선의 우선순위를 데이터로 정할 수 있습니다. 특정 오류가 반복되면 재시도로 때우지 말고 프롬프트나 스키마를 고쳐야 합니다.

한도 소진 시 에스컬레이션 경로를 정의합니다. 선택지는 세 가지입니다. (1) 사람 검토 큐로 보내기 — 정확성이 중요한 파이프라인의 기본값입니다. (2) 더 강한 모델로 승급 — 일상 처리는 Haiku, 실패 건은 Sonnet으로 보내는 계단식 구성은 비용과 품질의 균형점입니다. (3) 명시적 실패 반환 — 침묵 속에 잘못된 데이터를 흘려보내는 것보다 항상 낫습니다.

def process(document):
    try:
        return call_with_retry(client, build_messages(document),
                               validate, max_retries=2)
    except ExtractionFailed as e:
        log_failure(document.id, e.errors)
        return escalate_to_review_queue(document, e.errors)

시험에서는 "재시도 무한 반복"과 "실패를 무시하고 기본값 저장"이 대표적 오답 선택지입니다. 전자는 비용 폭주, 후자는 데이터 오염을 만듭니다.

temperature와 결정성 전략

temperature는 출력 분포의 무작위성을 제어합니다. 검증·재시도 설계와 결합할 때의 전략은 작업 성격에 따라 갈립니다.

추출·분류처럼 정답이 있는 작업은 temperature 0 근처로 설정합니다. 무작위성은 이런 작업에서 순수한 오류 원천입니다. 다만 temperature 0이 완전한 결정성(determinism)을 보장하지는 않는다는 점이 시험 포인트입니다. 같은 입력에도 미세하게 다른 출력이 나올 수 있으므로, 결정성이 필요하면 출력 검증과 캐싱으로 보완해야 합니다.

재시도 시 temperature를 올리는 기법도 있습니다. temperature 0에서 같은 실패가 반복된다면, 재시도에서 0.3~0.7로 올려 다른 경로의 출력을 유도하는 것입니다. 이는 모델이 특정 잘못된 패턴에 갇혔을 때 유효합니다.

생성 작업의 검증은 형식과 내용을 분리합니다. 마케팅 문구 생성처럼 다양성이 필요한 작업은 temperature를 0.7 이상으로 두되, 형식 규칙(길이, 금지어)은 코드 검증으로 잡고 재시도합니다. 다양성과 형식 준수를 모두 얻는 구성입니다.

def call_adaptive(client, messages, attempt: int):
    # 첫 시도는 결정적으로, 재시도부터 탐색적으로
    temp = 0.0 if attempt == 0 else min(0.3 * attempt, 0.8)
    return client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=2048,
        temperature=temp,
        messages=messages,
    )

마지막으로, 검증 루프는 4.6에서 다룰 다중 패스 리뷰와 다릅니다. 검증 루프는 기계적 규칙 위반을 코드가 감지해 고치는 것이고, 다중 패스 리뷰는 품질·논리 문제를 모델이 평가하는 것입니다. 두 용어의 구분을 묻는 문제가 출제됩니다.

시험 함정

  • '검증 실패 시 같은 요청을 그대로 재전송한다'는 선택지는 오답입니다. 실패 출력과 구체적 오류를 피드백해야 합니다.
  • 재시도 횟수를 제한하지 않는 설계는 오답입니다. 2~3회 제한 후 에스컬레이션이 정석입니다.
  • 'temperature 0은 완전한 결정성을 보장한다'는 선택지는 함정입니다. 변동성을 줄일 뿐 보장하지 않습니다.
  • 반복 실패 입력을 기본값으로 저장하는 선택지는 오답입니다. 침묵 실패는 데이터 오염을 만들며, 사람 검토 큐나 명시적 실패가 정답입니다.
  • 추출 작업에 높은 temperature를 권하는 선택지는 오답입니다. 정답이 있는 작업은 0 근처가 원칙입니다.
  • 코드 기반 검증 루프와 모델 기반 다중 패스 리뷰를 혼동시키는 선택지에 주의하세요.

실습 시나리오

실제 시험과 같은 형식의 시나리오 문제입니다.

일일 1만 건의 영수증을 처리하는 추출 파이프라인에서 약 3%가 스키마 검증에 실패합니다. 현재는 실패 시 빈 값으로 저장합니다. 비용 증가를 최소화하면서 데이터 품질을 높이는 가장 적절한 설계는 무엇입니까?

빌드 연습 · 오류 피드백 재시도 루프와 계단식 에스컬레이션 구현

50
  1. 1.검증 함수 작성

    구문(JSON 파싱)·스키마(필수 필드)·의미(금액 합계) 3계층을 검사하고 구체적 오류 목록을 반환하는 validate 함수를 만듭니다.

    기대 결과 · 실패 시 '어느 필드가 왜 틀렸는지' 문자열 목록이 반환됩니다.

  2. 2.재시도 루프 구현

    실패 출력과 오류 목록을 대화에 추가해 수정을 요청하는 call_with_retry를 구현합니다. 최대 2회로 제한합니다.

    기대 결과 · 1회 피드백 후 성공하는 사례가 로그로 확인됩니다.

  3. 3.적응형 temperature

    첫 시도는 0, 재시도마다 0.3씩 올리는 로직을 추가합니다.

    기대 결과 · 동일 실패 반복 사례에서 재시도 성공률이 변화하는지 관찰됩니다.

  4. 4.에스컬레이션 경로

    2회 실패 건을 상위 모델로 1회 승급 호출하고, 그래도 실패하면 검토 큐 파일에 기록합니다.

    기대 결과 · 실패 건이 침묵 저장되지 않고 모델 승급 → 검토 큐의 경로를 따릅니다.

  5. 5.실패 통계 리포트

    100건 처리 후 시도 횟수 분포, 오류 유형별 빈도를 집계합니다.

    기대 결과 · 가장 잦은 오류 유형이 식별되어 프롬프트 개선 우선순위가 도출됩니다.

출처 및 더 읽기