API Timeout 원인 진단 및 긴 LLM 생성을 위한 안전한 재시도 설정 가이드

LLM API 타임아웃 진단 및 해결 가이드: connect/read/pool 타임아웃 분리 설정, 긴 추론 생성을 위한 SSE 스트리밍 유지 및 안전한 재시도 구현.

OpenAI o3-mini나 Claude 3.7 Sonnet과 같은 최신 추론(Reasoning) 모델을 연동할 때 API Timeout(연결 또는 읽기 시간 초과) 오류는 매우 흔하게 발생하는 문제입니다. 복잡한 다단계 추론 과정에서는 첫 번째 토큰이 생성되기까지의 시간(TTFT)이나 전체 응답 출력이 30초에서 90초 이상 소요될 수 있어 기본 HTTP 클라이언트의 설정을 초과하게 됩니다.

긴 생성 출력을 안정적으로 처리하기 위해 엔지니어들은 BetterToken을 사용합니다. BetterToken은 중간 버퍼링 없는 최적화된 Server-Sent Events(SSE) 스트리밍을 제공합니다. 자세한 연결 규격과 엔드포인트 파라미터는 BetterToken API Reference에서 확인할 수 있습니다.


타임아웃 발생 지점: HTTP 요청 수명 주기의 4단계

LLM API 요청은 네 가지 독립적인 기술 단계로 구성되며, 각 단계에 맞는 타임아웃 설정이 필요합니다:

[클라이언트] --- (1. Connect Timeout) ---> [API Gateway] [클라이언트] --- (2. Write Timeout) ---> [프롬프트 데이터 전송] [모델] --- (3. Read / TTFT) ---> [추론 및 응답 생성] [클라이언트] <--- (4. Pool Timeout) --- [소켓 풀 유지 관리]
  1. Connect Timeout(연결 타임아웃): TCP 핸드셰이크 및 TLS 협상 완료 시간 (5~10초 권장).
  2. Write Timeout(전송 타임아웃): 10만 토큰 이상의 긴 프롬프트를 전송할 때 소요되는 시간.
  3. Read Timeout(읽기 타임아웃): 첫 번째 토큰(TTFT) 응답 대기 및 SSE 청크 간 대기 시간.
  4. Pool Timeout(풀 타임아웃): 동시 요청이 많을 때 클라이언트 소켓 풀에서 유휴 소켓을 획득하는 대기 시간.

타임아웃 진단 매트릭스

증상 / 예외발생 단계주요 원인권장 엔지니어링 대책
httpx.ConnectTimeoutConnect (1)DNS 지연, 포트 차단 또는 네트워크 순간 단절라우팅 확인, connect=5.0s 설정
httpx.ReadTimeout (첫 토큰 전)Read / TTFT (3)긴 추론 연산 또는 공급사 대기열 혼잡stream=True 활성화, read timeout을 60-120초로 연장
RemoteProtocolError / SSE 중단Streaming (3)프록시 유휴 시간 초과 또는 Keep-Alive 부재HTTP/2 사용 또는 TCP Keep-Alive 활성화
httpx.PoolTimeoutPool (4)클라이언트 연결 풀 소켓 고갈max_connections 및 풀 한도 확장

Python(HTTPX) 세부 타임아웃 구성

표준 라이브러리의 기본 타임아웃(10초)은 추론 모델 호출 시 거의 반드시 연결 중단을 유발합니다. 아래와 같이 견고한 클라이언트를 구성하세요:

import os import httpx from openai import OpenAI API_KEY = os.environ.get("BETTERTOKEN_API_KEY", "your_api_key_here") custom_timeout = httpx.Timeout( connect=5.0, # 네트워크 장애 시 빠른 실패 read=120.0, # 긴 추론 처리를 위한 충분한 대기 시간 write=10.0, # 프롬프트 전송 시간 pool=10.0 # 풀 소켓 획득 시간 ) http_client = httpx.Client( timeout=custom_timeout, limits=httpx.Limits(max_keepalive_connections=50, max_connections=100) ) client = OpenAI( base_url="https://www.bettertoken.ai/v1", api_key=API_KEY, http_client=http_client ) response = client.chat.completions.create( model="claude-3-7-sonnet-20250219", messages=[ {"role": "system", "content": "You are a senior systems architect."}, {"role": "user", "content": "Design a high-throughput distributed message broker."} ], stream=True ) for chunk in response: delta = chunk.choices[0].delta.content or "" print(delta, end="", flush=True)

SSE 스트림 안정화 및 안전한 재시도 전략

네트워크 순간 단절 시 중복 과금과 서버 과부하를 방지하기 위해 재시도는 세 가지 원칙을 따라야 합니다:

  1. 스트리밍 시작 후 전체 재시도 금지: 일부 토큰이 이미 클라이언트에 수신된 상태에서 전체 프롬프트를 재전송하면 중복 과금이 발생합니다.
  2. 지수 백오프와 Full Jitter 적용: 재시도 간격을 점진적이고 무작위로 분산시켜 재시도 폭풍(Thundering Herd)을 방지합니다.
  3. 멱등성 키 활용: 배치 작업 시 고유 작업 ID를 활용하여 중복 실행을 차단합니다.
import time import random import httpx def execute_with_safe_retry(client, model, messages, max_retries=3): base_delay = 1.0 for attempt in range(max_retries): try: response = client.chat.completions.create( model=model, messages=messages, stream=True ) return response except (httpx.ConnectTimeout, httpx.ReadTimeout, httpx.NetworkError) as err: if attempt == max_retries - 1: raise err sleep_time = random.uniform(0, base_delay * (2 ** attempt)) time.sleep(sleep_time)

최종 검증 체크리스트

  • HTTP 응답 코드 200 OK 확인.
  • 긴 출력 도중 SSE 스트림이 끊김 없이 전체 토큰을 수신 완료.
  • 요청 완료 후 연결 풀 소켓이 정상 반환되는지 확인.

추가적인 연동 가이드는 BetterToken API Reference를 참고하세요.

LLM 워크플로를 최적화할 준비가 되셨나요?

하나의 API로 모델을 연결하고 키와 AI 비용을 관리하세요.