Step3-VL-10B
Step3-VL-10B
Step3-VL-10B는 StepFun이 개발한 경량 오픈소스 멀티모달 모델로, 컴팩트한 효율성과 프론티어 수준의 멀티모달 지능 사이의 트레이드오프를 재정의하도록 설계됐어요. 단 10B 파라미터 풋프린트에도 불구하고 시각 지각, 복잡한 추론, 인간 중심 정렬에서 뛰어난 성능을 보여요. AIME 2025에서 94.43%, MathVision(PaCoRe 포함)에서 75.95%를 기록하며 10배~20배 더 큰 모델을 능가하는 추론 능력을 입증해요.
단일 GPU에서 실행 가능한 컴팩트 10B dense 모델이며, SGLang은 NVIDIA B200, H200, H100과 AMD MI355X, MI325X, MI300X GPU에서 서빙을 지원해요.
출처: 문서
본문
1. 모델 소개
Step3-VL-10B는 StepFun이 개발한 경량 오픈소스 멀티모달 모델로, 컴팩트한 효율성과 프론티어 수준의 멀티모달 지능 사이의 트레이드오프를 재정의하도록 설계됐어요. 컴팩트한 10B 파라미터 풋프린트에도 불구하고 시각 지각, 복잡한 추론, 인간 중심 정렬에서 뛰어나요.
Step3-VL-10B의 주요 하이라이트:
- STEM 추론: AIME 2025에서 94.43%, MathVision(PaCoRe 포함)에서 75.95%를 달성해 10×–20× 더 큰 모델을 능가하는 탁월한 복잡 추론 능력을 입증.
- 시각 지각: MMBench에서 92.05%, MMMU에서 80.11%를 기록해 강력한 일반 시각 이해와 멀티모달 추론 확립.
- GUI & OCR: ScreenSpot-V2(92.61%), ScreenSpot-Pro(51.55%), OCRBench(86.75%)에서 최고 수준의 성능 제공, 에이전트 및 문서 이해 작업에 최적화.
- 공간 이해: BLINK에서 66.79%, All-Angles-Bench에서 57.21%로 출현적 공간 인식 능력, 임보디드 지능 애플리케이션의 강력한 잠재력 확립.
자세한 내용은 Hugging Face의 Step3-VL-10B 모델 카드를 참조하세요.
2. SGLang 설치
SGLang은 여러 설치 방법을 제공해요. 하드웨어 플랫폼과 요구사항에 따라 가장 적합한 방법을 선택할 수 있어요.
설치 지침은 공식 SGLang 설치 가이드를 참조하세요.
3. 모델 배포
이 절은 하드웨어 플랫폼과 사용 사례별로 최적화된 배포 구성을 제공해요.
3.1 기본 구성
Step3-VL-10B는 단일 GPU에서 실행할 수 있는 컴팩트한 10B dense 모델이에요. 권장 시작 구성은 하드웨어에 따라 달라져요.
인터랙티브 명령 생성기: 아래 구성 선택기를 사용해 하드웨어 플랫폼과 양자화 방법에 맞는 배포 명령을 자동 생성하세요. SGLang은 NVIDIA B200, H200, H100 및 AMD MI355X, MI325X, MI300X GPU에서 Step3-VL-10B 서빙을 지원해요.
3.2 구성 팁
- 단일 GPU 배포: Step3-VL-10B는 BF16 정밀도로 단일 GPU에 여유롭게 맞으며 텐서 병렬 처리가 필요 없어요.
- 메모리 관리: 필요하면
--context-length을 낮춰 메모리를 절약하세요.32768값이 대부분의 시나리오에 충분해요. - FP8 양자화: FP8 양자화를 사용해 품질을 유지하면서 메모리 사용을 더 줄이세요.
4. 모델 호출
4.1 기본 사용
기본 API 사용과 요청 예제는 다음을 참조하세요:
4.2 고급 사용
4.2.1 멀티모달 입력
Step3-VL-10B는 이미지 입력을 지원해요. 이미지 입력이 있는 기본 예제:
from openai import OpenAI
client = OpenAI(
api_key="EMPTY",
base_url="http://localhost:30000/v1",
timeout=3600
)
messages = [
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://ofasys-multimodal-wlcb-3-toshanghai.oss-accelerate.aliyuncs.com/wpf272043/keepme/image/receipt.png"
}
},
{
"type": "text",
"text": "Read all the text in the image."
}
]
}
]
start = time.time()
response = client.chat.completions.create(
model="stepfun-ai/Step3-VL-10B",
messages=messages,
max_tokens=2048,
extra_body={"top_k": -1}
)
print(f"Response costs: {time.time() - start:.2f}s")
print(f"Generated text: {response.choices[0].message.content}")
예제 출력:
Response costs: 5.89s
Generated text: Auntie Anne's
CINNAMON SUGAR
1 × 17,000 17,000
SUB TOTAL 17,000
GRAND TOTAL 17,000
CASH IDR 20,000
CHANGE DUE 3,000
다중 이미지 입력 예제:
Step3-VL-10B는 비교나 분석을 위해 단일 요청에서 여러 이미지를 처리할 수 있어요:
from openai import OpenAI
client = OpenAI(
api_key="EMPTY",
base_url="http://localhost:30000/v1",
timeout=3600
)
messages = [
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://www.civitatis.com/f/china/hong-kong/guia/taxi.jpg"
}
},
{
"type": "image_url",
"image_url": {
"url": "https://cdn.cheapoguides.com/wp-content/uploads/sites/7/2025/05/GettyImages-509614603-1280x600.jpg"
}
},
{
"type": "text",
"text": "Compare these two images and describe the differences in 100 words or less."
}
]
}
]
start = time.time()
response = client.chat.completions.create(
model="stepfun-ai/Step3-VL-10B",
messages=messages,
max_tokens=2048,
extra_body={"top_k": -1}
)
print(f"Response costs: {time.time() - start:.2f}s")
print(f"Generated text: {response.choices[0].message.content}")
예제 출력:
Response costs: 3.24s
Generated text: First image: Single red Hong Kong taxi close - up, clear license plate (RX 5004), "4 SEATS" sticker, urban street with shops behind. Second image: Aerial view of many taxis (red, green) on a highway with a viaduct, some hoods open, dense arrangement. Differences: Scale (single vs many), perspective (close - up vs aerial), context (street shops vs highway), and taxi conditions (normal vs some open hoods).
4.2.2 Reasoning Parser
Step3-VL-10B는 reasoning 모드를 지원해요. 배포 시 reasoning parser를 활성화해 thinking과 content 섹션을 분리하세요:
python -m sglang.launch_server \
--model stepfun-ai/Step3-VL-10B \
--reasoning-parser deepseek-r1 \
--host 0.0.0.0 \
--port 30000 \
--trust-remote-code
Thinking 프로세스가 있는 스트리밍:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:30000/v1",
api_key="EMPTY"
)
# Enable streaming to see the thinking process in real-time
response = client.chat.completions.create(
model="stepfun-ai/Step3-VL-10B",
messages=[
{"role": "user", "content": "Solve this problem step by step: What is 15% of 240?"}
],
temperature=0.7,
max_tokens=2048,
stream=True,
extra_body={"top_k": -1}
)
# Process the stream
has_thinking = False
has_answer = False
thinking_started = False
for chunk in response:
if chunk.choices and len(chunk.choices) > 0:
delta = chunk.choices[0].delta
# Print thinking process
if hasattr(delta, 'reasoning_content') and delta.reasoning_content:
if not thinking_started:
print("=============== Thinking =================", flush=True)
thinking_started = True
has_thinking = True
print(delta.reasoning_content, end="", flush=True)
# Print answer content
if delta.content:
# Close thinking section and add content header
if has_thinking and not has_answer:
print("\n=============== Content =================", flush=True)
has_answer = True
print(delta.content, end="", flush=True)
print()
예제 출력:
=============== Thinking =================
Okay, let's see. The problem is asking for 15% of 240. Hmm, I need to remember how to calculate percentages. So, percentage means "per hundred," right? So, 15% is the same as 15 per 100, or 15/100. To find a percentage of a number, I think you convert the percentage to a decimal and then multiply it by the number. Let me check that.
First, 15% as a decimal. To convert a percentage to a decimal, you divide by 100. So 15 divided by 100 is 0.15. Yeah, that's right. So 15% is 0.15 in decimal form. Then, to find 15% of 24
0, I need to multiply 0.15 by 240. Let me do that calculation.
Let me write it out: 0.15 * 240. Let's compute that. Maybe break it down. 0.1 is 10%, and 0.05 is 5%, so 10% of 240 is 24, and 5% of 240 is 12. Then 10% + 5% is 15%, so 24 + 12 is 36. Oh,
that's a good way to check. So 15% is 10% plus 5%, which adds up to 36. Let me verify with the decimal method. 0.15 * 240. Let's multiply 240 by 0.1 first: 24. Then 240 by 0.05: 12. Adding them gives 36. Yep, that matches. Alternatively, 240 * 15 = 3600, and then divide by 100 (since it's per hundred), so 3600 / 100 = 36. That's another way. So 15% of 240 is 36. Let me make sure I didn't make a mistake. Let's check with another method. 10% of 240 is 24, 20% would be 48, so 15% is halfway between 10% and 20%, which is (24 + 48)/2 = 36. Yep, that works too
. So all methods point to 36. I think that's the answer.
=============== Content =================
To solve the problem "What is 15% of 240?" step by step:
---
### **Step 1: Understand the concept of percentage**
A percentage represents a portion of a whole. Specifically, "percent" means "per hundred." So, 15% means **15 out of 100**, or **15/100**.
---
### **Step 2: Convert the percentage to a decimal**
To use percentages in calculations, convert them to decimals by dividing by 100:
$$
15\% = \frac{15}{100} = 0.15
$$
---
### **Step 3: Multiply the decimal by the given number**
Now, multiply 0.15 (the decimal form of 15%) by 240:
$$
0.15 \times 240 = 36
$$
---
### **Alternative Verification Methods**
#### **Method A: Break into parts**
- 10% of 240 = $ 0.10 \times 240 = 24 $
- 5% of 240 = $ 0.05 \times 240 = 12 $
- Add them: $ 24 + 12 = 36 $
#### **Method B: Use direct multiplication**
- $ 15\% \text{ of } 240 = \frac{15}{100} \times 240 = \frac{3600}{100} = 36 $
#### **Method C: Estimate using known percentages**
- 20% of 240 = $ 0.20 \times 240 = 48 $
- 10% of 240 = $ 0.10 \times 240 = 24 $
- 15% is halfway between 10% and 20%: $ \frac{24 + 48}{2} = 36 $
---
### **Final Answer**
$$
\boxed{36}
$$
참고: reasoning parser는 모델의 단계별 thinking 프로세스를 캡처해 모델이 결론에 어떻게 도달하는지 볼 수 있게 해줘요.
4.2.3 Tool Calling
Step3-VL-10B는 툴 호출 능력을 지원해요. 툴 호출 파서를 활성화하세요:
python -m sglang.launch_server \
--model stepfun-ai/Step3-VL-10B \
--reasoning-parser deepseek-r1 \
--tool-call-parser hermes \
--host 0.0.0.0 \
--port 30000 \
--trust-remote-code
Python 예제 (Thinking 프로세스 포함):
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:30000/v1",
api_key="EMPTY"
)
# Define available tools
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city name"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["location"]
}
}
}
]
# Make request with streaming to see thinking process
response = client.chat.completions.create(
model="stepfun-ai/Step3-VL-10B",
messages=[
{"role": "user", "content": "What's the weather in Beijing?"}
],
tools=tools,
temperature=0.7,
stream=True,
extra_body={"top_k": -1}
)
# Process streaming response
thinking_started = False
has_thinking = False
tool_calls_accumulator = {}
for chunk in response:
if chunk.choices and len(chunk.choices) > 0:
delta = chunk.choices[0].delta
# Print thinking process
if hasattr(delta, 'reasoning_content') and delta.reasoning_content:
if not thinking_started:
print("=============== Thinking =================", flush=True)
thinking_started = True
has_thinking = True
print(delta.reasoning_content, end="", flush=True)
# Accumulate tool calls
if hasattr(delta, 'tool_calls') and delta.tool_calls:
# Close thinking section if needed
if has_thinking and thinking_started:
print("\n=============== Content =================\n", flush=True)
thinking_started = False
for tool_call in delta.tool_calls:
index = tool_call.index
if index not in tool_calls_accumulator:
tool_calls_accumulator[index] = {
'name': None,
'arguments': ''
}
if tool_call.function:
if tool_call.function.name:
tool_calls_accumulator[index]['name'] = tool_call.function.name
if tool_call.function.arguments:
tool_calls_accumulator[index]['arguments'] += tool_call.function.arguments
# Print content
if delta.content:
print(delta.content, end="", flush=True)
# Print accumulated tool calls
for index, tool_call in sorted(tool_calls_accumulator.items()):
print(f"Tool Call: {tool_call['name']}")
print(f" Arguments: {tool_call['arguments']}")
print()
예제 출력:
=============== Thinking =================
The user is asking about the weather in Beijing. I have a function called "get_weather" that can provide weather information for a location. Let me check the parameters:
- location: required (string) - "Beijing"
- unit: optional (string, enum: ["celsius", "fahrenheit"]) - not specified by the user, so I won't include it
I should call the function with location="Beijing".
<tool_calls>
=============== Content =================
</tool_calls>Tool Call: get_weather
Arguments: {"location": "Beijing"}
툴 호출 결과 처리:
# After getting the tool call, execute the function
def get_weather(location, unit="celsius"):
# Your actual weather API call here
return f"The weather in {location} is 22°{unit[0].upper()} and sunny."
# Send tool result back to the model
messages = [
{"role": "user", "content": "What's the weather in Beijing?"},
{
"role": "assistant",
"content": None,
"tool_calls": [{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": '{"location": "Beijing", "unit": "celsius"}'
}
}]
},
{
"role": "tool",
"tool_call_id": "call_123",
"content": get_weather("Beijing", "celsius")
}
]
final_response = client.chat.completions.create(
model="stepfun-ai/Step3-VL-10B",
messages=messages,
temperature=0.7,
extra_body={"top_k": -1}
)
print(final_response.choices[0].message.content)
참고:
- reasoning parser는 모델이 툴을 어떻게 사용하기로 결정하는지 보여줘요.
- 툴 호출은 함수 이름과 인자로 명확히 표시돼요.
- 그런 다음 함수를 실행하고 결과를 다시 보내 대화를 계속할 수 있어요.
5. 벤치마크
5.1 속도 벤치마크
테스트 환경:
- 하드웨어: NVIDIA B200 GPU (1x)
- 모델: stepfun-ai/Step3-VL-10B
- Tensor Parallelism: 1
- sglang 버전: 0.5.8+
무작위 이미지로 SGLang의 내장 벤치마킹 도구를 사용해 성능 평가를 수행해요.
5.1.1 지연 시간 민감 벤치마크
- 모델 배포 명령:
python -m sglang.launch_server \
--model stepfun-ai/Step3-VL-10B \
--host 0.0.0.0 \
--port 30000 \
--trust-remote-code
- 벤치마크 명령:
python3 -m sglang.bench_serving \
--backend sglang-oai-chat \
--host 127.0.0.1 \
--port 30000 \
--model stepfun-ai/Step3-VL-10B \
--dataset-name image \
--image-count 2 \
--image-resolution 720p \
--random-input-len 128 \
--random-output-len 1024 \
--num-prompts 10 \
--max-concurrency 1
- 결과:
============ Serving Benchmark Result ============
Backend: sglang-oai-chat
Traffic request rate: inf
Max request concurrency: 1
Successful requests: 10
Benchmark duration (s): 30.85
Total input tokens: 14120
Total input text tokens: 720
Total input vision tokens: 13400
Total generated tokens: 4220
Total generated tokens (retokenized): 4217
Request throughput (req/s): 0.32
Input token throughput (tok/s): 457.71
Output token throughput (tok/s): 136.79
Peak output token throughput (tok/s): 240.00
Peak concurrent requests: 2
Total token throughput (tok/s): 594.50
Concurrency: 1.00
----------------End-to-End Latency----------------
Mean E2E Latency (ms): 3083.40
Median E2E Latency (ms): 2747.00
P90 E2E Latency (ms): 4574.50
P99 E2E Latency (ms): 5462.49
---------------Time to First Token----------------
Mean TTFT (ms): 1327.69
Median TTFT (ms): 1341.01
P99 TTFT (ms): 1486.11
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms): 4.16
Median TPOT (ms): 4.17
P99 TPOT (ms): 4.18
---------------Inter-Token Latency----------------
Mean ITL (ms): 4.17
Median ITL (ms): 4.18
P95 ITL (ms): 4.30
P99 ITL (ms): 4.38
Max ITL (ms): 8.24
==================================================
5.1.2 처리량 민감 벤치마크
- 벤치마크 명령:
python3 -m sglang.bench_serving \
--backend sglang-oai-chat \
--host 127.0.0.1 \
--port 30000 \
--model stepfun-ai/Step3-VL-10B \
--dataset-name image \
--image-count 2 \
--image-resolution 720p \
--random-input-len 128 \
--random-output-len 1024 \
--num-prompts 1000 \
--max-concurrency 100
- 결과:
============ Serving Benchmark Result ============
Backend: sglang-oai-chat
Traffic request rate: inf
Max request concurrency: 100
Successful requests: 1000
Benchmark duration (s): 976.52
Total input tokens: 1416949
Total input text tokens: 76949
Total input vision tokens: 1340000
Total generated tokens: 510855
Total generated tokens (retokenized): 510526
Request throughput (req/s): 1.02
Input token throughput (tok/s): 1451.02
Output token throughput (tok/s): 523.14
Peak output token throughput (tok/s): 20429.00
Peak concurrent requests: 103
Total token throughput (tok/s): 1974.16
Concurrency: 99.81
----------------End-to-End Latency----------------
Mean E2E Latency (ms): 97463.22
Median E2E Latency (ms): 91872.75
P90 E2E Latency (ms): 118553.42
P99 E2E Latency (ms): 198445.56
---------------Time to First Token----------------
Mean TTFT (ms): 94379.07
Median TTFT (ms): 87163.09
P99 TTFT (ms): 194871.41
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms): 5.89
Median TPOT (ms): 5.72
P99 TPOT (ms): 23.58
---------------Inter-Token Latency----------------
Mean ITL (ms): 6.05
Median ITL (ms): 0.13
P95 ITL (ms): 0.56
P99 ITL (ms): 3.99
Max ITL (ms): 97551.06
==================================================
5.2 정확도 벤치마크
5.2.1 MMMU 벤치마크
MMMU 데이터셋으로 모델의 정확도를 평가할 수 있어요:
- 모델 배포 명령:
python -m sglang.launch_server \
--model stepfun-ai/Step3-VL-10B \
--host 0.0.0.0 \
--port 30000 \
--trust-remote-code
- 벤치마크 명령:
python3 benchmark/mmmu/bench_sglang.py \
--port 30000 \
--concurrency 64
- 결과:
Benchmark time: 934.6179109360091
answers saved to: ./answer_sglang.json
Evaluating...
answers saved to: ./answer_sglang.json
{'Accounting': {'acc': 0.667, 'num': 30},
'Agriculture': {'acc': 0.367, 'num': 30},
'Architecture_and_Engineering': {'acc': 0.4, 'num': 30},
'Art': {'acc': 0.467, 'num': 30},
'Art_Theory': {'acc': 0.5, 'num': 30},
'Basic_Medical_Science': {'acc': 0.367, 'num': 30},
'Biology': {'acc': 0.3, 'num': 30},
'Chemistry': {'acc': 0.467, 'num': 30},
'Clinical_Medicine': {'acc': 0.567, 'num': 30},
'Computer_Science': {'acc': 0.467, 'num': 30},
'Design': {'acc': 0.567, 'num': 30},
'Diagnostics_and_Laboratory_Medicine': {'acc': 0.3, 'num': 30},
'Economics': {'acc': 0.6, 'num': 30},
'Electronics': {'acc': 0.567, 'num': 30},
'Energy_and_Power': {'acc': 0.633, 'num': 30},
'Finance': {'acc': 0.733, 'num': 30},
'Geography': {'acc': 0.333, 'num': 30},
'History': {'acc': 0.533, 'num': 30},
'Literature': {'acc': 0.533, 'num': 30},
'Manage': {'acc': 0.6, 'num': 30},
'Marketing': {'acc': 0.767, 'num': 30},
'Materials': {'acc': 0.6, 'num': 30},
'Math': {'acc': 0.7, 'num': 30},
'Mechanical_Engineering': {'acc': 0.333, 'num': 30},
'Music': {'acc': 0.4, 'num': 30},
'Overall': {'acc': 0.523, 'num': 900},
'Overall-Art and Design': {'acc': 0.483, 'num': 120},
'Overall-Business': {'acc': 0.673, 'num': 150},
'Overall-Health and Medicine': {'acc': 0.513, 'num': 150},
'Overall-Humanities and Social Science': {'acc': 0.492, 'num': 120},
'Overall-Science': {'acc': 0.5, 'num': 150},
'Overall-Tech and Engineering': {'acc': 0.481, 'num': 210},
'Pharmacy': {'acc': 0.6, 'num': 30},
'Physics': {'acc': 0.7, 'num': 30},
'Psychology': {'acc': 0.467, 'num': 30},
'Public_Health': {'acc': 0.733, 'num': 30},
'Sociology': {'acc': 0.433, 'num': 30}}
eval out saved to ./val_sglang.json
Overall accuracy: 0.523