SGLang으로 Qwen 배포하기

SGLang으로 Qwen 배포하기

SGLang은 대규모 언어 모델과 비전 언어 모델을 위한 빠른 서빙 프레임워크예요. SGLang에 대해 더 알고 싶다면 공식 문서를 참고하세요. 이 글에서는 SGLang으로 OpenAI 호환 API 서비스를 만들고 Qwen3를 서빙하는 방법을 소개해요.

출처: 문서

본문

환경 설정

기본적으로 깨끗한 환경에서 pip으로 sglang을 설치할 수 있어요:

pip install "sglang[all]>=0.4.6.post1"

설치 중 문제가 있다면 공식 문서의 설치 안내(링크)를 확인해 보세요.

API 서비스

SGLang으로 OpenAI 호환 API 서비스를 만드는 것은 쉬워요. OpenAI API 프로토콜을 구현하는 서버로 배포할 수 있거든요. 기본적으로 서버는 http://localhost:30000에서 시작돼요. --host와 --port 인자로 주소를 지정할 수 있어요. 아래처럼 명령을 실행하세요:

python -m sglang.launch_server --model-path Qwen/Qwen3-8B

기본적으로 --model-path가 유효한 로컬 디렉터리를 가리키지 않으면 Hugging Face Hub에서 모델 파일을 다운로드해요. ModelScope에서 모델을 다운로드하려면 위 명령을 실행하기 전에 다음을 설정하세요:

export SGLANG_USE_MODELSCOPE=true

텐서 병렬 분산 추론은 아래처럼 간단해요:

python -m sglang.launch_server --model-path Qwen/Qwen3-8B --tensor-parallel-size 4

위 명령은 4개의 GPU에서 텐서 병렬을 사용해요. 필요에 따라 GPU 수를 변경하면 돼요.

기본 사용법

그 다음 create chat 인터페이스로 Qwen과 통신할 수 있어요.

curl:

curl http://localhost:30000/v1/chat/completions -H "Content-Type: application/json" -d '{
  "model": "Qwen/Qwen3-8B",
  "messages": [
    {"role": "user", "content": "Give me a short introduction to large language models."}
  ],
  "temperature": 0.6,
  "top_p": 0.95,
  "top_k": 20,
  "max_tokens": 32768
}'

Python:

아래처럼 openai Python SDK로 API 클라이언트를 사용할 수 있어요:

from openai import OpenAI
# Set OpenAI's API key and API base to use SGLang's API server.
openai_api_key = "EMPTY"
openai_api_base = "http://localhost:30000/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

chat_response = client.chat.completions.create(
    model="Qwen/Qwen3-8B",
    messages=[
        {"role": "user", "content": "Give me a short introduction to large language models."},
    ],
    max_tokens=32768,
    temperature=0.6,
    top_p=0.95,
    extra_body={
        "top_k": 20,
    },
)
print("Chat response:", chat_response)

💡 팁: 기본 샘플링 파라미터가 thinking 모드에서는 대부분 잘 동작하지만, 애플리케이션에 맞게 샘플링 파라미터를 조정하고 항상 API에 샘플링 파라미터를 전달하는 것을 권장해요.

Thinking & Non-Thinking 모드

Qwen3 모델은 응답하기 전에 먼저 생각해요. 이 동작은 thinking을 완전히 끄는 하드 스위치(hard switch)로 제어하거나, 모델이 사용자가 생각해야 하는지에 대한 지시를 따르는 소프트 스위치(soft switch)로 제어할 수 있어요.

하드 스위치는 SGLang에서 API 호출에 다음 설정으로 사용할 수 있어요. thinking을 끄려면:

curl:

curl http://localhost:30000/v1/chat/completions -H "Content-Type: application/json" -d '{
  "model": "Qwen/Qwen3-8B",
  "messages": [
    {"role": "user", "content": "Give me a short introduction to large language models."}
  ],
  "temperature": 0.7,
  "top_p": 0.8,
  "top_k": 20,
  "max_tokens": 8192,
  "presence_penalty": 1.5,
  "chat_template_kwargs": {"enable_thinking": false}
}'

Python:

아래처럼 openai Python SDK로 API 클라이언트를 사용할 수 있어요 (thinking 켜기 예시):

from openai import OpenAI
# Set OpenAI's API key and API base to use SGLang's API server.
openai_api_key = "EMPTY"
openai_api_base = "http://localhost:30000/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

chat_response = client.chat.completions.create(
    model="Qwen/Qwen3-8B",
    messages=[
        {"role": "user", "content": "Give me a short introduction to large language models."},
    ],
    max_tokens=8192,
    temperature=0.7,
    top_p=0.8,
    presence_penalty=1.5,
    extra_body={
        "top_k": 20,
        "chat_template_kwargs": {"enable_thinking": True},
    },
)
print("Chat response:", chat_response)

📝 참고: enable_thinking을 전달하는 것은 OpenAI API 호환 방식이 아니에요. 정확한 방법은 프레임워크마다 다를 수 있어요.

💡 팁: thinking을 완전히 끄려면 모델을 시작할 때 커스텀 채팅 템플릿을 사용하면 돼요:

python -m sglang.launch_server --model-path Qwen/Qwen3-8B --chat-template ./qwen3_nonthinking.jinja

이 채팅 템플릿은 사용자가 /think로 지시하더라도 모델이 thinking 콘텐츠를 생성하는 것을 막아요.

💡 팁: thinking 모드와 non-thinking 모드에 샘플링 파라미터를 다르게 설정하는 것을 권장해요.

Thinking 콘텐츠 파싱

SGLang은 모델 생성에서 thinking 콘텐츠를 구조화된 메시지로 파싱하는 것을 지원해요:

python -m sglang.launch_server --model-path Qwen/Qwen3-8B --reasoning-parser qwen3

응답 메시지에는 content 외에 reasoning_content라는 필드가 생기며, 여기에 모델이 생성한 thinking 콘텐츠가 담겨요.

📝 참고: 이 기능은 OpenAI API 호환 방식이 아니에요.

⚠️ 중요: enable_thinking=False는 이 기능과 호환되지 않을 수 있어요. API에 enable_thinking=False를 전달해야 한다면 thinking 콘텐츠 파싱을 끄는 것을 고려하세요.

도구 호출 파싱

SGLang은 모델 생성에서 도구 호출 콘텐츠를 구조화된 메시지로 파싱하는 것을 지원해요:

python -m sglang.launch_server --model-path Qwen/Qwen3-8B --tool-call-parser qwen25

자세한 내용은 우리의 함수 호출 가이드를 참고하세요.

구조화/JSON 출력

SGLang은 구조화/JSON 출력을 지원해요. SGLang 문서를 참고하세요. 또한 시스템 메시지나 프롬프트에서 모델에 특정 형식을 생성하도록 지시하는 것도 권장해요.

양자화 모델 서빙

Qwen3에는 FP8과 AWQ 두 가지 유형의 사전 양자화 모델이 제공돼요. 이 모델들을 서빙하는 명령은 이름만 바뀌고 원본 모델과 동일해요:

# For FP8 quantized model
python -m sglang.launch_server --model-path Qwen/Qwen3-8B-FP8

# For AWQ quantized model
python -m sglang.launch_server --model-path Qwen/Qwen3-8B-AWQ

컨텍스트 길이

Qwen3 모델의 사전 훈련 컨텍스트 길이는 최대 32,768 토큰이에요. 32,768 토큰을 크게 초과하는 컨텍스트를 처리하려면 RoPE 스케일링 기법을 적용해야 해요. 모델 길이 외삽을 향상시키는 기법인 YaRN의 성능을 검증해서 긴 텍스트에서 최적의 성능을 보장했어요.

SGLang은 YaRN을 지원하며 다음처럼 설정할 수 있어요:

python -m sglang.launch_server --model-path Qwen/Qwen3-8B --json-model-override-args '{"rope_scaling":{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}}' --context-length 131072

📝 참고: SGLang은 정적 YaRN을 구현하므로 스케일링 인자가 입력 길이와 무관하게 일정하며, 이는 짧은 텍스트의 성능에 영향을 줄 수 있어요. 긴 컨텍스트 처리가 필요한 경우에만 rope_scaling 설정을 추가하는 것을 권장해요. 필요에 따라 factor를 수정하는 것도 권장해요. 예를 들어 애플리케이션의 일반적인 컨텍스트 길이가 65,536 토큰이라면 factor를 2.0으로 설정하는 것이 좋아요.

📝 참고: config.json의 기본 max_position_embeddings는 40,960으로 설정되어 있으며 SGLang이 이를 사용해요. 이 할당에는 출력용 32,768 토큰과 일반적인 프롬프트용 8,192 토큰이 포함되며, 대부분의 짧은 텍스트 처리 시나리오에 충분하고 모델 thinking에 충분한 여유를 남겨줘요. 평균 컨텍스트 길이가 32,768 토큰을 초과하지 않는다면, 이 시나리오에서는 YaRN을 활성화하지 않는 것을 권장해요. 모델 성능이 저하될 수 있기 때문이에요.

더 알아보기 (Learn more)