Laguna-M.1
Laguna-M.1
Laguna-M.1은 poolside가 만든 오픈 가중치 225B 파라미터 Mixture-of-Experts 모델(토큰당 23B 활성화)로, 에이전틱 코딩과 장기 지평 소프트웨어 엔지니어링 작업을 위해 설계되었어요. Apache 2.0으로 공개됩니다.
출처: 문서
본문
Deployment
# Laguna-M.1 support is on SGLang main (PRs #28400 + #28604, plus #28649 for FP8), not yet in a
# tagged release — install from main. The serving runtime is in the base dependencies, no extra needed:
git clone https://github.com/sgl-project/sglang.git
cd sglang
uv pip install --prerelease=allow -e python
```
그런 다음 해당 환경에서 아래 명령 패널의 **Python** 출력을 실행하세요. **Docker** 탭이 더 간단해요 — `lmsysorg/sglang:latest`가 CUDA-13 런타임과 M.1 코드를 번들합니다.
</Tab>
<Tab title="Docker">
```bash
# CUDA 13 — covers H200 + all Blackwell:
docker pull lmsysorg/sglang:latest
```
이미지 실행 방법은 [Install → Method 3: Using Docker](../../../docs/get-started/install#method-3-using-docker)를 참고하세요. 안쪽의 `sglang serve ...`를 아래 명령 생성기가 만들어 내는 것으로 대체하세요.
</Tab>
하드웨어와 양자화를 선택해 실행 명령을 생성하세요. Laguna-M.1은 셀당 단일 Balanced 레시피를 제공해요 — poolside의 권장 동작 포인트로, 일반적인 다중 사용자 서빙에 좋은 속도/처리량 트레이드오프예요. 8-GPU HGX 플랫폼(H200 / B200 / B300)은 --tp 8을, 4-GPU Grace-Blackwell 단일 노드(GB200 / GB300)는 --tp 4를 사용해요.
Playground
Playground는 검증된 매트릭스를 넘어 SGLang 기능을 실험하는 곳이에요. 위 Deploy 패널은 SGLang 팀이 승인한 조합만 생성하며, Playground는 Deploy 패널이 현재 표시 중인 셀 위에 추가 노브(파서, DP-Attention, DeepEP / EP)를 켤 수 있게 해 줘요.
1. Model Introduction
Laguna-M.1은 poolside의 오픈 가중치 225B 파라미터 Mixture-of-Experts 모델(토큰당 23B 활성화)로, 에이전틱 코딩과 장기 지평 소프트웨어 엔지니어링 작업을 위해 만들어졌어요. Apache 2.0으로 공개됩니다.
주요 특징:
- 대규모 희소 MoE: 70개 레이어 transformer — 처음 3개 레이어는 밀집 SwiGLU, 나머지 67개는 256개 전문가, top-16 라우팅(+1 공유 전문가)과 auxiliary-loss-free 부하 균형을 갖춘 희소 MoE예요.
- 출력 게이팅이 있는 Global attention: 모든 레이어에 걸친 global attention, 64 Q-heads / 8 KV-heads(head dim 128), softplus attention output gating(PR #28400 필요).
- 긴 컨텍스트: 262,144 토큰, YaRN 포함 RoPE.
- 에이전틱 코딩: SWE-bench Verified, SWE-bench Multilingual, SWE-Bench Pro, Terminal-Bench 2.0에서 경쟁력 있음.
- 네이티브 추론: 도구 호출 사이에 interleaved thinking,
chat_template_kwargs={"enable_thinking": ...}으로 요청마다 토글.
사용 가능한 양자화:
| Quantization | Hugging Face path |
|---|---|
| BF16 | poolside/Laguna-M.1 |
| FP8 | poolside/Laguna-M.1-FP8 |
| NVFP4 | poolside/Laguna-M.1-NVFP4 |
라이선스: Apache 2.0
리소스: Hugging Face · Release blog post · Technical report · API platform.
2. Configuration Tips
- Trust remote code (
--trust-remote-code): Laguna-M.1은 Hugging Face Hub에 커스텀 모델링/config 코드를 실어 보내므로 서버가 모델을 로드하려면 이 플래그가 필요해요. - 장기 컨텍스트 메모리: M.1은 global-attention(sliding-window 없음)이므로 262,144 토큰 KV 캐시가 커요. 전체 컨텍스트에서 OOM이 나면
--mem-fraction-static을 낮추거나--context-length를 제한하세요. - FP8: Blackwell에서는 레시피에
--fp8-gemm-backend triton이 추가돼요 — compressed-tensors 블록-FP8 가중치 스케일이 UE8M0-패킹되어 있지 않아 기본 DeepGEMM 경로가 Blackwell(sm_100)에서 쓰레기를 생성하거든요. Triton 백엔드가 올바르고(~19% 느림) PR #28662까지의 임시 해결책이에요(그 PR은 스케일을 수정하고 DeepGEMM 고속 경로를 복원). **Hopper (H200)**에서 FP8은 추가 플래그 없이 DeepGEMM을 사용하며, 재시작마다 비용을 내지 않도록python3 -m sglang.compile_deep_gemm --model poolside/Laguna-M.1-FP8로 멀티세션 JIT를 미리 웜업하세요. - 파서 (
poolside_v1): 에이전틱/도구 사용 배포에서는 위 Playground에서 Reasoning Parser와 Tool Call Parser를 활성화하세요 —--reasoning-parser poolside_v1(thinking →reasoning_content)과--tool-call-parser poolside_v1(구조화된tool_calls)을 생성해요. - Thinking 기본값: thinking은 기본적으로 꺼져 있어요; 요청마다
extra_body={"chat_template_kwargs": {"enable_thinking": True}}로 옵트인하세요. - Served model id: 서버는
--model-path에 전달한 대로 모델을 등록하므로, 클라이언트의model필드가 그것과 일치해야 해요 —poolside/Laguna-M.1(BF16) 또는 양자화 셀의poolside/Laguna-M.1-FP8/-NVFP4. §3 예시는 BF16 id를 사용하며, 실행한 id로 바꿔 넣으세요. - 권장 샘플링: poolside는 thinking 활성화 상태에서
temperature=1.0,top_k=20으로 M.1을 벤치마크해요. 이것들은 요청별 샘플링 파라미터(실행 플래그 아님)예요 — 예: OpenAI 클라이언트에서temperature=1.0, extra_body={"top_k": 20}.
3. Advanced Usage
3.1 Reasoning
--reasoning-parser poolside_v1로 실행하거나(또는 위 Playground의 Parsers 카드에서 Reasoning Parser 토글) 하세요. Reasoning은 옵트인이에요: Laguna 채팅 템플릿이 enable_thinking=True(chat_template_kwargs로 전달)에 게이트하므로 — 일반 thinking 키는 무시돼요. 그러면 thinking 트레이스는 message.content의 최종 답과 분리되어 message.reasoning_content에 들어가며, 클라이언트 측 태그 제거가 필요 없어요.
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
response = client.chat.completions.create( model="poolside/Laguna-M.1", messages=[{"role": "user", "content": "What is 15% of 240? Explain briefly."}], max_tokens=2048, extra_body={"chat_template_kwargs": {"enable_thinking": True}}, )
message = response.choices[0].message print("=============== Reasoning ===============") print(message.reasoning_content) print("=============== Answer ==================") print(message.content)
</Accordion>
<Accordion title="Example Output">
```text
=============== Reasoning ===============
Okay, so I need to find out what 15% of 240 is. Hmm, percentages can sometimes be
tricky, but let me think. I remember that "percent" means per hundred, right? So 15%
is the same as 15 per 100 or 15/100. Maybe I can convert that percentage into a decimal
first? ... 15 divided by 100 is 0.15. ... Now, to find 15% of 240, I just need to
multiply 240 by 0.15. ... 240 times 0.1 is 24 (10% of 240), and 240 times 0.05 is 12
(half of that), so 24 + 12 = 36.
[… verifies the same result several more ways: 15/100 × 240, 240 × 15 ÷ 100,
1% × 15, and the fraction 3/20 × 240 — all give 36 …]
So ... all methods are pointing to 36. I'm pretty confident that 15% of 240 is 36.
=============== Answer ==================
To find 15% of 240, convert the percentage to a decimal (0.15) and multiply by 240:
**240 × 0.15 = 36**.
**Step-by-Step Explanation:**
1. **Convert 15% to a decimal:** 15% = 15/100 = 0.15.
2. **Multiply by 240:**
- Break it down:
- 10% of 240 = 24 (since 240 × 0.1 = 24).
- 5% of 240 = 12 (half of 24).
- Add them: 24 + 12 = **36**.
**Answer:** 15% of 240 is **36**.
3.2 Tool Calling
--tool-call-parser poolside_v1로 실행하거나(또는 위 Playground의 Parsers 카드에서 Tool Call Parser 토글) 하세요. 파서는 Laguna의 <tool_call> 출력을 표준 OpenAI tool_calls 구조로 변환해요. 도구 호출은 reasoning이 꺼진 상태에서도(enable_thinking=False, 기본값) 작동해요.
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
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"]}, }, "required": ["location"], }, }, } ]
response = client.chat.completions.create( model="poolside/Laguna-M.1", messages=[{"role": "user", "content": "What's the weather in Beijing?"}], tools=tools, )
message = response.choices[0].message if message.tool_calls: for call in message.tool_calls: print(f"Tool: {call.function.name}") print(f"Args: {call.function.arguments}")
</Accordion>
<Accordion title="Example Output">
```text
Tool: get_weather
Args: {"location": "Beijing"}
3.3 Prefill-Decode (PD) Disaggregation
PD disaggregation은 prefill과 decode를 RDMA KV 전송 패브릭(mooncake 또는 NIXL)으로 연결된 별도 SGLang 서버에서 실행하고, 앞에 PD 라우터를 둬요. Laguna-M.1은 표준 KV 캐시를 가진 global-attention(sliding window 없음, 희소 "index" 사이드 버퍼 없음)이므로 KV 페이지가 모델 특정 플래그 없이 전송돼요 — 그냥 --disaggregation-* 노브만 있으면 돼요. 두 역할이 모델과 플래그를 공유하므로 자동으로 같은 attention 백엔드(FlashAttention-3)와 페이지 크기를 선택해 KV 레이아웃이 전송을 위해 정렬돼요.
지원/검증된 토폴로지:
- 동일한 텐서 병렬 처리 — prefill과 decode가 같은
--tp를 실행. - 단일 파이프라인 스테이지 — PP = 1(기본값).
- mooncake 또는 NIXL 전송 백엔드, RDMA / InfiniBand 위에서.
- 2 × 8×H200(TP8 prefill + TP8 decode, BF16), 각각 한 노드, 8×400 Gb/s NDR InfiniBand 패브릭에서 검증.
prefill 서버를 실행한 다음 decode 서버를 실행하세요 — --disaggregation-mode decode이고 bootstrap 포트가 없는 같은 레시피예요. --disaggregation-ib-device를 RDMA NIC에 지정하세요.
# Prefill server (node A)
sglang serve \
--model-path poolside/Laguna-M.1 \
--trust-remote-code \
--reasoning-parser poolside_v1 \
--tool-call-parser poolside_v1 \
--tp 8 \
--disaggregation-mode prefill \
--disaggregation-transfer-backend mooncake \
--disaggregation-ib-device mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_4,mlx5_5,mlx5_6,mlx5_7 \
--host 0.0.0.0 --port 30000 \
--disaggregation-bootstrap-port 8998
# Decode server (node B)
sglang serve \
--model-path poolside/Laguna-M.1 \
--trust-remote-code \
--reasoning-parser poolside_v1 \
--tool-call-parser poolside_v1 \
--tp 8 \
--disaggregation-mode decode \
--disaggregation-transfer-backend mooncake \
--disaggregation-ib-device mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_4,mlx5_5,mlx5_6,mlx5_7 \
--host 0.0.0.0 --port 30001
그런 다음 prefill bootstrap(URL + 해당 --disaggregation-bootstrap-port)과 decode 엔드포인트를 가리키며 PD 라우터를 시작하세요:
# PD router
python3 -m sglang_router.launch_router \
--pd-disaggregation \
--prefill http://<prefill-host>:30000 8998 \
--decode http://<decode-host>:30001 \
--policy round_robin \
--host 0.0.0.0 --port 8000
클라이언트는 라우터를 단일 서버처럼 정확히 호출해요 — 각 요청을 두 스테이지에 투명하게 분할해요:
client = OpenAI(base_url="http://
response = client.chat.completions.create( model="poolside/Laguna-M.1", messages=[{"role": "user", "content": "What is 2 + 2?"}], max_tokens=64, ) print(response.choices[0].message.content)
**출력 예시:**
```text
2 + 2 = 4
전송 백엔드 — mooncake(권장). mooncake는 --disaggregation-ib-device를 존중하고 등록 시점에 RDMA 연결을 설정하므로 첫 요청이 이미 빠릅니다(콜드 스타트 없음). 단일 NIC 또는 전체 8개로 작동하고, 8개 NIC 모두를 사용하면 TTFT가 낮아집니다(KV 페이로드에 더 많은 총 대역폭 — 긴 컨텍스트일수록 격차가 커짐). 8×H200(임의 isl=512 / osl=256, 16 동시)에서 약 717 tok/s 출력(약 2.2k tok/s 총), 평균 TTFT 244 ms, 평균 TPOT 17.7 ms를 제공했어요. 단일 mlx5_0 NIC로는 약 697 tok/s와 TTFT 287 ms(TPOT는 동일 — decode는 compute-bound).
전송 백엔드 — NIXL(작동하되 주의 사항 두 가지).
UCX_NET_DEVICES를 고정하면 NIXL은 품질과 정상 상태 속도에서 mooncake와 일치해요(약 720 tok/s, TTFT 230 ms, TPOT 17.7 ms). 한 가지 차이는 시작 후 첫 요청이 약 38초의 일회성 UCX 연결 콜드 스타트를 부담한다는 것(단일 포트든 8개 모두든 동일)이에요. 시작 후 한 번의 던져버리는 요청으로 경로를 웜업하거나, SGLANG_DISAGGREGATION_WAITING_TIMEOUT(기본 300s)을 올려 UCX가 연결되는 동안 첫 실제 요청이 떨어지지 않게 하세요.
검증. PD disaggregation은 출력 품질을 보존해요 — disaggregated 출력은 non-disaggregated 서빙과 일치하며, GSM8K(no-thinking, 라우터 통한 200-질문 부분집합)는 0.945(mooncake, 8 NIC) / 0.940(NIXL) / 0.950(mooncake, 1 NIC)을 기록했고, 모두 100% stop-rate와 0% 오류 — 단일 노드 BF16(전체 분할에서 약 0.93)과 부합해요. 로그가 분할을 확인해 줘요: prefill 노드는 Prefill batch(CUDA graph 꺼짐)를, decode 노드는 Decode batch(CUDA graph 켜짐)를 기록해요.