如何从入口开始读仓库#
这一节会把"第一次打开仓库应该先看哪里"这件事流程化,同时告诉你在每个文件里应该具体找什么。
为什么仓库入口顺序很重要#
如果你第一次进入 SGLang 仓库,最容易犯的错误是直接钻进 python/sglang/srt。这样当然能看到大量核心实现,但你会很快失去"哪些入口是给用户的,哪些入口是给 runtime 的"这层方位感。
第一站:README.md#
看什么:快速安装块(pip install sglang)和两个最小示例——一个展示 language API,一个展示 OpenAI-compatible serving。
你应该能答出来:
- SGLang 的语言 API(
sgl.function、sgl.gen())和 serving API(launch_server+ HTTP 请求)是两条平行入口,还是一条路径的两种写法?(答案:平行,两条独立入口) - 一个最简服务是一个进程还是多个进程?
读完 README,你对整个项目的外形应该有了基本感知。
第二站:python/sglang/__init__.py#
命令:
# 先看 __init__.py 暴露了什么
cat python/sglang/__init__.py | grep -v "^#" | grep -v "^$"看什么:__init__.py 里 from ... import 的符号就是项目对外承诺的 public API。
重点找这两类:
- 语言层符号:
function、gen、user、assistant、system、image等——这些是 language API 的构造块; - runtime 层符号:
Engine、launch_server、RuntimeEndpoint等——这些是 serving API 的入口。
看完之后你应该能画一条分界线:lang/ 下的是给 Python 程序调用的语言 API,srt/ 下的是 runtime 本体。
第三站:python/sglang/lang/api.py#
看什么:gen()、user()、system()、assistant() 这些函数的签名,以及 @function 装饰器怎样把一个 Python 函数变成 SGLang 程序。
关键问题:gen() 函数的 temperature、max_new_tokens 这些参数是直接传给模型,还是先被包装成一个中间对象?
# 找这类签名
def gen(name=None, max_new_tokens=128, temperature=1.0, ...):
...这里你会发现语言层并不直接执行,而是构造一个"待执行程序"的中间表示。这个延迟执行的设计解释了为什么 sgl.function 返回的是一个可以 .run() 的对象,而不是立刻执行的结果。
第四站:python/sglang/srt/entrypoints/http_server.py(或 launch_server.py)#
看什么:进程启动逻辑——server 启动时会创建哪些子进程,每个进程的角色是什么。
关键代码模式:找类似 mp.Process(target=...) 或 launch_xxx_process 的调用,以及它们的 target 参数指向哪个函数。
grep -n "mp.Process\|Process(" python/sglang/srt/launch_server.py | head -20读完这里你应该能确认:启动一个 SGLang server 涉及至少三个进程(TokenizerManager、Scheduler/Worker、DetokenizerManager),而不是单进程多线程。
进入 python/sglang/srt 之后的顺序#
有了上面四个入口的方位感之后,进入 srt/ 内部:
entrypoints/#
ls python/sglang/srt/entrypoints/找两类文件:
- HTTP 服务器入口(
http_server.py):路由是怎样分发的,/generate、/v1/chat/completions各自调用了哪个处理函数; - OpenAI-compatible 路由(
openai/目录):serving_chat.py、serving_completions.py各自的主要函数。
你应该能答出来:一个 POST /v1/chat/completions 请求,在 entrypoints/ 里经过哪些函数,最后调用了 TokenizerManager 的哪个方法?
managers/#
ls python/sglang/srt/managers/重点找三个文件:tokenizer_manager.py、scheduler.py、detokenizer_manager.py。
在 scheduler.py 里,先找两个方法:
event_loop_normal()(或类似名称):scheduler 的主循环;_get_next_batch_to_run():批次组织的核心函数。
这两个函数的存在说明 scheduler 是一个事件驱动的主循环,不是简单的请求队列处理器。
model_executor/#
ls python/sglang/srt/model_executor/找 model_runner.py,在里面找 forward() 方法的签名:它接受什么对象(ForwardBatch),返回什么(logits 或 sampling 结果)。
这里不需要读完 forward 的全部实现,只需要确认:执行层的输入边界是 ForwardBatch,而不是原始的 Req 对象。
mem_cache/#
ls python/sglang/srt/mem_cache/找 radix_cache.py,在里面找 match_prefix() 和 insert() 的签名。确认这两个操作的输入是 token id 序列,输出是 KV slot 索引。
docs/、examples/、test/ 不该放到最后#
docs/里有 benchmark 方法和部署建议,能帮你快速理解对外承诺的性能边界;examples/里的最小示例是验证自己理解的最快工具——读懂一个示例后跑起来,比看 500 行源码更容易建立方位感;test/srt/里的测试文件是维护者关于"什么行为必须成立"的显式声明:
ls test/srt/ | head -20
# 每个 test_*.py 文件名就是维护者认为需要长期回归的一类行为一个实际的探索顺序#
以下是从零开始读 SGLang 仓库的一个具体顺序:
Day 1: README → __init__.py → lang/api.py
确认语言层和 serving 层的边界
Day 2: launch_server.py 或 http_server.py
画出进程拓扑,确认 TokenizerManager/Scheduler/DetokenizerManager 各自的进程边界
Day 3: entrypoints/openai/serving_chat.py → tokenizer_manager.py
追一个 chat 请求从 HTTP 入口到 TokenizerManager 的完整路径
Day 4: scheduler.py 主循环和 _get_next_batch_to_run()
理解批次怎样被组织出来
Day 5: model_runner.py forward() + radix_cache.py match_prefix/insert
理解执行层和缓存层的边界这个顺序不是唯一路径,但它保证你每天都先建立外形,再往里推进,不会在第一天就陷进 attention kernel 实现细节。
小结#
从入口读仓库的价值不在于"先看了哪里",而在于每读一个文件时,都能明确回答一个问题:
| 文件 | 应该能回答的问题 |
|---|---|
README.md | 语言 API 和 serving API 是什么关系 |
__init__.py | public API 边界在哪里 |
lang/api.py | gen() 的延迟执行机制是什么 |
launch_server.py | 启动时有几个进程,各自的角色是什么 |
entrypoints/openai/ | 一个 HTTP 请求怎样进入 TokenizerManager |
managers/scheduler.py | 批次是怎样被组织出来的 |
model_runner.py | 执行层的输入边界是什么 |
radix_cache.py | KV 复用的操作接口是什么 |
答不出来就回去继续读,能回答了再往下一层走。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。