콘텐츠로 이동

라우팅, 안정성, 튜닝

hint: 기반 라우팅, 쿼리 분류, 비용 최적화 및 임베딩 라우트, 폴백 체인, 재시도, 워밍업, 요청별 튜닝.

Revka는 모든 기본 공급자 위에 두 가지 선택적 래퍼를 추가합니다. 하나는 단일 논리 요청을 다양한 (provider, model) 쌍으로 분배하는 라우터이고, 다른 하나는 재시도, 폴백 체인, 키 교체를 제공하는 안정성 래퍼입니다. 그 위에 타임아웃, 토큰 상한, 추론 수준, 헤더를 공급자 설정을 직접 건드리지 않고 요청별로 조정할 수 있는 몇 가지 세부 설정이 있습니다.

이 페이지는 다음과 같은 경우에 활용하세요: 모델 지원 종료에 대비해 호출 지점을 안정적으로 유지하고 싶을 때, 트래픽 유형에 따라 다른 모델로 라우팅하고 싶을 때, 한 공급자가 다운됐을 때 백업 공급자로 전환하고 싶을 때, 또는 배포에서 지연 시간과 비용을 줄이고 싶을 때. 아직 단일 공급자를 선택하고 구성하는 단계라면 공급자 빠른 시작공급자 카탈로그부터 시작하세요.

hint: 접두사를 활용한 모델 라우팅

섹션 제목: “hint: 접두사를 활용한 모델 라우팅”

라우터를 사용하면 요청에 구체적인 모델 이름 대신 심볼릭 모델 이름을 지정할 수 있습니다. hint:reasoning이라는 모델 파라미터는 설정에서 "reasoning"으로 매핑한 (provider, model) 조합으로 해석됩니다. 이점은 채널, 도구, 에이전트 단계 등 호출 지점은 그대로 유지되고, 설정 항목 하나만 수정하여 모델을 업그레이드할 수 있다는 것입니다.

라우트는 [[model_routes]] 테이블로 정의합니다:

[[model_routes]]
hint = "reasoning"
provider = "openrouter"
model = "anthropic/claude-opus-4-5"
api_key = "" # optional per-route key override
[[model_routes]]
hint = "fast"
provider = "groq"
model = "llama-3.3-70b-versatile"

모델을 받을 수 있는 모든 곳에서 hint를 전달하세요:

hint:reasoning
필드필수 여부의미
hint필수고유한 심볼릭 이름(hint: 뒤에 오는 부분).
provider필수알려진 공급자 ID.
model필수해당 공급자에서 사용할 모델 ID.
api_key선택라우트별 키 재정의.

이것이 hint를 사용하는 주된 이유입니다. 모든 호출 지점에 hint:reasoning(및 관련 hint)을 하드코딩해두고, 공급자가 모델 ID를 지원 종료할 때는 라우트의 model = 값만 변경하면 됩니다.

  1. 호출 지점을 안정적으로 유지하세요: hint:reasoning, hint:fast, hint:semantic.

  2. [[model_routes]](또는 [[embedding_routes]]) 아래의 대상만 변경하세요.

  3. 새 설정을 검증하세요:

    Terminal window
    revka doctor
    revka status
  4. 배포 전에 대표적인 플로우 하나(채팅 + 메모리 리콜)를 스모크 테스트하세요.

revka doctor는 모델 라우트(및 임베딩 라우트)가 알려진 공급자를 가리키는지 검증하므로, 오타가 운영 환경에 도달하기 전에 발견됩니다.

두 가지 특별한 hint는 고정된 라우트를 조회하지 않습니다. 대신 테이블의 모든 라우트를 가격 기준으로 점수를 매겨 가장 저렴한 후보를 선택합니다:

  • hint:cost-optimized
  • hint:cheapest

점수는 모델 이름을 기준으로 [cost.prices] 데이터(100만 토큰당 입력 + 출력 비용)를 사용하며, [cost] 섹션의 prices 맵에 위치합니다. 후보는 기능 요구 사항(비전, 네이티브 도구 호출)에 따라 필터링될 수 있습니다. 가격 데이터가 없으면 라우터는 기본 라우트로 폴백합니다.

[[model_routes]]
hint = "fast"
provider = "groq"
model = "llama-3.3-70b-versatile"
[[model_routes]]
hint = "reasoning"
provider = "openrouter"
model = "anthropic/claude-opus-4-5"
[cost.prices]
# add per-1M-token pricing so cost-optimized routing has data to score
# e.g. "groq/llama-3.3-70b-versatile" = { input = 0.59, output = 0.79 }

쿼리 분류는 수신 메시지의 내용을 기반으로 hint:를 자동으로 선택합니다. 분류를 위한 LLM 호출은 발생하지 않으며, 순수 문자열 매칭으로 동작합니다. [query_classification] 아래에서 구성합니다:

[query_classification]
enabled = true
[[query_classification.rules]]
hint = "reasoning"
keywords = ["explain", "analyze", "why"]
min_length = 200
priority = 10
[[query_classification.rules]]
hint = "fast"
keywords = ["hi", "hello", "thanks"]
max_length = 50
priority = 5
기본값의미
enabledfalse분류 마스터 스위치.
rules[]우선순위 순서대로 평가되는 규칙.

각 규칙:

기본값의미
hint필수구성된 [[model_routes]] hint와 일치해야 합니다.
keywords[]대소문자 구분 없는 부분 문자열 매칭.
patterns[]대소문자 구분하는 리터럴 매칭(예: 코드 펜스, "fn ").
min_length미설정메시지가 최소 N자 이상일 때만 매칭.
max_length미설정메시지가 최대 N자 이하일 때만 매칭.
priority0높은 우선순위가 먼저 확인됩니다.

이는 길고 분석적인 질문을 강력한 추론 모델로, 짧은 인사말을 저렴하고 빠른 모델로 보내는 무비용 방법입니다. 매칭이 리터럴이므로 patterns는 코드 위주의 메시지(코드 펜스나 fn 으로 매칭)를 코딩 모델로 라우팅하는 데 적합합니다.

[[embedding_routes]] 항목은 설정에서 정의할 수 있으며 revka doctor가 검증하지만, Revka에서 임베딩은 제거되었습니다 — 현재 라우트는 비활성 상태이며 런타임에 임베딩 수행에 사용되지 않습니다. 스키마 검증만 남아 있습니다.

[[embedding_routes]]
hint = "semantic"
provider = "openai"
model = "text-embedding-3-small"
dimensions = 1536
[[embedding_routes]]
hint = "archive"
provider = "custom:https://embed.example.com/v1"
model = "your-embedding-model-id"
dimensions = 1024
api_key = "route-specific-key"
필드의미
hint심볼릭 이름.
providernone, openai, 또는 custom:<url> 중 하나(OpenAI 호환 임베딩 엔드포인트).
model임베딩 모델 ID.
dimensions선택 사항. API 기본값이 저장소 스키마와 다를 때 재정의합니다.
api_key선택적 라우트별 키 재정의.

더 광범위한 메모리 설정은 Kumiho 그래프 메모리메모리 개요를 참조하세요.

에이전트는 자연어로 라우팅 설정을 업데이트할 수 있습니다. 채팅에서 요청하면(예: “대화 공급자를 kimi로, 모델은 moonshot-v1-8k로 설정해줘”) 어시스턴트가 model_routing_config 도구를 호출하여 config.toml에 변경 사항을 저장합니다. TOML을 직접 편집할 필요가 없습니다.

이 도구는 라우트 테이블에서 hint, 모델, 공급자를 추가하거나 변경할 수 있습니다. 보안 정책 변경이나 데이터 삭제는 불가능합니다. 안전한 업그레이드 패턴과 잘 어울립니다: 호출 지점을 안정적인 hint로 유지하고 어시스턴트가 대상을 재지정하도록 하세요.

안정성: ReliableProvider 재시도 래퍼

섹션 제목: “안정성: ReliableProvider 재시도 래퍼”

ReliableProvider는 기본 공급자(및 선택적 폴백 체인)를 3단계 복원력 전략으로 래핑하며, 순서대로 적용됩니다:

  1. 모델 폴백 체인 — 구성된 체인의 각 모델로 요청을 순차적으로 시도합니다.
  2. 공급자 폴백 체인 — 기본 공급자의 재시도가 소진되면 fallback_providers의 각 공급자를 시도합니다.
  3. 내부 재시도 루프(provider, model) 쌍별 지수 백오프.

오류는 분류되어 도움이 될 때만 재시도됩니다:

분류예시동작
재시도 가능5xx, 타임아웃백오프와 함께 재시도.
재시도 불가4xx 인증/유효성 검사(예: 400, 401)즉시 실패 — 재시도 없음.
속도 제한Retry-After가 있는 429재시도; Retry-After 준수(Gemini 429 본문 파싱됨).
비즈니스 속도 제한할당량 소진재시도 없음.

컨텍스트 윈도우 오류는 재시도 전에 자동 히스토리 잘라내기를 트리거합니다(시스템 메시지가 아닌 메시지 중 가장 오래된 절반이 삭제됨).

[reliability] 아래에서 구성합니다:

[reliability]
provider_retries = 2 # attempts per provider before fallover (default: 2)
provider_backoff_ms = 500 # base backoff ms; doubles each retry, capped at 10000
fallback_providers = ["anthropic", "openai"]
api_keys = ["sk-second-key", "sk-third-key"] # round-robin on 429
[reliability.model_fallbacks]
"claude-opus-4-20250514" = ["claude-sonnet-4-20250514", "gpt-4o"]
기본값의미
provider_retries2폴오버 전 공급자당 시도 횟수.
provider_backoff_ms500ms 단위 기본 백오프; 재시도마다 두 배로 증가, 최대 10000ms.
fallback_providers미설정기본 공급자 이후 시도할 순서가 있는 공급자 이름.
api_keys미설정429 발생 시 라운드 로빈 교체를 시도하는 추가 키(현재 요청 시점에 키 교체는 무효 처리됨 — Provider 트레이트에 set_api_key가 없어 원본 키로 재시도가 계속됨).
model_fallbacks미설정model → [fallback_model, …] 맵.

이 두 설정은 가장 일반적인 안정성 시나리오를 처리합니다:

기본 공급자를 유지하고, 연결할 수 없을 때 다른 벤더로 전환합니다:

default_provider = "anthropic"
default_model = "claude-sonnet-4-6-20250514"
[reliability]
fallback_providers = ["openai", "openrouter"]

실제로 필요해지기 전에 폴백 공급자를 검증하세요 — revka doctorfallback_providers의 모든 이름이 알려진 공급자인지 확인합니다.

ProviderRuntimeOptions를 통한 요청별 튜닝

섹션 제목: “ProviderRuntimeOptions를 통한 요청별 튜닝”

런타임 설정 집합이 공급자가 빌드될 때 모든 공급자에 전달됩니다. 각각은 설정 키(또는 환경 변수)에 매핑되며 공급자의 아웃바운드 요청에 적용됩니다:

필드설정 키기본값의미
API URL 재정의api_url공급자 기본값사용자 정의 기본 URL(프록시, 자체 호스팅 엔드포인트).
추론 켜기/끄기[runtime] reasoning_enabled미설정확장 사고 토글; 환경 변수 REVKA_REASONING_ENABLED.
추론 수준[runtime] reasoning_effort미설정minimal, low, medium, high, 또는 xhigh.
HTTP 타임아웃provider_timeout_secs120호출당 타임아웃(초).
추가 헤더extra_headers (맵){}추가 HTTP 헤더; 환경 변수 REVKA_EXTRA_HEADERS.
API 경로api_path공급자 기본값특수한 API의 요청 경로 접미사 재정의.
최대 출력 토큰provider_max_tokens공급자 기본값출력 토큰 상한.
provider_timeout_secs = 180
provider_max_tokens = 8192
api_path = "/v2/generate"
[runtime]
reasoning_enabled = true
reasoning_effort = "high"
[extra_headers]
"X-Org" = "research"

REVKA_EXTRA_HEADERS 환경 변수는 Key:Value,Key2:Value2 형식을 사용합니다:

Terminal window
export REVKA_EXTRA_HEADERS="X-Org:research,X-Trace:on"

모든 공급자는 첫 번째 실제 요청 전에 DNS, TLS 핸드셰이크, HTTP/2 설정 등 HTTP 연결을 미리 수립하는 warmup() 메서드를 노출합니다. ReliableProvider와 라우터는 시작 시 등록된 모든 하위 공급자에 대해 warmup()을 순차적으로 호출합니다.

워밍업은 순전히 지연 시간 최적화입니다: 실패는 치명적이지 않으며 WARN 레벨로 기록됩니다. 첫 번째 요청이 전체 연결 설정 비용을 부담하게 되는 지연 시간에 민감한 배포에 주로 도움이 됩니다.

스트리밍을 지원하는 공급자는 비동기 채널을 통해 StreamEvent 값을 방출합니다: TextDelta, ToolCall, Usage, Final, 그리고 OpenAI 호환 커스텀 프록시(예: claude-max-api-proxy / Claude Code 프록시)의 경우 PreExecutedToolCallPreExecutedToolResult도 포함됩니다(도구를 사전 실행하고 커스텀 SSE 필드를 통해 관찰 가능성 이벤트를 전달). 게이트웨이는 이를 대시보드와 API를 위한 Server-Sent Events로 제공합니다.

래퍼가 적용된 경우:

  • ReliableProvider는 첫 번째 적격 공급자의 스트림을 전달합니다.
  • 라우터는 hint가 해석된 공급자에 스트리밍을 위임합니다.

스트리밍 도구 이벤트는 별도의 기능(supports_streaming_tool_events)입니다. 스트리밍 도구 호출 요청의 경우, 공급자가 스트리밍을 지원하고 다음 조건 중 하나를 만족하면 스트리밍 모드로 사용됩니다: (a) 네이티브 도구 호출을 사용하지 않는 경우(도구 호출은 이후 텍스트에서 파싱됨), 또는 (b) supports_streaming_tool_events도 선언한 경우. 스트리밍 도구 이벤트 기능은 네이티브 도구 호출을 사용할 때만 필요합니다. 실시간 전송에 대해서는 실시간: WebSocket, SSE & Live Canvas를 참조하세요.

DeepSeek-R1, GLM-4.7, Kimi K2.5 등 여러 “사고” 모델은 응답 텍스트와 함께 reasoning_content 필드를 반환합니다. Revka는 이를 채팅 응답에서 불투명한 블롭으로 보존하고 이후의 멀티턴 요청에서 다시 전송합니다. 일부 공급자 API는 이 필드가 없는 히스토리를 거부하기 때문입니다.

이것이 해당 모델을 사용할 때 대화 전반에 걸쳐 전체 추론 체인이 보존되는 이유입니다. 이는 투명하게 처리되므로 별도로 구성할 필요가 없습니다. 단, 일부 공급자는 반대 방식을 취합니다: Gemini의 사고 모델 추론 부분은 보존되지 않고 최종 응답에서 필터링됩니다.