주요 콘텐츠로 건너뛰기
AI 튜토리얼

SGLang으로 OpenJev를 배포하고 원 토큰 분류에 사용하기

Modal에 openjev-sglang을 배포하고 Noul, Choice, Score 평가를 제출하며 서비스 상태를 확인하고 제한 및 인증을 구성하는 방법을 알아보세요. 또한 API를 기존 SGLang 백엔드에 연결하는 방법도 설명합니다.

SGLang으로 OpenJev를 배포하고 원 토큰 분류에 사용하기

openjev-sglang이란?

openjev-sglang은 SGLang에서 Qwen3.6-35B-A3B를 사용해 TypeSafe/Jev HTTP API를 구현하는 서버입니다. 긴 응답을 생성하는 대신 분류 질문을 효율적인 첫 토큰 확률 조회로 변환합니다.

기본 배포는 NVIDIA B200 GPU당 SGLang 컨테이너 하나를 실행합니다. SGLang 0.5.19는 Rust 프런트엔드, radix 캐싱, 중단 가능한 prefill CUDA 그래프를 제공합니다. 별도의 Python 프로세스가 FastAPI, uvloop, Rust 기반 Hugging Face 토크나이저와 localhost의 SGLang에 대한 비동기 연결 풀을 사용해 공개 API를 처리합니다.

TypeSafe로 요청을 보내지 않습니다. 공개 엔드포인트는 평가 API를 노출하며, SGLang 생성 및 관리 라우트는 localhost에서 비공개로 유지됩니다.

주요 기능

  • 세 가지 평가 유형: 예/아니요 확률을 위한 Noul, 범주 선택을 위한 Choice, 순서가 있는 수준을 위한 Score를 제공합니다.
  • 단일 토큰 추론: 각 질문을 생성된 사고 과정 없이 답변 레이블의 로그 확률로 평가합니다.
  • 공통 접두사 캐싱: 질문 분기를 동시에 실행하기 전에 공통 프롬프트 접두사를 미리 준비합니다.
  • 최대 64개 질문: Choice 및 Score 질문은 기본적으로 2개에서 64개 사이의 답변을 지원합니다.
  • 구조화된 상태: 상태와 지침은 문자열, JSON 객체 또는 배열로 지정할 수 있습니다.
  • 운영 엔드포인트: 상태 확인, 활성 상태 확인, 모델 카탈로그, 제한, OpenAPI 스키마, Swagger UI 및 대화형 Scalar 레퍼런스를 포함합니다.
  • Modal 배포: 제공된 애플리케이션은 자동 확장, 영구 모델 캐시, 시작 복구 및 scale-to-zero 동작을 지원합니다.
  • 구성 가능한 보호 기능: 요청 제한, 동시성 제어, 타임아웃 및 선택적 Bearer 인증을 사용할 수 있습니다.

사전 요구 사항

제공된 클라우드 배포를 사용하려면 uv를 설치하고 Modal 계정을 생성하세요. 첫 번째 빌드에서는 대용량 SGLang 이미지를 가져오며, 첫 GPU 시작 시 모델 가중치를 다운로드하고 커널을 컴파일하거나 캡처합니다.

CUDA 의존성은 SGLang 컨테이너에 유지됩니다. 워크스테이션에서 uv sync를 실행하면 API, 배포 유틸리티 및 테스트만 설치됩니다.

프로젝트 설치

저장소를 복제하고 해당 디렉터리로 이동한 다음 Python 환경을 동기화합니다.

uv sync

현재 컴퓨터에서 Modal 인증을 완료하지 않았다면 다음 설정을 실행하세요.

uv run modal setup

임시 Modal 배포 실행

엔드투엔드 테스트를 위해 임시 서버를 시작하고, 포함된 추론 검사를 실행한 후 서버를 종료합니다.

uv run modal run modal_app.py

이 명령은 스모크 테스트 보고서를 smoke-result.json으로 저장합니다. 보고서에는 시작 대기 시간, 추론 지연 시간 및 사용 가능한 캐시 사용량 정보가 포함됩니다.

안정적인 엔드포인트 배포

애플리케이션을 영구 Modal 서비스로 배포합니다.

uv run modal deploy modal_app.py

배포가 https://YOUR-SERVER.us-west.modal.direct와 유사한 URL을 출력합니다. 편리하게 사용하려면 환경 변수에 저장하세요.

export OPENJEV_URL="https://YOUR-SERVER.us-west.modal.direct"

기본 Modal 엔드포인트는 의도적으로 인증 없이 제공됩니다. US West 라우팅 리전을 사용하며, 구성된 미국 리전에서 컴퓨팅을 예약할 수 있습니다. 명시적인 컨테이너 상한은 없으며, 인스턴스는 5분간 유휴 상태가 지속되면 scale-to-zero로 전환됩니다.

scale-to-zero 상태인 서버는 시작하는 동안 HTTP 503을 반환합니다. 포함된 스모크 명령은 이러한 시작 응답을 자동으로 재시도합니다.

배포된 URL을 대상으로 스모크 테스트를 실행합니다.

uv run openjev smoke "$OPENJEV_URL"

스모크 테스트는 세 가지 답변 유형을 모두 실행하고, 64개 답변 질문을 확인하며, 기본 의미 검사를 수행하고, 65개 답변이 거부되는지 검증합니다.

첫 번째 평가 제출

기본 경로는 POST /v1/systemone입니다. 다음 요청은 Noul, Choice 및 Score 질문으로 고객 메시지를 분류합니다.

curl "$OPENJEV_URL/v1/systemone" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "jev-latest",
    "state": [
      {"role": "system", "content": "You are a support assistant."},
      {"role": "user", "content": "I was charged twice. Please refund the duplicate."}
    ],
    "questions": {
      "refund": {
        "type": "noul",
        "instructions": "Does the user request a refund?"
      },
      "department": {
        "type": "choice",
        "instructions": "Which department should handle this?",
        "criteria": {
          "billing": "Payments and refunds",
          "technical": "Software bugs"
        }
      },
      "urgency": {
        "type": "score",
        "instructions": "How urgent is the request?",
        "criteria": ["Routine", "Urgent", "Emergency"]
      }
    }
  }'

JSON을 파일에 저장한 다음 셸 인용 없이 제출할 수도 있습니다.

curl "$OPENJEV_URL/v1/systemone" \
  -H 'Content-Type: application/json' \
  --data-binary @examples/request.json

질문 유형 이해하기

  • Noul: yes의 정규화된 확률을 반환합니다.
  • Choice: 확률이 가장 높은 옵션과 전체 확률 분포를 반환합니다. 원래 옵션 이름은 응답에서 유지됩니다.
  • Score: 각 수준의 인덱스와 확률로 계산한 0부터 시작하는 예상 수준을 범례와 함께 반환합니다.

선택지 옵션은 설명이 뒤따르는 문자 레이블로 모델에 제시됩니다. 응답 키는 일반적으로 모델에 표시되지 않습니다. 옵션 설명이 null이면 해당 키가 대신 의미를 제공합니다.

상태 및 지침 제공

state와 instructions 모두 문자열, JSON 객체 또는 배열을 허용합니다. 채팅 메시지 목록이나 정확히 {"messages": [...]}를 포함하는 객체는 모델의 기본 채팅 템플릿으로 렌더링됩니다.

원래 메시지의 역할과 객체는 유지되며, 각 분류 질문은 또 하나의 사용자 발화로 추가됩니다. 그 밖의 구조화된 값은 사용자 메시지에 그대로 직렬화됩니다. 다른 필드와 함께 messages를 포함하는 객체도 메타데이터가 조용히 삭제되지 않도록 유지됩니다.

채팅 상태는 텍스트 콘텐츠만 지원합니다. 이미지, 오디오 및 동영상 콘텐츠는 지원되지 않습니다.

서비스 엔드포인트 살펴보기

  • POST /v1/systemone은 Noul, Choice 및 Score 평가를 수행합니다.
  • GET /v1/models는 TypeSafe 및 OpenAI 스타일 필드를 포함한 모델 카탈로그를 반환합니다.
  • GET /v1/limits는 요청 허용 한도를 보고합니다.
  • GET /health는 준비 상태, SGLang 상태 및 시작 소요 시간을 보고합니다.
  • GET /health/live는 API 프로세스가 실행 중인지 확인합니다.
  • GET /은 편집 가능한 요청 예제가 포함된 Scalar API 참조 페이지를 엽니다.
  • GET /docs는 기본 제공 Swagger UI를 엽니다.
  • GET /openapi.json은 생성된 OpenAPI 스키마를 반환합니다.

요청 모델 이름 jev-latest는 호환성을 위한 별칭입니다. 기본 공개 모델 ID는 Qwen/Qwen3.6-35B-A3B이며, NVIDIA의 저장소는 내부적으로 가중치 소스로 사용됩니다.

추론 작동 방식

  1. API는 스키마, 답변 수, 본문 크기, 컨텍스트 길이 및 총 토큰 예산을 검증합니다.
  2. 사고 기능을 비활성화한 상태로 기본 채팅 템플릿을 한 번 렌더링한 다음, 공통 접두사와 독립적으로 토큰화된 질문 접미사를 분리합니다.
  3. 공통 접두사를 max_new_tokens=1로 SGLang에 전송합니다. 샘플링된 토큰은 폐기되며, 접두사는 필요에 따라 radix 캐시에서 재사용할 수 있도록 남겨 둡니다.
  4. 질문마다 하나의 분기를 동시에 제출합니다. 각 분기에는 접두사, 질문 접미사, 어시스턴트 헤더 및 Answer: 마커가 포함됩니다.
  5. 유효한 답변 레이블에 대해서만 로그 확률을 요청하고, 안정적인 softmax로 재정규화합니다.

N개의 질문에 대해 서버는 한 토큰 호출을 N + 1번 수행합니다. 캐시를 준비하는 호출 한 번과 각 질문에 대한 호출이 한 번씩입니다. 생성된 토큰은 무시되므로 자기회귀 연속 생성이나 생성된 사고 과정은 없습니다. 추측 디코딩은 활성화되지 않습니다.

레이블은 A부터 Z까지 사용한 뒤 AA, AB와 같은 검증된 단일 토큰 조합을 사용합니다. 시작 시 활성 토크나이저로 64개 레이블을 모두 확인합니다. 따라서 10이나 64와 같은 여러 토큰으로 이루어진 숫자 레이블을 사용하지 않습니다.

사용량 및 캐시 지표 이해

응답 헤더 x-openjev-prefix-tokens는 요청된 공통 접두사 크기를 보고합니다. SGLang이 캐시 수를 제공하는 경우 x-openjev-cached-tokens에는 분기 캐시 적중 수의 합계가 포함됩니다.

SGLang 0.5.19의 Rust 프런트엔드는 이러한 캐시 수를 노출하지 않습니다. 이 구성에서는 헤더가 없으며 smoke 보고서에는 0이 아닌 null이 기록됩니다. 스케줄러 로그에는 실제 캐시 적중이 계속 표시될 수 있습니다. Server-Timing 헤더는 프롬프트 준비, 공통 프리필 및 분기 추론을 구분합니다.

usage.input_tokens는 워밍업 및 분기 호출에서 보고된 전체 프롬프트 수의 합계이며, 캐시된 토큰을 포함합니다. usage.output_tokens는 N + 1입니다. 이 수치는 고유하게 계산된 토큰이나 TypeSafe 청구 추정치가 아니라 백엔드 활동을 나타냅니다.

한도 및 인증 구성

기본 요청 허용 한도에는 질문 64개, Choice 또는 Score 질문당 답변 2~64개, 2MiB JSON 본문, 출력을 포함한 분기당 32,768개 토큰, 제출된 입력 토큰 총 262,144개, 동시 평가 16개 및 동시 백엔드 호출 64개가 포함됩니다.

잘못된 요청은 추론 전에 HTTP 422를 반환합니다. 크기 제한을 초과한 본문은 413을 반환하고, 과부하는 Retry-After와 함께 529를 반환하며, 백엔드 시간 초과는 504를 반환합니다. 평가가 실패하거나 취소되면 형제 분기를 취소하고 SGLang에서 중단을 시도합니다.

구성은 OPENJEV_* 환경 변수를 통해 사용할 수 있습니다. 일반적인 설정은 다음과 같습니다.

  • OPENJEV_MODEL: 기본값은 nvidia/Qwen3.6-35B-A3B-NVFP4입니다.
  • OPENJEV_SERVED_MODEL_NAME: 공개 모델 ID를 재정의하며 SGLang에 전달됩니다.
  • OPENJEV_REVISION: 고정된 체크포인트 리비전을 선택합니다.
  • OPENJEV_FRONTEND: 기본값은 rust이며, python은 명시적인 대체 옵션입니다.
  • OPENJEV_MAX_INPUT_TOKENS: 기본값은 32768입니다.
  • OPENJEV_MAX_TOTAL_INPUT_TOKENS: 기본값은 262144입니다.
  • OPENJEV_MAX_CONCURRENT_REQUESTS: 기본값은 16입니다.
  • OPENJEV_MAX_CONCURRENT_BRANCHES: 기본값은 64입니다.
  • OPENJEV_REQUEST_TIMEOUT: 기본값은 120초입니다.
  • OPENJEV_TEMPERATURE: 기본값은 1.0이며 레이블 정규화에 영향을 줍니다.
  • OPENJEV_API_KEY: 공개 API에 선택적 Bearer 인증을 활성화합니다.
  • OPENJEV_BACKEND_API_KEY: SGLang에 사용할 별도의 선택적 Bearer 키를 구성합니다.

Modal 실행기는 로컬 환경의 OPENJEV_PROFILE, OPENJEV_FRONTEND, OPENJEV_SERVED_MODEL_NAME을 자동으로 전달합니다. 다른 원격 설정은 image.env(...)에 추가하거나, 자격 증명에는 Modal Secret을 사용하세요.

기존 SGLang 백엔드로 로컬 실행

이미 실행 중인 백엔드를 사용하려면 API가 해당 로컬 URL을 가리키도록 설정하세요.

uv run openjev serve --connect http://127.0.0.1:30000

백엔드는 호환되는 모델 및 토크나이저 리비전을 사용해야 합니다. 또한 선택된 토큰의 로그 확률, radix 캐싱, 충분한 컨텍스트 길이를 지원해야 합니다.

SGLang 0.5.19가 별도의 Python 환경에 설치된 B200 호스트 또는 컨테이너에서는 해당 인터프리터를 명시적으로 지정하세요.

uv run openjev serve --sglang-python /path/to/sglang/bin/python

개발 및 검증

포함된 명령을 사용하여 오프라인 테스트, 토크나이저 통합 검사, 린팅 및 스키마 생성을 실행하세요.

uv run pytest
uv run pytest -m integration
uv run ruff check .
uv run openjev schema

일반 테스트는 오프라인에서 단위 및 API 동작을 검증합니다. 통합 테스트는 실제 토크나이저와 소규모 Hugging Face 다운로드를 사용하지만 GPU는 필요하지 않습니다. 스키마 생성에도 GPU나 모델 다운로드가 필요하지 않습니다.

고급 배포 팁

GPU 컨테이너를 계속 준비 상태로 유지하기

Modal은 5분 동안 유휴 상태가 지속되면 서비스를 0으로 축소합니다. 유휴 GPU 사용을 줄이는 것보다 일관되게 낮은 시작 지연 시간이 더 중요하다면 modal_app.py에서 min_containers=1을 설정하세요.

최초 시작 초기화 고려하기

최초 GPU 시작 시 가중치를 다운로드하고 커널을 컴파일하거나 캡처합니다. 모델 가중치, SGLang 튜닝 데이터 및 Triton 컴파일 데이터는 openjev-huggingface Modal Volume에 유지됩니다. 이후 시작에서는 해당 파일을 재사용하지만, CUDA 그래프 캡처는 시작 시 계속 수행됩니다.

캐시 재사용을 기회 기반 동작으로 취급하기

Radix 재사용은 요청별로 고정된 KV 세션이 아닙니다. 하이브리드 Qwen 모델의 반복 상태, 캐시 페이지 경계, 캐시 압박 및 동시 트래픽에 따라 적중률이 낮아질 수 있습니다. 백엔드는 --mamba-radix-cache-strategy extra_buffer를 사용합니다.

신뢰도를 신중하게 해석하기

OpenJev는 신뢰도를 1 - H(probabilities) / log(number_of_options)으로 정의하고, 그 값을 0과 1 사이로 제한합니다. 균등한 확률은 신뢰도 0을, 하나의 값에 집중된 확률 분포는 신뢰도 1을 생성합니다.

이러한 확률은 제공된 옵션을 조건으로 하며 프롬프트의 표현과 레이블 순서에 따라 달라질 수 있습니다. 이는 정답 여부를 보정한 추정치가 아닙니다.

백엔드 장애에 대비하기

SGLang이 예기치 않게 종료되면 API도 함께 종료됩니다. 그러면 Modal 실행기가 컨테이너를 종료하여, 공개 HTTP 프로세스가 종료된 추론 백엔드에 연결된 채 남지 않고 Modal이 컨테이너를 교체할 수 있도록 합니다. 정상 종료 시에는 두 감시 프로세스가 모두 깔끔하게 비활성화됩니다.

결론

openjev-sglang은 효율적인 1토큰 확률 조회를 통해 Qwen3.6에서 Jev 호환 분류를 실행하는 데 특화된 방법을 제공합니다. Modal 구성은 확장 가능한 B200 배포를 지원하며, 로컬 명령은 테스트와 기존 SGLang 서비스 연결을 지원합니다. 스모크 테스트부터 시작하고, 한도와 인증 설정을 검증한 다음, 트래픽이 증가함에 따라 처리 시간과 캐시 헤더를 모니터링하세요.