Tutorial SGLang: Serving LLM Cepat dan Pemrograman Structured Generation

# SGLang: Serving LLM yang Cepat dan Model Pemrograman untuk Generasi Terstruktur SGLang adalah dua hal dalam satu paket: sebuah runtime serving berthroughput tinggi dan sebuah bahasa frontend untuk...

By Ruby Abdullah · · tutorial
SGLangLLM ServingRadixAttentionStructured GenerationInferencePython

SGLang: Serving LLM yang Cepat dan Model Pemrograman untuk Generasi Terstruktur

SGLang adalah dua hal dalam satu paket: sebuah runtime serving berthroughput tinggi dan sebuah bahasa frontend untuk menulis program LLM yang terstruktur dan multi-langkah. Runtime-nya menarik karena RadixAttention, yang secara otomatis menggunakan ulang key-value cache antar request yang berbagi prefix, dan frontend-nya memungkinkan Anda mengekspresikan fork, output terbatas, dan alur kendali tanpa menjahit prompt secara manual. Tutorial ini membahas kedua pilar tersebut dengan satu contoh ekstraksi-dan-penalaran yang koheren dan dapat Anda adaptasi untuk beban kerja nyata.

Apa Itu SGLang

Memisahkan kedua lapisan ini sejak awal akan membantu, karena kebanyakan kebingungan tentang SGLang berasal dari mencampuradukkan keduanya.

Runtime adalah server yang Anda jalankan sekali lalu Anda query melalui HTTP. Ia menangani batching, penjadwalan, dan pengelolaan KV-cache di GPU. Anda bisa menggunakannya lewat API yang kompatibel dengan OpenAI tanpa pernah menyentuh bahasa frontend sama sekali. Bahasa frontend adalah DSL Python (tertanam sebagai decorator dan fungsi pembantu) untuk mendeskripsikan program generasi. Ia mengompilasi program Anda menjadi serangkaian panggilan terhadap sebuah backend, yang bisa berupa runtime SGLang lokal, atau penyedia eksternal seperti OpenAI. Di frontend inilah fitur seperti fork paralel dan decoding terbatas menjadi nyaman digunakan.

Dua pilar

Pilar 1 — runtime dengan RadixAttention. Continuous batching menjaga GPU tetap sibuk dengan menambah dan mengeluarkan request dari batch yang berjalan pada level token, alih-alih menunggu seluruh batch selesai. RadixAttention menambahkan penggunaan ulang prefix-cache secara otomatis: runtime memelihara sebuah radix tree berisi urutan token yang pernah dilihat beserta KV cache-nya, sehingga ketika sebuah request baru berbagi prefix (prompt sistem, blok few-shot, dokumen bersama) runtime melewati komputasi ulangnya. Pilar 2 — bahasa frontend. Alih-alih membangun satu string besar lalu mem-parsing respons, Anda menyusun sebuah program: menambahkan pesan, memanggil sgl.gen untuk mengisi slot, bercabang dengan s.fork, dan membatasi output dengan pilihan, regex, atau skema JSON. Frontend melacak state dan dapat menjalankan cabang-cabang independen secara paralel.

Bedanya dengan vLLM

Jika Anda pernah memakai vLLM, runtime ini akan terasa akrab: keduanya melakukan continuous batching, tensor parallelism, dan menyediakan API yang kompatibel dengan OpenAI. Perbedaan praktisnya adalah RadixAttention pada SGLang membuat beban kerja dengan prefix bersama (prompting few-shot, agen yang memakai ulang prompt sistem panjang, percabangan gaya tree-of-thought) menjadi lebih murah secara nyata, dan SGLang menyertakan bahasa frontend untuk mengorkestrasi program multi-langkah. vLLM fokus menjadi mesin serving; SGLang adalah mesin serving plus sebuah model pemrograman. Kami tidak akan menjelaskan ulang PagedAttention di sini.

Instalasi dan Catatan Perangkat Keras

Instalasi paling sederhana menarik runtime, frontend, dan dependensi umum.

# Disarankan: instal semuanya (runtime + frontend + ekstra)

pip install "sglang[all]"

Hanya frontend, jika Anda hanya ingin DSL terhadap backend remote

pip install "sglang[openai]"

Verifikasi

python -c "import sglang as sgl; print(sgl.version)"

Catatan perangkat keras yang perlu diingat:

  • Runtime menargetkan GPU NVIDIA dengan CUDA terkini. Ampere (A100) dan yang lebih baru (L4, L40S, H100) adalah jalur mulus; sebagian kernel mengasumsikan sm80 atau lebih tinggi.
  • Memori menentukan seberapa banyak KV cache yang bisa Anda tampung dan dengan demikian seberapa banyak RadixAttention dapat menggunakan ulang. Lebih banyak VRAM bebas berarti radix tree lebih besar dan tingkat cache-hit lebih tinggi.
  • Dukungan AMD ROCm ada tetapi kurang teruji; periksa matriks dukungan terkini sebelum berkomitmen.
  • Untuk multi-GPU, tensor parallelism (--tp) memecah bobot model antar GPU pada satu node.

# Pemeriksaan kewarasan lingkungan secara cepat

python -c "import torch; print('cuda', torch.cuda.isavailable(), 'gpus', torch.cuda.devicecount())"

nvidia-smi --query-gpu=name,memory.total,memory.used --format=csv

Menjalankan Server

Server cukup satu perintah. Mulailah dengan model kecil untuk memastikan instalasi, lalu naikkan skalanya.

python -m sglang.launchserver \

--model-path meta-llama/Llama-3.1-8B-Instruct \

--port 30000 \

--host 0.0.0.0

Flag yang umum Anda butuhkan:

# Multi-GPU dengan tensor parallelism (4 GPU pada satu node)

python -m sglang.launchserver \

--model-path meta-llama/Llama-3.1-70B-Instruct \

--tp 4 \

--port 30000

Kuantisasi agar model lebih besar muat di GPU yang lebih kecil

python -m sglang.launchserver \

--model-path meta-llama/Llama-3.1-8B-Instruct \

--quantization fp8 \

--port 30000

Atur panjang konteks dan anggaran memori KV-cache

python -m sglang.launchserver \

--model-path meta-llama/Llama-3.1-8B-Instruct \

--context-length 8192 \

--mem-fraction-static 0.85 \

--port 30000

Beberapa catatan tentang flag ini:

  • --tp N membutuhkan N GPU yang terlihat; kombinasikan dengan CUDAVISIBLEDEVICES untuk menambatkan perangkat tertentu.
  • --quantization menerima nilai seperti fp8, awq, dan gptq. Bobot terkuantisasi mengurangi memori dan dapat menaikkan throughput dengan biaya kualitas yang biasanya kecil.
  • --context-length menetapkan panjang urutan maksimum; menaikkannya memperbesar jejak KV-cache per request.
  • --mem-fraction-static memesan sebagian VRAM untuk pool KV statis. Turunkan jika Anda mengalami out-of-memory saat startup; naikkan untuk memberi RadixAttention lebih banyak ruang cache.

Setelah berjalan, pemeriksaan kesehatan memastikan kesiapan:

curl http://localhost:30000/health

Query lewat API yang Kompatibel dengan OpenAI

Server mengekspos /v1/chat/completions dan /v1/completions. Klien OpenAI apa pun bisa dipakai; cukup arahkan ke base URL lokal.

curl http://localhost:30000/v1/chat/completions \

-H "Content-Type: application/json" \

-d '{

"model": "meta-llama/Llama-3.1-8B-Instruct",

"messages": [

{"role": "system", "content": "Anda asisten yang ringkas."},

{"role": "user", "content": "Sebutkan tiga penyebab GPU OOM saat serving."}

],

"temperature": 0.2,

"maxtokens": 256

}'

Endpoint yang sama dari klien Python OpenAI:

from openai import OpenAI

client = OpenAI(baseurl="http://localhost:30000/v1", apikey="EMPTY")

resp = client.chat.completions.create(

model="meta-llama/Llama-3.1-8B-Instruct",

messages=[

{"role": "system", "content": "Anda asisten yang ringkas."},

{"role": "user", "content": "Ringkas RadixAttention dalam satu kalimat."},

],

temperature=0.2,

maxtokens=128,

)

print(resp.choices[0].message.content)

Streaming bekerja sama seperti pada OpenAI:

stream = client.chat.completions.create(

model="meta-llama/Llama-3.1-8B-Instruct",

messages=[{"role": "user", "content": "Jelaskan continuous batching secara singkat."}],

stream=True,

)

for chunk in stream:

delta = chunk.choices[0].delta.content

if delta:

print(delta, end="", flush=True)

Endpoint Generate Native dan Klien SGLang

Selain permukaan OpenAI, server memiliki endpoint /generate native yang mengekspos parameter sampling khas SGLang secara langsung.

curl http://localhost:30000/generate \

-H "Content-Type: application/json" \

-d '{

"text": "Tiga pilar observability adalah",

"samplingparams": {"temperature": 0.0, "maxnewtokens": 64}

}'

Untuk memakai bahasa frontend terhadap runtime ini, arahkan klien SGLang ke endpoint server.

import sglang as sgl

Ikat frontend ke runtime lokal

backend = sgl.RuntimeEndpoint("http://localhost:30000")

sgl.setdefaultbackend(backend)

Mulai dari sini, fungsi-fungsi frontend di bawah akan dieksekusi terhadap runtime tersebut.

Bahasa Frontend Secara Mendalam

Frontend mengubah program generasi menjadi fungsi Python biasa. Blok bangunan utamanya:

  • @sgl.function mendekorasi sebuah fungsi yang argumen pertamanya adalah state program s.
  • s += ... menambahkan teks atau peran pesan ke state.
  • sgl.user(...), sgl.assistant(...), sgl.system(...) menandai peran chat.
  • sgl.gen("name", ...) mengisi slot bernama dengan menghasilkan token; hasilnya dibaca kembali lewat s["name"].
  • s.fork(n) membuat salinan paralel dari state untuk cabang-cabang independen.

Contoh minimal:

import sglang as sgl

@sgl.function

def explain(s, topic):

s += sgl.system("Anda penulis teknis yang presisi.")

s += sgl.user(f"Jelaskan {topic} dalam dua kalimat.")

s += sgl.assistant(sgl.gen("answer", maxtokens=120, temperature=0.2))

state = explain.run(topic="RadixAttention")

print(state["answer"])

Output terbatas: pilihan, regex, dan skema JSON

Decoding terbatas membatasi token yang boleh dipancarkan model, sehingga output cukup andal untuk di-parse.

@sgl.function

def classify(s, ticket):

s += sgl.system("Klasifikasikan tiket dukungan.")

s += sgl.user(ticket)

# Model harus memilih tepat satu dari label berikut

s += sgl.assistant(sgl.gen("label", choices=["billing", "bug", "feature", "other"]))

state = classify.run(ticket="Aplikasi crash saat saya mengunggah PDF.")

print(state["label"]) # salah satu dari empat pilihan, dijamin

Batasan regex mengunci bentuk permukaan sebuah nilai:

@sgl.function

def extractphone(s, text):

s += sgl.user(f"Ekstrak nomor telepon dari: {text}")

s += sgl.assistant(

sgl.gen("phone", regex=r"\+?\d{1,3}[- ]?\d{3,4}[- ]?\d{3,4}")

)

Untuk record terstruktur, batasi ke skema JSON agar output langsung ter-parse pada percobaan pertama:

import json

productschema = json.dumps({

"type": "object",

"properties": {

"name": {"type": "string"},

"price": {"type": "number"},

"instock": {"type": "boolean"},

},

"required": ["name", "price", "instock"],

})

@sgl.function

def torecord(s, description):

s += sgl.user(f"Konversi ke JSON: {description}")

s += sgl.assistant(sgl.gen("record", maxtokens=128, jsonschema=productschema))

state = torecord.run(description="Mug Helix berharga 12.50 dan tersedia.")

record = json.loads(state["record"])

print(record["name"], record["price"], record["instock"])

Fork dan paralelisme

s.fork menjalankan cabang-cabang independen yang dapat dieksekusi runtime secara bersamaan. Karena semua cabang berbagi prefix yang sama, RadixAttention menghitung prefix itu sekali dan menggunakan ulang KV cache-nya di seluruh fork.
@sgl.function

def multiperspective(s, question):

s += sgl.system("Jawab dengan hati-hati.")

s += sgl.user(question)

forks = s.fork(3)

for f in forks:

f += sgl.assistant(sgl.gen("view", maxtokens=100, temperature=0.8))

# Gabungkan tiga draf independen kembali ke parent

drafts = [f["view"] for f in forks]

s += sgl.user("Berikut tiga draf:\n" + "\n---\n".join(drafts))

s += sgl.user("Tulis satu jawaban yang dikonsolidasikan.")

s += sgl.assistant(sgl.gen("final", maxtokens=160))

state = multiperspective.run(question="Bagaimana cara menentukan ukuran KV cache untuk layanan chat?")

print(state["final"])

Contoh Koheren: Ekstraksi Terstruktur Multi-Langkah

Mari satukan semua bagian menjadi satu program yang menyerupai tugas agentic nyata: membaca laporan insiden, memutuskan apakah perlu eskalasi, mengekstrak field terstruktur, dan menghasilkan catatan remediasi. Ini adalah pola branch-solve-merge dengan prefix bersama.

import json

import sglang as sgl

sgl.setdefaultbackend(sgl.RuntimeEndpoint("http://localhost:30000"))

incidentschema = json.dumps({

"type": "object",

"properties": {

"service": {"type": "string"},

"severity": {"type": "string", "enum": ["low", "medium", "high"]},

"rootcause": {"type": "string"},

},

"required": ["service", "severity", "rootcause"],

})

@sgl.function

def triageincident(s, report):

# Prefix bersama: instruksi sistem dipakai ulang di setiap request.

# RadixAttention meng-cache ini sekali saja.

s += sgl.system(

"Anda asisten SRE. Bersikaplah presisi dan hindari spekulasi."

)

s += sgl.user(f"Laporan insiden:\n{report}")

# Langkah 1: keputusan routing yang terbatas

s += sgl.assistant(

"Perlu eskalasi: "

  • sgl.gen("escalate", choices=["yes", "no"])
)

# Langkah 2: ekstrak field terstruktur dengan skema JSON

s += sgl.user("Ekstrak field terstruktur sebagai JSON.")

s += sgl.assistant(sgl.gen("fields", maxtokens=200, jsonschema=incidentschema))

# Langkah 3: bercabang hanya ketika eskalasi diperlukan

if s["escalate"] == "yes":

s += sgl.user("Buat catatan remediasi singkat untuk engineer on-call.")

s += sgl.assistant(sgl.gen("note", maxtokens=180))

reporttext = (

"Pada 02:14 checkout-api mengembalikan 500 selama ~8 menit. "

"Log error menunjukkan kehabisan connection pool ke DB orders."

)

state = triageincident.run(report=reporttext)

print("escalate:", state["escalate"])

print("fields:", json.loads(state["fields"]))

if state["escalate"] == "yes":

print("note:", state["note"])

Yang membuatnya efisien: prompt sistem dan laporan adalah prefix bersama. Jika Anda menjalankan program ini atas sekumpulan insiden yang berbagi instruksi yang sama, token instruksi dihitung sekali lalu dipakai ulang. Slot escalate yang terbatas menjamin keluaran yes/no yang dapat di-parse, dan slot skema JSON menjamin field ter-parse tanpa pembersihan string defensif.

Mengapa RadixAttention Membantu Beban Kerja Prefix Bersama

Biaya inferensi LLM didominasi oleh atensi terhadap KV cache. Ketika banyak request berbagi token awal — prompt sistem panjang, blok few-shot, dokumen hasil retrieval — menghitung ulang token-token itu untuk setiap request adalah pekerjaan yang terbuang. RadixAttention menyimpan KV cache berkunci urutan token dalam radix tree, sehingga request baru yang cocok dengan prefix yang sudah ada menggunakan ulang KV ter-cache dan hanya menghitung sufiks barunya.

Inilah sebabnya prompting few-shot dan loop agen paling diuntungkan: contoh few-shot atau instruksi tetap milik agen membentuk prefix panjang yang stabil dan dipakai ulang pada setiap panggilan. fork pada frontend membuatnya eksplisit — cabang-cabang berbagi prefix secara konstruktif — tetapi manfaatnya juga berlaku untuk request API biasa yang kebetulan berbagi teks awal.

Membatch Banyak Request dengan runbatch

Ketika Anda punya banyak input untuk program yang sama, runbatch mengirimkannya bersama-sama sehingga runtime dapat menjadwalkan dan meng-cache lintas input.

reports = [

{"report": "Latensi DB melonjak ke 4d selama jendela backup malam."},

{"report": "Frontend 404 setelah deploy terakhir; path aset berubah."},

{"report": "CPU layanan auth 95% di bawah trafik normal."},

]

states = triageincident.runbatch(

reports,

progressbar=True,

)

for st in states:

print(st["escalate"], json.loads(st["fields"])["severity"])

Karena setiap item berbagi prompt sistem yang sama, RadixAttention memakai ulang prefix itu di seluruh batch, dan continuous batching menjaga GPU tetap jenuh.

Multimodal dan Embeddings (Ringkas)

Model vision. SGLang melayani model vision-language (seperti model gaya Llava dan gaya Qwen-VL) melalui server yang sama. Jalankan dengan path model multimodal, lalu lewatkan gambar dalam konten pesan chat memakai format image-URL OpenAI. Frontend mengekspos sgl.image(...) untuk menyisipkan gambar ke dalam program.
@sgl.function

def describe(s, imagepath):

s += sgl.user(sgl.image(imagepath) + "Deskripsikan gambar ini dalam satu kalimat.")

s += sgl.assistant(sgl.gen("caption", maxtokens=64))

Embeddings. Jalankan server dengan --is-embedding untuk model embedding, lalu panggil endpoint /v1/embeddings (kompatibel dengan OpenAI) untuk memperoleh vektor bagi retrieval atau clustering.
python -m sglang.launchserver \

--model-path BAAI/bge-large-en-v1.5 \

--is-embedding --port 30000

Benchmarking dan Catatan Berimbang SGLang-vs-vLLM

SGLang menyertakan harness benchmarking. Jalankan terhadap server yang aktif untuk mengukur throughput dan latensi pada distribusi request yang dipilih.

python -m sglang.benchserving \

--backend sglang \

--host 127.0.0.1 --port 30000 \

--dataset-name random \

--num-prompts 1000 \

--random-input-len 1024 \

--random-output-len 256

Baca output sebagai distribusi, bukan angka tunggal: perhatikan throughput request, throughput token output, serta persentil time-to-first-token dan latensi per token (p50/p99).

Pandangan berimbang SGLang vs vLLM:

  • Untuk beban kerja dengan banyak pembagian prefix — prompting few-shot, agen dengan instruksi tetap yang panjang, percabangan gaya tree — RadixAttention SGLang sering menang pada throughput dan latensi karena melewati komputasi ulang.
  • Untuk beban kerja yang tiap request-nya pada dasarnya unik dengan sedikit prefix bersama, kedua runtime lebih berdekatan; keduanya mengandalkan continuous batching dan kernel atensi yang efisien, dan pemenangnya bergantung pada model, perangkat keras, dan versi.
  • Jika Anda butuh model pemrograman untuk generasi multi-langkah, terbatas, atau paralel, frontend SGLang adalah pembeda nyata dan mengurangi kode perekat aplikasi.
  • Benchmark pada trafik Anda sendiri. Angka sintetis jarang berpindah mulus ke bentuk prompt produksi.

Praktik Terbaik

  • Letakkan teks yang stabil dan dapat dipakai ulang (prompt sistem, contoh few-shot, dokumen bersama) di depan setiap prompt agar RadixAttention dapat meng-cache-nya. Konten variabel diletakkan paling akhir.
  • Gunakan decoding terbatas (choices, regex, jsonschema) setiap kali Anda berniat mem-parsing output. Ini menghilangkan satu kelas bug pembersihan string yang rapuh.
  • Utamakan runbatch untuk beban kerja offline atau massal; ia memberi penjadwal dan cache materi terbanyak untuk dikerjakan.
  • Setel --mem-fraction-static secara sengaja. Terlalu rendah membuang kapasitas cache; terlalu tinggi berisiko OOM pada konteks panjang. Pantau nvidia-smi saat uji beban.
  • Jaga struktur program frontend tetap deterministik. Bercabanglah pada output terbatas (seperti yes/no di atas) alih-alih teks bebas, agar alur kendali tetap dapat diprediksi.
  • Pisahkan tanggung jawab: gunakan API kompatibel OpenAI untuk integrasi chat sederhana, dan beralih ke bahasa frontend hanya ketika Anda benar-benar butuh fork, batasan, atau orkestrasi multi-langkah.
  • Pin versi SGLang di produksi dan jalankan ulang benchserving setelah upgrade; perubahan kernel dan penjadwal dapat menggeser performa.

Kesimpulan dan Poin Utama

SGLang paling baik dipahami sebagai sebuah runtime serving dan model pemrograman yang kebetulan dirilis bersama. RadixAttention pada runtime mengubah prefix bersama dari biaya berulang menjadi biaya sekali bayar, yang persis bentuk beban kerja few-shot dan agentic. Bahasa frontend memungkinkan Anda menulis beban kerja itu sebagai program Python yang mudah dibaca dengan fork, output terbatas, dan alur kendali multi-langkah, alih-alih mengelola string prompt secara manual.

Poin utama:

  • Dua pilar: runtime continuous-batching dengan prefix caching RadixAttention, dan DSL frontend untuk generasi terstruktur.
  • Instal dengan pip install "sglang[all]"; jalankan dengan python -m sglang.launchserver; query lewat API kompatibel OpenAI atau endpoint /generate native.
  • @sgl.function, sgl.gen, pembantu peran, s.fork, dan decoding terbatas pada frontend membuat program multi-langkah ringkas dan andal.
  • RadixAttention paling terbayar ketika request berbagi prefix panjang; letakkan teks stabil paling depan untuk memaksimalkan cache hit.
  • Gunakan runbatch untuk pekerjaan massal dan sglang.benchserving untuk memvalidasi performa pada trafik Anda sendiri sebelum menyimpulkan SGLang versus vLLM.

Artikel Terkait

Fireworks AI: Platform Inference Super Cepat buat Open LLM dan Model Multimodal

Fireworks AI: Platform Inference Super Cepat buat Open LLM dan Model Multimodal Halo temen-temen, di tutorial kali ini a...

Tutorial Text Generation Inference (TGI): Serving LLM untuk Produksi

Menyajikan LLM di Produksi dengan Text Generation Inference (TGI) Text Generation Inference (TGI) adalah toolkit buatan ...

Outlines: Structured Generation dari LLM dengan Constrained Decoding

Outlines: Structured Generation dari LLM dengan Constrained Decoding Salah satu tantangan terbesar saat bekerja dengan L...

Tutorial Lengkap vLLM: High-Performance LLM Serving

Tutorial Lengkap vLLM: High-Performance LLM Serving vLLM adalah library Python untuk inference dan serving LLM dengan pe...