모델 라우팅: 커스텀 매핑, 와일드카드 우선순위 및 프리셋 전략
클라이언트에서 작성한 model은 Antigravity Tools가 최종적으로 업스트림에 요청할 때 사용하는 "물리 모델"과 반드시 같지는 않습니다. 모델 라우팅은 하는 일이 간단합니다: "대외적으로 안정적인 모델 이름"을 "내부적으로 실제로 사용하는 모델"로 매핑하고, 결과를 응답 헤더 X-Mapped-Model에 넣어, 예상 경로로 갔는지 확인하기 쉽게 합니다.
이 수업을 마치면 할 수 있는 것
- UI에서
proxy.custom_mapping을 구성할 수 있습니다(정확 매핑 + 와일드카드 매핑) - 한 규칙이 어떻게 일치하는지 명확히 설명할 수 있습니다(정확 > 와일드카드 > 기본 매핑)
- 원클릭으로 프리셋 규칙을 적용하여, 빠르게 OpenAI/Claude 클라이언트와 호환할 수 있습니다
curl -i를 사용하여X-Mapped-Model을 확인하고, "내 생각대로 라우팅하지 않는 이유"를 찾을 수 있습니다
현재의 문제점
- 클라이언트가 항상
gpt-4o를 작성하도록 원하지만, 업스트림은 안정적으로 특정 Gemini 모델로 떨어지기를 원합니다 - 버전화된 모델 이름이 많은데(예:
gpt-4-xxxx), 매번 수동으로 매핑을 추가하고 싶지 않습니다 - 요청이 성공했지만, 실제로 어떤 물리 모델이 실행되는지 확실하지 않습니다
언제 이 방법을 사용해야 할까요?
- 팀에 "고정된 대외 모델 집합"을 제공하고 싶으며, 업스트림 모델 변화를 차단하고 싶습니다
- 다양한 OpenAI/Claude 모델 이름을 소수의 고성능 모델로 통합 라우팅하고 싶습니다
- 401/429/0 토큰을 문제 해결할 때, 매핑된 실제 모델을 확인하고 싶습니다
🎒 시작 전 준비
- 이미 로컬 리버스 프록시를 시작할 수 있고, 외부 요청을 실행할 수 있습니다(먼저 로컬 리버스 프록시 시작 및 첫 번째 클라이언트 연결(/healthz + SDK 구성)를 완료하는 것이 좋습니다)
curl -i를 사용하여 응답 헤더를 보는 방법을 알고 있습니다(이전 수업에서X-Mapped-Model을 사용함)
이 수업의 두 가지 핵심 키워드
custom_mapping: "커스텀 규칙 테이블"입니다. 키는 클라이언트가 전달한 모델 이름(또는 와일드카드 pattern), 값은 최종적으로 사용할 모델 이름입니다(출처:src/types/config.ts).- 와일드카드
*: 모델 이름을 일괄 일치시키는 데 사용됩니다(예:gpt-4*). 일치 구현은 대소문자 구분입니다(출처:src-tauri/src/proxy/common/model_mapping.rs).
핵심 아이디어
백엔드는 요청을 처리할 때, 먼저 mapped_model을 계산합니다:
- 먼저
custom_mapping에 정확히 일치하는 것이 있는지 확인(key가model과 완전히 같음) - 다음 와일드카드 일치를 시도: "
*가 아닌 문자가 더 많은" 규칙을 선택합니다(더 구체적인 규칙 우선) - 모두 일치하지 않으면 시스템 기본 매핑을 사용합니다(예: 일부 OpenAI/Claude 모델 별칭을 내부 모델로 매핑)
이 mapped_model은 응답 헤더 X-Mapped-Model에 작성됩니다(최소한 OpenAI 핸들러는 이렇게 합니다). 이것을 사용하면 "내가 작성한 model이 결국 무엇이 되었는지"를 확인할 수 있습니다.
핫 리로드 의미(재시작 불필요)
리버스 프록시 서비스가 실행 중일 때, 프론트엔드가 update_model_mapping을 호출하면 백엔드가 즉시 custom_mapping을 메모리의 RwLock에 쓰고, 동시에 영구 구성에도 저장합니다(출처: src-tauri/src/commands/proxy.rs; src-tauri/src/proxy/server.rs).
따라해 보세요
1단계: API Proxy 페이지에서 "모델 라우팅" 카드 찾기
왜 필요한가요? 모델 라우팅 구성 진입점은 UI에 있습니다. 구성 파일을 수동으로 편집할 필요가 없습니다.
Antigravity Tools → API Proxy 페이지를 열고, 아래로 스크롤합니다.
보아야 할 것: 제목이 "모델 라우팅 센터"와 유사한 카드가 나타나고, 우측 상단에 두 개의 버튼이 있습니다: "프리셋 매핑 적용"과 "매핑 초기화"(출처: src/pages/ApiProxy.tsx).
2단계: "정확 매핑" 하나 추가(가장 제어 가능)
왜 필요한가요? 정확 매핑의 우선순위가 가장 높습니다. "이 모델 이름은 반드시 이 물리 모델로 떨어지게 하고 싶다"는 경우에 적합합니다.
"매핑 추가" 영역에서:
- Original에 대외적으로 공개할 모델 이름을 입력합니다. 예:
gpt-4o - Target에서 드롭다운으로 타겟 모델을 선택합니다. 예:
gemini-3-flash
Add를 클릭합니다.
보아야 할 것: 매핑 목록에 gpt-4o -> gemini-3-flash가 나타나고, 저장 성공 프롬프트가 팝업됩니다.
3단계: "와일드카드 매핑" 하나 추가(대량 커버)
왜 필요한가요? 버전화된 모델 이름이 많을 때(예: gpt-4-turbo, gpt-4-1106-preview), 와일드카드를 사용하면 많은 중복 구성을 줄일 수 있습니다.
매핑을 하나 더 추가합니다:
- Original:
gpt-4* - Target:
gemini-3-pro-high
보아야 할 것: 목록에 gpt-4* -> gemini-3-pro-high가 나타납니다.
규칙 우선순위의 "함정"
gpt-4o가 정확 규칙 gpt-4o와 와일드카드 규칙 gpt-4*를 동시에 만족할 때, 백엔드는 먼저 정확 일치를 거칩니다(출처: src-tauri/src/proxy/common/model_mapping.rs).
4단계: 원클릭으로 프리셋 규칙 적용(빠르게 호환)
왜 필요한가요? 주요 목적이 "빠르게 일반적인 OpenAI/Claude 모델 이름에 적응하는 것"이라면, 프리셋이 한 번에 일괄 와일드카드 규칙을 채워줄 수 있습니다.
"프리셋 매핑 적용"을 클릭합니다.
보아야 할 것: 목록에 여러 규칙이 추가되며, 다음과 유사한 것이 포함됩니다(출처: src/pages/ApiProxy.tsx):
{
"gpt-4*": "gemini-3-pro-high",
"gpt-4o*": "gemini-3-flash",
"gpt-3.5*": "gemini-2.5-flash",
"o1-*": "gemini-3-pro-high",
"o3-*": "gemini-3-pro-high",
"claude-3-5-sonnet-*": "claude-sonnet-4-5",
"claude-3-opus-*": "claude-opus-4-5-thinking",
"claude-opus-4-*": "claude-opus-4-5-thinking",
"claude-haiku-*": "gemini-2.5-flash",
"claude-3-haiku-*": "gemini-2.5-flash"
}5단계: X-Mapped-Model을 사용하여 라우팅 적용 여부 확인
왜 필요한가요? "구성이 작성되었습니다"를 확인하고 싶을 뿐만 아니라, "요청이 실제로 이 규칙에 따라 진행되었는지"도 확인하고 싶습니다. 가장 쉬운 방법은 X-Mapped-Model을 보는 것입니다.
curl -i http://127.0.0.1:8045/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "hi"}]
}'$resp = Invoke-WebRequest "http://127.0.0.1:8045/v1/chat/completions" -Method Post -ContentType "application/json" -Body '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "hi"}]
}'
$resp.Headers["X-Mapped-Model"]보아야 할 것: 응답 헤더에 X-Mapped-Model: ...가 있습니다. 2단계에서 gpt-4o를 gemini-3-flash로 정확 매핑했다면, 여기서 해당 값을 볼 수 있습니다(응답 헤더 작성은 src-tauri/src/proxy/handlers/openai.rs 참조).
6단계: "순수 기본 매핑"으로 돌아가려면, custom_mapping 초기화
왜 필요한가요? 문제 해결할 때, 종종 "커스텀 규칙의 영향"을 먼저 제외하고 싶습니다. custom_mapping을 비우는 것이 가장 직접적인 롤백 방법입니다.
"매핑 초기화"를 클릭합니다.
보아야 할 것: 매핑 목록이 비어 있습니다. 이후 요청에서 커스텀 규칙에 일치하지 않으면 시스템 기본 매핑을 사용합니다(출처: src/pages/ApiProxy.tsx; src-tauri/src/proxy/common/model_mapping.rs).
체크포인트 ✅
- [ ] UI에서
custom_mapping규칙을 추가/삭제할 수 있습니다 - [ ] 정확 규칙이 왜 와일드카드 규칙보다 우선하는지 설명할 수 있습니다
- [ ]
curl -i또는 PowerShell을 사용하여X-Mapped-Model을 읽을 수 있습니다
일반적인 실수
| 시나리오 | 할 수 있는 것(❌) | 추천 방법(✓) |
|---|---|---|
| 와일드카드가 적용되지 않음 | GPT-4*를 작성하여 gpt-4-turbo를 일치시키기를 기대함 | 소문자 gpt-4* 사용; 백엔드 와일드카드 일치는 대소문자 구분임 |
| 두 와일드카드가 모두 일치함 | 동시에 gpt-*와 gpt-4*를 작성하고, 어떤 규칙을 따를지 확실하지 않음 | 더 구체적인 규칙이 더 "길게" 만들어, *가 아닌 문자가 더 많게 함 |
| 규칙이 맞는 것 같지만 변하지 않음 | 응답 body만 보고, 응답 헤더를 보지 않음 | curl -i를 사용하여 X-Mapped-Model을 확인합니다(이것은 백엔드가 명시적으로 반환한 결과임) |
| 두 규칙이 "동일하게 구체적임" | 두 와일드카드 규칙을 작성했고, *가 아닌 문자 수가 같음 | 이러한 구성을 피하세요; 소스 코드 주석은 이 경우 결과가 HashMap 순회 순서에 의존하며 불안정할 수 있다고 설명합니다(출처: src-tauri/src/proxy/common/model_mapping.rs) |
이 수업 요약
proxy.custom_mapping은 "대외 모델 이름 → 물리 모델"을 제어하는 주 진입점입니다- 백엔드 라우팅 우선순위는: 정확 일치 > 와일드카드 일치(더 구체적인 것 우선) > 시스템 기본 매핑
X-Mapped-Model은 가장 신뢰할 수 있는 검증 수단입니다. 문제 해결할 때 우선적으로 보세요
다음 수업 예고
다음 수업에서는 쿼터 거버넌스: Quota Protection + Smart Warmup의 조합 전술을 계속 살펴봅니다(해당 장:
advanced-quota).
부록: 소스 코드 참조
클릭하여 소스 코드 위치 확인
업데이트 시간: 2026-01-23
| 기능 | 파일 경로 | 라인 |
|---|---|---|
구성 필드: proxy.custom_mapping(프론트엔드 타입) | src/types/config.ts | 6-20 |
UI: 쓰기/초기화/프리셋(update_model_mapping 호출) | src/pages/ApiProxy.tsx | 371-475 |
| UI: 모델 라우팅 카드(프리셋 매핑 적용 / 매핑 초기화 / 목록 및 추가 양식) | src/pages/ApiProxy.tsx | 1762-1931 |
백엔드 명령: 핫 리로드 및 custom_mapping 영구화 | src-tauri/src/commands/proxy.rs | 344-365 |
서버 상태: custom_mapping은 RwLock<HashMap<..>>로 저장 | src-tauri/src/proxy/server.rs | 16-53 |
| 라우팅 알고리즘: 정확 > 와일드카드(더 구체적인 것 우선) > 기본 매핑 | src-tauri/src/proxy/common/model_mapping.rs | 180-228 |
와일드카드 일치: 다중 * 지원, 대소문자 구분 | src-tauri/src/proxy/common/model_mapping.rs | 134-178 |
요청에서 mapped_model 계산(예: OpenAI 핸들러) | src-tauri/src/proxy/handlers/openai.rs | 154-159 |
| --- | --- | --- |
핵심 함수:
resolve_model_route(original_model, custom_mapping): 모델 라우팅 주 진입점(src-tauri/src/proxy/common/model_mapping.rs참조)