文档编写指南#

模型教程文档编写指南#

docs/source/_templates/Model-Deployment-Tutorial-Template.md 是编写模型部署教程的模板。您可以复制并修改它以创建新文档。

可测试文档代码块生成(model-code#

  • 对于文档作者:如何在文档中插入可测试的命令块

  • 对于开发者:如何添加新的转换器

内置支持的 converter_tag 值:

converter_tag

渲染内容

YAML 源

single_node

单个节点的环境变量导出 + vllm serve 脚本

test_cases[case_index]

multi_node

单个主机的环境变量导出 + vllm serve 脚本

deployment[host_index]

external_dp_template

单个外部 DP 节点的环境变量导出 + vllm serve 命令

templates[host_index]

external_dp_launch

每个节点一行 launch_online_dp.py

config[]

external_dp_proxy

负载均衡代理启动命令

config[] + routing

作者指南:添加代码块#

重要

默认情况下,生成器仅扫描 docs/source/tutorials/models/ 下的 .md 文件并生成产物。如果您将 model-code 块放在其他目录中,Sphinx 构建将不会自动生成相应的脚本。

所有 model-code 块需要:

选项

必需

描述

block_name

块名称;在当前文档中必须唯一

converter_tag

选择内置转换器之一

test_case_path

仓库内相对 YAML 路径;文件必须存在

使用块体添加 shell 包装行,例如 set -eux。始终将 {{ generated }} 占位符放置在转换器输出应插入的位置。

converter_tag: single_node#

single_nodetest_cases 中读取一个条目。可选的 case_index 元数据选择条目;省略时默认为 0

只有此转换器读取的字段会在下面展开。其他测试元数据可以保留在 YAML 中,此转换器会忽略它们。

test_cases:
  - name: qwen3-8b-single
    model: Qwen/Qwen3-8B
    envs:
      HCCL_BUFFSIZE: "1024"
      SERVER_PORT: DEFAULT_PORT
    server_cmd:
      - --tensor-parallel-size
      - "1"
      - --port
      - $SERVER_PORT
      - --trust-remote-code
    server_cmd_extra:
      - --enable-expert-parallel
    benchmarks: ...

envs 渲染为 export 行。SERVER_PORT: DEFAULT_PORT 解析为默认单节点端口 8000model 变为 vllm serve <model>server_cmd 加上可选的 server_cmd_extra 成为命令参数。两个命令字段可以是 shell 字符串或扁平令牌列表。

按如下方式编写文档块:

```{model-code}
:block_name: qwen3_8b_single_node
:converter_tag: single_node
:test_case_path: tests/e2e/nightly/single_node/models/configs/your_model.yaml
:case_index: 0

set -eux
{{ generated }}
```

生成的 shell 脚本:

set -eux
export HCCL_BUFFSIZE=1024
export SERVER_PORT=8000

vllm serve Qwen/Qwen3-8B \
  --tensor-parallel-size 1 \
  --port $SERVER_PORT \
  --trust-remote-code \
  --enable-expert-parallel

converter_tag: multi_node#

multi_nodedeployment 中读取一个条目。必需的 host_index 元数据选择要渲染的主机。

deployment:
  - envs:
      SERVER_PORT: "8000"
    server_cmd: >
      vllm serve Qwen/Qwen3-235B-A22B
      --host 0.0.0.0
      --port $SERVER_PORT
      --data-parallel-size 2
      --tensor-parallel-size 8
      --data-parallel-address $LOCAL_IP
  - envs:
      SERVER_PORT: "8000"
    server_cmd: >
      vllm serve Qwen/Qwen3-235B-A22B
      --headless
      --port $SERVER_PORT
      --data-parallel-size 2
      --tensor-parallel-size 8
      --data-parallel-start-rank 1
      --data-parallel-address $MASTER_IP
benchmarks: ...

server_cmd 必须是完整的命令,以 vllm serve <model> 开头。它可以写成 shell 字符串或扁平令牌列表。

按如下方式编写文档块:

```{model-code}
:block_name: qwen3_235b_worker_1
:converter_tag: multi_node
:test_case_path: tests/e2e/nightly/multi_node/internal_dp/config/your_model.yaml
:host_index: 1

set -eux
{{ generated }}
```

host_index: 1 生成的 shell 脚本:

set -eux
export MASTER_IP=192.168.1.10
export SERVER_PORT=8000

vllm serve Qwen/Qwen3-235B-A22B \
  --headless \
  --port $SERVER_PORT \
  --data-parallel-size 2 \
  --tensor-parallel-size 8 \
  --data-parallel-start-rank 1 \
  --data-parallel-address $MASTER_IP

converter_tag: external_dp_template#

external_dp_templatetemplates 中读取一个条目。必需的 host_index 元数据选择要渲染的模板。顶级 model 字段也是必需的,因为转换器构建 vllm serve <model>

model: Eco-Tech/GLM-Test
templates:
  - node_index: 0
    envs:
      HCCL_BUFFSIZE: "1024"
      ASCEND_RT_VISIBLE_DEVICES: "${VISIBLE_DEVICES}"
    server_cmd_template:
      - --host
      - 0.0.0.0
      - --port
      - ${PORT}
      - --data-parallel-size
      - ${DP_SIZE}
      - --data-parallel-rank
      - ${DP_RANK}
      - --data-parallel-address
      - ${DP_ADDRESS}
      - --data-parallel-rpc-port
      - ${DP_RPC_PORT}
      - --tensor-parallel-size
      - ${TP_SIZE}
      - --trust-remote-code
config: ...
routing: ...

已知的花括号模板变量被重写为 run_dp_template.shlaunch_online_dp.py 接收的位置 shell 参数:

模板变量

渲染后的位置参数

${VISIBLE_DEVICES}

$1

${PORT}

$2

${DP_SIZE}

$3

${DP_RANK}

$4

${DP_ADDRESS}

$5

${DP_RPC_PORT}

$6

${TP_SIZE}

$7

未知的花括号变量和无花括号的 shell 引用(如 $SERVER_PORT)保持不变。

按如下方式编写文档块:

```{model-code}
:block_name: glm_external_dp_template_node0
:converter_tag: external_dp_template
:test_case_path: tests/e2e/nightly/multi_node/external_dp/config/your_model.yaml
:host_index: 0

set -eux
export HCCL_IF_IP=$local_ip
export GLOO_SOCKET_IFNAME=$nic_name
export TP_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name

{{ generated }}
```

host_index: 0 生成的 shell 脚本:

set -eux
export HCCL_IF_IP=$local_ip
export GLOO_SOCKET_IFNAME=$nic_name
export TP_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name

export HCCL_BUFFSIZE=1024
export ASCEND_RT_VISIBLE_DEVICES=$1

vllm serve Eco-Tech/GLM-Test \
  --host 0.0.0.0 \
  --port $2 \
  --data-parallel-size $3 \
  --data-parallel-rank $4 \
  --data-parallel-address $5 \
  --data-parallel-rpc-port $6 \
  --tensor-parallel-size $7 \
  --trust-remote-code

converter_tag: external_dp_launch#

external_dp_launch 读取完整的 config 列表,并为每个节点渲染一个 launch_online_dp.py 命令。它不接受索引选项。

config:
  - node_index: 0
    port_start: 7100
    dp_rpc_port: 12321
    dp_size: 2
    dp_size_local: 2
    dp_rank_start: 0
    tp_size: 8
    dp_address: "${NODE_0_IP}"
  - node_index: 1
    port_start: 7200
    dp_rpc_port: 12321
    dp_size: 4
    dp_size_local: 4
    dp_rank_start: 0
    tp_size: 4
    dp_address: "${NODE_1_IP}"
templates: ...
routing: ...

按如下方式编写文档块:

```{model-code}
:block_name: glm_external_dp_launch
:converter_tag: external_dp_launch
:test_case_path: tests/e2e/nightly/multi_node/external_dp/config/your_model.yaml

set -eux
{{ generated }}
```

生成的 shell 脚本:

set -eux
python launch_online_dp.py --dp-size 2 --tp-size 8 --dp-size-local 2 --dp-rank-start 0 --dp-address ${NODE_0_IP} --dp-rpc-port 12321 --vllm-start-port 7100

python launch_online_dp.py --dp-size 4 --tp-size 4 --dp-size-local 4 --dp-rank-start 0 --dp-address ${NODE_1_IP} --dp-rpc-port 12321 --vllm-start-port 7200

converter_tag: external_dp_proxy#

external_dp_proxy 读取 configrouting。它为 routing.type: disaggregated_prefill 渲染 load_balance_proxy_server_example.py 命令。它不接受索引选项。

routing:
  type: disaggregated_prefill
  groups:
    prefiller: [0]
    decoder: [1]
config:
  - node_index: 0
    port_start: 7100
    dp_size_local: 2
    dp_rpc_port: 12321
    dp_size: 2
    dp_rank_start: 0
    tp_size: 8
    dp_address: "${NODE_0_IP}"
  - node_index: 1
    port_start: 7200
    dp_size_local: 4
    dp_rpc_port: 12321
    dp_size: 4
    dp_rank_start: 0
    tp_size: 4
    dp_address: "${NODE_1_IP}"
templates: ...

routing.groups.prefillerrouting.groups.decoder包含指向config的索引。每个被引用的节点会展开为dp_size_local个主机和端口条目。代理本身在${NODE_0_IP}:1999上呈现。

按如下方式编写文档块:

```{model-code}
:block_name: glm_external_dp_proxy
:converter_tag: external_dp_proxy
:test_case_path: tests/e2e/nightly/multi_node/external_dp/config/your_model.yaml

set -eux
{{ generated }}
```

生成的 shell 脚本:

set -eux
python load_balance_proxy_server_example.py \
  --host ${NODE_0_IP} \
  --port 1999 \
  --prefiller-hosts \
    ${NODE_0_IP} \
    ${NODE_0_IP} \
  --prefiller-ports \
    7100 \
    7101 \
  --decoder-hosts \
    ${NODE_1_IP} \
    ${NODE_1_IP} \
    ${NODE_1_IP} \
    ${NODE_1_IP} \
  --decoder-ports \
    7200 \
    7201 \
    7202 \
    7203

本地调试与生成#

仅生成(不构建完整站点)#

# Generate all model-code artifacts under docs/source/tutorials/models/
python3 tools/docs_codegen/cli.py

# Generate artifacts for a single document
python3 tools/docs_codegen/cli.py --doc docs/source/tutorials/models/Kimi-K2-Thinking.md

# Generate a single block and print it (no files written)
python3 tools/docs_codegen/cli.py \
  --block docs/source/tutorials/models/Kimi-K2-Thinking.md::kimi_k2_thinking_single_node \
  --dry-run --stdout

默认情况下,生成物写入:docs/_build/doc_codegen/<doc_stem>/<block_name>.sh

备注

脚本生成后,请务必检查生成内容是否可运行,特别是环境变量和命令行参数等关键部分。

构建站点并在本地预览#

# Install documentation build dependencies
python3 -m pip install -r docs/requirements-docs.txt

# (Optional) Clean previous builds
make -C docs clean

# Build the English site
make -C docs html

# (Optional) Build the Chinese site
make -C docs intl

# Preview locally
python3 -m http.server -d docs/_build/html 8000

# Then open in a browser:
# http://localhost:8000

面向开发者:添加新的转换器#

转换器将一个已加载的YAML文件加上一个解析后的ModelCodeBlock转换为GeneratedScript。当前流程为:

  1. BlockScanner解析model-code围栏,仅接受MODEL_CODE_OPTION_NAMES中列出的选项。

  2. YamlLoader加载test_case_path

  3. get_converter()build_default_converters()中查找block.converter_tag

  4. 选定的转换器返回GeneratedScript(content=..., language="shell")

  5. GeneratorService替换块体中的{{ generated }},验证最终脚本非空,并写入docs/_build/doc_codegen/<doc_stem>/<block_name>.sh

添加转换器的步骤:

  1. tools/docs_codegen/converters.py中,添加一个具有唯一nameBaseConverter子类。该名称即为作者在:converter_tag:中填入的值。

  2. 实现convert(self, loaded_yaml, *, block) -> GeneratedScript。使用make_docs_codegen_error(..., block=block)处理面向用户的验证错误,以便CLI和Sphinx输出包含文档上下文。

  3. 复用tools/docs_codegen/utils.py中的辅助函数,例如require_mappingrequire_mapping_listrequire_scalar_mappingrequire_indexed_mappingrequire_node_fieldparse_command_tokenssubstitute_template_positionalsrender_cli_command

  4. build_default_converters()中注册该转换器。如果未注册,get_converter()将拒绝新的converter_tag

  5. 如果转换器需要新的指令元数据,请将选项名称添加到tools/docs_codegen/scanner.py中的MODEL_CODE_OPTION_NAMES,以及tools/docs_codegen/sphinx_extension.py中的ModelCodeDirective.option_spec。使用block.get_option("<option_name>")读取该选项。

  6. tests/ut/tools/test_docs_codegen.py中添加或更新测试。覆盖成功渲染路径、必需选项验证、YAML结构验证,以及受新元数据影响的任何CLI/Sphinx扫描器行为。

  7. 在模型教程中添加一个真实的model-code示例,最好放在docs/source/tutorials/models/下,并将其指向tests/下的现有YAML文件。

  8. 使用CLI进行验证:

    python3 tools/docs_codegen/cli.py --doc <your_doc> --dry-run
    python3 tools/docs_codegen/cli.py --block <your_doc>::<block_name> --dry-run --stdout
    

如果转换器应渲染shell以外的内容,请相应设置GeneratedScript.language,以便Sphinx能正确高亮生成的文字块。