추론 파서
추론 파서 (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.py의 BaseReasoningFormatDetector의 서브클래스로 추론 파서를 구현하고, 새 추론 모델 스키마에 맞게 추론 파서를 지정하면 됩니다.