读 parser/ 与 function_call/:模板、reasoning 与 detector 树#

这章解决什么问题#

代码导读前面已经把 entrypoints/openaiprotocol.pyio_struct.pyobservability/disaggregation/models/ / layers/ 树都串起来了,但还有两棵和结构化生成强相关、同时又特别容易让人读散的目录没有正式导读:

  • python/sglang/srt/parser/
  • python/sglang/srt/function_call/

这一章的目标,就是把这两棵树重新收成一条稳健的阅读顺序。

为什么这两棵树容易读散#

因为它们都不是单一主题目录:

  • parser/ 里既有 conversation/template,也有 reasoning parser、harmony parser、code completion parser、jinja utilities
  • function_call/ 里既有通用 parser,也有很多模型专属 detector

如果没有导读,读者很容易在 detector 名字和模板工具之间来回跳,却始终不知道这两棵树在整本系统里的角色。

一张图:这两棵树分别站在“解释”和“检测”两侧#

这张图解决的理解障碍是:很多人会把 parser 和 function_call 混成一个目录层,而从系统角度看,它们分别承担不同工作。

flowchart LR
    Parser["parser/"] --> Surface["template / reasoning / conversation interpretation"]
    FC["function_call/"] --> Detect["tool-call detector / constraint builder"]
    Surface --> OpenAI["serving_chat / responses"]
    Detect --> OpenAI

这张图比纯文字多解释的一点是:一棵树更偏解释输入与上下文,一棵树更偏解释模型输出里的工具调用模式。

为什么应该先读 parser/#

因为它更像“进入 structured / tool / reasoning 语义之前的解释层”。

建议优先看:

  • conversation.py
  • jinja_template_utils.py
  • reasoning_parser.py
  • code_completion_parser.py

这样做的好处是:你先理解系统怎样解释 message/template/reasoning,再去看 function-call detector 如何从输出中识别结构。

function_call/function_call_parser.py 为什么是第二入口#

这相当于 function_call/ 树的总枢纽,因为它会:

  • 组织工具描述
  • 选择 detector
  • 生成 structure constraint
  • 在 streaming / non-streaming 路径里协助解析 tool call

也就是说,虽然目录里 detector 文件很多,但读的时候不应该先逐个模型 detector 地毯式扫描,而应先看总 parser 怎么分派它们。

模型专属 detector 应该怎样读#

目录里有很多 detector,例如:

  • deepseek*
  • qwen*
  • mistral*
  • gpt_oss*
  • llama*

这些文件更适合在你已经知道:

  • 某个模型为什么要特殊 detector
  • 该 detector 是由哪个总 parser 选中的

之后再按需进入。这样读起来,detector 文件会像“模型差异的落点”,而不是满目录平行的主线。

为什么这两棵树会和很多前文章节回扣#

它们几乎是前面多条桥的落点:

  • TemplateManager:解释 chat/completion template 的下游消费
  • 与结构化生成:解释 response_format、tool constraint 和 reasoning parser 的中间层
  • serving_chat.py / serving_responses.py:解释为什么这些 entrypoint 会依赖 parser / detector

所以把这两棵树导读出来,会明显增强整本书的“前后咬合感”。

更稳的阅读顺序#

建议这样读:

  1. parser/conversation.py
  2. parser/jinja_template_utils.py
  3. parser/reasoning_parser.py
  4. function_call/function_call_parser.py
  5. 最后按具体模型进入对应 detector

这条顺序的关键是:先总后分,先解释层后检测层。

这一层最容易出现的误判#

1. 直接从某个 detector 开始读#

这样很容易把模型差异误当成系统主线。

2. 把 parser/ 目录看成“只管模板”#

实际上它还覆盖 reasoning、code completion、harmony 等输入解释路径。

3. 把 function_call/ 看成输出后处理附录#

它实际上会直接影响约束生成和 tool-call 语义解释。

小结#

这一章真正要补齐的,是代码导读里对另一对重要源码树的稳定入口:

  • parser/ 更偏输入解释与上下文组织
  • function_call/ 更偏输出检测与约束生成
  • 两者都不该从最细文件开始,而应该先抓总 parser,再进具体变体

到这里,代码导读对结构化生成相关源码树也有了正式入口。