도구 오류는 재시도 대상이 아닙니다: LangChain vs OpenAI Agents SDK vs Microsoft Agent Framework
요약
LangChain, OpenAI Agents SDK, Microsoft Agent Framework의 에이전트 도구 오류 처리 방식을 비교 분석합니다. 단순 재시도가 아닌 오류 유형(일시적, 수정 가능, 영구적)에 따른 정교한 복구 정책 수립의 중요성을 강조합니다.
핵심 포인트
- 도구 호출 실패를 일시적 오류와 영구적 오류로 구분하여 처리해야 함
- LangChain은 ToolErrorMiddleware와 ToolRetryMiddleware를 통해 명시적 제어 제공
- 에이전트의 비용 효율성과 안전성을 위해 정교한 복구 정책(recovery policy) 필요
- 프레임워크별 오류 가로채기 위치와 모델 피드백 방식의 차이 분석
도구 호출(tool call)의 실패가 자동으로 재시도 가능한 실패를 의미하지는 않습니다.
타임아웃(timeout)은 다시 실행해도 안전할 수 있습니다. 유효성 검사 오류(validation error)는 대개 수정된 인자(arguments)가 필요합니다. 권한 거부(permission denial)는 종종 실행을 중단해야 합니다. 이 세 가지를 모두 "다시 시도"로 취급하는 것이 에이전트(agent)를 느리고, 비용이 많이 들며, 때로는 안전하지 않게 만드는 원인입니다.
이 기사는 LangChain, OpenAI Agents SDK, 그리고 Microsoft Agent Framework에서 **문서화된 오류 처리 프리미티브(error-handling primitives)**를 비교합니다. 목표는 보편적인 승자를 가리는 것이 아닙니다. 에이전트가 프로덕션(production)에 도달하기 전에 복구 정책(recovery policy)을 명확히 하는 것입니다.
평가 기준
저는 다섯 가지 질문을 바탕으로 각 프레임워크를 비교했습니다:
- 실패가 어디에서 가로채지는가? 도구 미들웨어(Tool middleware), 모델/런타임 재시도(model/runtime retry), 또는 외부 에이전트 미들웨어(outer agent middleware)?
- 시스템이 일시적 오류(transient error)와 영구적 오류(permanent error)를 구분할 수 있는가?
- 실패 후 모델이 무엇을 받는가? 정제된 도구 메시지(sanitized tool message), 예외(exception), 또는 폴백 응답(fallback response)?
- 재시도(retries)는 어떻게 제한되는가? "재시도"라는 단어보다 시도 횟수(attempts), 백오프(backoff), 지터(jitter), 그리고 정책 훅(policy hooks)이 더 중요합니다.
- 얼마나 많은 애플리케이션 코드가 필요한가? 강력한 프리미티브(primitive)라 할지라도 운영 비용이 발생합니다.
테스트 조건 및 제한 사항: 이것은 2026년 7월 20일에 수행된 문서 비교입니다. 저는 현재의 공식 문서와 Sources 섹션에 링크된 예제들을 검토했습니다. 일반적인 벤치마크를 실행하거나, 지연 시간(latency)을 측정하거나, 모델 품질을 비교하지는 않았습니다. 아래의 "최적의 선택(Best fit)"은 측정된 순위가 아니라, 문서화된 제어 표면(control surface)에 가장 적합함을 의미합니다.
짧은 답변
| 우선순위가...라면 | ...로 시작하세요 | 이유 |
|---|---|---|
| 도구 오류 표시를 재시도 정책과 분리하는 것이라면 | LangChain | 전용 ToolErrorMiddleware와 ToolRetryMiddleware를 명시적으로 조합하고 순서를 정할 수 있습니다. |
| ... | ... | ... |
| 해당 표는 순위표가 아니라 경로 안내를 위한 도구입니다. |
1. LangChain: 도구 경계에서의 명시적 조합
LangChain의 현재 미들웨어(middleware) 문서는 중요한 차이점을 명시하고 있습니다: ToolErrorMiddleware는 실패한 호출을 자동으로 재시도하지 않습니다. 대신, 처리된 실패를 모델이 사용할 수 있는 제어된 메시지로 변환하는 반면, ToolRetryMiddleware는 재시도 시도를 별도로 처리합니다.
이러한 분리는 유용한데, 도구가 매우 다른 두 가지 방식으로 실패할 수 있기 때문입니다:
- 일시적 오류 (Transient): 502 오류, 타임아웃 또는 일시적인 속도 제한 (rate limit)은 재시도할 가치가 있을 수 있습니다.
- 수정 가능한 오류 (Correctable): 잘못된 인자(arguments)나 누락된 필수 필드는 모델에 보여주어 모델이 호출을 수정할 수 있도록 해야 합니다.
- 영구적 또는 안전하지 않은 오류 (Permanent or unsafe): 권한 부여 실패, 정책 거부 또는 비멱등적 (non-idempotent) 부분 쓰기는 맹목적으로 다시 실행해서는 안 됩니다.
미들웨어의 순서 또한 의미를 갖습니다. LangChain의 예제는 on_failure="error" 설정과 함께 재시도 미들웨어를 안쪽에 배치하여, 재시도가 소진된 예외(exception)가 외부의 도구 오류 핸들러(tool-error handler)에 도달하도록 합니다. 쉽게 말해, 먼저 재시도를 수행한 다음, 최종 실패를 모델이 볼 수 있는 결과로 변환하는 방식입니다.
from langchain.agents import create_agent
from langchain.agents.middleware import (
ToolErrorMiddleware,
...
여기서의 실질적인 강점은 **국소성 (locality)**입니다. 도구의 실패가 발생하는 지점에서 처리되며, 애플리케이션은 어떤 도구에 정책을 적용할지 선택할 수 있습니다. 트레이드오프(tradeoff)는 미들웨어 순서를 이해해야 하며 어떤 예외 클래스가 재시도 가능한지 직접 결정해야 한다는 점입니다. 프레임워크가 사용자의 도구에 따른 부작용 (side effects)을 대신 추론해주지는 않습니다.
2. OpenAI Agents SDK: 모델 호출을 위한 정책 기반 재시도
OpenAI Agents SDK는 다른 중심점을 보여줍니다. 이 SDK의 재시도 참조(retry reference)는 최대 재시도 횟수와 백오프 (backoff)를 설정할 수 있는 **모델 호출 (model calls)**에 대한 러너 관리형 (runner-managed) 재시도를 설명합니다. 재시도 정책 콜백 (retry policy callbacks)은 예외, 시도 횟수, 스트림 상태, 정규화된 오류 사실, 그리고 제공자 권고 (provider advice)와 같은 컨텍스트를 전달받습니다.
이를 통해 애플리케이션은 다음과 같은 결정에 유용한 근거를 가질 수 있습니다:
- 네트워크 오류 및 타임아웃 재시도;
- 제공자(provider)의
Retry-After지침 준수; - 선택된 HTTP 상태 코드에 대한 재시도;
- 중단(aborts) 또는 안전하지 않은 재실행(unsafe replays)의 재시도 방지;
- 스트리밍(streaming)이 시작된 후 발생한 오류를 응답을 전혀 생성하지 못한 요청보다 더 신중하게 처리.
이는 제어해야 할 실패가 모델/API 요청 자체인 경우에 매우 적합합니다. 이는 실패한 비즈니스 운영(business operation)을 자동으로 반복 가능한 안전한 상태로 만드는 것과는 다른 개념입니다. 만약 연결이 끊기기 전에 도구(tool)가 이미 리소스를 생성했다면, 모델 요청이 재시도 가능하더라도 두 번째 도구 호출은 리소스를 중복 생성할 수 있습니다.
좋은 설계는 SDK의 모델 재시도 정책(model retry policy)을 좁게 유지하고, 멱등성 키(idempotency keys)와 도구별 보상(tool-specific compensation) 로직을 도구 계층(tool layer)에 두는 것입니다:
# 스케치(sketch)이며, 모든 제공자에 즉시 적용 가능한 정책은 아닙니다.
def retry_model_call(ctx):
err = ctx.normalized
...
이 코드 조각은 의도적으로 스케치 형태로 작성되었습니다. 정확한 정책 유형과 제공자 어댑터(provider adapter)는 고정(pin)한 SDK 버전을 따라야 하기 때문입니다. 문서화된 개념이 중요한 부분입니다: 재시도 적격성(retry eligibility)은 정규화된 사실(normalized facts)에 기반한 정책 결정이지, 포괄적인 예외 처리기(blanket exception handler)가 아닙니다.
3. Microsoft Agent Framework: 우아한 성능 저하(graceful degradation)를 위한 미들웨어
Microsoft Agent Framework의 예외 처리(exception-handling) 지침은 제어권을 에이전트 미들웨어(agent middleware) 계층에 둡니다. 예제들은 에이전트 실행 주변에서 예외를 포착하며, 대체 응답을 반환하거나, 실패를 기록하거나, 재시도 및 폴백(fallback) 동작을 추가할 수 있습니다.
이는 여러 에이전트나 함수 도구(function tools)를 둘러싼 중앙 집중식 안전망을 원하는 팀에게 자연스럽게 적합합니다. 예를 들어, 미들웨어 계층은 다음과 같은 작업을 수행할 수 있습니다:
- 상관 관계 ID(correlation ID) 부착;
- 다운스트림 서비스(downstream service)의 타임아웃 포착;
- 가공되지 않은 예외 상세 정보 대신 사용자에게 안전한 설명 반환;
- 운영자를 위해 원래의 예외 기록;
- 폴백 에이전트(fallback agent) 또는 저하된 기능(degraded capability)으로 라우팅.
트레이드오프(tradeoff)는 범위(scope)입니다. 외부 미들웨어(outer middleware)는 에이전트 실행이 실패했음을 인지할 수 있지만, 실패한 도구(tool)가 이미 부수 효과(side effect)를 발생시켰는지 여부는 알지 못할 수 있습니다. 도구별 특정 재시도 규칙(tool-specific retry rules)이 필요하다면, 해당 규칙을 도구 또는 함수 경계(boundary)에 더 가깝게 배치하고, 자동 재생(automatic replay)을 추가하기 전에 도구를 멱등(idempotent)하게 만드세요.
유용한 패턴은 **사용자 경험을 위한 복구(recovery for the user experience)**와 **작업의 재생(replay of the operation)**을 분리하는 것입니다:
async def safe_run(inner_agent, messages, context):
try:
return await inner_agent.run(messages, context=context)
...
첫 번째 응답은 재생(replay)이 안전하다고 주장하지 않고도 우아하게(graceful) 처리될 수 있습니다.
프레임워크보다 더 중요한 정책
재시도(retries)를 활성화하기 전에, 모든 도구 작업(tool operation)을 두 가지 축에 따라 분류하세요:
| 오류 클래스 (Error class) | 재생 가능 여부 (Safe to replay?) | 에이전트 응답 (Agent response) |
|---|---|---|
| 요청이 수락되기 전 타임아웃 (Timeout before a request is accepted) | 일반적으로, 클라이언트가 부수 효과가 발생하지 않았음을 증명할 수 있다면 가능 | 제한된 지수 백오프 (bounded exponential backoff)를 사용하여 재시도. |
| ... |
이곳이 바로 관측성(observability)이 필요한 지점이기도 합니다. 도구 이름, 작업 클래스(operation class), 시도 횟수(attempt number), 오류 클래스, 지속 시간, 부수 효과 발생 가능 여부, 그리고 최종 처리 결과(final disposition)를 기록하세요. 모델이 볼 수 있는 오류 메시지에 비밀 정보나 민감한 전체 페이로드(payload)를 포함하지 마세요.
저는 Stop Replaying Coding-Agent Bugs by Hand에서 트레이스 기반 회귀 테스트(trace-driven regression testing)를 다루었습니다. 동일한 아이디어가 여기에도 적용됩니다. 대표적인 실패 사례를 저장하고, 단순히 실행이 완료되는지뿐만 아니라 에이전트가 올바른 복구 경로(recovery path)를 선택하는지 확인(assert)하세요.
지금 해야 할 일
- 도구(tools)를 인벤토리화하고 각 도구를 읽기 전용(read-only), 멱등적 쓰기(idempotent write), 비멱등적 쓰기(non-idempotent write), 또는 승인 게이트(approval-gated)로 표시하세요.
- 작은 오류 분류 체계(error taxonomy)를 정의하세요: 일시적(transient), 수정 가능(correctable), 거부됨(denied), 알 수 없는 부작용(unknown-side-effect), 잘못된 결과 형식(malformed-result).
- 첫 번째 카테고리(일시적 오류)와 선택된 멱등적 읽기 작업에 대해서만 자동 재시도(automatic retries)를 활성화하세요.
- 정제되고 실행 가능한(actionable) 도구 오류를 모델에 반환하세요. 스택 트레이스(stack traces)나 자격 증명(credentials)을 절대 노출하지 마세요.
- 최대 시도 횟수 제한(hard attempt cap)과 백오프 지터(backoff jitter)를 추가하세요. 모든 재시도와 모든 최종 실패를 로그로 남기세요.
- 쓰기 작업 후 연결이 끊기는 상황을 테스트하세요. 시스템이 쓰기가 커밋되었는지 여부를 판단할 수 없는 경우, 상태 조회(status lookup)나 멱등성 키(idempotency keys)를 도구 계약(tool contract)의 일부로 만드세요.
- Turn Traces Into Regression Tests에서 설명한 대로, 캡처된 트레이스(traces)를 사용하여 CI에서 해당 케이스들을 다시 실행하세요.
솔직한 한계점
프레임워크 문서는 사용 가능한 제어 표면(control surfaces)을 보여줄 뿐, 다운스트림 서비스의 신뢰성이나 모델의 복구 결정 품질을 보장하지는 않습니다. 특히 빠르게 변화하는 에이전트 SDK에서는 기본값과 API가 변경될 수 있으므로, 버전을 고정(pin versions)하고 업그레이드 시 연결된 참조 문서를 다시 확인하세요. 이 프레임워크들 중 그 어떤 것도 임의의 외부 쓰기 작업이 재실행(replay)하기에 안전하다는 것을 증명할 수 없습니다. 그것은 애플리케이션 수준의 계약(application-level contract)이 필요합니다.
출처
- LangChain prebuilt middleware: tool errors and retries
- OpenAI Agents SDK retry reference
- OpenAI Agents SDK overview
- Microsoft Agent Framework exception handling
토론
에이전트 시스템에서 어떤 실패가 더 큰 고통을 유발했나요: 재시도 가능한 장애(retryable outage), 잘못된 도구 인자(bad tool argument), 아니면 커밋 상태를 알 수 없는 쓰기 작업(write whose commit status was unknown)인가요? 결과적으로 어떤 복구 계약(recovery contract)을 채택하게 되었나요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기