본문으로 이동
LLM 라우팅

문서 탐색

핵심 개념레퍼런스

LLM 라우팅

프로바이더 선택과 모델 해석입니다. 폴백 체인은 기본 비활성으로 출하되고, 치명 오류는 재시도 없이 빠르게 실패합니다.

GEODE는 Anthropic, OpenAI(+ChatGPT 구독), OpenRouter, GLM 네 프로바이더 경로를 명시적으로 라우팅합니다. 이 페이지는 모델이 어떻게 결정되고, 호출이 어느 어댑터로 가며, 실패했을 때 무엇이 일어나는지 정리합니다.

구성 요소

구성코드
프로바이더 유틸리티core/llm/providers/anthropic.py, codex.py, glm.py, openrouter.py. quota·identity·request shaping을 어댑터에 제공
비동기 호출 어댑터core/llm/adapters/. SDK client와 acomplete() 호출 표면 소유
프로바이더 compositioncore/llm/registry.py. 모델 identity, credential route, transport/API shape를 분리해 선언
어댑터 레지스트리core/llm/adapters/registry.py의 bootstrap_builtins(). 내장 factory와 운영자 정책이 승인한 geode.llm_adapters 진입점을 불변 generation snapshot으로 검색
라우팅 매니페스트core/config/routing.toml (+ ~/.geode/routing.toml 사용자 오버라이드). 모델 id prefix로 프로바이더 결정

서브프로세스 워커는 부모의 wiring 컨테이너를 거치지 않으므로 bootstrap_builtins()를 명시적으로 호출해야 합니다. 빈 레지스트리는 AdapterNotFoundError로 끝납니다. 각 AgenticLoop 세션은 생성 시 현재 generation을 캡처하므로, reload는 새 세션에만 보이고 실행 중 세션의 라우팅은 바뀌지 않습니다.

OpenRouter 경계

OpenRouter는 openrouter provider identity와 Chat Completions transport를 조합합니다. GEODE model id는 openrouter/<publisher>/<model>이며 어댑터가 외부 namespace 하나만 제거합니다. direct Anthropic/OpenAI와 equivalence group을 만들지 않으므로 자격이나 비용 경계가 조용히 바뀌지 않습니다. 응답이 제공한 실제 charge와 최종 serving route는 공통 usage/event 경로로 들어가고, 없을 때만 기존 정적 가격 추정을 사용합니다.

컴팩션 등 텍스트 요약도 같은 OpenRouter 어댑터를 사용해 캐시 마커, 세션 식별과 사용량을 보존합니다. 알려진 OpenAI reasoning 모델은 선택한 effort를 relay 요청에 전달하며, 다른 모델의 effort 지원을 추정하지 않습니다. 응답 스키마를 요청하면 JSON Schema 형식과 require_parameters: true를 함께 전달합니다. 명시적으로 이 옵션을 false로 지정한 호출은 거절합니다. 실제 스키마 지원과 강제 수준은 상위 endpoint에 달려 있고, 로컬 응답 검증도 유지됩니다.

알려진 OpenAI reasoning 경로는 기본 temperature를 전송하지 않고, 지원하지 않는 사용자 지정 값을 호출 전에 거절합니다. 이 relay에서는effort=none도 sampling 지원을 의미하지 않습니다. 선택한 effort와 공급자 제한은 그대로 유지합니다.

선택한 effort 보존

모델 선택 화면에서 확인한 effort는 요청의 값과 같아야 합니다. 프로바이더별 필드명은 달라도 값을 임의로 낮추거나 높이지 않습니다. 선택한 모델이 저장된 값을 지원하지 않으면 설정 변경이나 호출 전에 거절하므로, /model에서 지원하는 값을 직접 선택해야 합니다. 역할 탭을 이동하거나 Space로 선택을 추가한 뒤 Enter로 확정해도 primary effort를 보존합니다. 선택 화면에는 지원되는 값만 표시하며, 저장된 값이 맞지 않으면 방향키로 유효한 값을 고르기 전까지 Enter·Space 확정을 막습니다. 취소하면 기존 설정은 유지됩니다. Reflection 모델은 primary effort를 상속하므로, 함께 고른 모델과 effort의 호환성도 저장 전에 검사합니다.

지원되는 OpenAI·Claude·GLM 텍스트 보조 요청에도 명시한 effort를 전달합니다. OpenRouter의 알려진 OpenAI 경로도 같은 값을 전달합니다. effort 제어가 없는 모델은 선택 화면에 조절 기능을 표시하지 않으며, 서로 다른 모델의 같은 effort가 같은 계산량을 뜻하지는 않습니다.

모델 해석 우선순위

강한 쪽이 이깁니다.

CLI 인자
  > env (os.environ + .env)
    > 프로젝트 .geode/config.toml
      > 글로벌 ~/.geode/config.toml
        > routing.toml 기본값

어느 레이어가 이기는지는 geode config explain model이 레이어별 후보와 함께 보여줍니다. 실효 모델 확인은 항상 geode about입니다. config.toml만 보고 판단하면 상위 env 레이어에 가려진 값을 놓칩니다.

폴백 체인은 비어서 출하됩니다

[model.fallbacks]는 기본값이 전부 빈 목록입니다. primary 모델이 실패하면 GEODE는 다른 모델로 몰래 갈아타지 않습니다. 쿼터 소진이면 BillingError를, 일시 오류면 마지막 예외를 그대로 올리고, 다음 모델은 사용자가 /model로 직접 고릅니다. 자동 폴백을 원하면 ~/.geode/routing.toml의 체인을 채워 옵트인합니다. 체인 실행기는 core/llm/router/calls/_failover.py의 call_with_failover입니다.

조용한 cross-provider 자동 전환은 의도적으로 삭제된 기능입니다. 관측 불가능한 폴백은 어느 모델이 답했는지에 대한 신뢰를 무너뜨립니다.

재시도와 fast-fail

분류와 지연은 core/llm/fallback.py의 RetryPolicy, classify_retry_error, retry_delay_for가 공유합니다. 메인 루프는 동일 모델 안에서, 보조 호출은 run_with_retry_policy로 명시된 모델 체인 안에서만 실행합니다. SDK 재시도는 0입니다. 상태를 가진 CircuitBreaker 클래스는 없습니다.

판정대상효과
is_billing_fatal결제, 쿼터 소진재시도 없이 즉시 실패
is_request_fatal400류 요청 오류같은 요청을 다시 보내봤자 같은 결과이므로 즉시 실패
classify_retry_error연결, timeout, 408/409/429/5xx설정된 총 시도 횟수 안에서 jitter backoff
stream/effect guard이미 보인 출력, 불확실한 부작용동일 호출을 재실행하지 않고 중단 또는 reconcile

모델별 동작 차이

모델 패밀리별 능력 앵커는 core/llm/model_capabilities.py에 있습니다. 한 가지가 운영에서 특히 중요합니다. Fable 5는 안전 거절을 HTTP 200의 stop_reason: "refusal"로 보내며, Anthropic 프로바이더의 normalize_anthropic이 stop_details를 보존하고 루프가 model_refusal 종료로 매핑합니다. 자세한 동작은 안쪽 agentic 루프의 종료 경로 절을 참고합니다.

실패 모드

증상원인해법
모델을 바꿨는데 효과가 없음상위 레이어(env)가 가리는 중geode config explain model로 이기는 레이어를 찾고 geode about으로 실효값을 확인합니다
primary 실패 시 다른 모델로 안 넘어감폴백 체인이 기본값(빈 목록)의도된 동작입니다. /model로 전환하거나 ~/.geode/routing.toml에서 옵트인합니다
서브프로세스에서 AdapterNotFoundErrorbootstrap_builtins() 미호출워커 진입점에서 명시적으로 호출합니다
400 오류가 재시도 없이 바로 실패is_request_fatal fast-fail의도된 동작입니다. 요청 자체(스키마, 크기)를 고칩니다

다음