콘텐츠로 이동

모델 폴백

모델 폴백은 요청 모델을 순서가 있는 대체 모델 목록에 매핑합니다. 설정한 HTTP 오류, 타임아웃, 연결 실패, 모델 없음 또는 비정상 백엔드 선택 실패 뒤에 이 목록을 차례로 시도할 수 있습니다.

설정

fallback:
  enabled: true
  mid_stream_enabled: true

  fallback_chains:
    gpt-5.4:
      - gpt-5.4-mini
      - claude-sonnet-4-6

  fallback_policy:
    trigger_conditions:
      error_codes: [429, 500, 502, 503, 504]
      timeout: true
      connection_error: true
      model_not_found: true
      backend_unhealthy: true
    max_fallback_attempts: 3
    fallback_timeout_multiplier: 1.5
    preserve_parameters: true
    max_concurrent_dials_per_backend: 50

  model_settings:
    gpt-5.4:
      fallback_enabled: true
      notify_on_fallback: true

fallback_chains의 키는 기본 모델이고 값은 시도 순서입니다. 체인은 비어 있거나 자기 자신을 참조하거나 순환할 수 없습니다. 모델 이름은 128자 이하의 영숫자, ., _, -만 사용할 수 있습니다.

fallback_policy.max_fallback_attempts 범위는 1–10, fallback_timeout_multiplier 범위는 1.0–5.0입니다. 이 값은 각 홉이 자신의 요청 타임아웃을 얼마나 늘려 잡는지를 결정합니다. 기본 시도는 attempt 1로 설정이 해석한 타임아웃을 그대로 쓰고, 홉 nbase * multiplier^(n-1)로 동작하며 해당 타임아웃 종류의 timeouts.limits 상한으로 클램프됩니다. 따라서 체인을 통해 설정 자체가 허용하지 않는 값에 도달할 수는 없습니다. 기준값(base)은 그 시도가 체인 없이 사용했을 값입니다. OpenAI 호환 비스트리밍 경로는 모델별 timeouts.request.standard.total, 스트림 시작 전 연결 단계는 시도별 스트리밍 창, /anthropic/v1/messages/v1/responses는 모델별 프로필(total, 스트리밍 arm은 첫 SSE 바이트 데드라인 포함)입니다. timeouts.connection, 폴백 다이얼 퍼밋 대기, 홉 내부 재시도, 클라이언트가 이미 스트림을 받고 있는 상태에서의 스트림 중간 홉은 스케일하지 않습니다. 스트리밍 요청의 모든 시도가 공유하는 벽시계 예산을 정하는 timeouts.streaming_fallback_budget_multiplier와는 다릅니다. 둘은 곱해지는 것이 아니라 함께 적용됩니다. 연결 단계가 홉의 창을 먼저 스케일한 뒤 그 예산의 남은 분량으로 다시 상한을 걸기 때문입니다. max_concurrent_dials_per_backend 범위는 0–10000 (0 = 무제한, 기본값 50)이며, 모든 폴백 경로가 공유하는 백엔드별 동시 폴백 홉 다이얼 상한으로 프로바이더 핸드셰이크가 돌아올 때까지만 유지됩니다. 보유 범위, 포화 시 동작, 리로드 의미는 엣지 케이스 처리의 "동시 요청 폭주" 항목을 참고하세요.

스키마는 trigger_conditions.circuit_breaker_open도 받습니다. 이제 프록시는 선택 시 서킷 브레이커 상태를 조회하지만, 열린 서킷에 거부된 요청은 별도의 circuit_breaker_open 사유가 아니라 backend_unhealthy 트리거로 표면화되므로 일반 LLM 트래픽에서는 이 특정 조건이 만들어지지 않습니다. 백엔드의 서킷이 열렸거나 백엔드가 비정상일 때 폴백 체인을 진행시키려면 backend_unhealthy(및 능동 상태 확인)를 활성화하세요.

스트림 시작 전 실행

비스트리밍 요청 또는 스트림이 시작되기 전에는 라우터가 다음 순서로 동작합니다.

  1. 기본 모델과 설정된 체인을 결정합니다.
  2. 기본 시도를 실행합니다.
  3. 폴백 대상 실패인지 분류합니다.
  4. 다음 모델을 선택하고 페이로드의 모델 이름을 바꿉니다. 홉이 바꾸는 것은 이것뿐입니다.
  5. 처음 성공하거나 max_fallback_attempts에 도달하면 중단합니다.

폴백은 모델 단위입니다. 각 대체 모델은 일반 백엔드 풀, 상태 필터, 접근 정책, 선택 전략을 거쳐 해석됩니다.

교차 공급자 변환

폴백 홉의 페이로드는 처음부터 끝까지 표준 OpenAI chat-completions 형태를 유지하며, 와이어 형식 변환은 디스패치 시점에 선택된 백엔드의 설정값 backend_type을 기준으로 정확히 한 번만 일어납니다. 따라서 messages, tools, tool_choice, stop을 비롯한 모든 샘플링 매개변수는 클라이언트가 보낸 그대로 백엔드 변환기에 도달하고, 해당 홉을 실제로 처리하는 백엔드가 무엇인지 아는 유일한 계층인 변환기가 그 표준 입력으로부터 공급자 고유 요청을 만듭니다.

홉 자체가 하는 일은 하나뿐입니다. 페이로드의 모델 이름을 홉 대상으로 바꿉니다. 무엇을 더하거나 빼거나 형태를 고치지 않으며, 어떤 매개변수도 미리 걸러내지 않습니다. 홉이 알 수 있는 것은 모델 이름뿐이고, 그 추정은 Bedrock으로 서비스되는 claude-* 모델, OpenAI 호환 엔드포인트 뒤의 Gemini, vLLM에 올린 gpt- 접두 오픈 모델에서 틀리며, 운영자가 붙인 별칭에서는 아예 아무 공급자도 추정하지 못하기 때문입니다.

표준 본문에 함께 실려 오는 Anthropic 고유 필드가 세 개 있습니다. speed(fast mode), 그리고 확장 사고 쌍인 thinkingoutput_config입니다. 셋 다 OpenAI chat-completions 스키마에 없으며, 홉이 아니라 디스패치에서 선택된 백엔드의 설정값 backend_type을 기준으로 제거됩니다.

  • OpenAI 와이어를 쓰는 모든 유형(generic, openai, azure, gemini, vllm, ollama, llamacpp, mlxcel, lmstudio, sglang)에서는 모든 전송 지점의 /v1/chat/completions 본문에서 제거됩니다. 이 이름들을 읽는 OpenAI 와이어 대상은 없고, 엄격한 클라우드 엔드포인트는 알 수 없는 최상위 키에 HTTP 400을 돌려주므로 하나라도 새어 나가면 요청을 구하려던 홉 자체가 죽습니다.
  • anthropicbedrock은 그대로 둡니다. thinkingoutput_config를 실제로 읽는 코드가 이들 변환기이고, speed는 백엔드의 별도 옵트인인 anthropic_fast_mode가 계속 관장합니다.
  • continuumrouter도 그대로 둡니다. 하위 Continuum Router는 동일한 표준 확장 필드를 이해하고 자기 백엔드에 대해 같은 규칙을 적용하므로, 여기서 제거하면 Anthropic을 앞단에 둔 하위 라우터로 가는 홉에서 클라이언트의 추론 의도가 조용히 사라집니다.
  • 건드리지 않는 경우는 선택된 백엔드의 이름이 디스패치가 참조하는 설정 스냅샷에 없을 때뿐입니다. 예를 들어 요청이 진행 중인 동안 핫 리로드가 그 백엔드를 삭제하거나 이름을 바꾼 경우입니다. 여기서 추측하는 순간 이 설계가 없앤 바로 그 추론으로 되돌아갑니다. type:을 생략한 백엔드는 기본값인 generic으로 해석되어 다른 OpenAI 와이어 유형과 마찬가지로 제거되므로, type: 생략이 이 필드들을 보존하지는 않습니다.

reasoning_effort는 제거되지 않으며, 클라이언트가 보낸 값을 덮어쓰지도 않습니다. 모든 대상이 존중하거나 무해하게 무시하는 공급자 중립 표기이며, 홉을 건너 클라이언트의 추론 의도를 실어 나르는 것이 바로 이 키입니다. 아래에서 설명하는 변환이 클라이언트가 값을 주지 않았을 때 이 키에 값을 쓰는 이유도 같습니다. extra_body도 건드리지 않습니다. 제거는 최상위 키에만 적용됩니다.

v1.27.0 이전에는 이 제거가 홉에 있었고, 두 모델 이름에서 추정한 공급자를 기준으로 삼았습니다. 그 표는 규칙을 표현할 수 없었습니다. 사용자 지정 별칭 두 개는 양쪽 모두 unknown으로 추정되어 "같은 공급자, 할 일 없음"으로 읽혔고 세 필드가 그대로 OpenAI 와이어까지 나갔습니다. 반대로 claude-* 기본 모델이 사용자 지정 별칭을 쓰는 Anthropic 유형 백엔드로 넘어갈 때는 공급자가 바뀐 것처럼 보여서, 대상이 존중했을 thinking을 그 사실을 알 길이 없는 계층이 제거해 버렸습니다.

같은 릴리스에서 홉이 페이로드를 대상의 고유 형태로 미리 변환하던 동작도 제거했습니다. 이것이 디스패치의 변환과 충돌했습니다. 미리 변환된 tools는 두 번째 변환에서 건너뛰어져 빈 배열로 도착했고, 미리 변환된 tool_choice는 아예 거부되어 홉 전체를 실패시켰으며, stop_sequences로 이름이 바뀐 stop은 읽히지 않았습니다. 해당 변환은 모두 제거되었습니다.

thinking 설정은 reasoning_effort가 됩니다

제거만 하는 것은 안전하지만 정보를 잃습니다. 확장 사고를 요청한 요청이 대상의 기본 추론 동작으로 실행되는 모델로 넘어가 버리기 때문입니다. v1.27.0부터는 같은 디스패치 게이트가 제거하기 전에 먼저 변환합니다. 제거가 일어나는 바로 그 요청들에 한해 Anthropic thinking 설정을 추론 수준으로 매핑해 reasoning_effort로 기록하고, 그다음에야 세 필드를 제거합니다. 이 매핑은 OpenAI에서 Anthropic으로 가는 방향의 역방향이며 같은 표를 재사용하므로, 두 방향은 서로의 역함수로 유지됩니다.

표준 본문의 입력 방출되는 수준
output_config: {"effort": "max"} 대상 어휘에 있으면 xhigh, 없으면 high
output_config: {"effort": "high"} / "medium" / "low" 같은 수준
thinking: {"type": "enabled", "budget_tokens": N}, N <= 4096 low
thinking: {"type": "enabled", "budget_tokens": N}, 4097 <= N <= 10240 medium
thinking: {"type": "enabled", "budget_tokens": N}, N > 10240 high
budget이 없는 thinking: {"type": "enabled"} medium
effort가 없는 thinking: {"type": "adaptive"} 없음
thinking: {"type": "disabled"} 없음. output_config에 effort가 있어도 마찬가지
형식이 잘못된 모든 경우 없음

이 budget 행들은 홉에만 적용되는 규칙이 아닙니다. /anthropic/v1/messages 인그레스도 같은 함수인 ReasoningEffort::from_budget_tokensbudget_tokens를 해석하며, Chat Completions 변환과 responses_only 모델용 Responses API 변환 양쪽 모두에 적용됩니다. 따라서 budget 하나는 어느 문으로 들어오든 같은 추론 수준으로 해석됩니다.

output_config.effortthinking보다 우선합니다. 다만 disabled인 경우에는 표현할 effort 자체가 없으므로 thinking이 이깁니다. 알 수 없는 effort 문자열은 budget 기반 매핑으로 넘어갑니다. none, minimal, auto, max는 절대 방출하지 않습니다. 각각을 거부하는 대상이 있고, "기본값을 쓰라"는 뜻인 값들은 아무것도 쓰지 않는 것으로 표현하기 때문입니다.

기존 reasoning_effort, 또는 중첩 표기인 reasoning.effort가 있으면 항상 그 값이 이기며 절대 덮어쓰지 않습니다. 그것이 클라이언트가 직접 밝힌 공급자 중립 의도이고, Anthropic 고유 필드는 같은 의도의 파생형이기 때문입니다. 형식이 잘못된 thinking 설정은 홉 실패가 아니라 단순 제거로 격하됩니다.

대상 어휘에 맞추기

이 변환은 클라이언트가 보낸 reasoning_effort를 검사하는 추론 검증 이후, 전송 지점에서 실행됩니다. 그래서 여기서 만들어 낸 값은 검증을 거치지 않고 와이어까지 나가게 됩니다. 따라서 선택된 대상이 문서화한 어휘에 맞춥니다.

  • openaiazure는 OpenAI 모델 표를 참조합니다. reasoning_effort 자체를 지원하지 않는 모델(gpt-4o, chat/instant 계열, 그리고 표가 인식하지 못하는 Azure 배포 이름)에는 아무것도 넣지 않고, 홉은 단순 제거로 진행됩니다. 지원하는 경우에는 해당 모델의 문서화된 집합에 맞춥니다. 어긋날 수 있는 지점이 xhigh만은 아니기 때문입니다. gpt-5-prohigh만, 구형 pro 계열은 medium, high, xhigh를, o 시리즈와 codex 계열은 low, medium, high를 허용합니다. 허용되는 수준이면 그대로 쓰고, xhigh를 지원하지 않는 대상에서는 high로 낮추며, 그 외에는 low < medium < high < xhigh 사다리에서 가장 가까운 허용 수준을 고르되 거리가 같으면 더 높은 쪽을 택합니다. 그래서 gpt-5-pro로 가는 lowhigh가 되고, gpt-5.2-pro로 가는 lowmedium이 됩니다. 표에 아직 없는 추론 가능 GPT-5 변형은 low, medium, high를 허용하는 것으로 간주합니다. azuremodel에 담긴 배포 이름이 실제로 서비스하는 모델군을 가리킨다고 신뢰하는데, 이는 클라이언트가 보낸 reasoning_effort 값을 검증기가 이미 같은 방식으로 신뢰하는 것과 같으므로, 실제로 서비스하지 않는 모델군 이름을 붙인 배포는 그 모델군이 거부하는 수준으로 맞춰질 수 있어 Azure 배포 이름은 실제로 서비스하는 모델과 일치하게 지어야 합니다.
  • 그 밖의 모든 OpenAI 와이어 대상(generic, gemini, 그리고 자체 호스팅 엔진 vllm, ollama, llamacpp, mlxcel, lmstudio, sglang)에는 low, medium, high만 보내며 xhigh는 보내지 않습니다.
로컬 엔진 대상도 변환된 effort를 받습니다

자체 호스팅 엔진을 포함시킨 것은 의도한 선택입니다. reasoning_effort는 라우터가 이미 보편적으로 받아들여진다고 취급하는 표준 chat-completions 표기입니다. 모든 대상이 소비하거나 무시하기 때문에 어떤 제거 목록에도 넣지 않았고, 인바운드 /v1/messages 핸들러는 출시 이후 줄곧 같은 엔진들을 향해 이 값을 만들어 왔습니다. vLLM, SGLang, llama.cpp, Ollama, LM Studio는 OpenAI 호환 표면에서 이 필드를 받아들이거나, 모르는 샘플링 옵션으로 무시할 뿐 요청을 실패시키지 않습니다. 클라우드 Gemini의 변환기는 이 값을 thinking_level로 매핑하므로 직접적인 이득이 있습니다. 이 필드를 보내지 않으면 구조 홉이 가장 자주 착지하는 자체 호스팅 대상에서 추론 의도가 조용히 사라집니다.

스트리밍 폴백

fallback.mid_stream_enabled는 활성 SSE 응답을 버퍼링해 업스트림 스트림 실패 뒤 복구할지 결정합니다.

  • true(기본값): 스트림 중간 폴백과 스트림별 버퍼를 허용합니다.
  • false: 스트림 시작 전 폴백은 유지하지만 SSE 출력이 시작된 뒤의 실패는 스트림 오류로 반환합니다.

streaming.mid_stream_fallback.enabled는 버퍼링 스위치가 아니라 모드 선택자입니다.

  • true: 충분한 내용이 누적되면 누적 출력으로 연속 요청을 만듭니다.
  • false: 폴백 모델에서 원 요청을 처음부터 다시 실행합니다.

streaming.mid_stream_fallback.max_fallback_attempts, min_accumulated_tokens, fallback_delay_ms, continuation_prompt로 이 경로를 조정할 수 있습니다. 스트림 전체 및 청크 간격 제한 시간은 계속 적용됩니다.

Cross-provider 스트리밍 홉

스트리밍 폴백 홉은 직접 라우팅이 쓰는 것과 같은 백엔드별 타입 디스패치로 다시 진입하며, 선택된 백엔드의 backend_type 설정을 기준으로 분기합니다. native Anthropic, Bedrock, Gemini 백엔드로 향하는 홉은 해당 provider의 native 스트리밍 파이프라인으로 처리되고(Anthropic이면 x-api-keyanthropic-version을 붙여 /v1/messages의 Messages API 호출), 클라이언트는 계속 OpenAI 형식의 SSE를 받습니다. 기본 백엔드가 native인 모델에 체인을 설정해도 정상 상태의 직접 스트리밍에는 영향이 없습니다.

기본 시도도 같은 디스패치를 거치므로 체인 탐색은 기본 백엔드의 wire에도 좌우되지 않습니다. native 타입 기본 백엔드(anthropic, gemini, 또는 bedrock Runtime·Converse 엔드포인트)가 provider 응답 전에 실패하면(연결 거부, 전송 오류, 핸드셰이크 타임아웃, 서킷 거부) OpenAI wire 기본 백엔드와 같은 pre-stream 체인 탐색에 진입합니다. 두 mid_stream_enabled 모드 모두에서 같은 트리거 조건과 시도 횟수 제한을 따르며, 구조 백엔드의 응답에는 같은 X-Fallback-* 헤더가 붙습니다. native 백엔드로 해석되는 체인 항목도 종결 홉이 아닙니다. 죽은 native 항목은 죽은 OpenAI wire 항목과 같은 방식으로 다음 항목으로 진행합니다. Gemini 또는 Bedrock이 응답한 상태는 OpenAI wire 상태 정책을 따릅니다. 429가 아닌 4xx 응답은 최종 응답이고, 429와 5xx 응답은 정확한 상태가 fallback.fallback_policy.trigger_conditions.error_codes에 있을 때만 다음 홉으로 진행합니다. 연결 거부는 계속 trigger_conditions.connection_error의 적용을 받습니다. Anthropic 형태 native 응답은 기존 동작을 유지하며, Unix 소켓으로 접속하는 native 기본 백엔드는 통째로 서비스되므로 그곳의 실패는 클라이언트의 응답이 됩니다.

복구 시점이 홉이 도달할 수 있는 범위를 결정합니다.

  • SSE 응답이 확정되기 전, 즉 초기 연결과 모든 pre-stream 홉(두 mid_stream_enabled 모드 공통)에서는 어떤 백엔드 타입으로도 홉할 수 있고, 실패하는 시도 역시 어떤 백엔드 타입이든 될 수 있습니다. 응답은 첫 provider 핸드셰이크가 성공한 시점에 확정되므로, pre-stream 단계에서 체인이 소진되면 오류 이벤트를 담은 200 스트림이 아니라 정상적인 HTTP 오류로 반환되고, 아래의 X-Fallback-* 헤더도 붙일 수 있습니다.
  • SSE 출력이 시작된 뒤의 스트림 중간 복구는 OpenAI 호환 wire만 사용하고 응답 헤더도 이미 전송된 상태이므로, native 프로토콜 또는 Unix 소켓 백엔드로 해석되는 체인 항목은 결정적으로 건너뛰고 다음 항목을 시도합니다. 요청 전체를 실패시키는 오류로 바뀌는 일은 없습니다. native 파이프라인으로 처리된 스트림 역시 자체적인 스트림 중간 복구가 없으며, 출력 시작 뒤의 실패는 일반 스트림 오류로 드러납니다.

튜닝 옵션도 같은 기준으로 나뉩니다. pre-stream 홉은 비스트리밍 폴백과 동일하게 fallback.fallback_policy.trigger_conditionsfallback.fallback_policy.max_fallback_attempts를 따르고, streaming.mid_stream_fallback.max_fallback_attempts는 확정된 릴레이가 수행하는 홉만 제한합니다. 또한 응답이 처음부터가 아니라 첫 핸드셰이크 성공 시점에 확정되므로 라우터가 아직 연결 중인 동안에는 SSE keep-alive 주석이 전송되지 않으며, mid_stream_enabled: false인 경로나 체인이 아예 없는 요청과 같은 동작입니다. 이 침묵 구간에는 상한이 있습니다. 연결 단계는 릴레이와 하나의 시도 간 예산(timeouts.request.streaming.totaltimeouts.streaming_fallback_budget_multiplier를 곱한 값)을 공유하며, wire에 관계없이 각 핸드셰이크 시도를 남은 예산만큼으로 제한하고, 예산이 소진되면 체인의 나머지를 전체 길이로 시도하는 대신 보존된 업스트림 실패로 응답합니다. 연결 단계가 확보한 핸드셰이크는 항상 서비스되며, 그 읽기 구간은 릴레이가 스트림을 넘겨받은 시점부터 측정됩니다.

인그레스 적용 범위

체인은 요청이 디스패치되는 모델 이름을 키로 하며, 그 이름으로 백엔드를 고르는 모든 인그레스에서 발동합니다. OpenAI 형태의 인그레스(/v1/chat/completions, /v1/completions, /v1/embeddings, rerank, sparse embeddings, /v1/images/generations)는 공용 디스패치 funnel을 통해 체인을 실행합니다. 공급자 형태의 두 인그레스 /anthropic/v1/messages/v1/responses는 네이티브로 디스패치하며, 자체 시도별 선택과 디스패치를 통해 같은 체인을 실행하므로 트리거 조건, max_fallback_attempts, 백엔드별 홉 다이얼 상한이 모두 chat과 동일하게 적용됩니다. /v1/responses/compactcount_tokens는 체인을 실행하지 않습니다.

공급자 형태의 두 인그레스에서 각 시도는 와이어 충실도를 유지합니다. 기본 시도는 클라이언트가 보낸 그대로의 네이티브 Anthropic Messages 또는 Responses 요청이고, 홉은 모델 이름만 바꾼 같은 타입 요청으로, 선택된 백엔드의 backend_type에 따라 디스패치 시점에 변환됩니다. 그 모델로 직접 보낸 요청과 정확히 같은 방식입니다. /v1/responses에서는 passthrough 전략과 모든 convert 전략에 이 규칙이 적용됩니다. 시도는 반환한 결과로 판정합니다. 2xx는 시도를 확정하고, 연결 거부, 타임아웃, 알 수 없는 모델, 허용 가능한 백엔드 없음은 각각 connection_error, timeout, model_not_found, backend_unhealthy로 홉하며, 공급자가 반환한 상태 코드는 정확한 코드가 trigger_conditions.error_codes에 있을 때만 홉합니다. 두 가지 실패는 이 인그레스들에서 클래스가 아닌 상태 코드로 분류됩니다. 라우터 측 전체 타임아웃 응답(504)과 convert 전략의 연결 실패(502)이며, 둘 다 기본 error_codes에 포함됩니다. 체인이 소진되었거나 첫 실패가 트리거가 아닐 때(예: 400), 클라이언트는 마지막 시도의 오류를 해당 인그레스의 고유 형식으로 받습니다. 공급자의 응답을 라우터가 만든 오류로 대체하지 않습니다.

두 인그레스의 스트리밍 arm은 첫 바이트 전에만 홉합니다. 공급자 핸드셰이크 전의 실패는 다음 체인 항목을 선택해 그 위에서 네이티브 스트림을 다시 시작하고, 첫 성공 핸드셰이크에서 응답이 확정되며, 아래의 X-Fallback-* 헤더는 그 확정된 응답에 실립니다. 핸드셰이크가 돌아온 뒤에는 홉하지 않습니다. 그 이후에 실패하는 본문은 클라이언트의 응답이 되며, 두 인그레스 모두 스트림 중간 복구가 없습니다. 스트림 중간 복구는 위에서 설명한 대로 /v1/chat/completions의 OpenAI 호환 와이어 기능으로 남습니다.

응답 헤더

모델의 notify_on_fallback이 true(기본값)이고 폴백이 성공하면 응답에 다음 헤더가 포함될 수 있습니다.

X-Fallback-Used: true
X-Original-Model: gpt-5.4
X-Fallback-Model: gpt-5.4-mini
X-Fallback-Reason: error_code_429
X-Fallback-Attempts: 2

라우터가 내보내는 폴백 헤더는 X-Fallback-Used, X-Original-Model, X-Fallback-Model, X-Fallback-Reason, X-Fallback-Attempts 다섯 개입니다. 모델에 notify_on_fallback: false를 지정하면 폴백 응답은 그대로 제공하면서 이 헤더들만 억제합니다.

X-Original-Model은 클라이언트가 요청한 모델을 가리킵니다. 요청이 메타데이터 별칭을 지정했고 라우터가 그것을 정규 id로 디스패치했다면, 헤더에는 체인 조회에 쓰인 정규 id가 아니라 클라이언트가 보낸 별칭이 실립니다. X-Fallback-Model은 여전히 실제로 응답한 모델을 가리킵니다.

X-Fallback-Attempts는 요청을 처리하기 위해 시도한 모델 수를 기본 시도를 포함해 보고합니다. 이 헤더는 폴백 백엔드가 응답을 만든 경우에만 나타나므로 값은 항상 2 이상입니다. 값이 2이면 기본 모델이 한 번 실패하고 첫 폴백이 성공했다는 뜻이고, 값이 더 크면 성공 전에 실패한 홉이 더 있었다는 뜻입니다. 이를 통해 클라이언트는 한 번에 복구된 경우와 여러 모델을 거친 경우를 구분할 수 있습니다.

이 헤더들은 비스트리밍 응답과, SSE 출력이 시작되기 전에 폴백이 일어난 스트리밍 응답에 붙습니다. 후자는 선택 시점에 폴백 모델로 넘어간 경우와 pre-stream 체인 전진이며, 두 mid_stream_enabled 모드 모두에 적용됩니다. 체인을 실행하는 모든 인그레스에서 내보내며, /anthropic/v1/messages/v1/responses의 비스트리밍 arm과 pre-stream 스트리밍 arm도 포함됩니다. 스트리밍(SSE) 응답은 본문보다 먼저 헤더를 보내므로 스트림 중간 복구는 시도 횟수를 헤더로 보고할 수 없으며, 대신 streaming_fallback_* 메트릭으로 관찰합니다.

메트릭

metrics 기능이 활성화되면 폴백 수집기가 다음 항목을 등록합니다.

  • fallback_attempts_total{original_model,fallback_model,backend}
  • fallback_success_total{original_model,fallback_model,backend}
  • fallback_exhausted_total{original_model}
  • models_using_fallback{original_model}
  • fallback_duration_seconds{original_model,success}
  • fallback_triggers_by_reason{original_model,reason}

스트림 중간 복구는 streaming_fallback_* 메트릭도 제공합니다.

재적용 동작

fallback_chainsfallback_policy 변경은 실행 중인 폴백 서비스가 사용합니다. 폴백 서비스의 존재 여부나 마스터 enabled 상태를 바꾸려면 재시작해야 합니다. 스트리밍 서비스 설정 변경은 /admin/config/hot-reload-status와 생성 설정의 주석을 따르세요.

관련 문서