콘텐츠로 이동

Reasoning Effort 파라미터

이 문서는 Continuum Router가 다양한 LLM 백엔드에서 reasoning_effort 파라미터를 어떻게 처리하는지 설명합니다. 토큰 예산 변환 및 지원되는 effort 레벨을 포함합니다.

개요

reasoning_effort 파라미터는 모델이 응답을 생성하기 전에 추론에 사용하는 계산 노력의 양을 제어합니다. 각 백엔드는 이 기능을 다르게 구현합니다:

  • OpenAI: O-series 및 GPT-5.x reasoning 모델에 대한 네이티브 reasoning_effort 파라미터
  • Anthropic: 확장 사고(extended thinking)를 지원하는 Claude 모델의 thinking.budget_tokens로 변환, 또는 auto effort에 대해 {"type": "adaptive"}로 변환
  • Gemini: OpenAI 호환 엔드포인트를 통한 thinking 모델에 대한 네이티브 reasoning_effort
  • 기타 백엔드: 패스스루 (변환 없음)

파라미터 형식

Continuum Router는 두 가지 입력 형식을 지원하며, 내부적으로 정규화됩니다:

평면 형식 (Chat Completions API)

{
  "model": "o3-mini",
  "reasoning_effort": "high",
  "messages": [...]
}

중첩 형식 (Responses API)

{
  "model": "o3-mini",
  "reasoning": {
    "effort": "high"
  },
  "input": "..."
}

두 형식 모두 처리 전에 평면 reasoning_effort 형식으로 자동 정규화됩니다. 둘 다 존재하는 경우 평면 형식이 우선합니다.

GPT-5.6 네이티브 Responses 제어

백엔드가 네이티브 Responses API를 제공하면 Continuum Router는 새로운 GPT-5.6 요청 표면을 그대로 보존합니다:

  • reasoning.mode(standard 또는 pro)와 reasoning.context(auto, current_turn, all_turns)
  • text.verbosity, safety_identifier, include
  • prompt_cache_key, prompt_cache_options, 그리고 input_text·input_image·input_file의 명시적 prompt_cache_breakpoint
  • Programmatic Tool Calling 도구와 재생 항목(program, program_output, caller가 연결된 함수 출력)
  • 베타 Multi-agent 설정과 출력 항목
{
  "model": "gpt-5.6-sol",
  "input": "조사를 계속하세요.",
  "previous_response_id": "resp_123",
  "reasoning": {
    "mode": "pro",
    "effort": "max",
    "context": "all_turns"
  },
  "text": {
    "verbosity": "high"
  },
  "safety_identifier": "usr_privacy_preserving_hash"
}

네이티브 previous_response_id는 변경하지 않고 전달하므로 OpenAI가 저장된 reasoning을 재사용할 수 있습니다. 변환 백엔드는 계속 라우터의 로컬 세션 확장을 사용합니다. 네이티브 전용 필드가 있는 요청이 변환 전략으로 라우팅되면 필드를 조용히 버리지 않고 unsupported_request_parameter 오류로 실패합니다. 상태 기반 요청과 네이티브 전용 요청은 라우터의 로컬 응답 캐시를 우회합니다.

Multi-agent raw HTTP 클라이언트는 OpenAI-Beta: responses_multi_agent=v1을 보내야 합니다. 라우터는 클라이언트 자격 증명과 전송 헤더는 계속 필터링하면서 이 헤더는 업스트림으로 전달합니다.

토큰 예산 직접 지정

고급 사용 사례의 경우, reasoning_effort 레벨 대신 토큰 예산을 직접 지정할 수 있습니다:

Anthropic: thinking 파라미터 직접 사용

{
  "model": "claude-sonnet-4-20250514",
  "thinking": {
    "type": "enabled",
    "budget_tokens": 16000
  },
  "messages": [...]
}

적응형 사고(Adaptive Thinking)의 경우 (모델이 사고의 시점과 정도를 결정):

{
  "model": "claude-opus-4-6-20260205",
  "thinking": {
    "type": "adaptive"
  },
  "messages": [...]
}

Gemini: extra_body를 통한 thinking_budget 직접 지정

{
  "model": "gemini-2.5-pro",
  "extra_body": {
    "google": {
      "thinking_config": {
        "thinking_budget": 10000,
        "include_thoughts": true
      }
    }
  },
  "messages": [...]
}

우선순위

reasoning_effort와 직접 토큰 지정이 모두 있는 경우, Anthropic에서는 직접 지정(thinking 파라미터)이 우선합니다. Gemini의 경우 둘 다 공존할 수 있지만 extra_bodythinking_budget이 세밀한 제어에 사용됩니다.


백엔드별 동작

OpenAI 백엔드

OpenAI 모델은 reasoning_effort 파라미터를 네이티브로 지원합니다. 라우터는 값을 OpenAI API에 직접 전달합니다.

지원되는 Effort 레벨

Effort 레벨 지원 모델 설명
none 이를 제공하는 GPT-5.x 모델 추론 비활성화
minimal 원래 GPT-5 계열 최소 추론 effort
low O-series, GPT-5.x reasoning 최소 추론, 빠른 응답
medium O-series, GPT-5.x reasoning 균형 잡힌 추론 노력
high O-series, GPT-5.x reasoning 깊은 추론, 느린 응답
xhigh GPT-5.2부터 GPT-5.6 reasoning 티어, GPT-6 Astra 매우 높은 추론 노력
max GPT-5.6 계열과 GPT-6 Astra 최대 추론 노력

reasoning_effort를 지원하는 모델

O-series 모델 (low, medium, high 지원):

  • o1, o1-mini, o1-preview
  • o3, o3-mini, o3-pro
  • o4-mini

GPT-6 모델:

  • gpt-6-astra (low, medium, high, xhigh, max). none은 OpenAI가 400으로 거절하므로, 추론을 끌 방법이 아예 없는 첫 OpenAI reasoning 모델입니다.

GPT-5.x reasoning 모델 (세대별 정확한 레벨은 다름):

  • gpt-5.6, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna (none부터 max)
  • gpt-5.2부터 gpt-5.5 reasoning 티어 (none, low, medium, high, xhigh)
  • gpt-5.1 reasoning 티어 (none, low, medium, high)
  • gpt-5, gpt-5-mini, gpt-5-nano (minimal, low, medium, high)
  • gpt-5-pro (high만 지원, Responses API 전용)
  • gpt-5.2-pro, gpt-5.4-pro, gpt-5.5-pro (medium, high, xhigh, Responses API 전용)

정확한 모델별 검증

스트리밍과 비스트리밍 Chat Completions 요청은 같은 모델별 검증을 사용합니다. GPT-5 + xhigh, GPT-5.5 + max, OpenAI 모델 + auto처럼 지원하지 않는 조합은 400 invalid_request_error로 거절합니다. 라우터는 요청된 OpenAI effort를 조용히 다른 값으로 바꾸지 않습니다.

함수 도구와 reasoning effort 조합

OpenAI는 일부 모델에서 요청에 함수 tools가 있고 실제 적용되는 reasoning effort가 none이 아니면 /v1/chat/completions를 거절합니다.

400 Function tools with reasoning_effort are not supported for gpt-5.6-sol in
    /v1/chat/completions. To use function tools, use /v1/responses or set
    reasoning_effort to 'none'.

이 제약은 계열 단위가 아니라 모델 단위이며, 영향을 받는 두 세대의 규칙도 서로 다릅니다.

모델 tools + effort 생략 tools + low/medium/high tools + none tools 없음
gpt-6-astra 거절 거절 모델이 거절 허용
gpt-5.6-sol (별칭 gpt-5.6), gpt-5.6-terra, gpt-5.6-luna 거절 거절 허용 허용
gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano 허용 거절 허용 허용
gpt-5.2 이하, O-시리즈 허용 허용 허용 허용

gpt-5.6 행이 다른 이유는 이 계열의 업스트림 기본 effort가 medium이기 때문입니다. 필드를 생략해도 "reasoning 없음"을 뜻하지 않으므로, 생략한 경우에도 거절이 발생합니다.

GPT-6 Astra는 한 단계 더 엄격합니다. tools가 있든 없든 reasoning_effort: "none" 자체를 거절하므로, OpenAI 오류 메시지가 안내하는 두 번째 해결책이 이 모델에는 존재하지 않습니다. 따라서 함수 도구를 실은 Chat Completions 요청은 전부 우회 대상이며, 항목에 적힌 chat_completions_default_reasoning_effort: "medium"none이 아닌 값이면 무엇이든 같은 판단이 나오기 때문에 넣은 자리표시자입니다. OpenAI가 생략 시 적용하는 effort를 공개하지 않아서입니다. 이 행은 OpenAI 모델 문서와 거절 메시지에서 읽은 값이고, 여기서 라이브 API로 측정하지는 않았습니다.

라우터는 거절되는 요청만 골라 우회합니다. 해석된 모델 메타데이터에 chat_completions_tools_require_none_reasoning이 있고, 요청에 비어 있지 않은 tools 배열이 있으며, 실제 적용될 effort가 거절 대상이면 그 요청을 /v1/responses(이 조합을 허용합니다)로 보내고 응답을 다시 Chat Completions 형태로 변환합니다. 스트리밍도 마찬가지입니다. 나머지는 그대로 보존됩니다. 요청한 effort는 reasoning.effort로 전달되고, 생략한 effort는 생략된 채로 남으며, 클라이언트는 평범한 chat.completion 또는 chat.completion.chunk 스트림과 그 안의 tool call을 받습니다.

그 밖의 요청은 모두 /v1/chat/completions로 직행하므로 지금 정상 동작하는 경로의 비용과 지연은 달라지지 않습니다. tools와 함께 보낸 reasoning_effort: "none", tools가 없는 요청, 영향을 받지 않는 모든 모델이 여기에 해당합니다. 검증은 여전히 먼저 실행되므로, 모델이 지원하지 않는 effort는 우회 이전에 라우터 자체의 400으로 거절됩니다. 라우터는 요청된 effort를 none으로 바꿔 쓰지 않습니다. 그렇게 하면 모델 동작이 조용히 달라지기 때문입니다.

동작을 결정하는 메타데이터 키는 responses_only 옆에 놓이는 두 개입니다.

타입 의미
chat_completions_tools_require_none_reasoning bool (기본값 false) 이 모델은 /v1/chat/completions에서 함수 도구와 none이 아닌 effort의 조합을 업스트림이 거절합니다.
chat_completions_default_reasoning_effort string (선택) 요청이 reasoning_effort를 생략했을 때 업스트림이 적용하는 effort입니다. gpt-5.6과 gpt-6-astra 항목에 medium으로 지정해 생략한 경우에도 우회하도록 하고, gpt-5.4gpt-5.5에는 지정하지 않습니다. 라우터가 이 값을 전송하지는 않습니다.

두 키는 영향을 받는 항목(gpt-6-astra, gpt-5.6-sol과 별칭 gpt-5.6, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano)에 내장 OpenAI 레지스트리와 model-metadata.yaml 양쪽에 이미 설정되어 있습니다. 평범한 메타데이터이므로, OpenAI가 제약을 푸는 날 라우터 릴리스를 기다리지 않고 model-metadata.d/ 드롭인으로 끌 수 있습니다.

# model-metadata.d/90-lift-tools-reasoning-bridge.yaml
models:
  - id: gpt-5.6-sol
    metadata:
      chat_completions_tools_require_none_reasoning: false

운영자는 responses_bridge_total 메트릭에서 이 구분을 볼 수 있습니다. reason 레이블이 tools_with_reasoning과 무조건 우회되는 responses_only_model을 나눕니다.

이 우회가 없는 릴리스에서의 임시 조치

조건부 우회가 없는 라우터에서는 드롭인으로 해당 모델에 responses_only: true를 지정하면 /v1/responses로 라우팅되어 같은 요청이 성공합니다. 동작은 하지만, tools가 없는 평범한 대화까지 포함해 그 모델의 모든 요청이 Responses API를 거치게 되는 비용을 치릅니다. continuum-router config validatebackends[].model_configs[]에 이 재정의가 걸려 있으면 더 좁은 플래그를 가리키는 경고를 냅니다.

reasoning_effort를 지원하지 않는 모델

다음 모델들은 reasoning 파라미터를 지원하지 않습니다 (파라미터가 제거됨):

  • GPT-4o, GPT-4o-mini, GPT-4-turbo, GPT-4
  • GPT-5.2-chat-latest, GPT-5.2-instant (non-thinking 변형)
  • GPT-3.5-turbo
  • 임베딩 모델, 이미지 모델

Anthropic 백엔드 (Claude)

Anthropic Claude 모델은 모델 세대에 따라 다른 메커니즘을 사용합니다:

  • Claude Fable 5와 Mythos 5 (Mythos급; Mythos 5는 Fable 5에서 안전장치를 해제한 한정 출시 버전): 적응형 사고({"type": "adaptive"})와 output_config.effort를 사용합니다. Opus 4.8과 같은 요청 형식으로 레거시 budget_tokens는 HTTP 400으로 거부되고 temperature/top_p/top_k는 자동으로 제거되며 max effort를 지원합니다. 추가 제약으로 명시적 thinking.type == "disabled"를 HTTP 400으로 거부하기 때문에 라우터가 thinking 파라미터를 전달하지 않고 생략합니다.
  • Claude 4.8+ 모델 (Opus 4.8): 적응형 사고({"type": "adaptive"})와 output_config.effort로 사고 깊이를 제어. 레거시 thinking.type: "enabled" + budget_tokens 형식은 HTTP 400으로 거부됩니다. temperature, top_p, top_k는 허용하지 않으며, 라우터가 자동으로 제거합니다. Effort 기본값은 high입니다.
  • Claude Sonnet 5 (claude-sonnet-5-*, alias claude-sonnet-5-latest): 적응형 사고({"type": "adaptive"})와 output_config.effort를 사용합니다. 레거시 thinking.type: "enabled" + budget_tokens 형식은 HTTP 400으로 거부됩니다. temperature, top_p, top_k는 자동으로 제거됩니다. xhigh"high"로 다운그레이드됩니다(max는 Opus/Mythos급 전용으로 Sonnet 5는 지원하지 않습니다). Effort 기본값은 high입니다.
  • Claude 4.6+ 적응형 모델 (Opus 4.6, Sonnet 4.6, Opus 4.7): 적응형 사고({"type": "adaptive"})와 output_config.effort로 사고 깊이를 제어. Claude Opus 4.7은 이 API가 필수이며, 레거시 budget_tokens 형식을 보내면 HTTP 400 오류가 발생합니다.
  • 4.6 이전 모델 (Opus 4.5, Sonnet 4 등): 확장 사고에 thinking.budget_tokens 사용

변환 테이블: Claude 4.6+ 모델 (적응형 사고)

Claude 4.6+ 적응형 모델(claude-opus-4-6-*, claude-sonnet-4-6-*, claude-opus-4-7-*, claude-opus-4-8-*, claude-sonnet-5-*, claude-fable-5-*, claude-mythos-5-*)은 budget_tokens 대신 output_config.effort를 사용합니다:

reasoning_effort thinking output_config.effort 설명
none 비활성화 (생략) Thinking 비활성화
minimal {"type": "adaptive"} "low" Anthropic API에 minimal 없으므로 low로 매핑
auto {"type": "adaptive"} (생략) 모델이 기본 effort(high) 사용
low {"type": "adaptive"} "low" 낮은 사고 깊이
medium {"type": "adaptive"} "medium" 중간 사고 깊이
high {"type": "adaptive"} "high" 높은 사고 깊이
xhigh (Opus 4.6, Opus 4.7, Opus 4.8, Fable 5, Mythos 5) {"type": "adaptive"} "max" 최대 사고 깊이
xhigh (Sonnet 4.6, Sonnet 5) {"type": "adaptive"} "high" 다운그레이드 (max는 Opus/Mythos급 전용)

변환 테이블: 4.6 이전 Claude 모델 (Budget Tokens)

4.6 이전 모델은 thinking.budget_tokens를 사용합니다:

reasoning_effort Anthropic thinking 설명
none 비활성화 Thinking 기능 비활성화
minimal {"type": "enabled", "budget_tokens": 1024} 최소 허용 예산
auto {"type": "adaptive"} 모델이 사고의 시점과 정도를 결정
low {"type": "enabled", "budget_tokens": 4096} 가벼운 추론
medium {"type": "enabled", "budget_tokens": 10240} 중간 추론
high {"type": "enabled", "budget_tokens": 32768} 깊은 추론
xhigh {"type": "enabled", "budget_tokens": 32768} high 예산으로 폴백

변환 예시: Claude 4.6+ (적응형 사고 + Effort)

입력 (OpenAI 형식):

{
  "model": "claude-opus-4-6-20260205",
  "reasoning_effort": "high",
  "messages": [...]
}

변환됨 (Anthropic 형식):

{
  "model": "claude-opus-4-6-20260205",
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "high"
  },
  "messages": [...]
}

Opus 4.6 또는 Opus 4.7에서 xhigh를 사용하면 output_config.effort"max"로 설정됩니다:

{
  "model": "claude-opus-4-6-20260205",
  "thinking": {"type": "adaptive"},
  "output_config": {"effort": "max"},
  "messages": [...]
}

변환 예시: 4.6 이전 Claude (Budget Tokens)

입력 (OpenAI 형식):

{
  "model": "claude-sonnet-4-20250514",
  "reasoning_effort": "high",
  "messages": [...]
}

변환됨 (Anthropic 형식):

{
  "model": "claude-sonnet-4-20250514",
  "thinking": {
    "type": "enabled",
    "budget_tokens": 32768
  },
  "messages": [...]
}

적응형 사고 (Auto)

reasoning_effort"auto"로 설정되면 라우터는 output_config.effort 없이 {"type": "adaptive"}를 생성합니다. 그러면 Anthropic이 기본 effort 레벨(현재 high)을 사용하고 모델이 언제, 얼마나 추론할지를 동적으로 결정합니다.

입력 (OpenAI 형식):

{
  "model": "claude-opus-4-6-20260205",
  "reasoning_effort": "auto",
  "messages": [...]
}

변환됨 (Anthropic 형식):

{
  "model": "claude-opus-4-6-20260205",
  "thinking": {
    "type": "adaptive"
  },
  "messages": [...]
}

output_config 직접 전달 (Pass-Through)

요청에 이미 명시적인 thinkingoutput_config 매개변수가 포함되어 있으면 변환 없이 직접 전달됩니다:

{
  "model": "claude-opus-4-6-20260205",
  "thinking": {"type": "adaptive"},
  "output_config": {"effort": "medium"},
  "messages": [...]
}

적응형 사고 가용성

output_config.effort를 사용한 적응형 사고는 Claude 4.6+ 적응형 모델(claude-opus-4-6-*, claude-sonnet-4-6-*, claude-opus-4-7-*, claude-opus-4-8-*, claude-sonnet-5-*)과 Mythos급 계열(claude-fable-5-*, claude-mythos-5-*)에서 사용할 수 있습니다. Claude Opus 4.7/4.8, Claude Sonnet 5, Mythos급 모델은 이 API가 필수이며, 라우터는 이 모델들에 대한 명시적 레거시 thinking.type == "enabled" 요청을 정규화해 업스트림 HTTP 400 오류를 방지합니다. 4.6 이전 모델은 budget_tokens를 사용합니다.

max Effort 레벨

output_config.effort"max" 레벨은 Opus 모델(Opus 4.6, Opus 4.7, Opus 4.8)과 Mythos급 계열(Fable 5, Mythos 5)에서 사용할 수 있습니다. Sonnet 4.6에 xhigh가 요청되면 라우터가 자동으로 "high"로 다운그레이드하고 info 레벨 로그를 남깁니다.

Claude Opus 4.7/4.8, Sonnet 5 및 Mythos급 샘플링 파라미터 지원 중단

Anthropic은 Claude Opus 4.7, Opus 4.8, Sonnet 5, Fable 5, Mythos 5에 대해 temperature, top_p, top_k를 지원하지 않습니다. 라우터는 claude-opus-4-7-*, claude-opus-4-8-*, claude-sonnet-5-*, claude-fable-5-*, claude-mythos-5-* 모델로 전달하기 전에 세 파라미터를 자동으로 제거해 HTTP 400 오류를 방지합니다.

Mythos급 비활성 thinking 거부

Claude Fable 5와 Mythos 5는 명시적 thinking: {"type": "disabled"}를 모든 effort 수준에서 HTTP 400으로 거부합니다. 클라이언트가 claude-fable-5-* 또는 claude-mythos-5-* 모델에 비활성 thinking 설정을 보내면 라우터는 이를 전달하지 않고 thinking 파라미터를 통째로 생략합니다(공식 우회 방법). Opus 4.8 이하와 모든 Sonnet 계열은 명시적 비활성 thinking 설정을 그대로 받습니다. Opus 5는 effort high 이하에서만 받습니다(아래 참고).

Claude Opus 5의 high 초과 effort + 비활성 thinking

Claude Opus 5는 명시적 thinking: {"type": "disabled"} 설정을 받지만, output_config.efforthigh 이하일 때만 유효합니다. xhigh/max와 함께 보내면 Anthropic이 HTTP 400으로 거부합니다.

라우터는 이 제약을 모델링해서 업스트림에 요청을 보내기 전에 자체적으로 HTTP 400을 반환합니다. 검사는 이 조합을 표현할 수 있는 두 인입 경로, 즉 /v1/chat/completions(스트리밍/논스트리밍)와 /anthropic/v1/messages에서 동작합니다. Responses에서 Anthropic으로 변환하는 컨버터도 조립한 페이로드에 같은 검사를 걸어 두었지만, 지금은 /v1/responses 클라이언트가 여기에 도달할 수 없습니다. Responses 요청은 "thinking 없음"을 reasoning.effort: "none"으로만 표현하고, 이 값은 thinking을 명시적으로 비활성화하는 대신 thinking 파라미터를 아예 생략하기 때문입니다. 검증은 요청 단위이므로 대화의 첫 요청뿐 아니라, thinking을 비활성으로 둔 채 나중에 effort만 올린 요청도 걸러냅니다.

Anthropic 형식 표면 중 두 곳은 의도적으로 검사하지 않습니다. /anthropic/v1/messages/count_tokens는 토큰을 생성하지도 과금하지도 않으므로, 검사를 걸면 업스트림이 세어 줄 의사가 있는 요청까지 거부할 위험이 있습니다. Bedrock Converse(endpoint_type: converse)는 설계상 extra_body/additionalModelRequestFields를 그대로 전달하므로, 그 통로로 넣은 thinking 설정은 호출자가 직접 선택한 우회로입니다.

오류 본문에는 클라이언트가 스스로 고칠 수 있도록 두 가지 해결책이 모두 담깁니다. output_config.efforthigh 이하로 낮추거나, 비활성 thinking 설정을 제거해 요청한 effort로 적응형 사고를 쓰는 것입니다. 라우터가 둘 중 하나를 대신 고르지 않는 것은 의도적입니다. effort를 깎는 것도, thinking 설정을 버리는 것도 클라이언트가 명시한 지시를 조용히 고쳐 쓰는 일이고, 어느 쪽도 기본값으로 안전하지 않습니다. thinking을 끈 Opus 5에는 알려진 오작동이 둘 있습니다. 도구 호출을 구조화된 tool_use 블록 대신 응답 본문에 평문으로 내보내는 것과, <thinking> 태그가 출력에 새어 나오는 것입니다.

Mythos급 5.1의 강제 tool_choice 제거

Claude Fable 5.1과 Claude Mythos 5.1은 강제 도구 호출을 없앴습니다. tool_choice: {"type": "any"}{"type": "tool", "name": ...} 둘 다 Anthropic이 HTTP 400으로 거부합니다(tool_choice: type "tool" and "any" are not supported for this model.). autonone은 영향이 없고, disable_parallel_tool_useauto와 함께 그대로 동작합니다. Claude Fable 5와 Claude Mythos 5는 강제 도구 호출을 받으므로, 이 게이트는 Mythos급 계열 전체가 아니라 파싱한 계열 버전을 기준으로 걸립니다.

라우터는 업스트림에 요청을 보내기 전에 자체적으로 HTTP 400을 반환합니다. 검사는 강제 호출을 표현할 수 있는 세 인입 경로에서 모두 동작합니다. /v1/chat/completions(스트리밍/논스트리밍, OpenAI의 "required"{"type": "function", ...}이 Anthropic의 anytool이 되는 경로), /anthropic/v1/messages, 그리고 Responses에서 Anthropic으로 변환하는 컨버터입니다. 위의 비활성 thinking 검사와 달리 Responses 경로는 장래 변경에 대비한 경계 방어가 아니라 실제 요청이 도달하는 경로입니다. /v1/responses 클라이언트는 지금도 강제 호출을 표현할 수 있기 때문입니다.

검사하지 않는 Anthropic 형식 표면 두 곳은 위와 같습니다. /anthropic/v1/messages/count_tokens는 토큰을 생성하지도 과금하지도 않고, Bedrock Converse는 설계상 additionalModelRequestFields를 그대로 전달합니다.

오류 본문은 모델 이름을 밝히고, 클라이언트가 실제로 보낸 표기를 그대로 되돌려 주며, Anthropic이 문서화한 해결책을 나열합니다. tool_choiceauto로 두고 호출할 도구를 지시문에 명시하거나, 도구에 strict: true를 설정해 인자가 스키마를 지키게 하거나, 구조화된 출력을 요청하는 것입니다. 라우터가 그중 무엇도 대신 적용하지 않는 것은 의도적입니다. 강제 선택을 auto로 낮추는 것은 같은 요청이 아닙니다. 모델이 평문으로 답해도 되는 상태가 되고, 도구 호출이 보장된다고 전제하고 작성된 에이전트 루프가 깨지며, 그 실패는 원인이 된 요청에서 멀리 떨어진 곳에서 드러납니다.

확장 사고를 지원하는 모델

확장 사고는 다음에서 지원됩니다:

  • Claude Fable 5 / Mythos 5 (적응형 사고 + effort; max 지원; 샘플링 파라미터 제거; 명시적 비활성 thinking 생략): claude-fable-5-*(alias claude-fable-5-latest), claude-mythos-5-*(alias claude-mythos-5-latest, 한정 출시)
  • Claude Opus 4.8 (적응형 사고 + effort; 샘플링 파라미터 미지원; effort 기본값 high): claude-opus-4-8-*, alias claude-opus-4-8-latest
  • Claude Sonnet 5 (적응형 사고 + effort; 샘플링 파라미터 제거; xhighhigh로 다운그레이드; effort 기본값 high): claude-sonnet-5-*, alias claude-sonnet-5-latest
  • Claude Opus 4.7 (적응형 사고 + effort; 샘플링 파라미터 지원 중단): claude-opus-4-7-*
  • Claude 4.6 계열 (적응형 사고 + effort): claude-opus-4-6-*, claude-sonnet-4-6-*
  • Claude Opus 모델 (budget tokens): claude-opus-4-*, claude-opus-4-5-*
  • Claude Sonnet 4 모델 (budget tokens): claude-sonnet-4-*, claude-sonnet-4-5-*

Temperature 제한

확장 사고가 활성화되면 Claude는 사용자 정의 temperature 설정을 지원하지 않습니다. 라우터는 thinking이 활성화되면 temperature 파라미터를 자동으로 제거합니다. Claude Opus 4.7, Opus 4.8, Fable 5, Mythos 5의 경우 thinking 활성화 여부와 관계없이 temperature, top_p, top_k가 항상 제거됩니다.

4.6 이전 Claude의 xhigh 폴백

4.6 이전 Claude 모델에 xhigh가 요청되면 라우터가 자동으로 high (32,768 budget_tokens)로 다운그레이드합니다.

Anthropic 외부의 auto

auto effort 레벨은 Anthropic의 적응형 사고에 매핑됩니다. OpenAI에서는 유효한 effort가 아니므로 auto 요청을 거절합니다. Gemini는 공급자별 automedium 변환을 유지합니다.

폴백 홉에서의 역방향 변환

위 내용은 모두 정방향, 즉 OpenAI reasoning_effort가 Claude thinking 설정이 되는 방향을 설명합니다. 교차 공급자 폴백 홉에서는 그 반대가 필요할 수 있습니다. thinking이나 output_config를 실은 요청이 OpenAI 와이어 백엔드로 넘어가면, 디스패치가 그 설정을 선택된 대상이 받아들이는 reasoning_effort 값으로 변환한 다음에야 Anthropic 고유 필드를 제거합니다. 덕분에 추론 의도가 버려지지 않고 홉을 건너갑니다. output_config.effort는 수준 그대로 매핑되며 max는 대상 어휘에 있으면 xhigh가 됩니다. budget_tokens는 같은 구간표로 되돌아갑니다. effort가 없는 적응형 사고와 비활성 thinking은 아무것도 방출하지 않으며, 클라이언트가 보낸 reasoning_effort가 항상 우선합니다. 전체 표와 대상별 어휘 규칙, 로컬 엔진 관련 결정은 모델 폴백에 있습니다.

Anthropic Messages API 인그레스(/anthropic/v1/messages)도 같은 함수를 통해 같은 구간표를 적용합니다. Chat Completions 변환과 responses_only 모델용 Responses API 변환 모두 마찬가지입니다. 따라서 budget_tokens 값 하나는 라우터 전체에서 하나의 추론 수준으로 해석됩니다. 요청이 Anthropic 메시지로 들어왔든 폴백 홉을 타고 OpenAI 와이어 백엔드로 실려 갔든 같습니다. 구간표 자체는 모델 폴백에 있습니다.


Gemini 백엔드

Gemini 모델은 OpenAI 호환 엔드포인트를 통해 reasoning_effort를 네이티브로 지원합니다. 라우터는 값을 검증하고 직접 전달합니다.

지원되는 Effort 레벨

Effort 레벨 지원 모델 설명
none rejects_reasoning_effort_none가 붙지 않은 모든 모델 Thinking 비활성화
minimal 모든 thinking 모델 최소 추론
low 모든 thinking 모델 가벼운 추론
medium 모든 thinking 모델 중간 추론
high 모든 thinking 모델 깊은 추론

none을 받는 모델

none은 모델 메타데이터가 업스트림의 거절을 명시하지 않는 한 Gemini로 그대로 전달됩니다. 거절은 deny list이며, model-metadata.yamlrejects_reasoning_effort_none입니다. 거절이 측정된 모델에 설정되어 출하됩니다.

  • gemini-3.6-flash
  • gemini-3.5-flash-lite
  • 모든 Gemini Pro 티어

그 외에는, 라우터 빌드 이후에 나온 모델을 포함해 전부 전달되고 Gemini가 답합니다. 이 기본값은 의도적입니다. 이전 구현은 라우터에 컴파일된 Flash 계열 allowlist였고, 그래서 새 Gemini 마이너가 나올 때마다 누군가 측정해 릴리스를 낼 때까지 none 지원을 잃었습니다. Google은 2026년 5월부터 9월 사이에 Flash 마이너를 넷 냈습니다.

지원 여부는 계열 접두사를 따르지 않습니다. 2026-09-10에 OpenAI 호환 엔드포인트로 카탈로그 id마다 reasoning_effort: "none" 요청을 한 번씩 보내 측정했습니다. 2.5 Flash, 2.5 Flash-Lite, 3 Flash, 3.1 Flash-Lite, 3.5 Flash, 3.7 Flash, 3.8 Flash는 200, gemini-3.5-flash-litegemini-3.6-flash400 INVALID_ARGUMENT, Pro 티어는 400 Budget 0 is invalid. This model only works in thinking mode., 2.0 계열은 업스트림 퇴역으로 404였습니다.

라우터 자체의 400이 필요한 곳은 업스트림 오류가 필드도 모델도 밝히지 않는 두 Flash 티어입니다. Pro 모델은 업스트림 메시지가 이미 구체적이라 플래그의 이득은 주로 왕복 한 번을 아끼는 것입니다. 검사는 /v1/chat/completions 인입에서 백엔드 선택 전에 실행되므로 스트리밍과 논스트리밍을 함께 덮습니다. 특정 행에 동의하지 않는 운영자는 릴리스를 기다리지 않고 model-metadata.d/ 드롭인으로 설정하거나 해제할 수 있습니다.

폴백 홉은 거절이 아니라 맞춤 대상입니다. 인입 검사는 클라이언트가 요청한 모델을 판단하지만, 홉은 운영자의 체인이 고른 다른 모델에 착지하고 그 모델의 메타데이터에는 원래 모델에 없던 플래그가 있을 수 있습니다. 그런 홉에서는 라우터가 reasoning_effort: "none"을 떨어뜨리고 로그를 남깁니다. 홉은 이미 모델을 바꾼 상태이므로, 실패를 감수하고 다른 모델을 받아들인 클라이언트에게는 오류보다 그 모델이 추론하며 답하는 편이 낫기 때문입니다. 이전에는 홉이 none을 그대로 보내 업스트림이 400을 주고, 체인이 이를 실패한 홉으로 세어, 모델도 필드도 밝히지 않는 500 fallback exhausted가 돌아왔습니다. 알아둘 결과가 하나 있습니다. 대체 모델이 추론하므로, 추론 없는 답변에는 충분하던 작은 max_tokens가 이제 잘릴 수 있습니다.

네이티브 thinkingConfig.thinkingLevel: "MINIMAL" 필드는 다른 질문이고 답도 다릅니다. Gemini 3.8 Flash는 이를 거절하고 3.6 Flash는 받아, 위 두 행과 정반대입니다. Google이 문서화한 것은 minimal 레벨이지 이 매핑이 아니므로, minimal에 대한 문서 서술로 deny list를 고쳐서는 안 됩니다. id를 직접 측정해야 합니다.

xhigh 자동 폴백

Gemini 모델에 xhigh가 요청되면 라우터가 자동으로 high로 다운그레이드합니다. Gemini는 xhigh를 지원하지 않기 때문입니다.

추가 기능

Gemini thinking 모델의 경우, 라우터는 자동으로:

  1. reasoning content를 노출하기 위해 include_thoughts: true 설정
  2. 지정되지 않은 경우 기본 max_completion_tokens: 16384 설정
  3. 모델 기능에 대해 effort 레벨 검증

셋 다 두 경로 모두에 적용됩니다. 이슈 #1591 이전에는 변환이 stream: true일 때만 돌았습니다. 그래서 비스트리밍 클라이언트는 thinking 모델에서 reasoning_content를 전혀 받지 못했고, 16384 기본값 대신 Gemini의 낮은 암묵적 출력 상한이 걸렸으며, reasoning_effort: "xhigh"도 다운그레이드 없이 그대로 전달됐습니다. 같은 요청에 stream: true만 붙이면 셋 다 적용됐습니다. 이제 비스트리밍 경로도 스트리밍 경로와 같은 두 단계를 같은 순서로 실행하므로, 거절되는 effort는 어느 쪽이든 동일한 400입니다.

어떤 모델에 이 주입이 적용되는지는 이름 부분 문자열이 아니라 계열 테이블(src/infrastructure/backends/gemini/transform.rsTHINKING_FAMILIES)이 결정합니다. 이전의 부분 문자열 목록은 gemini-3.1-pro-preview는 잡았지만 gemini-3.1-pro는 놓쳐서, 같은 모델이 어느 이름으로 요청되느냐에 따라 다르게 동작했습니다. 버전, 날짜, preview 접미사는 제거되고 -lite 세그먼트는 남습니다. Flash와 Flash-Lite는 다른 모델이기 때문입니다.

thought 요약 자체는 응답 쪽 처리가 필요합니다. Google은 이를 별도 필드가 아니라 message.content 안에 <thought>...</thought>로 감싸 넣고 extra_content.google.thought: true로 표시해 반환합니다. 라우터는 그 텍스트를 message.reasoning_content로 옮기고 content에는 답변만 남기므로, 마크업이 클라이언트에 도달하지 않고 content 키는 항상 존재합니다. 모델이 thought만 내놓은 경우에는 키가 사라지는 대신 빈 문자열이 됩니다.

잘못된 함수 호출

Gemini는 도구 턴을 finish_reason: "function_call_filter: MALFORMED_FUNCTION_CALL"로 끝내는 경우가 간헐적으로 있습니다(네이티브 API 표기는 MALFORMED_FUNCTION_CALL입니다). HTTP 200이고, assistant 메시지에는 contenttool_calls도 없으며, completion_tokens: 0입니다. 모델이 함수 호출로 의도한 무언가를 내놓았지만 공급자가 파싱하지 못한 경우입니다. 2026-09-10 측정 기준, 도구 하나를 붙인 gemini-3.1-pro-preview에서 2회 중 1회, 5회 중 1회 발생했고 나머지 호출은 정상적인 tool_calls 응답을 반환했습니다.

라우터는 이를 성공으로 전달하지 않습니다. 재시도 가능한 업스트림 실패(502, type: upstream_error, 메시지에 malformed_function_call 명시)로 바꾸므로 재시도 루프와 설정된 fallback.fallback_chains 홉이 동작합니다. 관측된 모든 사례에서 재시도가 성공했고, 대신 stop으로 답하면 아무것도 생산하지 못했으면서 프롬프트 토큰은 쓴 턴을 숨기게 됩니다. 스트리밍 요청에서는 응답이 이미 커밋된 뒤에 감지되므로 HTTP 상태가 아니라 SSE 오류 이벤트로 전달됩니다.

그 결과 모든 Gemini 경로에서 두 가지 형태 보장이 성립합니다. finish_reason은 항상 OpenAI의 다섯 값(stop, length, tool_calls, content_filter, function_call) 중 하나입니다. 공유 테이블 하나가 공급자 표기를 매핑하고 인식하지 못하는 값은 stop으로 줄이기 때문입니다. assistant 메시지에는 항상 content 키가 있으며, nulltool_calls가 함께 있을 때뿐이고 그 외에는 빈 문자열입니다.


Generic/HTTP 백엔드

Generic HTTP 백엔드 (Ollama, vLLM, LocalAI, LM Studio 등에 사용)는 변환 없이 요청을 전달합니다.

동작 설명
패스스루 reasoning_effort가 백엔드에 그대로 전달됨
검증 없음 라우터가 effort 레벨을 검증하지 않음
변환 없음 토큰 예산 변환이 수행되지 않음

백엔드 책임

generic 백엔드의 경우, 대상 LLM 서버가 reasoning_effort 파라미터를 처리(또는 무시)할 책임이 있습니다. 서버가 지원하지 않으면 오류를 반환하거나 파라미터를 무시할 수 있습니다.


llama.cpp 백엔드

llama.cpp 백엔드는 패스스루 동작을 사용합니다:

동작 설명
패스스루 파라미터가 변경 없이 전달됨
서버 의존적 지원 여부는 llama-server 설정에 따라 다름

llama.cpp에서의 Thinking 모델 지원

최근 버전의 llama-server는 <think> 태그 처리를 통해 thinking 모델(예: DeepSeek-R1)을 지원합니다. 그러나 reasoning_effortbudget_tokens에 대한 표준화된 API 파라미터는 없습니다. Thinking 동작은 일반적으로 다음으로 제어됩니다:

  • thinking 태그가 있는 모델의 내장 chat 템플릿
  • 서버 측 구성 (예: --thinking-budget 가능한 경우)
  • temperature 및 top-p 같은 샘플링 파라미터

vLLM 백엔드

vLLM은 OpenAI 호환 API를 제공하지만 reasoning effort 지원은 모델에 따라 다릅니다:

동작 설명
패스스루 generic 백엔드를 통해 파라미터 전달
모델 의존적 모델 유형에 따라 지원이 다름

vLLM에서의 Thinking 모델 지원

vLLM은 thinking 가능 모델(예: DeepSeek-R1, QwQ)을 실행할 수 있지만 reasoning_effort 파라미터 처리는 다음에 따라 다릅니다:

  • 모델이 구조화된 thinking을 지원하는지 여부
  • vLLM 서버 버전 및 구성
  • 모델의 chat 템플릿 구성

DeepSeek-R1 및 유사 모델의 경우, thinking은 명시적인 budget 파라미터로 제어되기보다 모델의 동작에 내재되어 있는 경우가 많습니다.

응답 필드 정규화

최신 vLLM 버전은 추론 텍스트를 choices[].delta.reasoning(스트리밍) 또는 choices[].message.reasoning(비스트리밍) 필드로 반환합니다. 라우터는 Chat Completions의 모든 릴레이 경로에서 이 필드를 reasoning_content로 정규화하므로, vLLM 버전에 관계없이 클라이언트는 항상 reasoning_content를 받습니다. 이미 reasoning_content를 사용하는 백엔드는 영향을 받지 않습니다.


요약 테이블

백엔드 Effort 레벨 변환 비고
OpenAI none, minimal, low, medium, high, xhigh, max의 모델별 부분집합 없음 (네이티브) 정확한 모델별 검증, 조용한 effort 폴백 없음
Anthropic (Fable 5 / Mythos 5) none, minimal, auto, low, medium, high, xhigh adaptive + output_config.effort xhighmax; temperature/top_p/top_k 항상 제거; 명시적 비활성 thinking 생략
Anthropic (Opus 4.8) none, minimal, auto, low, medium, high, xhigh adaptive + output_config.effort xhighmax; temperature/top_p/top_k 항상 제거; effort 기본값 high
Anthropic (Opus 4.7) none, minimal, auto, low, medium, high, xhigh adaptive + output_config.effort xhighmax; temperature/top_p/top_k 항상 제거
Anthropic (4.6) none, minimal, auto, low, medium, high, xhigh* adaptive + output_config.effort *xhigh → Opus 4.6은 max, Sonnet 4.6은 high; temperature 제거
Anthropic (4.6 이전) none, minimal, auto, low, medium, high budget_tokens 또는 adaptive auto는 적응형 사고로 매핑; temperature 제거
Gemini none*, auto**, minimal, low, medium, high 없음 (네이티브) *none은 Flash 모델만; **automedium으로 다운그레이드
vLLM 모델 의존적 패스스루 DeepSeek-R1, QwQ는 암시적 thinking 사용
llama.cpp 모델 의존적 패스스루 chat 템플릿에서 <think> 태그 사용
Generic 모두 패스스루 백엔드가 검증 처리

응답 형식

reasoning/thinking이 활성화되면 응답에 모델의 추론 과정이 포함됩니다:

OpenAI 형식 (reasoning_content 포함)

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "답은 42입니다.",
      "reasoning_content": "단계별로 생각해보겠습니다..."
    },
    "finish_reason": "stop"
  }]
}

Claude 확장 사고 (OpenAI 형식으로 변환됨)

라우터는 Claude의 thinking 블록을 reasoning_content 필드로 변환합니다:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "답은 42입니다.",
      "reasoning_content": "여러 요소를 고려해야 합니다..."
    },
    "finish_reason": "stop"
  }]
}

모범 사례

  1. 적절한 effort 레벨 사용: 높은 effort = 더 나은 추론이지만 더 느리고 비용이 많이 듬
  2. 모델 지원 확인: 모든 모델이 reasoning 파라미터를 지원하지 않음
  3. 상위 effort 레벨 신중히 처리: GPT-5.2–5.6은 xhigh, GPT-5.6만 max를 지원하며 이전 모델에는 라우터가 문서화된 폴백을 적용
  4. 비용 고려: 확장 사고는 추가 토큰 소비 (특히 Anthropic의 budget_tokens)
  5. 백엔드 테스트: Generic 백엔드는 지원 수준이 다양할 수 있음

관련 문서