SGLang 到底是什么#
这一节先解决整本书最基础的问题:SGLang 到底应该被看成什么系统。只有这个问题先回答清楚,后面的请求路径、调度器和执行壳才不会被误读成彼此独立的局部实现。
它首先不是一个单一人格的项目#
很多系统对外只有一种主要形态,例如:
- 一个单纯的 HTTP server
- 一个单纯的 SDK
- 一个单纯的离线 engine
SGLang 不是这样。只要顺着最外层入口看一遍,你很快就会发现它同时暴露了三种不同的人格:
- language API
python/sglang/lang/api.py里的gen(...)、user(...)、assistant(...)、system(...) - runtime / engine API
Engine、RuntimeEndpoint、ServerArgs - serving surface
sglang serve、python -m sglang.launch_server、OpenAI-compatible HTTP 接口
也就是说,SGLang 不是”先有一个 server,后来顺手加了点 API”;它从一开始就是一套同时面向 prompt program、runtime 承载和服务暴露的系统。
三种入口的实际代码对比#
先看三段可以真正运行的代码,再回来看它们共享什么。
入口一:Language API(描述式编程)
用 @sgl.function 把生成逻辑写成程序:
import sglang as sgl
@sgl.function
def classify_sentiment(s, text):
s += sgl.system(“You are a classifier. Reply with one word: positive/negative/neutral.”)
s += sgl.user(text)
s += sgl.assistant(sgl.gen(“label”, max_new_tokens=5, regex=r”positive|negative|neutral”))
state = classify_sentiment.run(text=”The food was amazing!”, backend=sgl.RuntimeEndpoint(“http://localhost:30000”))
print(state[“label”]) # → “positive”关键特征:@sgl.function 把生成意图(包括结构化约束 regex)直接写成 Python 函数,执行时异步分派给 runtime。
入口二:Engine API(进程内嵌入)
不启动 HTTP server,直接在进程内持有 Engine:
from sglang import Engine
engine = Engine(model_path=”meta-llama/Llama-3.2-1B-Instruct”, tp_size=1)
output = engine.generate(
prompt=”Translate to French: Hello world”,
sampling_params={“temperature”: 0.0, “max_new_tokens”: 50}
)
print(output[“text”]) # → “Bonjour le monde”
engine.shutdown()关键特征:没有网络请求,模型直接跑在当前进程里,适合在 Python 测试或 notebook 里直接调用。
入口三:OpenAI-compatible HTTP(服务化)
先启动 server(python -m sglang.launch_server --model-path meta-llama/Llama-3.2-1B-Instruct),再用 OpenAI SDK 请求:
from openai import OpenAI
client = OpenAI(base_url=”http://localhost:30000/v1”, api_key=”EMPTY”)
response = client.chat.completions.create(
model=”meta-llama/Llama-3.2-1B-Instruct”,
messages=[{“role”: “user”, “content”: “Translate to French: Hello world”}],
max_tokens=50,
temperature=0.0,
)
print(response.choices[0].message.content) # → “Bonjour le monde”关键特征:标准 OpenAI 接口,适合对接已有的 LLM 工具链,不需要修改 client 侧代码。
三者共享同一套内部主链
这三种入口虽然写法完全不同,但在 python/sglang/srt/ 里,它们最终都流经同一条内部主链:
请求 → TokenizerManager → Scheduler → ModelRunner → DetokenizerManager → 响应Language API 会把 @sgl.function 的调用翻译成 GenerateReqInput 发给这条主链;
Engine API 绕过 HTTP 直接调 Scheduler;
HTTP serving 则通过 TokenizerManager 进入。
入口的写法不同,不变的是调度、执行和 KV cache 的核心逻辑。
为什么这个定义很重要#
如果一开始把 SGLang 简化成“高性能推理服务”,你后面读到这些东西时就会很别扭:
- 为什么
gen(...)里会直接出现regex、json_schema - 为什么
Runtime(...)和Engine(...)都是正式入口 - 为什么 HTTP 接口之外,还要有一整套语言层抽象
这不是因为项目“不够纯粹”,而是因为它本来就在把“描述生成过程”和“承载生成过程”放进同一套系统里。
从工程角度说,这个设计的收益很明显:
- 语言层 API 可以直接表达结构化生成意图;
- runtime 层可以复用同一套调度、cache 和执行主链;
- serving 层只是在这些能力之外再加一层对外协议表面。
代价也同样明显:你读源码时不能只带着“HTTP 请求怎样走”的心智模型,还要同时接受 language API 和 engine API 也是同一系统的正式部分。
最外层应该先看哪些入口#
如果你想先建立最外层地图,最稳的顺序通常是:
README.mdpython/sglang/__init__.pypython/sglang/lang/api.pypython/sglang/launch_server.py
这样做的原因很简单:
README.md说明项目怎样向外介绍自己;__init__.py说明 public surface 暴露了哪些符号;lang/api.py说明 language API 在组织什么能力;launch_server.py说明服务入口怎样分流到不同模式。
只要先把这四个入口看顺,后面再进入 python/sglang/srt/* 时,就不会把所有模块都当成“server 内部实现”。
为什么这本书要从这里起步#
一本像样的系统书,开头最怕的不是“讲得慢”,而是“问题坐标没立住”。如果系统定义模糊,后面每章都可能成立,但整本书仍然会像几篇彼此相关的专题文章,而不像一条稳定主线。
这一节的作用,就是先把最外层定义钉住:
- 它不是只靠 HTTP 存活的 server;
- 它也不是只有 prompt DSL 的 frontend;
- 它是一套把 language API、runtime API 和 serving surface 重新汇合起来的系统。
后面的请求路径、运行时架构、调度与执行模型,都是站在这个定义之上继续往里压的。
小结#
对这本书来说,“SGLang 到底是什么”不是礼貌性的开场,而是后文是否还能被读成同一本书的前提。只要这个定义先稳住,后面再看到 TokenizerManager、ScheduleBatch、ForwardBatch 或 Responses API,读者就更容易知道它们是在服务同一套系统,而不是散落在不同产品里的局部实现。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。