콘텐츠로 이동

고급 설정

전역 프롬프트

전역 프롬프트를 사용하면 모든 요청에 시스템 프롬프트를 주입하여 보안, 규정 준수 및 동작 가이드라인에 대한 중앙 집중식 정책 관리를 제공할 수 있습니다. 프롬프트는 인라인으로 정의하거나 외부 Markdown 파일에서 로드할 수 있습니다.

기본 설정

global_prompts:
  # 인라인 기본 프롬프트
  default: |
    회사 보안 정책을 따라야 합니다.
    내부 시스템 세부 정보를 공개하지 마십시오.
    도움이 되고 전문적이어야 합니다.

  # 병합 전략: prepend (기본), append, 또는 replace
  merge_strategy: prepend

  # 전역 프롬프트와 사용자 프롬프트 사이의 사용자 정의 구분자
  separator: "\n\n---\n\n"

외부 프롬프트 파일

복잡한 프롬프트의 경우 외부 Markdown 파일에서 콘텐츠를 로드할 수 있습니다. 그러면 구문 강조가 있는 편집 환경, 설정 파일 노이즈 없는 버전 관리, 프롬프트 업데이트에 대한 핫 리로드 지원이 가능해집니다.

global_prompts:
  # 프롬프트 파일이 있는 디렉토리 (설정 디렉토리 기준 상대 경로)
  prompts_dir: "./prompts"

  # 파일에서 기본 프롬프트 로드
  default_file: "system.md"

  # 파일에서 백엔드별 프롬프트
  backends:
    anthropic:
      prompt_file: "anthropic-system.md"
    openai:
      prompt_file: "openai-system.md"

  # 파일에서 모델별 프롬프트
  models:
    gpt-4o:
      prompt_file: "gpt4o-system.md"
    claude-3-opus:
      prompt_file: "claude-opus-system.md"

  merge_strategy: prepend

프롬프트 해석 우선순위

요청에 사용할 프롬프트 결정 시:

  1. 모델별 프롬프트 (최고 우선순위) - global_prompts.models.<model-id>
  2. 백엔드별 프롬프트 - global_prompts.backends.<backend-name>
  3. 기본 프롬프트 - global_prompts.default 또는 global_prompts.default_file

각 레벨에서 prompt (인라인)와 prompt_file이 모두 지정되면 prompt_file이 우선합니다.

병합 전략

전략 동작
prepend 전역 프롬프트가 사용자 시스템 프롬프트 앞에 추가 (기본)
append 전역 프롬프트가 사용자 시스템 프롬프트 뒤에 추가
replace 전역 프롬프트가 사용자 시스템 프롬프트를 완전히 대체

REST API 관리

프롬프트 파일은 Admin API를 통해 런타임에 관리할 수 있습니다:

# 모든 프롬프트 목록
curl http://localhost:8080/admin/config/prompts

# 특정 프롬프트 파일 가져오기
curl http://localhost:8080/admin/config/prompts/prompts/system.md

# 프롬프트 파일 업데이트
curl -X PUT http://localhost:8080/admin/config/prompts/prompts/system.md \
  -H "Content-Type: application/json" \
  -d '{"content": "# 업데이트된 시스템 프롬프트\n\n새 콘텐츠."}'

# 디스크에서 모든 프롬프트 파일 리로드
curl -X POST http://localhost:8080/admin/config/prompts/reload

전체 API 문서는 Admin REST API 참조를 참조하세요.

보안 고려 사항

  • 경로 탐색 보호: 디렉토리 탐색 공격 방지를 위한 모든 파일 경로 검증
  • 파일 크기 제한: 개별 파일 1MB, 전체 캐시 50MB 제한
  • 상대 경로만: 프롬프트 파일은 설정된 prompts_dir 또는 설정 디렉토리 내에 있어야 함
  • 샌드박스 접근: 허용된 디렉토리 외부 파일은 거부

핫 리로드

전역 프롬프트는 즉시 핫 리로드를 지원합니다. 프롬프트 설정 또는 파일 변경 사항은 서버 재시작 없이 다음 요청에 적용됩니다.

모델 메타데이터

Continuum Router는 모델 기능, 가격, 한도에 대한 상세 정보를 제공하는 풍부한 모델 메타데이터를 지원합니다. 이 메타데이터는 /v1/models API 응답에 반환되며 클라이언트가 정보에 입각한 모델 선택 결정을 내리는 데 사용할 수 있습니다.

메타데이터 소스

모델 메타데이터는 세 가지 방법으로 설정할 수 있습니다 (우선순위 순):

  1. 백엔드별 model_configs (최고 우선순위)
  2. 외부 메타데이터 파일 (model-metadata.yaml)
  3. 메타데이터 없음 (모델은 메타데이터 없이도 작동)

외부 메타데이터 파일

model-metadata.yaml 파일을 만드세요:

models:
  - id: "gpt-4"
    aliases:                    # 이 메타데이터를 공유하는 대체 ID
      - "gpt-4-0125-preview"
      - "gpt-4-turbo-preview"
      - "gpt-4-vision-preview"
    metadata:
      display_name: "GPT-4"
      summary: "복잡한 작업을 위한 가장 유능한 GPT-4 모델"
      capabilities: ["text", "image", "function_calling"]
      knowledge_cutoff: "2024-04"
      pricing:
        input_tokens: 0.03   # 1000 토큰당
        output_tokens: 0.06  # 1000 토큰당
      limits:
        context_window: 128000
        max_output: 4096

  - id: "llama-3-70b"
    aliases:                    # 동일 모델의 다른 양자화
      - "llama-3-70b-instruct"
      - "llama-3-70b-chat"
      - "llama-3-70b-q4"
      - "llama-3-70b-q8"
    metadata:
      display_name: "Llama 3 70B"
      summary: "강력한 성능의 오픈 소스 모델"
      capabilities: ["text", "code"]
      knowledge_cutoff: "2023-12"
      pricing:
        input_tokens: 0.001
        output_tokens: 0.002
      limits:
        context_window: 8192
        max_output: 2048

설정에서 참조하세요:

model_metadata_file: "model-metadata.yaml"

Thinking 패턴 설정

일부 모델은 추론/사고 콘텐츠를 비표준 방식으로 출력합니다. 라우터는 스트리밍 응답을 적절히 변환하기 위해 모델별 thinking 패턴 설정을 지원합니다.

패턴 유형:

패턴 설명 예시 모델
none thinking 패턴 없음 (기본값) 대부분의 모델
standard 명시적 시작/종료 태그 (<think>...</think>) 커스텀 추론 모델
unterminated_start 시작 태그 없이 종료 태그만 있음 nemotron-3-nano

설정 예시:

models:
    - id: nemotron-3-nano
      metadata:
        display_name: "Nemotron 3 Nano"
        capabilities: ["chat", "reasoning"]
        # Thinking 패턴 설정
        thinking:
          pattern: unterminated_start
          end_marker: "</think>"
          assume_reasoning_first: true

Thinking 패턴 필드:

필드 타입 설명
pattern string 패턴 유형: none, standard, 또는 unterminated_start
start_marker string standard 패턴용 시작 마커 (예: <think>)
end_marker string 종료 마커 (예: </think>)
assume_reasoning_first boolean true인 경우, 종료 마커까지 첫 토큰들을 추론으로 처리

작동 방식:

모델에 thinking 패턴이 설정되면:

  1. 스트리밍 응답이 가로채져 변환됨
  2. end_marker 이전 콘텐츠는 reasoning_content 필드로 전송
  3. end_marker 이후 콘텐츠는 content 필드로 전송
  4. 출력은 호환성을 위해 OpenAI의 reasoning_content 형식을 따름

출력 예시:

// 추론 콘텐츠 (종료 마커 이전)
{"choices": [{"delta": {"reasoning_content": "분석해 보겠습니다..."}}]}

// 일반 콘텐츠 (종료 마커 이후)
{"choices": [{"delta": {"content": "답은 42입니다."}}]}

Responses-API 전용 모델

OpenAI는 일부 모델을 Responses API(/v1/responses)로만 노출합니다. 이런 모델은 /v1/chat/completions에서 닿을 수 없어, Chat Completions 엔드포인트로 요청하면 업스트림에서 404 not_found가 돌아옵니다.

responses_only capability 플래그가 이런 모델을 표시하면, 라우터가 자동으로 Responses API 쪽으로 dispatch합니다. 기본값은 false이므로 기존 모델 항목은 그대로 두면 됩니다.

설정 예시:

models:
    - id: gpt-5.4-pro
      metadata:
        display_name: "GPT-5.4 Pro"
        capabilities: ["chat", "vision", "code", "reasoning", "tool"]
        # /v1/responses에서만 제공되며, /v1/chat/completions로는 접근 불가.
        responses_only: true
        limits:
          context_window: 1050000
          max_output: 128000

기본 제공되는 Responses-API 전용 모델

아래 목록은 model-metadata.yaml과 내장 OpenAI 레지스트리(src/infrastructure/backends/openai/models/gpt5_family.rs)와 동기화되어 있습니다. 새로운 Responses-API 전용 모델이 업스트림에 추가되면 두 파일을 함께 갱신해야 합니다.

모델 ID 출처 비고
gpt-5.2-pro 내장 OpenAI 메타데이터 + model-metadata.yaml 어려운 질문용 최상위 모델, xhigh reasoning effort
gpt-5.4-pro model-metadata.yaml Frontier 급 심층 추론, medium/high/xhigh 지원
gpt-5.5-pro model-metadata.yaml 고난도 작업용 GPT-5.5 고성능 변형

플래그는 메타데이터 전체와 같은 우선순위 체인(백엔드 model_configs > model-metadata.yaml > 내장 OpenAI 메타데이터)을 따르므로, 운영자가 정의한 항목으로 어떤 모델의 기본값도 덮어쓸 수 있습니다.

새 모델을 Responses-API 전용으로 표시하기

추가 모델을 Responses-API 전용으로 표시하려면, 지원되는 소스 중 한 곳의 모델 항목 metadata 블록에 responses_only: true를 추가합니다. 배포 범위에 맞는 우선순위를 골라 사용합니다:

  • model-metadata.yaml: 모든 백엔드에 적용되는 라우터 전체 기본값. 기존 capability 메타데이터 옆에 플래그를 추가하면 되고 다른 필드는 건드릴 필요가 없습니다. 공급자 전반에서 일관되게 Responses-API 전용으로 출시되는 새 Pro 모델에는 이 위치를 권장합니다.
  • config.yaml의 백엔드 model_configs: 백엔드별 오버라이드 (예: 자체 호스팅한 Pro 모델 클론을 Chat Completions 엔드포인트에 노출했고, /v1/responses로 보내면 안 되는 경우). 백엔드 단의 responses_only: false는 해당 백엔드에서만 메타데이터 파일 기본값을 덮어씁니다.
  • src/infrastructure/backends/openai/models/gpt5_family.rs의 내장 OpenAI 레지스트리: 바이너리에 같이 들어가는 모델용. 여기에 새 항목을 추가하면 외부에서 로드되는 메타데이터와 일관성을 유지하기 위해 model-metadata.yaml에도 같이 반영합니다.

이런 소스를 갱신한 뒤에는 라우터를 재시작하거나 핫 리로드를 트리거해야 새 플래그가 후속 요청에 적용됩니다.

Dispatch 동작

responses_only=true가 설정된 모델은 /v1/chat/completions로 도달했을 모든 공개 surface에서 라우터가 Responses API로 보냅니다:

  • /v1/chat/completions: 요청이 업스트림 /v1/responses 엔드포인트로 투명하게 전달되고, 응답은 strict 모드의 chat.completion(스트리밍은 chat.completion.chunk) 봉투로 다시 변환됩니다.
  • /anthropic/v1/messages: Anthropic 형식 요청이 Responses API 형태로 변환되어 /v1/responses로 dispatch되고, 업스트림 응답이 다시 Anthropic Messages JSON(스트리밍은 Anthropic SSE 이벤트 시퀀스)으로 변환됩니다. 도구 호출 왕복, 웹 검색 에뮬레이션, Unix 소켓 전송 모두 이 플래그에서 분기합니다.

두 경우 모두 dispatch는 클라이언트에 투명합니다. 요청과 응답 모양이 클라이언트가 호출한 surface와 일치하므로, responses_only 모델을 쓰기 위해 클라이언트 쪽을 바꿀 필요가 없습니다.

백엔드 유형 제약

/v1/responses는 OpenAI와 Azure OpenAI 백엔드에서만 제공됩니다. responses_only 모델이 OpenAI/Azure OpenAI가 아닌 백엔드와 짝지어지면, 라우터가 업스트림 dispatch 전에 400 invalid_request_error로 거절합니다(/anthropic/v1/messages에서는 Anthropic 모양, /v1/chat/completions에서는 OpenAI 모양). 메시지에 모델 이름과 설정된 백엔드 유형이 함께 들어가므로, 잘못된 설정이 클라이언트 로그에서 바로 보입니다.

(backend, model) 쌍별 첫 dispatch는 info 레벨로 로깅되어, 디버그 로그를 켜지 않고도 운영자가 Responses-API 라우팅 여부를 확인할 수 있습니다.

네임스페이스 인식 매칭

라우터는 네임스페이스 접두사가 있는 모델 ID를 지능적으로 처리합니다. 예:

  • 백엔드 반환: "custom/gpt-4", "openai/gpt-4", "optimized/gpt-4"
  • 메타데이터 정의: "gpt-4"
  • 결과: 모든 변형이 일치하고 동일한 메타데이터 수신

따라서 다른 백엔드가 공통 메타데이터 정의를 공유하면서 자체 명명 규칙을 사용할 수 있습니다.

메타데이터 우선순위 및 별칭 해석

모델의 메타데이터를 조회할 때, 라우터는 다음 우선순위 체인을 사용합니다:

  1. 정확한 모델 ID 매칭
  2. 정확한 별칭 매칭
  3. 날짜 접미사 정규화 (자동, 설정 불필요)
  4. 와일드카드 패턴 별칭 매칭
  5. 기본 모델 이름 폴백 (네임스페이스 제거)

각 소스 (백엔드 설정, 메타데이터 파일, 내장) 내에서 동일한 우선순위가 적용됩니다:

  1. 백엔드별 model_configs (최고 우선순위)

    backends:
      - name: "my-backend"
        model_configs:
          - id: "gpt-4"
            aliases: ["gpt-4-turbo", "gpt-4-vision"]
            metadata: {...}  # 이것이 우선
    

  2. 외부 메타데이터 파일 (두 번째 우선순위)

    model_metadata_file: "model-metadata.yaml"
    

  3. 내장 메타데이터 (OpenAI 및 Gemini 백엔드용)

자동 날짜 접미사 처리

LLM 프로바이더는 날짜 접미사가 있는 모델 버전을 자주 릴리스합니다. 라우터는 설정 없이 자동으로 날짜 접미사를 감지하고 정규화합니다:

지원되는 날짜 패턴:

  • -YYYYMMDD (예: claude-opus-4-5-20251130)
  • -YYYY-MM-DD (예: gpt-4o-2024-08-06)
  • -YYMM (예: o1-mini-2409)
  • @YYYYMMDD (예: model@20251130)

작동 방식:

요청: claude-opus-4-5-20251215
      ↓ (날짜 접미사 감지됨)
조회: claude-opus-4-5-20251101  (기존 메타데이터 항목)
      ↓ (기본 이름 일치)
결과: claude-opus-4-5-20251101 메타데이터 사용

이는 모델 패밀리당 메타데이터를 한 번만 설정하면 되고, 새로운 날짜 버전이 자동으로 메타데이터를 상속한다는 것을 의미합니다.

와일드카드 패턴 매칭

별칭은 * 문자를 사용한 glob 스타일 와일드카드 패턴을 지원합니다:

  • 접두사 매칭: claude-*claude-opus, claude-sonnet 등과 매칭
  • 접미사 매칭: *-previewgpt-4o-preview, o1-preview 등과 매칭
  • 중위 매칭: gpt-*-turbogpt-4-turbo, gpt-3.5-turbo 등과 매칭

와일드카드 패턴이 있는 설정 예:

models:
    - id: "claude-opus-4-5-20251101"
    aliases:
        - "claude-opus-4-5"     # 기본 이름의 정확한 매칭
        - "claude-opus-*"       # 모든 claude-opus 변형에 대한 와일드카드
    metadata:
        display_name: "Claude Opus 4.5"
        # 자동 매칭: claude-opus-4-5-20251130, claude-opus-test 등

    - id: "gpt-4o"
    aliases:
        - "gpt-4o-*-preview"    # 프리뷰 버전 매칭
        - "*-4o-turbo"          # 접미사 매칭
    metadata:
        display_name: "GPT-4o"

우선순위 참고: 정확한 별칭은 항상 와일드카드 패턴보다 먼저 매칭되어 둘 다 매칭될 수 있는 경우에도 예측 가능한 동작을 보장합니다.

모델 변형에 별칭 사용

별칭은 특히 다음에 유용합니다:

  • 다른 양자화: qwen3-32b-i1, qwen3-23b-i4 → 모두 qwen3 메타데이터 사용
  • 버전 변형: gpt-4-0125-preview, gpt-4-turbogpt-4 메타데이터 공유
  • 배포 변형: llama-3-70b-instruct, llama-3-70b-chat → 동일 기본 모델
  • 날짜 버전: claude-3-5-sonnet-20241022, claude-3-5-sonnet-20241201 → 메타데이터 공유 (날짜 접미사 처리로 자동)

별칭이 있는 설정 예:

model_configs:
  - id: "qwen3"
    aliases:
      - "qwen3-32b-i1"     # 1비트 양자화된 32B
      - "qwen3-23b-i4"     # 4비트 양자화된 23B
      - "qwen3-16b-q8"     # 8비트 양자화된 16B
      - "qwen3-*"          # 다른 모든 qwen3 변형에 대한 와일드카드
    metadata:
      display_name: "Qwen 3"
      summary: "Alibaba의 Qwen 모델 계열"
      # ... 나머지 메타데이터

별칭과 접미사 정규화: 선택 기준

비정규 모델 id를 해당 메타데이터 항목에 연결하는 두 가지 레이어가 있습니다. 명시적 YAML 별칭과 src/models/pattern_matching.rs의 레이어드 접미사 peel 허용 목록이 그것입니다. 이 둘은 상호 보완적이며 중복이 아닙니다. 이 섹션에서는 새 항목을 추가할 때 어느 쪽을 선택해야 하는지 설명합니다.

매칭 단계 순서

파이프라인은 다음 순서로 실행되며, 앞 단계에서 매칭이 성공하면 이후 단계는 건너뜁니다.

  1. 정확한 모델 id (대소문자 구분).
  2. 정확한 별칭 (대소문자 구분).
  3. 날짜 접미사 정규화 (-YYYYMMDD, -YYYY-MM-DD, -YYMM, @YYYYMMDD).
  4. 레이어드 양자화 / 형식 / 플레이버 peel (대소문자 무시; 각 peel 이후 정확한 id + 정확한 별칭 + 날짜 접미사 단계를 재실행; 날짜 + 형식 결합도 같은 루프에서 처리).
  5. HuggingFace 리포지토리 접두사 제거 (vendor/repo -> repo). 제거된 잔류 문자열로 1-4단계에 한 번만 재진입하며, 재귀는 구조적으로 1회로 제한.
  6. 와일드카드 별칭 (glob 스타일 * 패턴).

명시적 별칭은 2단계에서 실행되어 peel(4단계)과 접두사 제거(5단계)보다 엄격하게 앞섭니다. 이 순서가 중요한 이유는 보존된 별칭과 peel-or-strip 경로가 서로 다른 메타데이터를 가리킬 때 별칭이 결정론적으로 우선하기 때문입니다. 별칭은 peel-or-strip 커버리지보다 약한 신호가 아니라 더 강한 의도 신호입니다.

세 가지 별칭 클래스

model-metadata.yaml의 모든 별칭은 세 클래스 중 하나에 속합니다.

peel-coverable

별칭 없이도 정규화가 동일한 소유자 id에 도달하고, 대상 메타데이터가 올바른 경우입니다. 삭제 후보입니다. 예시: qwen3.6-35b-a3b의 별칭인 qwen3.6-35b-a3b-instruct. 4단계에서 FLAVOR 토큰 -instruct를 peel하면 바로 기본 id에 도달하므로, 명시적 별칭이 커버리지를 추가하지 않습니다. 이슈 #557에서 제거된 64개의 별칭이 모두 이 클래스에 속하며, 각각 tests/format_suffix_normalization_test.rs::real_metadata_removed_aliases_still_resolve에 회귀 테스트가 추가되어 있습니다.

vendor-prefix

별칭이 접미사 peel로는 제거할 수 없는 벤더 또는 리포지토리 접두사를 포함하는 경우입니다. 과거에는 (이슈 #555 이전에는) 옛 네임스페이스 폴백이 대소문자를 구분했기 때문에 이런 별칭이 엄격하게 load-bearing이었습니다. 이슈 #555에서 5단계(HuggingFace 접두사 제거 + 1-4단계 재진입, 그중 4단계는 대소문자를 구분하지 않음)가 추가된 이후에는 대소문자가 혼합된 HF 형식도 별칭 없이 해결됩니다. 예시: qwen3.6-35b-a3b의 별칭인 Qwen/Qwen3.6-35B-A3B. 오늘도 명시적 별칭이 2단계에서 먼저 승리하지만, 5단계도 Qwen/ -> 잔류 Qwen3.6-35B-A3B -> 4단계 대소문자 무시 매칭으로 기본 id에 도달합니다. 이들 별칭은 이제 peel-coverable-adjacent 클래스에 속합니다. 소급 제거는 후속 감사로 미룹니다. 당분간 유지하되 커버하는 단계를 YAML 주석에 기록하세요.

intentional-override

별칭이 운영자 결정으로 다른 가중치를 가진 모델을 다른 항목의 메타데이터 아래 의도적으로 라우팅하는 경우입니다. 유지해야 합니다. 예시: smoothie-qwen3의 별칭인 smoothie-qwen3-32b-i1. smoothie-qwen3-32b-i1 파인튜닝 모델은 자체 가중치를 가지고 있으며, 운영자가 전용 항목을 만드는 대신 smoothie-qwen3 메타데이터 아래 노출하기로 선택한 것입니다. peel이 이 동등성을 자체적으로 추론해서는 안 됩니다. 이 클래스에 속하는 별칭은 YAML 주석에 소유자 id와 가중치가 다름을 명시해야 합니다.

새 별칭 추가 지침

model-metadata.yaml에 줄을 추가하기 전에, peel이 이미 커버하는지 확인하세요.

  • 새 id가 허용 목록에 있는 후행 양자화, 형식, 플레이버 토큰이 붙은 정규 기본 형태이고 가중치가 기본 메타데이터와 동일하면, 별칭을 추가하지 마세요. peel이 처리하며 별칭은 데드 코드가 됩니다.
  • 새 id가 기본과 동일한 가중치를 공유하지만 peel이 아직 처리하지 않는 토큰 클래스로 끝나는 경우 (예: -abliterated 같은 새로운 파인튜닝 레이블이나 -nf4 같은 새 양자화 형식), src/models/pattern_matching.rs의 peel 허용 목록을 확장하는 것을 우선 고려하세요. 이는 테스트 커버리지가 있는 코드 변경이며, 한 번의 작업으로 해당 클래스의 모든 향후 변형에 적용됩니다.
  • 새 id에 벤더 접두사, 대소문자가 맞지 않는 리포지토리 네임스페이스, peel 체인을 막는 파라미터 수 토큰 (-Nb, -aNb, -eNb), 또는 의도적으로 다른 가중치가 있는 경우, 이유를 명시한 YAML 주석과 함께 별칭을 추가하세요. 가중치가 소유자 id와 다르다면 주석에 그 사실을 명시하세요.

표면 구분: 코드 게이트 vs. YAML 게이트

변경 위치 게이트 릴리스 주기 사용 목적
src/models/pattern_matching.rs의 peel 허용 목록 코드 리뷰 + Rust 릴리스 다음 라우터 릴리스에 포함 모든 모델에 걸쳐 전체 토큰 클래스를 커버하는 전략적 정규화.
model-metadata.yaml의 별칭 YAML 리뷰 + 핫 리로드 관리자 API를 통해 당일 리로드 개별 오버라이드, 벤더 접두사 수정, 가중치 차이 오버라이드, 새 토큰이 peel 허용 목록 항목을 얻기 전의 임시 커버리지.

peel 허용 목록은 전략적 레이어이고, 별칭은 전술적 오버라이드 및 긴급 채널입니다.

peel 허용 목록에 있는 토큰 카테고리

src/models/pattern_matching.rs의 허용 목록은 현재 다음을 커버합니다.

  • BIT_WIDTH: -2bit, -3bit, -4bit, -5bit, -6bit, -8bit, -16bit
  • GGUF_QUANT: -Q4_K_M, -Q4_K_S, -Q5_K_M, -Q6_K, -Q8_0, -Q2_K, -IQ2_XS, -IQ3_XXS, -IQ4_XS, -F16, -F32, -BF16
  • FP_FORMAT: -FP4, -FP8, -FP16, -FP32, -NVFP4, -MXFP4
  • INT_FORMAT: -INT2, -INT4, -INT8
  • LIBRARY: -AWQ, -GPTQ, -BNB, -HQQ, -EXL2, -EXL3, -MLX
  • IMATRIX: -i1부터 -i8, -q2부터 -q8
  • UNSLOTH: -UD-Q<digit>_<KIND>, -UD-IQ<digit>_<KIND>
  • CONTAINER: -GGUF, -GGML, -SAFETENSORS
  • FLAVOR: -it, -instruct, -chat, -base, -thinking, -qat

파라미터 수 접미사(-Nb, -aNb, -eNb, -0.6b, -1.7b)는 peel되지 않습니다. 이는 정규 모델 식별자의 일부이며 peel 체인을 종료시킵니다. 이 때문에 qwen3-32b-i1qwen3의 명시적 별칭으로 유지되어야 합니다. 4단계에서 -i1을 peel한 뒤 -32b에서 멈추기 때문에, 별칭이 없으면 체인이 기본 id에 도달하지 못하고 소진됩니다.

HuggingFace 리포지토리 접두사 제거 (5단계)

5단계는 모델 id의 왼쪽에서 HuggingFace 스타일 vendor/repo 접두사를 정규화해서 오른쪽 접미사 peel을 보완합니다. 이슈 #555에서 도입되었으며, 사용자가 unsloth/Qwen3.6-35B-A3B-GGUF 같은 id를 제출할 때 매번 별칭을 등록하지 않고도 정규 qwen3.6-35b-a3b 메타데이터로 라우팅되게 하려는 것이 목적입니다. 즉, vendor x base x quant 조합마다 별칭을 수동으로 추가할 필요가 없어집니다.

5단계 동작 방식
  1. 입력에 / 구분자가 있는지 확인합니다. 없으면 no-op입니다.
  2. 전체 세그먼트 수(/ 개수 + 1)가 MAX_PREFIX_SEGMENTS (3) 이하여야 합니다. org/team/repo는 허용되고, a/b/c/d/model은 즉시 거부됩니다.
  3. 모든 세그먼트는 비어있지 않아야 하며 ASCII 공백 문자를 포함하지 않아야 합니다. /repo, vendor/, vendor//repo, vendor /repo 같은 잘못된 입력은 거부됩니다.
  4. 성공 시 마지막 / 이후의 부분 문자열이 잔류 문자열입니다. 이 잔류 문자열이 재진입 게이트가 닫힌 상태로 1-4단계에 다시 투입됩니다. 5단계는 재귀하지 않고, 내부 호출은 다시 5단계를 트리거할 수 없으므로 재귀 깊이는 구조적으로 정확히 1입니다.
접미사 peel과의 조합

재진입은 4단계를 거치므로 접두사 제거는 단일 조회 내에서 접미사 peel과 조합됩니다. unsloth/Qwen3.6-35B-A3B-GGUFQwen3.6-35B-A3B-GGUF로 제거되고, 4단계가 -GGUF를 peel한 뒤 대소문자를 무시하고 qwen3.6-35b-a3b에 매칭됩니다. 이 단계의 핵심 사용 사례이며, 수동으로 등록된 별칭 없이 HuggingFace GGUF 포크를 커버합니다.

등록된 별칭 우선 순위

운영자가 vendor/repo 형식을 명시적으로 YAML 별칭으로 등록하면 결정론적인 제어권이 유지됩니다. 2단계가 5단계보다 먼저 실행되므로, 정확한 별칭은 접두사 제거 레이어가 입력을 고려하기도 전에 승리합니다. 접두사 형식이 정규 기본 id와는 다른 메타데이터 항목으로 라우팅되어야 할 때 이 기법을 사용하세요.

범위 밖
  • 하이픈으로 구분된 벤더 접두사 (예: smoothie-qwen/smoothie-qwen3-32b-i1). 다른 의미 클래스이고, 탐지 난이도도 다르며, 종종 다른 가중치를 의미하기 때문에 기본 메타데이터로의 조용한 라우팅이 잘못된 결정일 수 있습니다. 수요가 생기면 별도 이슈에서 다시 살펴볼 수 있습니다.
  • HuggingFace API를 통한 자동 벤더 발견. 이 레이어는 순수하게 구문적입니다.
  • 접미사 peel 허용 목록 확장. 직교적인 변경입니다. 새 토큰 클래스에 대해서는 peel 확장 경로를 따르세요.
보안 경계

접미사 peel과 동일한 구조입니다.

경계 효과
MAX_PREFIX_SEGMENTS 3 세그먼트가 더 많은 입력은 스캔 전에 거부됩니다.
MAX_MODEL_ID_LEN 256 과도하게 긴 입력은 4단계와 마찬가지로 5단계를 건너뜁니다.
재진입 깊이 1 카운터가 아니라 재귀 게이트로 구조적으로 강제됩니다.

5단계는 적대적 입력에 대해 상수 시간입니다. 세그먼트 수, 빈 세그먼트, 공백, 길이 가드를 통과한 뒤에는 작업이 단일 슬라이스 조회 + 1-4단계 한 번의 추가 순회로 축소됩니다.

감사 절차

YAML을 재감사하려면 다음을 실행하세요.

cargo test --test alias_audit_helper -- --ignored --nocapture audit_metadata_aliases

헬퍼는 각 별칭을 분류(REDUNDANT, LOAD-BEARING-DRIFT, LOAD-BEARING-LOSS, 또는 WILDCARD)와 제거 후 결정 대상과 함께 출력합니다. 현재 스냅샷은 docs/reports/alias-audit-2026-04.md에 기록되어 있습니다.

API 응답

/v1/models 엔드포인트는 풍부한 모델 정보를 반환합니다:

{
  "object": "list",
  "data": [
    {
      "id": "gpt-4",
      "object": "model",
      "created": 1234567890,
      "owned_by": "openai",
      "backends": ["openai-proxy"],
      "metadata": {
        "display_name": "GPT-4",
        "summary": "복잡한 작업을 위한 가장 유능한 GPT-4 모델",
        "capabilities": ["text", "image", "function_calling"],
        "knowledge_cutoff": "2024-04",
        "pricing": {
          "input_tokens": 0.03,
          "output_tokens": 0.06
        },
        "limits": {
          "context_window": 128000,
          "max_output": 4096
        }
      }
    }
  ]
}

핫 리로드

Continuum Router는 서버 재시작 없이 런타임 설정 업데이트를 위한 핫 리로드를 지원합니다. 설정 변경은 자동으로 감지되어 분류에 따라 적용됩니다.

설정 항목 분류

설정 항목은 핫 리로드 기능에 따라 세 가지 범주로 분류됩니다:

즉시 업데이트 (서비스 중단 없음)

이 설정들은 서비스 중단 없이 즉시 업데이트됩니다:

# 로깅 설정
logging:
  level: "info"                  # ✅ 즉시: 로그 레벨 변경이 즉시 적용
  format: "json"                 # ✅ 즉시: 로그 형식 변경이 즉시 적용

# 속도 제한 설정
rate_limiting:
  enabled: true                  # ✅ 즉시: 속도 제한 활성화/비활성화
  limits:
    per_client:
      requests_per_second: 10    # ✅ 즉시: 새 제한이 즉시 적용
      burst_capacity: 20         # ✅ 즉시: 버스트 설정이 즉시 업데이트

# 서킷 브레이커 설정
circuit_breaker:
  enabled: true                  # ✅ 즉시: 서킷 브레이커 활성화/비활성화
  failure_threshold: 5           # ✅ 즉시: 임계값 업데이트가 즉시 적용
  timeout_seconds: 60            # ✅ 즉시: 타임아웃 변경이 즉시

# 재시도 설정
retry:
  max_attempts: 3                # ✅ 즉시: 재시도 정책이 즉시 업데이트
  base_delay: "100ms"            # ✅ 즉시: 백오프 설정이 즉시 적용
  exponential_backoff: true      # ✅ 즉시: 전략 변경이 즉시

# 전역 프롬프트
global_prompts:
  default: "당신은 도움이 됩니다"     # ✅ 즉시: 프롬프트 변경이 새 요청에 적용
  default_file: "prompts/system.md"  # ✅ 즉시: 파일 기반 프롬프트도 핫 리로드

# Admin 통계
admin:
  stats:
    retention_window: "24h"        # ✅ 즉시: 보존 기간 즉시 업데이트
    token_tracking: true           # ✅ 즉시: 토큰 추적 토글 즉시 적용

점진적 업데이트 (기존 연결 유지)

이 설정들은 기존 연결을 유지하면서 새 연결에 적용됩니다:

# 백엔드 설정
backends:
  - name: "ollama"               # ✅ 점진적: 새 요청이 업데이트된 백엔드 풀 사용
    url: "http://localhost:11434"
    weight: 2                    # ✅ 점진적: 새 요청에 로드 밸런싱 업데이트
    models: ["llama3.2"]         # ✅ 점진적: 모델 라우팅이 점진적으로 업데이트

# 헬스 체크 설정
health_checks:
  interval: "30s"                # ✅ 점진적: 다음 헬스 체크 주기가 새 간격 사용
  timeout: "10s"                 # ✅ 점진적: 새 체크가 업데이트된 타임아웃 사용
  unhealthy_threshold: 3         # ✅ 점진적: 임계값이 새 평가에 적용
  healthy_threshold: 2           # ✅ 점진적: 복구 임계값이 점진적으로 업데이트

# 타임아웃 설정
timeouts:
  connection: "10s"              # ✅ 점진적: 새 요청이 업데이트된 타임아웃 사용
  request:
    standard:
      first_byte: "30s"          # ✅ 점진적: 새 요청에 적용
      total: "180s"              # ✅ 점진적: 새 요청이 새 타임아웃 사용
    streaming:
      chunk_interval: "30s"      # ✅ 점진적: 새 스트림이 업데이트된 설정 사용

재시작 필요 (핫 리로드 불가)

이 설정들은 적용을 위해 서버 재시작이 필요합니다. 변경 사항은 경고로 기록됩니다:

server:
  bind_address: "0.0.0.0:8080"   # ❌ 재시작 필요: TCP/Unix 소켓 바인딩
  #   - "0.0.0.0:8080"
  #   - "unix:/var/run/router.sock"
  socket_mode: 0o660              # ❌ 재시작 필요: 소켓 권한
  workers: 4                      # ❌ 재시작 필요: 워커 스레드 풀 크기

이 설정들이 변경되면 라우터는 다음과 같은 경고를 기록합니다:

WARN server.bind_address가 '0.0.0.0:8080'에서 '0.0.0.0:9000'으로 변경됨 - 적용하려면 재시작 필요

핫 리로드 프로세스

  1. 파일 시스템 감시자 - 설정 파일 변경 자동 감지
  2. 설정 로딩 - 새 설정 로드 및 파싱
  3. 유효성 검사 - 스키마에 대해 새 설정 검증
  4. 변경 감지 - ConfigDiff 계산으로 변경 사항 식별
  5. 분류 - 변경 사항 분류 (즉시/점진적/재시작)
  6. 원자적 업데이트 - 유효한 설정이 원자적으로 적용
  7. 컴포넌트 전파 - 영향받는 컴포넌트에 업데이트 전파:
  8. HealthChecker가 체크 간격 및 임계값 업데이트
  9. RateLimitStore가 속도 제한 규칙 업데이트
  10. CircuitBreaker가 실패 임계값 및 타임아웃 업데이트
  11. BackendPool이 백엔드 설정 업데이트
  12. 즉시 헬스 체크 - 백엔드가 추가되면 즉시 헬스 체크가 실행되어 새 백엔드가 다음 주기적 체크를 기다리지 않고 1-2초 내에 사용 가능해짐
  13. 오류 처리 - 잘못된 경우 오류 기록 및 이전 설정 유지

핫 리로드 상태 확인

admin API를 사용하여 핫 리로드 상태 및 기능을 확인하세요:

# 핫 리로드가 활성화되었는지 확인
curl http://localhost:8080/admin/config/hot-reload-status

# 현재 설정 보기
curl http://localhost:8080/admin/config

핫 리로드 동작 예제

예제 1: 로그 레벨 변경 (즉시)

# 이전
logging:
  level: "info"

# 이후
logging:
  level: "debug"
결과: 로그 레벨이 즉시 변경됩니다. 재시작 불필요. 진행 중인 요청은 계속되고 새 로그는 debug 레벨 사용.

예제 2: 백엔드 추가 (즉시 헬스 체크와 함께 점진적)

# 이전
backends:
  - name: "ollama"
    url: "http://localhost:11434"

# 이후
backends:
  - name: "ollama"
    url: "http://localhost:11434"
  - name: "lmstudio"
    url: "http://localhost:1234"
결과: 새 백엔드가 풀에 추가되고 즉시 헬스 체크가 실행됩니다. 새 백엔드는 1-2초 내에 사용 가능해집니다(다음 주기적 헬스 체크까지 최대 30초를 기다리는 대신). 기존 요청은 현재 백엔드로 계속됩니다. 헬스 체크 통과 후 새 요청은 lmstudio로 라우팅 가능.

예제 2b: 백엔드 제거 (우아한 드레이닝)

# 이전
backends:
  - name: "ollama"
    url: "http://localhost:11434"
  - name: "lmstudio"
    url: "http://localhost:1234"

# 이후
backends:
  - name: "ollama"
    url: "http://localhost:11434"
결과: "lmstudio" 백엔드가 드레이닝 상태로 전환됩니다. 새 요청은 해당 백엔드로 라우팅되지 않지만, 기존 진행 중인 요청(스트리밍 포함)은 완료될 때까지 계속됩니다. 모든 참조가 해제되거나 5분 타임아웃 후에 백엔드가 메모리에서 완전히 제거됩니다.

백엔드 상태 생명주기

백엔드가 설정에서 제거되면 우아한 종료 프로세스를 거칩니다:

  1. Active → Draining: 백엔드가 드레이닝으로 표시됩니다. 새 요청은 이 백엔드를 건너뜁니다.
  2. 진행 중인 요청 완료: 기존 요청/스트림은 중단 없이 계속됩니다.
  3. 정리: 모든 참조가 해제되거나 5분 타임아웃 후에 백엔드가 제거됩니다.

설정 변경 중에도 진행 중인 연결에 영향을 주지 않습니다.

예제 3: 바인드 주소 변경 (재시작 필요)

# 이전
server:
  bind_address: "0.0.0.0:8080"

# 이후
server:
  bind_address: "0.0.0.0:9000"
결과: 경고 기록됨. 변경이 적용되지 않음. 새 포트에 바인드하려면 재시작 필요.

분산 추적

Continuum Router는 백엔드 서비스 간 요청 상관관계를 위한 분산 추적을 지원합니다. 이 기능은 여러 서비스를 통과하는 요청의 디버깅 및 모니터링에 도움을 줍니다.

설정

tracing:
  enabled: true                         # 분산 추적 활성화/비활성화 (기본값: true)
  w3c_trace_context: true               # W3C Trace Context 헤더 지원 (기본값: true)
  headers:
    trace_id: "X-Trace-ID"              # 추적 ID 헤더 이름 (기본값)
    request_id: "X-Request-ID"          # 요청 ID 헤더 이름 (기본값)
    correlation_id: "X-Correlation-ID"  # 상관 ID 헤더 이름 (기본값)

동작 방식

  1. 추적 ID 추출: 요청이 도착하면 라우터는 다음 우선순위로 헤더에서 추적 ID를 추출합니다:

    • W3C traceparent 헤더 (W3C 지원 활성화 시)
    • 설정된 trace_id 헤더 (X-Trace-ID)
    • 설정된 request_id 헤더 (X-Request-ID)
    • 설정된 correlation_id 헤더 (X-Correlation-ID)
  2. 추적 ID 생성: 헤더에서 추적 ID가 발견되지 않으면 새 UUID가 생성됩니다.

  3. 헤더 전파: 추적 ID는 여러 헤더를 통해 백엔드 서비스로 전파됩니다:

    • X-Request-ID: 광범위한 호환성을 위함
    • X-Trace-ID: 주요 추적 식별자
    • X-Correlation-ID: 상관관계 추적용
    • traceparent: W3C Trace Context (활성화 시)
    • tracestate: W3C Trace State (원본 요청에 존재 시)
  4. 재시도 시 보존: 동일한 추적 ID가 모든 재시도 시도에서 보존되어, 단일 클라이언트 요청에 대한 여러 백엔드 요청의 상관관계를 쉽게 파악할 수 있습니다.

구조화된 로깅

추적이 활성화되면 모든 로그 메시지에 trace_id 필드가 포함됩니다:

{
  "timestamp": "2024-01-15T10:30:00Z",
  "level": "info",
  "trace_id": "0af7651916cd43dd8448eb211c80319c",
  "message": "Processing chat completions request",
  "backend": "openai",
  "model": "gpt-4o"
}

W3C Trace Context

w3c_trace_context가 활성화되면 라우터는 W3C Trace Context 표준을 지원합니다:

  • 수신: traceparent 헤더 파싱 (형식: 00-{trace_id}-{span_id}-{flags})
  • 송신: 보존된 추적 ID와 새 span ID로 traceparent 헤더 생성
  • 상태: 원본 요청에 있는 경우 tracestate 헤더 전달

traceparent 예시: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

추적 비활성화

분산 추적을 비활성화하려면:

tracing:
  enabled: false

로드 밸런싱 전략

load_balancer:
  strategy: "round_robin"         # round_robin, weighted, random
  health_aware: true              # 정상 백엔드만 사용

전략:

  • round_robin: 백엔드 간 균등 분배
  • weighted: 백엔드 가중치에 기반한 분배
  • random: 무작위 선택 (패턴 방지에 좋음)

백엔드별 재시도 설정

backends:
  - name: "slow-backend"
    url: "http://slow.example.com"
    retry_override:               # 전역 재시도 설정 오버라이드
      max_attempts: 5             # 느린 백엔드에 더 많은 시도
      base_delay: "500ms"         # 더 긴 지연
      max_delay: "60s"

모델 폴백

Continuum Router는 기본 모델을 사용할 수 없을 때 자동 모델 폴백을 지원합니다. 이 기능은 계층화된 장애 조치 보호를 위해 서킷 브레이커와 통합됩니다.

설정

fallback:
  enabled: true

  # 각 기본 모델에 대한 폴백 체인 정의
  fallback_chains:
    # 동일 프로바이더 폴백
    "gpt-4o":
      - "gpt-4-turbo"
      - "gpt-3.5-turbo"

    "claude-opus-4-5-20251101":
      - "claude-sonnet-4-5"
      - "claude-haiku-4-5"

    # 크로스 프로바이더 폴백
    "gemini-2.5-pro":
      - "gemini-2.5-flash"
      - "gpt-4o"  # Gemini를 사용할 수 없으면 OpenAI로 폴백

  fallback_policy:
    trigger_conditions:
      error_codes: [429, 500, 502, 503, 504]
      timeout: true
      connection_error: true
      model_not_found: true
      circuit_breaker_open: true

    max_fallback_attempts: 3
    fallback_timeout_multiplier: 1.5
    preserve_parameters: true

  model_settings:
    "gpt-4o":
      fallback_enabled: true
      notify_on_fallback: true

트리거 조건

조건 설명
error_codes 폴백을 트리거하는 HTTP 상태 코드 (예: 429, 500, 502, 503, 504)
timeout 요청 타임아웃
connection_error TCP 연결 실패
model_not_found 백엔드에서 모델을 사용할 수 없음
circuit_breaker_open 백엔드 서킷 브레이커가 열림

응답 헤더

폴백이 사용되면 다음 헤더가 응답에 추가됩니다:

헤더 설명 예제
X-Fallback-Used 폴백이 사용되었음을 나타냄 true
X-Original-Model 원래 요청된 모델 gpt-4o
X-Fallback-Model 요청을 처리한 모델 gpt-4-turbo
X-Fallback-Reason 폴백이 트리거된 이유 error_code_429
X-Fallback-Attempts 폴백 시도 횟수 2

크로스 프로바이더 매개변수 변환

프로바이더 간 폴백 시 (예: OpenAI → Anthropic) 라우터가 요청 매개변수를 자동으로 변환합니다:

OpenAI 매개변수 Anthropic 매개변수 비고
max_tokens max_tokens 누락 시 자동 채움 (Anthropic 필수)
temperature temperature 직접 매핑
top_p top_p 직접 매핑
stop stop_sequences 배열 변환

프로바이더별 매개변수는 크로스 프로바이더 폴백 중에 자동으로 제거되거나 변환됩니다.

서킷 브레이커와의 통합

폴백 시스템은 서킷 브레이커와 함께 작동합니다:

  1. 서킷 브레이커가 실패 감지하고 임계값 초과 시 열림
  2. 폴백 체인 활성화 서킷 브레이커가 열렸을 때
  3. 요청이 폴백 모델로 라우팅 설정된 체인에 따라
  4. 서킷 브레이커가 복구 테스트하고 백엔드 복구 시 닫힘
# 예제: 서킷 브레이커와 폴백 결합 설정
circuit_breaker:
  enabled: true
  failure_threshold: 5
  timeout: 60s

fallback:
  enabled: true
  fallback_policy:
    trigger_conditions:
      circuit_breaker_open: true  # 서킷 브레이커에 연결

속도 제한

Continuum Router는 남용을 방지하고 공정한 리소스 할당을 보장하기 위해 /v1/models 엔드포인트에 대한 내장 속도 제한을 포함합니다.

현재 설정

속도 제한은 현재 다음 기본값으로 설정되어 있습니다:

# 참고: 이 값들은 현재 하드코딩되어 있지만 향후 버전에서 설정 가능해질 수 있습니다
rate_limiting:
  models_endpoint:
    # 클라이언트별 제한 (API 키 또는 IP 주소로 식별)
    sustained_limit: 100          # 분당 최대 요청 수
    burst_limit: 20               # 5초 윈도우에서 최대 요청 수

    # 시간 윈도우
    window_duration: 60s          # 지속 제한을 위한 슬라이딩 윈도우
    burst_window: 5s              # 버스트 감지를 위한 윈도우

    # 클라이언트 식별 우선순위
    identification:
      - api_key                   # Bearer 토큰 (처음 16자가 ID로 사용)
      - x_forwarded_for           # 프록시/로드 밸런서 헤더
      - x_real_ip                 # 대체 IP 헤더
      - fallback: "unknown"       # 식별자가 없을 때

작동 방식

  1. 클라이언트 식별: 각 요청은 다음을 사용하여 클라이언트와 연결:
  2. Authorization: Bearer <token> 헤더의 API 키 (선호)
  3. 프록시 헤더의 IP 주소 (폴백)

  4. 이중 윈도우 접근 방식:

  5. 지속 제한: 시간 경과에 따른 과도한 사용 방지
  6. 버스트 보호: 연속적인 빠른 요청 포착

  7. 독립적인 할당량: 각 클라이언트는 별도의 속도 제한:

  8. API 키 abc123...을 가진 클라이언트 A: 100 req/min
  9. API 키 def456...을 가진 클라이언트 B: 100 req/min
  10. IP 192.168.1.1의 클라이언트 C: 100 req/min

응답 헤더

속도 제한 시 응답 포함:

  • 상태 코드: 429 Too Many Requests
  • 오류 메시지: 버스트 또는 지속 제한 초과 여부 표시

캐시 TTL 최적화

캐시 오염 공격 방지를 위해:

  • 빈 모델 목록: 5초만 캐시
  • 일반 응답: 60초 캐시

이렇게 하면 공격자가 백엔드 장애 중에 빈 응답을 캐시하도록 강제하는 것을 방지합니다.

모니터링

속도 제한 위반은 메트릭에서 추적:

  • rate_limit_violations: 거부된 총 요청
  • empty_responses_returned: 제공된 빈 모델 목록
  • 문제가 있는 클라이언트 식별을 위한 클라이언트별 위반 추적

향후 개선 사항

향후 버전에서 지원될 수 있는 기능:

  • YAML/환경 변수를 통한 설정 가능한 속도 제한
  • 엔드포인트별 속도 제한
  • API 키별 사용자 정의 속도 제한
  • Redis 기반 분산 속도 제한

스마트 라우팅

스마트 라우팅은 요청의 복잡도와 도메인을 분류한 뒤, 설정 가능한 정책에 따라 가장 적합한 모델 티어로 요청을 보내는 기능입니다. 모델 티어 레지스트리(모델을 티어와 도메인에 매핑)와 규칙 기반 요청 분류기, 그리고 분류 결과를 라우팅 결정으로 변환하는 정책 엔진이 결합되어 동작합니다.

요청에서 model: "auto"를 사용하면 분류 → 정책 평가 → 티어 내 모델 선택 순으로 파이프라인이 실행됩니다. intercept_all: true로 설정하면 모든 요청에 동일한 파이프라인이 적용됩니다.

설정

smart_routing:
  enabled: true

  # 프로필 매칭 실패 및 자동 추론 불가 시 사용할 기본 티어.
  # 1 = Flagship, 2 = Standard, 3 = Lightweight. 기본값 2.
  default_tier: 2

  # 스마트 라우팅을 트리거하는 가상 모델 이름. 기본값 "auto".
  virtual_model: "auto"

  # true로 설정하면 모델 이름과 관계없이 모든 요청에 스마트 라우팅이 적용됩니다.
  intercept_all: false

  model_profiles:
    # 정확한 모델 이름 매칭
    - model: "gpt-4o"
      tier: 1
      domains: [general, code, reasoning, creative]
      cost_per_1k_input_tokens: 0.005
      cost_per_1k_output_tokens: 0.015

    # 또 다른 정확한 매칭
    - model: "gpt-4o-mini"
      tier: 2
      domains: [general, code]
      cost_per_1k_input_tokens: 0.00015
      cost_per_1k_output_tokens: 0.0006

    # 글로브 패턴 — Q4_K_M 양자화 모델 전체에 매칭
    - model_pattern: "*-q4_K_M"
      tier: 3
      domains: [general]

  # 라우팅 정책: 위에서 아래로 평가하며 첫 번째 매칭이 적용됩니다.
  routing_policies:
    - name: "trivial_to_lightweight"
      when:
        complexity: [trivial, simple]
        domain: [general]
      route_to:
        tier: 3

    - name: "code_to_flagship"
      when:
        domain: [code]
        complexity: [moderate, complex, expert]
      route_to:
        tier: 1
        prefer_domains: [code]

    - name: "vision_required"
      when:
        requires: [vision]
      route_to:
        tier: 1
        require_capabilities: [vision]

    - name: "complex_to_flagship"
      when:
        complexity: [complex, expert]
      route_to:
        tier: 1

    - name: "default_to_standard"
      when: {}                      # 항상 매칭되는 캐치올
      route_to:
        tier: 2

티어 분류

티어 의미 대표 예시
Flagship 1 최고 성능, 최고 비용 gpt-4o, claude-3.5-sonnet, gemini-1.5-pro
Standard 2 성능과 속도의 균형 gpt-4o-mini, claude-3-haiku
Lightweight 3 속도와 저비용에 최적화 llama-3-8b, phi-3-mini, 양자화 모델

도메인 전문화 태그

태그 설명
general 특정 전문 분야 없음
code 코드 생성, 디버깅, 리뷰
reasoning 복잡한 다단계 추론, 수학
creative 창작 글쓰기, 스토리텔링
multilingual 번역 및 다국어 작업
vision 이미지 이해

자동 추론

명시적 프로필이나 글로브 패턴에 매칭되는 모델이 없으면 라우터가 다음 순서로 티어를 자동 추론합니다.

  1. 가격 (model-metadata.yaml 기준): 입력 토큰 1,000개당 $3 이상이면 Flagship, $0.50 이상이면 Standard, 그 미만이면 Lightweight. 무료 모델은 가격 기반 추론을 건너뜁니다.

  2. 기능 (model-metadata.yaml 기준): vision, reasoning, audio, video, function_calling, tool 중 3개 이상이면 Flagship, 1개 이상이거나 전체 기능이 3개 이상이면 Standard.

  3. 이름 휴리스틱: pro, ultra, opus, sonnet, turbo 키워드가 포함되면 Flagship; mini, small, tiny, nano, lite, flash, haiku 및 양자화 마커(q4_, q5_, q8_, gguf, gptq, awq)가 포함되면 Lightweight.

자동 추론 결과는 모델 ID별로 최대 10,000개까지 캐시됩니다. 핫 리로드 시 또는 /admin/smart-routing/model-profiles PUT 엔드포인트 호출 시 캐시가 초기화됩니다.

글로브 패턴 문법

*만 와일드카드로 사용하며, 여러 개를 중첩할 수 있습니다.

패턴 매칭 미매칭
gpt-* gpt-4o, gpt-4o-mini claude-3
*-q4_K_M llama-3-8b-q4_K_M llama-3-8b-q5_K_M
gpt-*-turbo gpt-4-turbo, gpt-3.5-turbo gpt-4o
* 모든 문자열 해당 없음

요청 분류기

규칙 기반 분류기는 11가지 신호를 분석해 복잡도 수준, 도메인 태그, 필요 기능, 신뢰도 점수로 구성된 ClassificationResult를 생성합니다.

복잡도 수준

수준 설명 예시
trivial 인사말, 예/아니오, 단순 사실 조회 "2+2는?"
simple 짧은 설명, 기본 요약 "이 문단을 요약해줘"
moderate 다단계 추론, 중간 수준의 코드 작업 "이 함수를 리팩토링해줘"
complex 고급 알고리즘, 시스템 설계 "분산 캐시를 설계해줘"
expert 연구 수준 문제, 형식적 증명 "이 정리를 증명해줘"

분류 신호

신호 감지 내용
message_length 모든 메시지의 총 토큰 수
code_blocks 펜스 코드 블록 또는 인라인 코드
math_notation LaTeX, 수식, 수학 기호
system_prompt_complexity 시스템 프롬프트의 길이와 복잡도
conversation_depth 대화 턴 수
image_attachments 메시지의 멀티모달 이미지 콘텐츠
tool_definitions 요청의 도구/함수 정의
complexity_keywords "최적화", "설계", "증명" 같은 키워드
language_detection 비라틴 문자 또는 다국어 콘텐츠
creative_markers "소설", "시", "상상해줘" 같은 표현
analysis_markers "분석해줘", "비교해줘", "평가해줘" 같은 표현

감지된 신호가 많을수록 복잡도와 도메인 태그가 올라갑니다. 상충하는 신호(예: 창작 마커와 코드 마커가 동시에 존재)는 신뢰도 점수를 낮춥니다.

LLM 기반 분류기

규칙 기반 분류기는 빠르지만 일부 요청은 분류가 모호합니다. 정확도를 높여야 할 때, LLM 기반 분류기가 소형 저비용 모델에 분류 요청을 보냅니다. classifier.method로 세 가지 동작 방식을 선택할 수 있습니다.

방식 동작
rule 규칙 기반만 사용 (기본값). LLM 호출 없음.
llm 항상 LLM 분류기를 호출하고, 실패 시 규칙 기반으로 폴백.
hybrid 규칙 기반을 먼저 실행하고, 신뢰도가 confidence_threshold 미만일 때만 LLM으로 에스컬레이션.

하이브리드 모드가 권장 운영 설정입니다. 명확한 요청은 마이크로초 단위로 규칙 기반 분류기가 처리하고, 모호한 요청(보통 전체의 10~20%)에만 LLM 호출 지연이 추가됩니다.

smart_routing:
  enabled: true

  classifier:
    # 분류 방식: "rule" (기본값), "llm", "hybrid"
    method: hybrid

    rule:
      # 하이브리드 모드에서 LLM 에스컬레이션을 트리거하는 신뢰도 임계값.
      # 범위: 0.0 ~ 1.0. 기본값: 0.7.
      confidence_threshold: 0.7

    llm:
      # 분류에 사용할 모델. 빠르고 저렴한 모델이면 충분합니다.
      model: "gpt-4o-mini"

      # 분류 요청을 보낼 백엔드 이름. 설정된 백엔드여야 합니다.
      # 생략하면 백엔드 URL을 직접 사용합니다.
      backend: "openai-fast"

      # 분류 요청의 최대 허용 시간 (밀리초).
      timeout_ms: 2000

      # 분류기에 전송하는 최대 입력 토큰 수 (초과 콘텐츠는 잘림).
      max_input_tokens: 500

      # 파싱 실패 후 재시도 횟수 (0 또는 1). 기본값: 1.
      max_retries: 1

      # 분류 온도. 0.0이면 결정적 출력.
      temperature: 0.0

      # 분류 응답의 최대 출력 토큰 수.
      max_output_tokens: 150

      # 구조화 출력 전략. "auto"는 백엔드 유형에 따라 최적 방식을 선택합니다:
      # json_schema (OpenAI/vLLM), tool_use (Anthropic), json_object (Ollama/Gemini/LM Studio),
      # prompt_only (기타).
      structured_output: auto   # auto | json_schema | tool_use | json_object | prompt_only

      # 시스템 프롬프트에 기본 퓨샷 예제 포함 여부.
      few_shot_examples: true

      # 기본 예제 다음에 추가되는 커스텀 퓨샷 예제.
      custom_examples:
        - user: "이부프로펜의 권장 복용량은 얼마인가요?"
          classification:
            complexity: simple
            domain: medical

      # 분류 결과의 캐시 유효 시간 (초). 기본값: 300.
      cache_ttl_seconds: 300

      # 최대 캐시 항목 수. 기본값: 10000.
      max_cache_entries: 10000

      # 이 부하 상태에 도달하면 LLM 분류기를 비활성화합니다.
      # "critical" (기본값) 또는 "warning". 비워두면 항상 활성.
      disable_under_load_state: critical

    # 기본 복잡도 분류 체계를 커스텀 수준으로 확장합니다.
    custom_complexity_levels:
      - name: specialized
        description: "도메인 특화 파인튜닝 모델이 필요한 요청"
        rank: 6   # 선택적 순서 힌트 (높을수록 어려움)

    # 기본 도메인 분류 체계를 커스텀 카테고리로 확장합니다.
    custom_domains:
      - name: medical
        description: "의료  임상 관련 질문"

구조화 출력 전략

LLM 분류기는 분류 모델에서 구조화된 JSON을 받아야 합니다. auto 전략이 백엔드 유형에 따라 적절한 방식을 자동 선택하지만, 명시적으로 지정할 수도 있습니다.

전략 방식 지원 백엔드
json_schema response_format: { type: "json_schema" } OpenAI, Azure, vLLM
tool_use 도구/함수 호출 Anthropic, Gemini
json_object response_format: { type: "json_object" } OpenAI, Ollama, Gemini, LM Studio, llama.cpp
prompt_only 자유형 텍스트에서 정규식으로 JSON 추출 모든 백엔드

분류기 응답을 파싱할 수 없으면 수정 프롬프트로 한 번 재시도합니다(max_retries로 제어). 재시도도 실패하면 규칙 기반 분류기 결과를 사용합니다.

분류 캐시

동일한 요청에 대한 반복 LLM 호출을 줄이기 위해 분류 결과를 TTL과 함께 인메모리에 캐시합니다. 캐시 키는 잘린 사용자 메시지의 SHA-256 해시이므로, 의미적으로 동일한 요청은 같은 캐시 결과를 공유합니다. 캐시는 프로세스 단위이며 여러 라우터 인스턴스 간에 공유되지 않습니다.

커스텀 분류 체계

복잡도 수준과 도메인 태그 모두 확장 가능합니다. custom_complexity_levelscustom_domains로 추가한 커스텀 값은 분류기의 시스템 프롬프트와 구조화 출력 스키마에 반영됩니다. 라우팅 정책에서도 기본 값처럼 커스텀 값을 참조할 수 있습니다.

routing_policies:
  - name: specialized_to_flagship
    when:
      complexity: [specialized]
    route_to:
      tier: 1

우회 헤더

LLM 분류기는 분류 요청에 X-Smart-Route-Bypass: true 헤더를 추가합니다. 이 헤더가 있는 요청은 스마트 라우팅을 건너뛰어, 분류기 백엔드가 동일한 라우터 인스턴스 뒤에 있을 때 발생할 수 있는 순환 분류 루프를 방지합니다.

라우팅 정책

정책은 위에서 아래로 평가하며 첫 번째로 매칭된 정책이 적용됩니다. 매칭되는 정책이 없고 캐치올도 없으면 default_tier로 폴백됩니다.

정책 조건 논리

  • when 블록 내 필드들은 AND로 평가됩니다. 명시된 모든 필드가 일치해야 합니다.
  • 하나의 필드 안에서 값들은 OR로 평가됩니다. complexity: [trivial, simple]은 둘 중 하나와 일치하면 됩니다.
  • when: {}는 항상 매칭되는 캐치올입니다.

정책 필드

when 조건:

필드 타입 설명
complexity [string] 매칭할 복잡도 수준 목록 (OR 논리)
domain [string] 매칭할 도메인 태그 목록 (OR 논리)
requires [string] 모두 존재해야 하는 기능 목록 (AND 논리)

route_to 액션:

필드 타입 설명
tier int 대상 티어 (1=Flagship, 2=Standard, 3=Lightweight)
prefer_domains [string] 도메인 특화 모델에 대한 소프트 선호도
require_capabilities [string] 하드 필터: 해당 기능이 있는 모델만 선택

티어 내 모델 선택

매칭된 티어에 여러 모델이 있을 경우 다음 기준으로 점수를 매겨 선택합니다.

  1. 도메인 선호도 매칭 (소프트 보너스)
  2. 기능 매칭 (require_capabilities가 설정된 경우 하드 필터)
  3. 비용 점수 (같은 티어 안에서 낮은 비용이 높은 점수)
  4. 동점일 경우 무작위 선택

매칭된 티어에 사용 가능한 모델이 없으면 인접 티어를 순서대로 시도합니다(예: Lightweight가 비어 있으면 Standard, 그다음 Flagship 순).

부하 기반 동적 티어 조정

평상시에는 각 요청에 가장 적합한 티어를 선택하지만, 트래픽이 급증하거나 지연 시간이 높아지면 부하 모니터가 실시간 지표를 추적해 자동으로 더 가벼운 모델 티어로 라우팅을 전환하고, 부하가 회복되면 원래대로 돌아옵니다.

부하 관리는 기본적으로 비활성화되어 있습니다. smart_routing.load_management 아래에서 활성화합니다:

smart_routing:
  enabled: true
  load_management:
    enabled: true

    # 부하 지표 평가 주기(밀리초). 기본값: 1000.
    assessment_interval_ms: 1000

    # Warning/Critical 상태로 전환하는 임계값.
    # 하나의 지표라도 초과되면 해당 상태로 전환됩니다.
    thresholds:
      warning:
        requests_per_second: 100
        avg_latency_ms: 3000
        error_rate: 0.05
        in_flight_requests: 50
      critical:
        requests_per_second: 200
        avg_latency_ms: 5000
        error_rate: 0.15
        in_flight_requests: 100

    # 각 부하 상태에서 적용할 라우팅 제한.
    degradation:
      warning:
        max_tier: 2           # Standard 티어까지만 허용
        prefer_quantized: false
        reject_expert: false
      critical:
        max_tier: 3           # Lightweight 티어까지만 허용
        prefer_quantized: true
        reject_expert: false

    # 복구 동작.
    recovery:
      cooldown_seconds: 30    # 낮은 부하 상태로 전환하기 전 최소 대기 시간(초)
      hysteresis_factor: 0.8  # 임계값의 80% 이하로 떨어져야 복구됨

부하 상태

상태 의미 기본 라우팅 제한
normal 모든 지표가 정상 범위 내 제한 없음
warning Warning 임계값을 초과한 지표 발생 Standard 티어(tier 2) 이하로 제한
critical Critical 임계값을 초과한 지표 발생 Lightweight 티어(tier 3) 이하로 제한, 양자화 모델 우선

본래 Flagship(tier 1)으로 라우팅될 요청이 Warning 상태에서는 Standard로 자동 하향됩니다. 라우팅 결정 로그에는 조정된 정책 이름이 <원래_정책>__load_warning 또는 <원래_정책>__load_critical 형식으로 기록됩니다.

히스테리시스와 쿨다운

부하 상태가 빠르게 오락가락하면 그 자체로 시스템 불안정을 유발할 수 있어, 두 가지 메커니즘으로 이를 방지합니다:

  • 히스테리시스: Warning 상태를 벗어나려면 지표가 임계값 * hysteresis_factor(기본 0.8) 이하로 내려가야 합니다. 100 RPS에서 Warning에 진입했다면 80 RPS 이하로 떨어져야 Normal로 회복됩니다.
  • 쿨다운: 상태 전환 후 cooldown_seconds(기본 30초) 동안은 더 낮은 상태로의 복구가 차단됩니다. 반면 상태 상향(Normal에서 Warning, Warning에서 Critical)은 쿨다운과 무관하게 즉시 적용됩니다.

티어별 임계값 재정의

티어마다 처리 용량이 다를 때는 티어별로 임계값을 별도 지정할 수 있습니다:

smart_routing:
  load_management:
    enabled: true
    thresholds:
      warning:
        requests_per_second: 100
    tier_thresholds:
      "1":                      # Flagship은 RPS 내성이 낮음
        warning:
          requests_per_second: 50
      "3":                      # Lightweight는 더 많은 요청을 처리 가능
        warning:
          requests_per_second: 300

Prometheus 지표

metrics 기능이 활성화된 경우 부하 관리 관련 지표가 노출됩니다:

지표 유형 설명
smart_routing_load_state Gauge 현재 부하 상태: 0=Normal, 1=Warning, 2=Critical
smart_routing_tier_degradation_total Counter 부하로 인해 티어가 하향된 횟수
smart_routing_load_transitions_total Counter 부하 상태 전환 횟수(from, to 레이블 포함)

LLM 분류기는 추가로 6개의 지표를 노출합니다:

지표 유형 설명
smart_routing_llm_classifier_calls_total Counter LLM 분류기 호출 총 횟수
smart_routing_llm_classifier_cache_hits_total Counter 캐시에서 반환된 분류 결과 수
smart_routing_llm_classifier_duration_seconds Histogram LLM 분류 전체 지연 시간
smart_routing_llm_classifier_fallbacks_total Counter LLM 결과를 버리고 규칙 기반 결과를 사용한 횟수
smart_routing_llm_classifier_parse_errors_total Counter 재시도 전 응답 파싱 실패 횟수
smart_routing_llm_classifier_retries_total Counter 초기 파싱 실패 후 재시도 횟수

디버그 응답 헤더

debug_headers: true로 설정하면 HTTP 응답 헤더에 스마트 라우팅 결정 정보가 포함됩니다. 개발 및 스테이징 환경에서 사용하기 위한 기능입니다.

smart_routing:
  enabled: true
  debug_headers: true  # 개발/스테이징 환경에서 활성화

활성화하면 스마트 라우팅된 응답에 다음 헤더가 추가됩니다:

헤더 설명
X-Smart-Route-Source 요청된 원본 모델 (예: auto)
X-Smart-Route-Target 선택된 모델 (예: gpt-4o-mini)
X-Smart-Route-Complexity 분류된 복잡도 수준
X-Smart-Route-Domain 분류된 도메인
X-Smart-Route-Policy 매칭된 정책
X-Smart-Route-Load-State 라우팅 시점의 부하 상태
X-Smart-Route-Classifier 사용된 분류기 (rule_based 또는 llm_based)

Admin API

스마트 라우팅은 관찰 가능성과 관리를 위해 /admin/smart-routing/ 아래에 여러 admin 엔드포인트를 노출합니다. 전체 엔드포인트 참조는 Admin API 문서에서 확인하세요.

주요 엔드포인트:

  • GET /status -- 전체 상태, 부하 상태, 정책 수
  • POST /classify -- 실제 라우팅 없이 요청 분류 (진단용)
  • POST /simulate -- 전체 라우팅 파이프라인 시뮬레이션
  • GET /policiesPUT /policies -- 정책 조회 및 핫 리로드
  • GET /load-state -- 평가 정보를 포함한 현재 부하 상태
  • GET /cache/statsPOST /cache/clear -- LLM 분류기 캐시 관리

구조화 로깅

모든 스마트 라우팅 결정은 DEBUG 수준으로 구조화된 필드와 함께 기록됩니다:

level=DEBUG msg="Smart routing decision"
  source_model="auto"
  target_model="gpt-4o-mini"
  complexity="simple"
  domain="general"
  policy="trivial_to_lightweight"
  load_state="normal"
  classifier="rule_based"
  confidence=0.92
  classification_ms=0.3

부하 상태 전환 및 정책 변경은 INFO 수준으로 기록됩니다.

핫 리로드

smart_routing 섹션은 설정 파일이 변경될 때 즉시 리로드됩니다. 리로드 후 추론 캐시가 초기화되어 다음 요청부터 새 프로필을 기준으로 재평가합니다. routing_policiesload_management 설정도 서버 재시작 없이 즉시 반영됩니다. PUT /admin/smart-routing/policies 엔드포인트를 통해 런타임에 정책을 업데이트할 수도 있습니다.


환경별 설정

개발 설정

# config/development.yaml
server:
  bind_address: "127.0.0.1:8080"

backends:
  - name: "local-ollama"
    url: "http://localhost:11434"

health_checks:
  interval: "10s"                 # 더 빈번한 확인
  timeout: "5s"

logging:
  level: "debug"                  # 상세 로깅
  format: "pretty"                # 사람이 읽기 쉬운
  enable_colors: true

프로덕션 설정

# config/production.yaml
server:
  bind_address: "0.0.0.0:8080"
  workers: 8                      # 프로덕션용 더 많은 워커
  connection_pool_size: 300       # 더 큰 연결 풀

backends:
  - name: "primary-openai"
    url: "https://api.openai.com"
    weight: 3
  - name: "secondary-azure"
    url: "https://azure-openai.example.com"
    weight: 2
  - name: "fallback-local"
    url: "http://internal-llm:11434"
    weight: 1

health_checks:
  interval: "60s"                 # 덜 빈번한 확인
  timeout: "15s"                  # 네트워크 지연을 위한 더 긴 타임아웃
  unhealthy_threshold: 5          # 더 많은 허용
  healthy_threshold: 3

request:
  timeout: "120s"                 # 프로덕션용 짧은 타임아웃
  max_retries: 5                  # 더 많은 재시도

logging:
  level: "warn"                   # 덜 상세한 로깅
  format: "json"                  # 구조화된 로깅

컨테이너 설정

# config/container.yaml - 컨테이너에 최적화
server:
  bind_address: "0.0.0.0:8080"
  workers: 0                      # 컨테이너 제한에 따라 자동 감지

backends:
  - name: "backend-1"
    url: "${BACKEND_1_URL}"       # 환경 변수 치환
  - name: "backend-2"
    url: "${BACKEND_2_URL}"

logging:
  level: "${LOG_LEVEL}"           # 환경을 통해 설정 가능
  format: "json"                  # 컨테이너에서 항상 JSON