如何从入口开始读仓库#

这一节会把"第一次打开仓库应该先看哪里"这件事流程化,同时告诉你在每个文件里应该具体找什么。

为什么仓库入口顺序很重要#

如果你第一次进入 SGLang 仓库,最容易犯的错误是直接钻进 python/sglang/srt。这样当然能看到大量核心实现,但你会很快失去"哪些入口是给用户的,哪些入口是给 runtime 的"这层方位感。

第一站:README.md#

看什么:快速安装块(pip install sglang)和两个最小示例——一个展示 language API,一个展示 OpenAI-compatible serving。

你应该能答出来

  • SGLang 的语言 API(sgl.functionsgl.gen())和 serving API(launch_server + HTTP 请求)是两条平行入口,还是一条路径的两种写法?(答案:平行,两条独立入口)
  • 一个最简服务是一个进程还是多个进程?

读完 README,你对整个项目的外形应该有了基本感知。

第二站:python/sglang/__init__.py#

命令

# 先看 __init__.py 暴露了什么
cat python/sglang/__init__.py | grep -v "^#" | grep -v "^$"

看什么__init__.pyfrom ... import 的符号就是项目对外承诺的 public API。

重点找这两类:

  1. 语言层符号functiongenuserassistantsystemimage 等——这些是 language API 的构造块;
  2. runtime 层符号Enginelaunch_serverRuntimeEndpoint 等——这些是 serving API 的入口。

看完之后你应该能画一条分界线:lang/ 下的是给 Python 程序调用的语言 API,srt/ 下的是 runtime 本体。

第三站:python/sglang/lang/api.py#

看什么gen()user()system()assistant() 这些函数的签名,以及 @function 装饰器怎样把一个 Python 函数变成 SGLang 程序。

关键问题gen() 函数的 temperaturemax_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.pyserving_completions.py 各自的主要函数。

你应该能答出来:一个 POST /v1/chat/completions 请求,在 entrypoints/ 里经过哪些函数,最后调用了 TokenizerManager 的哪个方法?

managers/#

ls python/sglang/srt/managers/

重点找三个文件:tokenizer_manager.pyscheduler.pydetokenizer_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__.pypublic API 边界在哪里
lang/api.pygen() 的延迟执行机制是什么
launch_server.py启动时有几个进程,各自的角色是什么
entrypoints/openai/一个 HTTP 请求怎样进入 TokenizerManager
managers/scheduler.py批次是怎样被组织出来的
model_runner.py执行层的输入边界是什么
radix_cache.pyKV 复用的操作接口是什么

答不出来就回去继续读,能回答了再往下一层走。