라우팅, 안정성, 튜닝
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 = 값만 변경하면 됩니다.
-
호출 지점을 안정적으로 유지하세요:
hint:reasoning,hint:fast,hint:semantic. -
[[model_routes]](또는[[embedding_routes]]) 아래의 대상만 변경하세요. -
새 설정을 검증하세요:
Terminal window revka doctorrevka status -
배포 전에 대표적인 플로우 하나(채팅 + 메모리 리콜)를 스모크 테스트하세요.
revka doctor는 모델 라우트(및 임베딩 라우트)가 알려진 공급자를 가리키는지 검증하므로, 오타가 운영 환경에 도달하기 전에 발견됩니다.
비용 최적화 라우팅
섹션 제목: “비용 최적화 라우팅”두 가지 특별한 hint는 고정된 라우트를 조회하지 않습니다. 대신 테이블의 모든 라우트를 가격 기준으로 점수를 매겨 가장 저렴한 후보를 선택합니다:
hint:cost-optimizedhint: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 = 200priority = 10
[[query_classification.rules]]hint = "fast"keywords = ["hi", "hello", "thanks"]max_length = 50priority = 5| 키 | 기본값 | 의미 |
|---|---|---|
enabled | false | 분류 마스터 스위치. |
rules | [] | 우선순위 순서대로 평가되는 규칙. |
각 규칙:
| 키 | 기본값 | 의미 |
|---|---|---|
hint | 필수 | 구성된 [[model_routes]] hint와 일치해야 합니다. |
keywords | [] | 대소문자 구분 없는 부분 문자열 매칭. |
patterns | [] | 대소문자 구분하는 리터럴 매칭(예: 코드 펜스, "fn "). |
min_length | 미설정 | 메시지가 최소 N자 이상일 때만 매칭. |
max_length | 미설정 | 메시지가 최대 N자 이하일 때만 매칭. |
priority | 0 | 높은 우선순위가 먼저 확인됩니다. |
이는 길고 분석적인 질문을 강력한 추론 모델로, 짧은 인사말을 저렴하고 빠른 모델로 보내는 무비용 방법입니다. 매칭이 리터럴이므로 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 = 1024api_key = "route-specific-key"| 필드 | 의미 |
|---|---|
hint | 심볼릭 이름. |
provider | none, openai, 또는 custom:<url> 중 하나(OpenAI 호환 임베딩 엔드포인트). |
model | 임베딩 모델 ID. |
dimensions | 선택 사항. API 기본값이 저장소 스키마와 다를 때 재정의합니다. |
api_key | 선택적 라우트별 키 재정의. |
더 광범위한 메모리 설정은 Kumiho 그래프 메모리와 메모리 개요를 참조하세요.
model_routing_config 도구
섹션 제목: “model_routing_config 도구”에이전트는 자연어로 라우팅 설정을 업데이트할 수 있습니다. 채팅에서 요청하면(예: “대화 공급자를 kimi로, 모델은 moonshot-v1-8k로 설정해줘”) 어시스턴트가 model_routing_config 도구를 호출하여 config.toml에 변경 사항을 저장합니다. TOML을 직접 편집할 필요가 없습니다.
이 도구는 라우트 테이블에서 hint, 모델, 공급자를 추가하거나 변경할 수 있습니다. 보안 정책 변경이나 데이터 삭제는 불가능합니다. 안전한 업그레이드 패턴과 잘 어울립니다: 호출 지점을 안정적인 hint로 유지하고 어시스턴트가 대상을 재지정하도록 하세요.
안정성: ReliableProvider 재시도 래퍼
섹션 제목: “안정성: ReliableProvider 재시도 래퍼”ReliableProvider는 기본 공급자(및 선택적 폴백 체인)를 3단계 복원력 전략으로 래핑하며, 순서대로 적용됩니다:
- 모델 폴백 체인 — 구성된 체인의 각 모델로 요청을 순차적으로 시도합니다.
- 공급자 폴백 체인 — 기본 공급자의 재시도가 소진되면
fallback_providers의 각 공급자를 시도합니다. - 내부 재시도 루프 —
(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 10000fallback_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_retries | 2 | 폴오버 전 공급자당 시도 횟수. |
provider_backoff_ms | 500 | ms 단위 기본 백오프; 재시도마다 두 배로 증가, 최대 10000ms. |
fallback_providers | 미설정 | 기본 공급자 이후 시도할 순서가 있는 공급자 이름. |
api_keys | 미설정 | 429 발생 시 라운드 로빈 교체를 시도하는 추가 키(현재 요청 시점에 키 교체는 무효 처리됨 — Provider 트레이트에 set_api_key가 없어 원본 키로 재시도가 계속됨). |
model_fallbacks | 미설정 | model → [fallback_model, …] 맵. |
fallback_providers와 model_fallbacks
섹션 제목: “fallback_providers와 model_fallbacks”이 두 설정은 가장 일반적인 안정성 시나리오를 처리합니다:
기본 공급자를 유지하고, 연결할 수 없을 때 다른 벤더로 전환합니다:
default_provider = "anthropic"default_model = "claude-sonnet-4-6-20250514"
[reliability]fallback_providers = ["openai", "openrouter"]곧 지원 종료될 모델을 현재 사용 가능한 대체 모델로 매핑하여 전환 기간 동안 요청이 계속 처리되도록 합니다:
[reliability.model_fallbacks]"claude-opus-4-20250514" = ["claude-sonnet-4-6-20250514", "gpt-4o"]api_keys는 429 발생 시 라운드 로빈 교체를 시도하는 추가 키를 나열합니다. 단, 키 교체는 현재 요청 시점에 무효 처리됩니다 — Provider 트레이트에 set_api_key가 없어 교체 여부와 무관하게 재시도는 원본 키로 계속됩니다:
[reliability]api_keys = ["sk-key-a", "sk-key-b", "sk-key-c"]실제로 필요해지기 전에 폴백 공급자를 검증하세요 — revka doctor는 fallback_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_secs | 120 | 호출당 타임아웃(초). |
| 추가 헤더 | extra_headers (맵) | {} | 추가 HTTP 헤더; 환경 변수 REVKA_EXTRA_HEADERS. |
| API 경로 | api_path | 공급자 기본값 | 특수한 API의 요청 경로 접미사 재정의. |
| 최대 출력 토큰 | provider_max_tokens | 공급자 기본값 | 출력 토큰 상한. |
provider_timeout_secs = 180provider_max_tokens = 8192api_path = "/v2/generate"
[runtime]reasoning_enabled = truereasoning_effort = "high"
[extra_headers]"X-Org" = "research"REVKA_EXTRA_HEADERS 환경 변수는 Key:Value,Key2:Value2 형식을 사용합니다:
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 프록시)의 경우 PreExecutedToolCall과 PreExecutedToolResult도 포함됩니다(도구를 사전 실행하고 커스텀 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의 사고 모델 추론 부분은 보존되지 않고 최종 응답에서 필터링됩니다.