추론 파서

추론 파서 (Reasoning Parser)

SGLang은 DeepSeek R1 같은 추론 모델에서 추론(reasoning) 콘텐츠를 "일반" 콘텐츠에서 분리해 파싱하는 것을 지원합니다. 응답의 chain-of-thought(CoT)와 최종 답변을 따로 받아볼 수 있어요.

출처: 문서

본문

SGLang은 DeepSeek R1 같은 추론 모델의 "일반" 콘텐츠에서 추론 콘텐츠를 파싱하는 것을 지원합니다.

지원 모델 및 파서 (Supported Models & Parsers)

Model Reasoning tags Parser Notes
Apertus 2509 models `< inner_prefix ><
DeepSeek‑R1 series thinking response deepseek-r1 모든 변형 지원 (R1, R1-0528, R1-Distill)
DeepSeek‑V3 series thinking response deepseek-v3 DeepSeek‑V3.2 포함. thinking 파라미터 지원
Standard Qwen3 models thinking response qwen3 enable_thinking 파라미터 지원
Qwen3-Thinking models thinking response qwen3 or qwen3-thinking 항상 thinking 콘텐츠 생성
Kimi K2 Thinking ◁think▷◁/think▷ kimi_k2 특수 thinking 구분자 사용. 도구 사용에는 --tool-call-parser kimi_k2도 필요
GPT OSS `< channel >analysis<

모델별 동작 (Model-Specific Behaviors)

Apertus 2509:

  • <|inner_prefix|><|inner_suffix|>로 추론 콘텐츠를 구분. 에이전트 도구 사용에는 --tool-call-parser apertus2509도 지정.

DeepSeek-R1 팔리트 (DeepSeek-R1 Family):

  • DeepSeek-R1: thinking 시작 태그 없이 추론 콘텐츠로 바로 점프
  • DeepSeek-R1-0528: thinking 시작과 response 끝 태그를 모두 생성
  • 둘 다 같은 deepseek-r1 파서가 처리

DeepSeek-V3 팔리트 (DeepSeek-V3 Family):

  • DeepSeek-V3.1/V3.2: thinking·non-thinking 모드를 모두 지원하는 하이브리드 모델. deepseek-v3 파서와 thinking 파라미터 사용 (참고: enable_thinking이 아님)

Qwen3 팔리트 (Qwen3 Family):

  • 표준 Qwen3 (예: Qwen3-2507): qwen3 파서 사용, chat 템플릿에서 enable_thinking 지원
  • Qwen3-Thinking (예: Qwen3-235B-A22B-Thinking-2507): qwen3 또는 qwen3-thinking 파서 사용, 항상 thinking

Kimi K2:

  • Kimi K2 Thinking: 특수 ◁think▷◁/think▷ 태그 사용. 에이전트 도구 사용에는 --tool-call-parser kimi_k2도 지정.

GPT OSS:

  • GPT OSS: 특수 <|channel|>analysis<|message|><|end|> 태그 사용

사용법 (Usage)

서버 시작 (Launching the Server)

--reasoning-parser 옵션을 지정하세요.

import requests
from openai import OpenAI
from sglang.test.doc_patch import launch_server_cmd
from sglang.utils import wait_for_server, print_highlight, terminate_process

server_process, port = launch_server_cmd(
    "python3 -m sglang.launch_server --model-path deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --host 0.0.0.0 --reasoning-parser deepseek-r1 --log-level warning"
)

wait_for_server(f"http://localhost:{port}")

--reasoning-parser가 응답 해석에 사용되는 파서를 정의한다는 점을 기억하세요.

OpenAI 호환 API (OpenAI Compatible API)

OpenAI 호환 API를 사용할 때 계약은 DeepSeek-R1 출시와 함께 정립된 DeepSeek API 설계를 따릅니다:

  • reasoning_content: CoT의 콘텐츠.
  • content: 최종 답변의 콘텐츠.
# Initialize OpenAI-like client
client = OpenAI(api_key="None", base_url=f"http://0.0.0.0:{port}/v1")
model_name = client.models.list().data[0].id

messages = [
    {
        "role": "user",
        "content": "What is 1+3?",
    }
]

비스트리밍 요청 (Non-Streaming Request)

response_non_stream = client.chat.completions.create(
    model=model_name,
    messages=messages,
    temperature=0.6,
    top_p=0.95,
    stream=False,  # Non-streaming
    extra_body={"separate_reasoning": True},
)
print_highlight("==== Reasoning ====")
print_highlight(response_non_stream.choices[0].message.reasoning_content)

print_highlight("==== Text ====")
print_highlight(response_non_stream.choices[0].message.content)

스트리밍 요청 (Streaming Request)

response_stream = client.chat.completions.create(
    model=model_name,
    messages=messages,
    temperature=0.6,
    top_p=0.95,
    stream=True,  # Non-streaming
    extra_body={"separate_reasoning": True},
)

reasoning_content = ""
content = ""
for chunk in response_stream:
    if chunk.choices[0].delta.content:
        content += chunk.choices[0].delta.content
    if chunk.choices[0].delta.reasoning_content:
        reasoning_content += chunk.choices[0].delta.reasoning_content

print_highlight("==== Reasoning ====")
print_highlight(reasoning_content)

print_highlight("==== Text ====")
print_highlight(content)

선택적으로, 추론 콘텐츠를 마지막 추론 청크(또는 추론 콘텐츠 뒤 첫 청크)까지 버퍼링할 수 있습니다.

response_stream = client.chat.completions.create(
    model=model_name,
    messages=messages,
    temperature=0.6,
    top_p=0.95,
    stream=True,  # Non-streaming
    extra_body={"separate_reasoning": True, "stream_reasoning": False},
)

reasoning_content = ""
content = ""
for chunk in response_stream:
    if chunk.choices[0].delta.content:
        content += chunk.choices[0].delta.content
    if chunk.choices[0].delta.reasoning_content:
        reasoning_content += chunk.choices[0].delta.reasoning_content

print_highlight("==== Reasoning ====")
print_highlight(reasoning_content)

print_highlight("==== Text ====")
print_highlight(content)

추론 분리는 지정 시 기본적으로 활성화됩니다. 비활성화하려면 요청에서 separate_reasoning 옵션을 False로 설정하세요.

response_non_stream = client.chat.completions.create(
    model=model_name,
    messages=messages,
    temperature=0.6,
    top_p=0.95,
    stream=False,  # Non-streaming
    extra_body={"separate_reasoning": False},
)

print_highlight("==== Original Output ====")
print_highlight(response_non_stream.choices[0].message.content)

SGLang 네이티브 API (SGLang Native API)

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("deepseek-ai/DeepSeek-R1-Distill-Qwen-7B")
input = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, return_dict=False
)

gen_url = f"http://localhost:{port}/generate"
gen_data = {
    "text": input,
    "sampling_params": {
        "skip_special_tokens": False,
        "max_new_tokens": 1024,
        "temperature": 0.6,
        "top_p": 0.95,
    },
}
gen_response = requests.post(gen_url, json=gen_data).json()["text"]

print_highlight("==== Original Output ====")
print_highlight(gen_response)

parse_url = f"http://localhost:{port}/separate_reasoning"
separate_reasoning_data = {
    "text": gen_response,
    "reasoning_parser": "deepseek-r1",
}
separate_reasoning_response_json = requests.post(
    parse_url, json=separate_reasoning_data
).json()
print_highlight("==== Reasoning ====")
print_highlight(separate_reasoning_response_json["reasoning_text"])
print_highlight("==== Text ====")
print_highlight(separate_reasoning_response_json["text"])
terminate_process(server_process)

오프라인 엔진 API (Offline Engine API)

import sglang as sgl
from sglang.srt.parser.reasoning_parser import ReasoningParser
from sglang.utils import print_highlight

llm = sgl.Engine(model_path="deepseek-ai/DeepSeek-R1-Distill-Qwen-7B")
tokenizer = AutoTokenizer.from_pretrained("deepseek-ai/DeepSeek-R1-Distill-Qwen-7B")
input = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, return_dict=False
)
sampling_params = {
    "max_new_tokens": 1024,
    "skip_special_tokens": False,
    "temperature": 0.6,
    "top_p": 0.95,
}
result = llm.generate(prompt=input, sampling_params=sampling_params)

generated_text = result["text"]  # Assume there is only one prompt

print_highlight("==== Original Output ====")
print_highlight(generated_text)

parser = ReasoningParser("deepseek-r1")
reasoning_text, text = parser.parse_non_stream(generated_text)
print_highlight("==== Reasoning ====")
print_highlight(reasoning_text)
print_highlight("==== Text ====")
print_highlight(text)
llm.shutdown()

새 추론 모델 스키마 지원 (Supporting New Reasoning Model Schemas)

미래의 추론 모델을 위해 python/sglang/srt/reasoning_parser.pyBaseReasoningFormatDetector의 서브클래스로 추론 파서를 구현하고, 새 추론 모델 스키마에 맞게 추론 파서를 지정하면 됩니다.

더 알아보기 (Learn more)