다단계 에이전틱 파이프라인에서는 한 단계의 작은 오류가 후속 단계로 전파되며 눈덩이처럼 커집니다. 이 수업에서는 오류 복리 효과의 구조, 단계 간 검증 게이트, 재시도 전략, 그리고 실패를 모델에게 유용하게 되돌려주는 방법을 배웁니다.
오류는 복리로 쌓인다
단일 호출에서 95% 정확도인 작업도, 10단계 파이프라인에서 각 단계가 이전 단계 출력에 의존하면 전체 성공률은 0.95의 10제곱, 약 60%로 떨어집니다. 에이전틱 시스템 설계에서 이 오류 복리(compounding error) 구조를 이해하는 것이 출발점입니다.
전파되는 오류에는 두 부류가 있습니다.
- 하드 오류: 툴 실패, 파싱 불가 JSON, 타임아웃처럼 감지 가능한 실패입니다. 처리하지 않으면 루프가 중단되지만, 감지되므로 대응할 수 있습니다.
- 소프트 오류: 형식은 유효하지만 내용이 틀린 출력입니다. 잘못 추출된 금액, 존재하지 않는 함수 참조가 여기 속합니다. 감지되지 않은 채 다음 단계의 입력이 되는 것이 소프트 오류의 위험이며, 후속 단계는 오염된 입력을 사실로 취급해 그 위에 작업을 쌓습니다.
따라서 신뢰성 설계의 제1원칙은 "오류를 없애기"가 아니라 **"오류가 전파되기 전에 감지하기"**입니다. 구체적으로:
- 단계 사이에 **검증 게이트(validation gate)**를 둔다 — 다음 섹션에서 다룹니다.
- 각 단계의 출력에 검증 가능한 형태를 강제한다 — 스키마, 근거 인용, 참조 ID처럼 기계적으로 대조할 수 있는 요소를 포함시킵니다.
- 가장 이른 단계에 검증을 집중한다 — 초기 단계 오류일수록 전파 범위가 넓기 때문입니다.
[10단계 파이프라인의 단계별 성공률과 누적 성공률]
단계 정확도 95% → 누적 60%
단계 정확도 99% → 누적 90%
단계 정확도 99% + 3단계마다 검증 게이트 → 오류 조기 차단
시험에서는 "단계 수를 늘려 각 단계를 단순화하면 항상 신뢰성이 올라간다"는 함정이 나옵니다. 단계 분해는 단계당 정확도를 높이지만, 검증 없이 단계만 늘리면 오류 전파 경로도 함께 늘어납니다.
검증 게이트 설계
검증 게이트는 단계 출력이 다음 단계로 넘어가기 전에 통과해야 하는 검사입니다. 강도 순으로 세 층위가 있습니다.
1. 구조 검증(스키마): 출력이 기대한 형태인지 확인합니다. JSON 스키마 검증, 필수 필드 존재, 타입 일치가 여기 속합니다. 비용이 거의 없으므로 모든 단계에 적용합니다.
2. 규칙 검증(도메인 불변식): 내용이 도메인 규칙을 만족하는지 코드로 확인합니다. "합계 = 항목 합", "날짜는 미래가 아님", "참조된 파일이 실제 존재함" 같은 결정적 검사입니다. 소프트 오류의 상당수를 여기서 잡을 수 있습니다.
def validate_invoice(inv: dict) -> list[str]:
errors = []
if abs(sum(i["amount"] for i in inv["items"]) - inv["total"]) > 0.01:
errors.append(f"합계 불일치: 항목합과 total이 다름")
if inv["due_date"] < inv["issue_date"]:
errors.append("만기일이 발행일보다 빠름")
return errors
3. 모델 검증(리뷰 패스): 결정적 규칙으로 표현할 수 없는 품질(논리적 일관성, 요구사항 충족)은 별도의 검증 프롬프트로 확인합니다. 이때 생성한 컨텍스트와 분리된 새 호출로 검증해야 합니다. 같은 컨텍스트에서 "방금 답이 맞습니까?"라고 물으면 모델이 자기 출력을 옹호하는 경향이 있기 때문입니다. 신선한 눈(fresh eyes) 원칙입니다.
게이트 실패 시의 동작도 정의해야 합니다. 재시도(다음 섹션), 대체 경로(fallback), 에스컬레이션(5.2) 중 오류 성격에 맞는 것을 선택합니다. 시험 함정: "검증은 파이프라인 마지막에 한 번 수행하는 것이 효율적이다" — 오답입니다. 마지막 검증은 오류를 발견해도 어느 단계가 원인인지 알 수 없고, 이미 소모한 중간 작업이 전부 낭비됩니다.
재시도 전략: 무엇을 어떻게 다시 시도할까
실패를 감지했다면 재시도가 1차 대응입니다. 그러나 재시도에도 설계가 필요합니다.
오류 유형별로 재시도 방식이 다릅니다.
- 일시적 오류(속도 제한(레이트 리밋) 429, 5xx, 타임아웃): **지수 백오프(exponential backoff)**로 재시도합니다. 즉시 재시도는 혼잡을 악화시킵니다.
- 결정적 오류(스키마 불일치, 검증 실패): 같은 입력을 그대로 다시 보내면 같은 실패가 나올 가능성이 높습니다. 실패 정보를 프롬프트에 추가해 재시도합니다. "이전 시도는 X 때문에 실패했다. 이번에는 Y를 지켜라."
- 구조적 오류(모호한 지시, 불가능한 요구): 재시도로 해결되지 않습니다. 에스컬레이션 대상입니다.
for attempt in range(MAX_RETRIES):
result = generate(prompt, previous_error=last_error)
errors = validate(result)
if not errors:
break
last_error = "; ".join(errors) # 다음 시도에 실패 이유를 주입
else:
escalate(f"{MAX_RETRIES}회 실패: {last_error}")
재시도 한도는 반드시 유한해야 합니다. 한도 없는 재시도 루프는 비용 폭주와 무한 루프의 원인입니다. 통상 2~3회로 제한하고, 한도 초과 시 에스컬레이션으로 전환합니다.
**멱등성(idempotency)**도 함께 고려해야 합니다. 재시도 대상 단계가 외부 부수 효과(결제, 발송, 레코드 생성)를 가지면, 재시도가 중복 실행을 낳습니다. 부수 효과가 있는 툴에는 멱등 키를 도입하거나, "생성 전 존재 확인" 패턴을 적용합니다. 시험에서는 429 오류에 즉시 재시도를 고르게 하는 함정, 그리고 검증 실패 재시도에 실패 정보를 주지 않는 선택지가 자주 등장합니다.
실패를 모델에게 잘 전달하기
에이전틱 루프에서 툴 실패는 모델이 다음 행동을 결정하는 입력입니다. 따라서 오류 메시지의 품질이 곧 복구 능력입니다.
Messages API에서 툴 실패는 tool_result 블록에 is_error: true를 설정해 전달합니다.
{
"type": "tool_result",
"tool_use_id": "toolu_01A...",
"is_error": true,
"content": "파일을 찾을 수 없음: src/pay/checkout.ts — 비슷한 경로: src/payment/checkout.ts. 정확한 경로로 다시 시도하세요."
}
좋은 오류 응답의 조건은 사람에게 좋은 오류 메시지의 조건과 같습니다.
- 무엇이 실패했는지: 스택트레이스 전체가 아니라 실패의 핵심을 요약합니다.
- 왜 실패했는지: 원인을 알 수 있으면 명시합니다(권한 부족, 잘못된 인자 형식 등).
- 다음에 무엇을 하면 되는지: 가능한 복구 행동을 제안합니다. 위 예시의 "비슷한 경로" 제안이 이 역할입니다. 이런 제안 하나가 모델의 복구 시도 횟수를 크게 줄입니다.
반대로 나쁜 패턴은 다음과 같습니다.
- 빈 문자열이나
null반환 — 모델이 성공으로 오해할 수 있습니다. - 원시 스택트레이스 수천 줄 — 컨텍스트를 낭비하고 핵심을 묻습니다.
- 오류를 삼키고 부분 결과만 반환 — 소프트 오류를 만들어 하류로 전파시킵니다.
마지막으로, 반복 실패는 패턴으로 감지해야 합니다. 같은 툴이 같은 이유로 연속 실패하면 재시도가 아니라 접근 방식 전환이나 에스컬레이션이 필요하다는 신호입니다. 루프 감지기(동일 툴 호출 N회 반복 시 개입)는 에이전트 하네스의 표준 안전장치입니다.
시험 함정
- "검증은 파이프라인 마지막에 한 번만 하면 된다" — 오답. 늦은 검증은 원인 단계를 특정할 수 없고 중간 작업이 낭비됩니다.
- "속도 제한(429)은 즉시 재시도한다" — 오답. 지수 백오프가 표준입니다.
- "검증 실패 시 같은 프롬프트로 재시도한다" — 오답. 실패 이유를 프롬프트에 주입해야 결과가 달라집니다.
- "재시도는 성공할 때까지 반복한다" — 오답. 유한한 한도와 한도 초과 시 에스컬레이션이 필요합니다.
- "툴 오류 시 빈 결과를 돌려주면 모델이 알아서 판단한다" — 오답. is_error 표시와 복구 힌트가 있는 구조화된 오류를 반환해야 합니다.
- "단계를 잘게 나누면 검증 없이도 신뢰성이 올라간다" — 오답. 검증 게이트 없는 단계 증가는 오류 전파 경로만 늘립니다.
실습 시나리오
실제 시험과 같은 형식의 시나리오 문제입니다.
5단계 문서 처리 파이프라인(추출→정규화→분류→요약→저장)에서 최종 저장 단계의 스키마 검증이 자주 실패합니다. 조사해 보니 1단계 추출에서 금액 필드가 간헐적으로 잘못 추출되고 있었습니다. 가장 효과적인 개선은 무엇입니까?
빌드 연습 · 검증 게이트와 자기 수정 루프 구현
약 45분1.2단계 파이프라인 준비
영수증 텍스트에서 항목을 추출하는 단계와 요약 리포트를 만드는 단계를 구현합니다.
기대 결과 · 정상 입력에서 추출→리포트가 끝까지 동작합니다.
2.구조 검증 추가
추출 출력에 JSON 스키마 검증을 적용합니다.
기대 결과 · 필수 필드 누락 시 게이트에서 차단됩니다.
3.도메인 규칙 검증 추가
항목 합계와 총액 일치, 음수 금액 금지 규칙을 코드로 검증합니다.
기대 결과 · 합계가 틀린 출력이 리포트 단계로 넘어가지 않습니다.
4.실패 주입 재시도
검증 실패 메시지를 프롬프트에 넣어 최대 2회 재시도하는 루프를 만듭니다.
기대 결과 · 1차 실패 후 2차 시도에서 실패 이유가 프롬프트에 포함된 것이 로그로 확인됩니다.
5.오류 주입 테스트
금액이 애매한 영수증 3건을 넣어 게이트·재시도·에스컬레이션 동작을 관찰합니다.
기대 결과 · 재시도로 복구되는 건과 에스컬레이션되는 건이 구분되어 처리됩니다.