NLP——vLLM使用相关笔记


vLLM 采样参数:SamplingParams

  • SamplingParams 是控制模型“如何生成”的核心对象,常常包含下面的几个参数
    • n : 每个输入提示生成的输出序列数量(默认为 1)
    • best_of : 从生成的一组序列中选择最好的 k 个(用于集束搜索等)
    • temperature : 采样温度,控制随机性;0 表示贪心采样(确定性),值越高越随机
    • top_p : 核采样概率阈值,控制候选词的累积概率
    • top_k : 仅从概率最高的 k 个 token 中采样
    • max_tokens : 每个输出序列生成的最大 token 数
    • stop : 停止生成的字符串列表(遇到这些词即停止)
    • ignore_eos : 是否忽略结束符(EOS),强制生成直到达到最大长度
  • 更多详细参数见附录

输入格式:Prompts

  • vLLM 支持两种形式的输入,可以在同一个 batch 中混合使用:
    • 直接传入字符串,例如 "Hello, world"
      • vLLM 会自动调用内置 Tokenizer 进行编码
    • 传入已经编码好的 Token ID 列表
      • 这在需要自定义 Tokenizer 或复用已编码数据时非常有用
  • 还可以在一个列表中混合输入以上两种输入

vLLM 使用示例

  • 本文将通过三个维度的代码示例来展示 vLLM 的核心能力:
    • 高层同步接口 (LLM) :最常用的离线批量推理方式
    • 高层异步接口 (AsyncLLM) :适用于构建高并发服务的异步流式处理
    • 底层引擎接口 (LLMEngine) :展示如何手动控制调度循环 (Step-level control)

离线批量推理:LLM

  • 这是最简单的用法,适用于处理数据集
  • LLM 类封装了引擎的初始化和调度循环:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    from vllm import LLM, SamplingParams

    # # 初始化 LLM
    # tensor_parallel_size: 使用的 GPU 数量
    # gpu_memory_utilization: 显存占用比例 (0.0 - 1.0)
    llm = LLM(
    model="path_to_model",
    tensor_parallel_size=1,
    gpu_memory_utilization=0.9
    )

    # # 定义采样参数
    # sampling_params_greedy = SamplingParams(temperature=0, max_tokens=10) # 贪心采样策略
    sampling_params_creative = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=50)

    prompts = [
    "Hello, my name is", # 索引 0
    "The capital of France is" # 索引 1
    ]

    # # 执行批量解码 (Batch Decoding)
    # generate 函数是同步阻塞的,直到所有请求完成
    outputs = llm.generate(prompts, sampling_params_creative)

    # # 处理输出结果
    for i, output in enumerate(outputs):
    prompt = output.prompt
    # output.outputs 是一个列表,包含 'n' 个生成的序列 (这里 n=1,每个 Prompt 仅生成一个)
    generated_text = output.outputs[0].text

    print(f"--- Sample {i+1} ---")
    print(f"Prompt: {prompt!r}")
    print(f"Generated: {generated_text!r}")
    print(f"Finish Reason: {output.outputs[0].finish_reason}") # e.g., 'stop', 'length'

异步流式推理:AsyncLLM

  • AsyncLLMLLM 的异步版本,基于 AsyncLLMEngine 构建
  • AsyncLLM 允许你在 Python 的 asyncio 循环中非阻塞地提交请求并获取结果,非常适合搭建 API 服务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    import asyncio
    from vllm import AsyncLLM, SamplingParams
    from vllm.engine.arg_utils import AsyncEngineArgs

    async def run_async_inference(): # 使用 async 关键字定义一个协程函数
    # 1. 配置引擎参数
    # AsyncEngineArgs 允许更细粒度地控制引擎行为,如 max_num_seqs (最大并发序列数)
    engine_args = AsyncEngineArgs(
    model="path_to_model",
    tensor_parallel_size=1,
    disable_log_requests=True
    )

    # 2. 初始化异步 LLM
    # AsyncLLM 内部维护了一个后台循环来处理请求
    llm = AsyncLLM.from_engine_args(engine_args)

    # 3. 定义采样参数
    sampling_params = SamplingParams(temperature=0.7, max_tokens=20)

    # 4. 定义异步生成任务
    # request_id 是必须的,用于在引擎内部追踪请求,需保证唯一性
    async def generate_stream(request_id, prompt):
    results_generator = llm.generate(
    prompt,
    sampling_params,
    request_id=request_id
    )

    # 异步迭代生成结果 (Streaming)
    final_output = None
    async for request_output in results_generator:
    # 这里可以实现流式推送到前端
    final_output = request_output

    return final_output

    # 5. 模拟并发请求 (多样本解码)
    # 同时发送文本提示和 Token 提示
    tasks = [
    generate_stream("req_001", "To be or not to be,"),
    generate_stream("req_002", "The capital of France is") # TokensPrompt
    ]

    # 等待所有任务完成
    results = await asyncio.gather(*tasks)

    for res in results:
    print(f"Request ID: {res.request_id}")
    print(f"Output: {res.outputs[0].text}")

    # 运行异步主函数
    if __name__ == "__main__":
    asyncio.run(run_async_inference())

底层引擎手动调度:LLMEngine

  • LLMEngine 是 vLLM 最底层的核心
    • 通常用户不需要直接操作它,除非你需要极度定制化的调度逻辑(例如自定义 Web Server 或特殊的强化学习循环)
  • LLM 类本质上就是在这个类外面包了一层 while 循环
  • 这个示例展示了 vLLM 内部是如何通过 step() 函数一步步完成推理的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    from vllm import LLMEngine, SamplingParams, RequestOutput
    from vllm.engine.arg_utils import EngineArgs
    from vllm.utils import random_uuid

    def run_core_engine_loop():
    # 1. 初始化引擎参数与实例
    engine_args = EngineArgs(model="path_to_model")
    engine = LLMEngine.from_engine_args(engine_args)

    sampling_params = SamplingParams(temperature=0, max_tokens=10)

    # 2. 手动添加请求 (Add Requests)
    # 必须手动管理 request_id
    engine.add_request( # 注意:add_request 函数不会启动推理,需要等待 step 函数来执行
    request_id="req_text",
    prompt="Artificial Intelligence is",
    sampling_params=sampling_params
    )

    engine.add_request(
    request_id="req_text",
    prompt="The capital of France is",
    sampling_params=sampling_params
    )

    # 3. 手动执行调度循环 (The Step Loop)
    # 只要引擎中还有未完成的请求,就继续循环
    while engine.has_unfinished_requests():
    # step() 执行一次推理迭代:
    # 1. 调度器决定哪些请求进入 GPU 计算
    # 2. 执行模型的前向传播 (Model Forward)
    # 3. 采样下一个 Token
    # 4. 更新 KV Cache
    request_outputs: list[RequestOutput] = engine.step() # 注意 step 是一次仅采样一个 Token!streaming 也是借助 step 函数实现的;平时不需要 step 函数是因为封装到底层了

    # 打印当前步的中间结果 (Streaming 效果)
    for output in request_outputs:
    if output.finished:
    print(f"[{output.request_id}] Finished: {output.outputs[0].text}")
    else:
    # 仅打印当前生成的最新 token(简化展示)
    # 实际 output.outputs[0].text 包含完整的累积文本
    pass

    # 运行
    if __name__ == "__main__":
    run_core_engine_loop()

vLLM 部署及参数说明

部署命令

  • 推荐的 Linux 启动命令(可根据实际情况修改路径和显卡数量):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    vllm serve /data/models/Llama-3-8B-Instruct \
    --served-model-name llama3-8b \
    --host 0.0.0.0 \
    --port 8000 \
    --dtype auto \
    --tensor-parallel-size 1 \
    --pipeline-parallel-size 1 \
    --gpu-memory-utilization 0.90 \
    --swap-space 4 \
    --max-model-len 8192 \
    --max-num-seqs 256 \
    --max-num-batched-tokens 8192 \
    --trust-remote-code \
    --enable-chunked-prefill \
    --disable-custom-all-reduce \
    --quantization awq \
    --enforce-eager \
    --api-key "sk-your-secure-password"
  • TLDR:参数配置建议:

    • 如果追求极致吞吐量(Throughput) :增大 --max-num-batched-tokens
      • 这允许一次性处理更多数据,但可能会导致生成过程中的停顿感(因为大批量的预填充会抢占计算资源)
    • 如果追求低延迟和流畅度(Latency) :建议保持适中的 --max-num-batched-tokens,并 开启 --enable-chunked-prefill
      • 这样可以将大的预填充任务打散,避免计算尖峰,确保正在生成的对话不会卡顿
    • 显存限制 :请注意,max-num-batched-tokens 的大小直接影响 KV Cache 的瞬时显存需求
      • 如果该值过大而显存不足,可能会触发 OOM 或强制调度器减少并发序列数(--max-num-seqs

参数详细解析

  • vllm serve <path_to_model>
    • 这是 vLLM 的启动入口命令
    • 后面的 path_to_model 路径是模型在本地文件系统中的绝对路径(也可以是 Hugging Face 的模型 ID)
  • --served-model-name <model_name>
    • 指定服务对外显示的名称,建议使用类似 “llama-8b” 等类似名称标注
    • 当客户端调用 OpenAI 兼容 API 时,model 字段需要匹配这个名字
    • 如果不设置,默认使用模型路径作为名字
  • --host 0.0.0.0
    • 指定服务绑定的 IP 地址
    • 0.0.0.0 表示允许来自任何网络接口的连接(对外网开放);如果仅限本地访问,可设置为 127.0.0.1
  • --port 8000
    • 指定服务监听的端口号
  • --dtype auto
    • 指定模型权重的加载精度
    • 设置为 auto 时,vLLM 会根据配置文件(config.json)自动检测(通常是 float16bfloat16
    • 也可以强制指定为 float16bfloat16float32
  • --tensor-parallel-size 1 (TP)
    • 张量并行度,即把一个模型的层拆分到几张显卡上并行计算
    • 通常设置为单机内的 GPU 数量
  • --pipeline-parallel-size 1 (PP)
    • 流水线并行度,即把模型的不同层分配到不同的显卡上
    • 通常用于模型过大,单卡显存塞不下且 TP 无法解决时
    • 注:模型部署不建议开 PP,使用 TP 即可,一般情况下 PP 保持为 1
  • --gpu-memory-utilization 0.90
    • GPU 显存使用率上限,注意:这是影响并发能力的核心参数
    • vLLM 会预先占用这部分比例的显存(此处为 90%)
      • 其中一部分用于加载模型权重,剩余的所有空间都会被预分配给 KV Cache(键值缓存)
    • 如果设得太高容易 OOM(显存溢出),设得太低则浪费显存,导致并发量上不去
  • --swap-space 4 (新增重要参数)
    • CPU 交换空间大小(单位:GiB)
    • 当 GPU 显存不足以存放 KV Cache 时,vLLM 会将部分 KV Block 换出到 CPU 内存中
    • 设置此参数可以防止在请求突发高峰时发生 OOM 崩溃
  • --max-model-len 8192
    • 模型的最大上下文长度(输入+输出)
      • 如果未指定,vLLM 会尝试从模型配置中读取
    • 显式指定可以限制显存占用,避免处理过长的序列导致崩溃
  • --max-num-seqs 256
    • 最大并发序列数,即同一时刻 vLLM 能处理的请求数量(Batch Size)
    • 这个值越高,吞吐量越大,但每个请求的延迟可能会增加
  • --max-num-batched-tokens 8192
    • 一次迭代(iteration)中处理的最大 Token 总数
    • 这包括了 Prefill(预填充)阶段和 Decode(解码)阶段的所有 Token
    • 通常默认为 max(max_model_len, 2048),建议根据卡的性能灵活配置,以最大化效率
  • --trust-remote-code
    • 允许执行模型仓库中的自定义 Python 代码
    • 对于某些非标准架构的模型(如 ChatGLM、Qwen 的早期版本等),必须开启此选项才能正确加载模型架构
  • --enable-chunked-prefill
    • 为了解决长 Prompt 导致的“队头阻塞”问题(即一个超长 Prompt 占满计算资源,导致短请求延迟增加),引入了分块预填充机制
    • 开启分块预填充,这是一个优化参数,允许将长 Prompt 的 Prefill 阶段拆分成多个小块,与 Decode 阶段混合调度
    • 这可以显著降低长文本输入时的首字延迟(TTFT) ,因为允许解码(Decode)任务和预填充(Prefill)任务更平滑地交错执行,显著降低了其他并发请求的 Inter-Token Latency(ITL,Token 间延迟),使生成过程更加流畅
  • --max-num-partial-prefills
    • 并发预填充数,当启用了分块预填充(Chunked Prefill)后,这个参数变得非常重要
    • 限制了在同一时刻,有多少个请求可以处于“部分预填充”状态,
    • 默认为 1:意味着在任何给定的迭代中,调度器最多只允许 1 个请求进行部分预填充计算(与其他正在解码的请求并行),这有助于防止过多的上下文切换开销,同时保证显存管理的稳定性
  • --long-prefill-token-threshold
    • 长预填充阈值,这是一个辅助参数,用于配合分块预填充使用
    • 定义了多少 Token 数量的 Prompt 被视为“长请求”
    • 当 Prompt 长度超过此阈值时,vLLM 才会考虑对其应用特殊的调度策略或分块逻辑。默认值为 0,意味着所有请求都遵循统一的规则
  • --disable-custom-all-reduce
    • 禁用 vLLM 自定义的 All-Reduce 内核
    • 通常在某些 GPU 架构不支持或驱动不兼容导致 NCCL 通信错误时使用
    • 如果硬件环境标准,通常不需要加这个,但在排查多卡通信问题时很有用
  • --quantization awq (新增重要参数)
    • 指定量化格式
    • 如果模型是量化版本(如 AWQ, GPTQ, SqueezeLLM),必须指定此参数
    • 例如加载 Llama-3-8B-AWQ 时,需设置为 awq
    • 如果是非量化模型,请去掉此参数
  • --enforce-eager (新增重要参数)
    • 强制使用 PyTorch 的 Eager 模式,禁用 CUDA Graph
    • 虽然 CUDA Graph 能加速小 Batch 的推理,但在某些特定显卡或驱动版本上可能会导致显存分配错误或死锁
    • 开启此项有助于调试和提高稳定性
  • --api-key "sk-your-secure-password" (新增重要参数)
    • 设置访问 API 的密钥
    • 在生产环境中,为了防止未授权访问,配置 API Key 是必须的安全措施
    • 客户端请求头需携带 Authorization: Bearer sk-your-secure-password

附录:如果是量化模型,不添加 --quantization 参数 会怎样?

  • W8A8-QuaRot(Weight 8-bit / Activation 8-bit,使用了 QuaRot 旋转算法进行离群值抑制)量化模型 为例,如果在启动 vLLM 时不指定 --quantization 参数,通常会发生以下三种情况之一(具体取决于模型的 config.json 配置和 vLLM 的版本)
  • 注:QuaRot 是一种算法技术,它生成的模型最终通常以 FP8 (E4M3/E5M2) 或 Int8 的格式存储
情况1:直接报错并无法启动(最常见的情况)
  • 这是最可能发生的结果
  • vLLM 启动时会读取模型的 config.json
    • 如果该配置文件中包含 quantization_config 字段(例如标记为 fp8compressed-tensors 或自定义格式),但 vLLM 在默认模式下无法自动匹配到合适的 Kernel(内核),或者检测到硬件不支持该量化格式(例如在非 Hopper 架构显卡上加载 FP8),程序会直接抛出 ValueErrorRuntimeError
  • 终端会打印类似 ValueError: Unknown quantization method...RuntimeError: Shape mismatch... 的错误日志,服务启动失败
情况2:加载成功但输出乱码,Garbage Output
  • 这种情况比较危险,因为它看起来“跑起来了”,但完全不可用
  • 如果模型的 config.json缺失 了量化相关的元数据,或者 vLLM 错误地将其识别为标准模型,它会尝试以默认精度(通常是 float16bfloat16)来解释权重数据
  • 数据曲解:原本是 8-bit 的整数或 FP8 数据,被当成了 16-bit 的浮点数读取
  • 模型可以接受输入,但吐出来的全是乱码、重复符号或毫无逻辑的字符
情况3:自动识别成功(理想情况,但有前提)
  • 在较新的 vLLM 版本中,如果模型打包规范(例如使用 llm-compressorAutoGPTQ 正确导出),config.json 中会有明确的 quantization_config 字段
  • config.json 里的 quantization 字段(如 fp8compressed-tensors)被当前版本的 vLLM 原生支持
  • 如果显卡支持该精度(例如 w8a8 的 QuaRot 通常对应 FP8 ,这通常需要 NVIDIA Ada Lovelace (RTX 4090) 或 Hopper (H100) 架构的 GPU)
  • 此时即使你不写 --quantization,vLLM 也会根据配置文件自动启用对应的量化内核,服务正常运行
推荐做法
  • 先看模型文件夹下的 config.json,寻找 quantization_config 字段

  • 如果格式是 FP8(常见于 QuaRot 转换的模型):

    1
    --quantization fp8 --kv-cache-dtype fp8
    • 注意:支持 fp8 通常需要 H100/L40/RTX4090 等新显卡
  • 如果格式是 Compressed-Tensors / Neural Magic 格式

    • vLLM 通常能自动识别,但如果报错,可能需要指定:
      1
      --quantization compressed-tensors

附录:SamplingParams 参数项详解

  • vLLM 的 SamplingParams 参数很多,覆盖了多个方面:
    • 从基础生成控制(长度、终止)
    • 采样策略(随机性、候选集)
    • 重复控制(惩罚)
    • 输出格式(detokenize、 Special Token )
    • 高级自定义(logits 处理器、结构化输出)的全维度参数
  • 这些参数既兼容 OpenAI API 规范,又扩展了 beam search、结构化输出、不良词过滤等特有功能
  • 一些简单的常用理解:
    • 追求确定性可以配置:temperature=0 + top_k=1
      • 问题:temperature=0 其实就已经是贪心采样了,但是我们一般还是会使用 top_k=1 进一步明确 贪心采样
    • 追求多样性可以配置:temperature=0.7 + top_p=0.9
      • 理解:temperature=0.7 + top_p=0.9 是很常用的参数
    • 避免重复:presence_penalty=0.5 + frequency_penalty=0.3
      • presence_penalty 惩罚是否出现过
      • frequency_penalty 惩罚出现频次

SamplingParams 源码配置

  • 以下源码参考自:github.com/vllm-project
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    class SamplingParams(
    PydanticMsgspecMixin,
    msgspec.Struct,
    omit_defaults=True, # type: ignore[call-arg]
    # required for @cached_property.
    dict=True,
    ): # type: ignore[call-arg]
    """Sampling parameters for text generation.

    Overall, we follow the sampling parameters from the OpenAI text completion
    API (https://platform.openai.com/docs/api-reference/completions/create).
    In addition, we support beam search, which is not supported by OpenAI.
    """

    n: int = 1
    """Number of outputs to return for the given prompt request.

    NOTE:
    `AsyncLLM` streams outputs by default. When `n > 1`, all `n` outputs
    are generated and streamed cumulatively per request. To see all `n`
    outputs upon completion, use `output_kind=RequestOutputKind.FINAL_ONLY`
    in `SamplingParams`."""
    presence_penalty: float = 0.0
    """Penalizes new tokens based on whether they appear in the generated text
    so far. Values > 0 encourage the model to use new tokens, while values < 0
    encourage the model to repeat tokens."""
    frequency_penalty: float = 0.0
    """Penalizes new tokens based on their frequency in the generated text so
    far. Values > 0 encourage the model to use new tokens, while values < 0
    encourage the model to repeat tokens."""
    repetition_penalty: float = 1.0
    """Penalizes new tokens based on whether they appear in the prompt and the
    generated text so far. Values > 1 encourage the model to use new tokens,
    while values < 1 encourage the model to repeat tokens."""
    temperature: float = 1.0
    """Controls the randomness of the sampling. Lower values make the model
    more deterministic, while higher values make the model more random. Zero
    means greedy sampling."""
    top_p: float = 1.0
    """Controls the cumulative probability of the top tokens to consider. Must
    be in (0, 1]. Set to 1 to consider all tokens."""
    top_k: int = 0
    """Controls the number of top tokens to consider. Set to 0 (or -1) to
    consider all tokens."""
    min_p: float = 0.0
    """Represents the minimum probability for a token to be considered,
    relative to the probability of the most likely token. Must be in [0, 1].
    Set to 0 to disable this."""
    seed: int | None = None
    """Random seed to use for the generation."""
    stop: str | list[str] | None = None
    """String(s) that stop the generation when they are generated. The returned
    output will not contain the stop strings."""
    stop_token_ids: list[int] | None = None
    """Token IDs that stop the generation when they are generated. The returned
    output will contain the stop tokens unless the stop tokens are special
    tokens."""
    ignore_eos: bool = False
    """Whether to ignore the EOS token and continue generating
    tokens after the EOS token is generated."""
    max_tokens: int | None = 16
    """Maximum number of tokens to generate per output sequence."""
    min_tokens: int = 0
    """Minimum number of tokens to generate per output sequence before EOS or
    `stop_token_ids` can be generated"""
    logprobs: int | None = None
    """Number of log probabilities to return per output token. When set to
    `None`, no probability is returned. If set to a non-`None` value, the
    result includes the log probabilities of the specified number of most
    likely tokens, as well as the chosen tokens. Note that the implementation
    follows the OpenAI API: The API will always return the log probability of
    the sampled token, so there may be up to `logprobs+1` elements in the
    response. When set to -1, return all `vocab_size` log probabilities."""
    prompt_logprobs: int | None = None
    """Number of log probabilities to return per prompt token.
    When set to -1, return all `vocab_size` log probabilities."""
    flat_logprobs: bool = False
    """Whether to return logprobs in flatten format (i.e. FlatLogprob)
    for better performance.
    NOTE: GC costs of FlatLogprobs is significantly smaller than
    list[dict[int, Logprob]]. After enabled, PromptLogprobs and
    SampleLogprobs would populated as FlatLogprobs."""
    # NOTE: This parameter is only exposed at the engine level for now.
    # It is not exposed in the OpenAI API server, as the OpenAI API does
    # not support returning only a list of token IDs.
    detokenize: bool = True
    """Whether to detokenize the output."""
    skip_special_tokens: bool = True
    """Whether to skip special tokens in the output."""
    spaces_between_special_tokens: bool = True
    """Whether to add spaces between special tokens in the output."""
    # `list[LogitsProcessor] | None` type. We use Any here because
    # `list[LogitsProcessor] | None` type is not supported by msgspec.
    logits_processors: Any | None = None
    """Functions that modify logits based on previously generated tokens, and
    optionally prompt tokens as a first argument."""
    include_stop_str_in_output: bool = False
    """Whether to include the stop strings in output text."""
    truncate_prompt_tokens: Annotated[int, msgspec.Meta(ge=-1)] | None = None
    """If set to -1, will use the truncation size supported by the model. If
    set to an integer k, will use only the last k tokens from the prompt
    (i.e., left truncation). If set to `None`, truncation is disabled."""
    output_kind: RequestOutputKind = RequestOutputKind.CUMULATIVE

    # The below fields are not supposed to be used as an input.
    # They are set in post_init.
    output_text_buffer_length: int = 0
    _all_stop_token_ids: set[int] = msgspec.field(default_factory=set)

    # Fields used to construct logits processors
    structured_outputs: StructuredOutputsParams | None = None
    """Parameters for configuring structured outputs."""
    logit_bias: dict[int, float] | None = None
    """If provided, the engine will construct a logits processor that applies
    these logit biases."""
    allowed_token_ids: list[int] | None = None
    """If provided, the engine will construct a logits processor which only
    retains scores for the given token ids."""
    extra_args: dict[str, Any] | None = None
    """Arbitrary additional args, that can be used by custom sampling
    implementations, plugins, etc. Not used by any in-tree sampling
    implementations."""

    # Fields used for bad words
    bad_words: list[str] | None = None
    """Words that are not allowed to be generated. More precisely, only the
    last token of a corresponding token sequence is not allowed when the next
    generated token can complete the sequence."""
    _bad_words_token_ids: list[list[int]] | None = None

    skip_reading_prefix_cache: bool | None = None

基础参数说明

  • n: int = 1
    • 为单个 Prompt 请求返回的生成结果数量
    • vLLM 默认一个个输出结果,当 n > 1 时,所有 n 个结果会按请求累积流式一个个返回;
      • 问题:这里的流式,不是通常意义上的流式,而是针对 Response n 粒度的流式?
    • 若希望仅在生成完成后一次性获取所有 n 个结果,需将 output_kind 设置为 RequestOutputKind.FINAL_ONLY
  • max_tokens: int | None = 16
    • 每个输出序列允许生成的最大 token 数量
    • 若设为 None,需确保模型有明确的终止条件(如 EOS 或 stop 词),否则可能无限生成
  • min_tokens: int = 0
    • 每个输出序列在生成 EOS(结束符)或 stop_token_ids 之前必须生成的最小 token 数
    • 作用 :避免生成过短的结果,例如设置 min_tokens=5 时,即使模型提前触发终止条件 ,也会继续生成直到达到 5 个 token
  • ignore_eos: bool = False
    • 是否忽略 EOS token,强制模型在生成 EOS 后继续生成
    • 适用于需要生成超长文本、绕过模型默认终止逻辑的场景(如生成完整文档而非单句)

采样策略参数

  • 采样策略参数参数控制模型生成 token 时的随机性和候选范围,是最常用的参数,当不做采样时,no_sample
  • temperature: float = 1.0
    • 控制采样的随机性,本质是对 logits(token 概率对数)进行缩放(注意:是在 Softmax 前进行缩放)
    • temperature = 0:贪心采样(Greedy Sampling),直接选择概率最高的 token,结果完全确定;
    • 0 < temperature < 1:降低随机性(提高确定性),结果更聚焦、确定(如 0.7 是平衡随机性和确定性的常用值);
    • temperature > 1:提高随机性,结果更发散、创意性更强,但可能出现无意义内容
    • temperature 越小越容易出现重复现象
    • 注意 :当 temperature=0 时,top_p/top_k 等参数会失效(贪心采样无需候选集)
      • vLLM 中没有 do_sample 参数 参照了 HF Transformer 相似的思路,但是实现方式不同,通过 temperature 隐晦地实现了是否贪心采样的控制
      • temperature=0 强制 do_sample=False(贪心采样,只选概率最高的 token);
      • temperature>0 等价 do_sample=True(启用随机采样,按概率分布选 token)
  • top_p: float = 1.0
    • 核采样(Nucleus Sampling),控制待选 token 的累积概率阈值,取值范围 (0, 1]
    • 将所有 token 按概率从高到低排序,累加概率直到达到 top_p,仅从这些 token 中采样
      • top_p=0.9 时,仅选择累计概率前 90% 的 token 作为候选;
      • top_p=1.0 时,包含所有 token(等同于不限制)
    • 相比 top_k 更灵活,能自适应调整候选集大小(高概率 token 少则候选集小,反之则大)
  • top_k: int = 0
    • 限制采样的候选 token 数量,仅从概率最高的 top_k 个 token 中选择
      • top_k=0(或 -1):不限制,包含所有 token;
      • top_k=50:仅从概率前 50 的 token 中采样
    • 对比 top_ptop_k 是固定数量限制,top_p 是概率累积限制,通常两者二选一使用
      • 两者组合时:先按 top_k 筛选,再按 top_p 过滤
  • min_p: float = 0.0
    • 基于最高概率 token 的相对概率阈值,筛选候选 token,取值范围 [0, 1]
    • 设本次采样遇到的最高概率 token 的概率为 P_max(注意:是个随分布变化的值),仅保留概率 \(\ge\) min_p * P_max 的 token
      • min_p=0.1P_max=0.5 时,仅保留概率 \(\ge\) 0.05 的 token;
      • min_p=0 时禁用该规则
    • 优势 :相比 top_k/top_p,能避免极端情况下的不合理筛选(如 top_k 可能漏掉低概率但有意义的 token,top_p 可能包含过多低概率 token)
  • seed: int | None = None
    • 生成随机数的种子,用于复现生成结果
    • 设置固定 seed 后,相同 Prompt 和参数下,模型会生成完全相同的结果(解决采样随机性导致的不可复现问题)

重复/惩罚类参数

  • 用于控制模型生成时的重复率,避免生成冗余、重复的文本
  • presence_penalty: float = 0.0
    • 基于 token 是否“出现过”的惩罚,与出现次数无关
      • 正值(如 0.5):惩罚已出现的 token,鼓励生成新内容;
      • 负值(如 -0.5):奖励已出现的 token,鼓励重复;
      • 0:无惩罚/奖励
    • 适用场景 :需要避免模型重复提及相同实体(如人名、地名)的场景
  • frequency_penalty: float = 0.0
    • 基于 token 出现“频率”的惩罚,出现次数越多,惩罚越重
      • 正值:抑制高频 token,减少重复;
      • 负值:强化高频 token,增加重复;
      • 0:无惩罚/奖励
    • 区别于 presence_penalty :前者是“有无”惩罚,后者是“多少”惩罚,例如重复 3 次的 token 会比重复 1 次的 token 受到更重的频率惩罚
  • repetition_penalty: float = 1.0
    • 基于 prompt 和已生成文本中 token 出现的惩罚,核心是调整 token 的概率
      • 取值 > 1:惩罚重复 token(概率 = 原概率 / repetition_penalty),鼓励新内容;
      • 取值 < 1:奖励重复 token(概率 = 原概率 * repetition_penalty),鼓励重复;
      • 1:无惩罚/奖励
    • 覆盖范围(特别注意) :同时作用于 prompt 和生成文本中的 token,是更通用的重复控制参数
      • 理解:这里的含义是在 prompt 中的 Token 也会当做是否重复的判断依据进行累计

终止条件参数

  • 控制模型何时停止生成,避免无限制输出
  • stop: str | list[str] | None = None
    • 触发生成终止的字符串(单个或列表),返回的结果中默认不包含这些停止字符串
    • stop=["\n", "###"] 时,模型生成到换行符或 ### 时立即停止
  • stop_token_ids: list[int] | None = None
    • 触发生成终止的 token ID 列表(底层 token-level 的终止条件)
    • 返回结果中会包含 stop_token_ids 对应的 stop token(Special Token 服从本规则)
      • 如果是 Special Token,可能是不会在输出结果中的,有自己的规则
      • stop(字符串级)互补,分别用于指定字符串或者 Token
  • include_stop_str_in_output: bool = False
    • 是否将 stop 参数指定的停止字符串包含在输出文本中
      • 注意:这里只影响 stop,与 stop_token_ids 无关,stop_token_ids 不受此参数影响
    • 若设为 True,停止字符串会出现在最终输出里
  • 理解终止条件参数,vLLM 的 SamplingParams 内部会维护一个参数:_all_stop_token_ids: set[int] = msgspec.field(default_factory=set)
    • _all_stop_token_ids 存储所有终止 token ID
      • 包括 stop_token_ids 转换后的 ID、EOS token 等
      • 这个参数无需用户手动设置,由 post_init 自动初始化

日志概率(logprobs)参数

  • 用于获取 token 生成的概率信息,适用于需要分析模型决策过程的场景(如评估生成可靠性)
  • logprobs: int | None = None
    • 每个输出 token 返回的最高概率 token 的数量(包含选中的 token)
      • logprobs=None:不返回概率;
      • logprobs=k(\(k \in \mathbb{Z}^+\)):返回概率最高的 k 个 token 的 log 概率(实际返回 k+1 个,因为包含选中的 token);
        • 理解:这里选中的 Token 不一定是概率最高的, 所以被选中的一定会返回
      • logprobs=-1:返回全词表(vocab_size 维度)所有 token 的 log 概率
    • Following OpenAI API :始终返回选中 token 的 log 概率
  • prompt_logprobs: int | None = None
    • 每个 Prompt token 返回的最高概率 token 的数量
      • 取值规则同 logprobs-1 表示返回全词表概率
    • 问题:prompt 为什么也会对应概率?
      • prompt_logprobs 是专门针对输入的 prompt 部分(而非生成的 completion 部分)返回的每个 token 的对数概率信息
      • logprobs 则通常指生成部分的对数概率
  • flat_logprobs: bool = False
    • 是否展平返回 logprobs,优化性能
    • 优势 :FlatLogprob 的 GC(垃圾回收)成本远低于 list[dict[int, Logprob]] 格式,适合高并发场景;
    • 启用后 PromptLogprobsSampleLogprobs 均会以 FlatLogprob 格式返回

输出格式与处理参数

  • 控制生成结果的格式、是否过滤 Special Token 等
  • detokenize: bool = True
    • 是否将生成的 token ID 转换为文本
    • 注意 :仅在引擎层暴露,OpenAI API 不支持仅返回 token ID,默认开启,得到的就是文本而不是 Token ID
  • skip_special_tokens: bool = True
    • 是否在输出中跳过 Special Token (如 等)
    • 注意默认是 True(跳过),避免输出包含无意义的特殊标记
  • spaces_between_special_tokens: bool = True
    • 是否在 Special Token 之间添加空格
    • 优化 Special Token 的可读性,例如 <|endoftext|><|user|> 会变成 <|endoftext|> <|user|>
    • 理解:为什么这里默认是 True,目前我们几乎不用,但确从不需要设置?猜测如下(待确定):
      • Hugging Face Tokenizer 基类的 通用默认值 是 True;
      • LLaMA/Qwen 等模型的 专属默认值 是 False(通过代码硬编码覆盖了通用默认值)
  • output_kind: RequestOutputKind = RequestOutputKind.CUMULATIVE
    • 输出类型,控制流式返回的方式:
      • output_kind=RequestOutputKind.CUMULATIVE(默认):累积式输出(如第 1 次返回第 1 个 token,第 2 次返回前 2 个 token,依此类推);
      • output_kind=RequestOutputKind.FINAL_ONLY:仅在生成完成后返回最终完整结果(此时不是异步生成了)
      • output_kind=RequestOutputKind.DELTA:仅返回增量
    • 问题:这个参数的使用待测试确认
  • output_text_buffer_length: int = 0
    • 内部参数,存储输出文本缓冲区长度,无需用户设置,由 post_init 初始化

Prompt 处理参数

  • truncate_prompt_tokens: Annotated[int, msgspec.Meta(ge=-1)] | None = None
    • Prompt 的左截断规则(仅保留最后 k 个 token):
      • -1:使用模型支持的最大截断长度;
      • 正整数 k:仅保留 Prompt 最后 k 个 token;
      • None:禁用截断
    • 常用参数,适配模型的上下文窗口限制,避免 Prompt 过长导致超出模型容量
  • skip_reading_prefix_cache: bool | None = None
    • 是否跳过读取前缀缓存(prefix cache),用于优化 Prompt 处理性能,通常无需用户手动设置
  • logits_processors: Any | None = None
    • 修改 logits 的自定义处理器列表(函数),可基于已生成的 token(或 Prompt token)调整 token 概率
    • 因 msgspec 不支持 list[LogitsProcessor] | None,故用 Any 替代;适用于自定义生成逻辑(如强制生成特定 token、限制生成内容)
    • 问题:待确认这个参数
  • structured_outputs: StructuredOutputsParams | None = None
    • 结构化输出参数,用于控制模型生成符合特定格式的内容(如 JSON、XML)
    • 需要结构化结果的场景(如数据提取、API 调用返回)
  • logit_bias: dict[int, float] | None = None
    • token 级别的概率偏置,键为 token ID,值为偏置值
    • 调整指定 token 的生成概率(正值提高概率,负值降低概率),例如 logit_bias={123: 5.0} 会大幅提高 ID 为 123 的 token 被选中的概率
    • 问题:待尝试这个参数
  • allowed_token_ids: list[int] | None = None
    • 允许生成的 token ID 列表,后续生成时,会仅保留这些 token 的概率,其余 token 概率置 0
    • 严格限制生成内容的范围(如仅允许生成数字、特定词汇)
    • 问题:待尝试这个参数
  • extra_args: dict[str, Any] | None = None
    • 自定义额外参数,供第三方插件、自定义采样逻辑使用,vLLM 内置采样逻辑不使用该参数

不良词过滤参数

  • bad_words: list[str] | None = None
    • 禁止生成的词汇列表,核心逻辑是:当生成的 token 即将完成某个 bad word 的 token 序列时,禁止生成该序列的最后一个 token
    • 比如 bad_words=["暴力"] 时,模型会避免生成“暴力”这个词(通过阻止其最后一个 token 的生成),直接停止
    • 问题:待测试这个参数
  • _bad_words_token_ids: list[list[int]] | None = None
    • 内部参数,存储 bad_words 转换后的 token ID 序列,无需用户设置,由 post_init 初始化
    • 问题:待测试这个参数