已关闭
[RFC]: MindIE Motor 接入 vLLM Render/Derender,演进至 Token-in/Token-out 推理架构 #479
yilunh创建于  8月13日关闭于  4 天前
yilunh
8月13日 创建

Coordinator 接入 vLLM Render 的 Token In/Token Out Serving RFC

需求描述

背景

当前 MindIE Motor 由 Coordinator 作为推理服务统一入口,负责接收 OpenAI 请求、执行请求流控、选择推理实例,并根据部署拓扑完成 P/D 或 Union 调度。请求被选定实例后,仍以 OpenAI 请求的形式发送给 vLLM API Server,由 vLLM 在 NPU 推理实例内部完成 Chat Template 渲染、Tokenization、模型推理、Detokenization,以及 Reasoning 和 Tool Call 等后处理。

OpenAI-compatible 请求在 Coordinator、Prefill Engine 和 Decode Engine 之间传递时,各组件可能重复执行协议解析、模板渲染和分词。重复 Tokenize 会增加 CPU 开销和首 Token 延迟,也可能因模板或 tokenizer 配置差异导致 Coordinator、P 实例和 D 实例得到不同的 Token 序列。

vLLM 提供的 Render API 可以将标准 OpenAI 请求转换为包含 Prompt Token IDs、Sampling Parameters 等信息的 GenerateRequest;Derender API 则完成相反方向的转换,将 Engine 返回的生成 Token IDs 转换为 OpenAI Response,并复用 vLLM 的 Detokenizer、Reasoning Parser 和 Tool Parser。

vLLM Render 将 Frontend Preprocessing 从推理引擎中拆出:

  1. Render 将 Chat/Completion 请求转换为 Token IDs 和采样参数;
  2. token-only Generate 接口只处理推理;
  3. Derender 将 Engine Token 输出转换为 OpenAI-compatible 响应。

这与 Motor 的 P/D 解耦架构相匹配:Coordinator 仍掌握调度、重试和请求生命周期,Engine 不再重复执行 Frontend 处理。因此,本 RFC 提议在 MindIE Motor 中引入 Render → Generate → Derender 三阶段处理链路,将请求前后处理与 NPU 推理解耦,并逐步把 Engine 演进为纯 Token-in/Token-out 服务。

本方案分阶段落地:

  • 阶段一已经完成非流式 Render → Generate → Derender 闭环,用于验证 Render Token IDs 参与流控、KV Cache 查询和调度的可行性;
  • 当前阶段在同一架构上补齐流式 Generate、Streaming Derender、多 prompt fan-out/merge、单 prompt relay、异常取消和 Token 混淆适配;
  • 后续再根据实际部署规模演进 Render Pool、能力协商、更多 API 和多模型路由。

术语说明

为避免混淆,本文统一使用以下术语:

术语 含义 主要职责
OpenAI Request 客户端发送的 Chat Completions 或 Completions 请求 描述 messages、prompt、tools、采样参数等客户输入
Render 将 OpenAI Request 转换为 Engine 请求的前处理过程 Chat Template、Tokenization、多模态输入处理和请求校验
Render Sidecar 与 Coordinator 同 Pod 部署的无 NPU 前端服务 提供 Render、Derender、Tokenizer 和 Parser 能力
Generate Token Engine 执行模型推理的过程 输入 Token IDs,输出生成 Token IDs
Token Engine Token-in/Token-out 推理实例 执行 Prefill、Decode 或 Union 推理
Derender 将 Engine 输出转换为 OpenAI Response 的后处理过程 Detokenization、Reasoning 解析、Tool Call 解析
GenerateRequest Render 后供 Token Engine 消费的内部请求 携带 Token IDs、Sampling Parameters 等信息
GenerateResponse Token Engine 返回的推理结果 携带生成 Token IDs、Finish Reason、Logprobs 等信息
Token Chunk 流式生成过程中产生的一批增量 Token IDs 用于 Streaming Derender 和实时 SSE 返回
stream_state vLLM Streaming Derender 返回、下次调用需要携带的状态 保存增量 Detokenize 和 Parser 上下文
attempt 一次具体的 Engine 调度与执行尝试 Engine 故障后可由 Rescheduler 创建下一次 attempt
relay/replay 流式请求故障后的接续机制 使用已提交 Token 构造新实例的续推输入

本文所称 Frontend,统一指 Render/Derender 所在的无 NPU 前后处理层;本文所称 Engine,统一指执行模型推理的 Token Engine,不包含 OpenAI 协议拼装职责。

目标态

本需求的目标不是在当前请求转发链路前简单增加一次 /render 调用,而是重新明确 OpenAI Frontend、Motor Coordinator 和 Token Engine 三者之间的职责边界。

目标态下,客户端仍然调用 Coordinator 暴露的标准 OpenAI API。Coordinator 收到请求后,首先将其发送给与模型配套的 Render Sidecar。Render 负责执行 Chat Template、Tokenization 和模型相关的输入处理,返回 Engine 可以直接消费的 Token IDs 和推理参数。

Coordinator 随后使用这些真实 Token IDs 完成流控、KV Cache 查询和实例调度,再把 Token-in 请求发送给 P/D 或 Union Engine。

Engine 不再理解 OpenAI messages,也不负责 Tool Call、Reasoning 和 OpenAI Response 拼装,而是接收 Token IDs 并输出生成 Token IDs。生成结果返回 Coordinator 后,由 Derender 完成 Detokenization、Reasoning 和 Tool Call 解析,并转换为标准 OpenAI Response 返回客户端。

目标态职责边界:Frontend 负责 Request ↔ Token 的转换,Coordinator 负责编排和调度,Engine 只负责 Token ↔ Token 的推理。

完整目标链路如下:

flowchart LR
    Client["OpenAI Client"] --> Coordinator["Motor Coordinator"]
    Coordinator --> Render["Render Sidecar<br/>Chat Template + Tokenization"]
    Render --> Coordinator
    Coordinator --> Schedule["Flow Control<br/>KV Query<br/>P/D Scheduling"]
    Schedule --> Engine["Token-in/Token-out Engine"]
    Engine --> Coordinator
    Coordinator --> Derender["Derender<br/>Detokenization + Parsing"]
    Derender --> Coordinator
    Coordinator --> Client

当前采用 Sidecar 部署:

Client
  | OpenAI request / SSE response
  v
+------------------------------------------------------+
| Coordinator Pod                                      |
|                                                      |
|  Coordinator                                         |
|  - API / flow control / scheduler / rescheduler      |
|  - Render client / token-only protocol               |
|  - Streaming state / batch merge                     |
|  - Local tokenizer (lazy fallback)                   |
|                | localhost HTTP                      |
|                v                                     |
|  vLLM Render Sidecar                                 |
|  - Render / Derender                                 |
|  - Tokenizer / Chat Template                         |
|  - Reasoning Parser / Tool Parser                    |
+------------------------------------------------------+
                  | token-only Generate
                  v
          Prefill / Decode / Union Engine

在这一架构中:

  • Coordinator 是完整请求生命周期的编排者;
  • Render Sidecar 是不占用 NPU 的模型前端;
  • P/D 或 Union Engine 是纯推理后端;
  • 客户面保持 OpenAI 兼容
  • 内部调度和推理统一使用 Render 产生的 Token 语义
  • Scheduler 输入结构和调度策略不直接感知 Render 协议

目标态同时支持流式和非流式请求。

对于非流式请求,Coordinator 等待完整 GenerateResponse,再执行一次 Derender,最终返回完整 OpenAI Response。

对于流式请求,Engine 每生成一个 Token Chunk,Coordinator 就将其与上一份 stream_state 一起交给 Streaming Derender。Derender 返回 OpenAI Chunk 和下一份状态,Coordinator 随即通过原 SSE 连接返回客户端。

流式语义是“边生成、边解析、边返回”,不是“生成完成后再用一条 SSE 返回”。

阶段一:非流式闭环

阶段一已经实现 stream=false 请求的 Render → Generate → Derender 闭环。

Coordinator 在调度前调用 Render 获得最终 Prompt Token IDs,并基于这些 Token IDs 完成流控、KV Cache 查询和实例调度。Engine 接收 Tokenized GenerateRequest 后执行推理,Coordinator 等待 Engine 返回完整 GenerateResponse,再将结果交给 Derender。Derender 完成一次性后处理后,Coordinator 向客户端返回最终 OpenAI Response。

sequenceDiagram
    participant C as Client
    participant M as Motor Coordinator
    participant R as Render Sidecar
    participant E as Token Engine

    C->>M: OpenAI Request, stream=false
    M->>R: Render OpenAI Request
    R-->>M: GenerateRequest + Prompt Token IDs

    M->>M: Flow Control + KV Query + Scheduling
    M->>E: Token-in GenerateRequest
    E-->>M: Complete GenerateResponse

    M->>R: Derender GenerateResponse
    R-->>M: OpenAI Response
    M-->>C: Complete JSON Response

阶段一支持:

  • Chat Completions 和 Completions;
  • 单 prompt 和多 prompt Completions;
  • Handoff、Trigger 和 Union/PD Hybrid;
  • Render Token In 失败后回退本地 tokenizer;
  • context budget 裁剪和 sampling_params.max_tokens 同步;
  • token-only endpoint 不支持时的受控原生链路回退;
  • 非流式 Derender 错误映射。

阶段一解决了 Coordinator 在调度前拿不到真实 Token IDs 的核心问题,也验证了 Render、Generate、Derender 的协议闭环。其主要限制是必须等待完整 GenerateResponse,不能提供流式接口所需的实时增量输出。

当前阶段:流式闭环

当前阶段在非流式公共模型和 token-only Engine Client 上增加 Streaming Derender,不另建一套完全独立的 Render Token In 逻辑。

sequenceDiagram
    participant C as Client
    participant M as Motor Coordinator
    participant R as Render Sidecar
    participant E as Token Engine

    C->>M: OpenAI Request, stream=true
    M->>R: Render OpenAI Request
    R-->>M: GenerateRequest + Prompt Token IDs

    M->>M: Flow Control + KV Query + Scheduling
    M->>E: Token-only Generate Stream

    loop each GenerateResponse Chunk
        E-->>M: Token Chunk
        M->>R: generate_chunk + previous stream_state
        R-->>M: OpenAI Chunk + next stream_state
        M-->>C: SSE Chunk
    end

    M-->>C: [DONE]

vLLM Streaming Derender 采用无服务端请求会话的设计。Render Sidecar 不持有整个请求的 Session;Coordinator 在每次 Derender 调用中携带上一份 stream_state,并在请求生命周期内保存最新已提交状态。

这与原初稿设想的“Render 创建有状态 Derender Session”不同。当前实现不需要 Session ID 或粘性路由,状态所有权明确位于 Coordinator,也更适合在 Engine attempt 之间恢复。

流式实现需要额外处理:

  • Derender 状态只在成功后提交;
  • attempt 失败时回滚尚未提交的状态;
  • Engine 故障后从最后一个客户端可见状态继续 relay;
  • 多 prompt 拆分为多条独立 Generate stream 后再聚合;
  • 客户端断开或任一子流失败时取消所有关联任务;
  • 使用有界队列控制慢客户端造成的缓存增长。

功能要点

Render 阶段

Render 必须位于流控和实例调度之前。

Coordinator 通过 Render 得到最终 Prompt Token IDs,并以此作为 Token 流控、KV Cache 查询和实例选择的输入。对于 Chat 请求,这些 Token IDs 已经应用 Chat Template、工具定义和 Special Token。

Coordinator 不应使用另一套 tokenizer 再次计算或覆盖 Render 结果,否则会重新引入 Token 不一致问题。KV Conductor 中既有的 prefix hash 仍基于最终 Token IDs 计算,不需要 Render 定制或返回额外 prompt_hash

Render 响应会被转换为 Motor 内部的 TokenizedRequest,而不是直接传播 vLLM Response Object。内部结构区分:

  • 语义 Prompt Token IDs:供长度统计、流控、Derender 和 replay 使用;
  • 可选物理 Prompt Token IDs:启用 Token 混淆时供 Engine 和 KV Conductor 使用;
  • Sampling Parameters 和 Render metadata;
  • tokenizer_source=render|local

context_budget_mode=on 时,Coordinator 先使用 Render 返回的准确 Prompt Token IDs 计算剩余上下文预算,再把截断后的 max_tokens 同步到 sampling_params.max_tokens,确保调度预算和 Engine 实际生成预算一致。

Render Token In fallback

Render 连接失败、超时、明确不支持或响应结构无效时,Coordinator 自动回退本地 tokenizer。fallback 不再由单独开关控制,开启 Render 后始终具备该可靠性兜底。

本地 tokenizer 在首次真实 fallback 时延迟加载,避免 Render 正常时提前占用额外内存和启动时间。模型路径复用部署阶段从 Engine 配置解析出的 model path,不依赖 KV Conductor 是否开启。

请求本身的校验错误不应回退。例如非法参数、上下文超限或无法应用的请求配置,应保留原始错误,避免本地 tokenizer 接受请求后改变语义。

Token In fallback 只保证 Coordinator 可以获得 Prompt Token IDs。若 fallback 结果缺少构造 token-only Generate 所需的完整 Render metadata,则该请求继续沿用已有原生 Engine 链路,不会静默构造不完整的 token-only 请求。

Generate 阶段

Generate 位于调度之后。现有 P/D、Union 和降级调度职责保持不变,但支持的请求会从 OpenAI Request 转换为 token-only EngineRequest。

Engine 只负责模型推理,不负责 OpenAI Response 拼装。

  • 非流式请求返回完整 GenerateResponse;
  • 流式请求返回 GenerateResponse Chunk;
  • Handoff 中 Prefill 和 Decode 使用各自 leg 的上下文及 KV Transfer 参数;
  • Union 由同一实例完成完整生成;
  • Trigger 通过薄兼容调用公共 token-only 构造逻辑,不扩展新的 Trigger 专有协议。

TokenizedRequest 到 EngineRequest 的转换、GenerateResponse 校验和 batch request ID 生成集中在 token-only 公共层。Router 只负责资源分配、P/D 生命周期、重试和释放,避免三种拓扑重复理解 Render 协议。

非流式 Derender

非流式请求由 Coordinator 将完整 GenerateResponse 交给 Derender。Derender 根据原始请求上下文和 GenerateResponse 恢复:

  • 最终文本;
  • Finish Reason 和 Stop Reason;
  • Prompt、Completion 和 Total Tokens;
  • Logprobs;
  • Reasoning Content;
  • Tool Calls;
  • 其他 Engine metadata。

Derender 失败发生在推理完成后的响应转换阶段,因此不会重新执行 Prefill/Decode,也不会计入 Engine circuit breaker。

Streaming Derender

Streaming Derender 不能通过对每个 Token Chunk 独立调用非流式 Derender 实现。单个 Chunk 可能只包含半个 UTF-8 字符、未闭合的 Tool Call JSON、Reasoning 与 Content 的切换片段,或尚未完整出现的 Stop Token,这些处理都依赖前序状态。

当前由 Coordinator 维护请求级 StreamingRenderSession。每条 prompt/choice 逻辑流拥有独立 stream_state

  1. 收到 Engine Chunk 后,使用当前 committed state 调用 Derender;
  2. Derender 返回 OpenAI Chunk 和 next state;
  3. 只有 Derender 成功且 Chunk 可以提交给客户端后才保存 next state;
  4. Derender 失败时不推进状态,也不将该 Token 标记为已输出;
  5. attempt 在首个可见 Chunk 前失败时恢复 attempt 起点快照;
  6. 已有可见输出后发生 Engine 故障时保留最后 committed state,供下一 attempt 接续。

该状态模型既满足 vLLM 官方的无状态 Render tier 设计,也能与 Motor 的 Rescheduler 生命周期对齐。

多 prompt 流式 Completions

vLLM Render 可以把 Completions 的 prompt 列表转换为多个 GenerateRequest,而 token-only /inference/v1/generate 仍以单 GenerateRequest 为执行单位。因此,Coordinator 在 Handoff 和 Union 下执行 fan-out/merge:

prompt 0 -> Generate stream 0 -> Derender state 0 --+
prompt 1 -> Generate stream 1 -> Derender state 1 --+-> merge -> client SSE
prompt 2 -> Generate stream 2 -> Derender state 2 --+

处理规则如下:

  • 单 prompt 统一为长度为 1 的 batch,不维护两套协议模型;
  • 每个 prompt 使用唯一 Generate request ID;
  • 同一 prompt 内按顺序 Derender,不同 prompt 可以并发;
  • 每条流维护独立 stream_state
  • 聚合时恢复全局 choice index;
  • Prompt Token IDs 只输出一次;
  • Usage 按请求级语义合并;
  • 整个响应只输出一个 [DONE]
  • 有界队列限制慢客户端导致的内存增长;
  • 任一子流失败或客户端断开时,取消并等待所有 sibling producer;
  • 所有 Generate stream ready 后再提交客户端响应,避免部分子流尚未建立便提前返回成功。

Trigger 多 prompt 流式仍沿用原生链路。Trigger 已计划日落,本阶段不为它新增复杂的多流编排。

流式 replay 与重调度

Render 接入后不扩大 Motor 原有 Rescheduler 能力,但原来支持的单 prompt 流式 replay 需要继续保留。

单 prompt Engine attempt 失败时:

  1. Rescheduler 根据已成功 Derender 并提交给客户端的语义 Token IDs 构造 RetryRequestPlan;
  2. 将已提交输出追加到原 Prompt Token IDs,形成 replay prompt;
  3. 按已输出 Token 数扣减剩余生成预算;
  4. 新 attempt 的 Prefill 和 Decode 使用同一份 replay Token IDs;
  5. 新实例继续通过 token-only Generate 输出;
  6. Derender 从最后 committed stream_state 接续;
  7. 客户端继续使用原 SSE 连接,不重复接收已提交内容。

重调度后仍走 token-only 链路,不重新执行 Render Tokenization。replay 输入已经是确定的 Token IDs,重新应用 Chat Template 或 tokenizer 可能改变请求语义。

多 prompt 在客户端首个可见 Chunk 之前可以按请求级语义整体重试。已经输出后的逐 prompt 跨实例 replay 需要独立维护每条 prompt 的 Token ledger、剩余预算、实例和 Derender 状态;未接入 Render 前也不具备该能力,因此本阶段不扩展。

Token 混淆适配

主干已经提供 Token/图像混淆能力。流式 Render 需要明确语义 Token IDs 与物理 Token IDs 的边界:

Render semantic IDs
  -> obfuscate
  -> Engine physical IDs
  -> deobfuscate Engine output
  -> Derender semantic IDs
  • Engine 和与混淆权重匹配的 KV 链路使用物理 Token IDs;
  • 流式 Chunk 在进入 Derender 前解混淆;
  • Rescheduler 保存已经提交给客户端的语义 Token IDs;
  • replay prompt 在发往新 Engine attempt 前重新混淆;
  • 开启混淆时保持 fail closed,不能回退到可能暴露明文 Token 的原生 Engine API;
  • 未开启混淆时基础 Render 行为保持不变。

本方案只适配 Render 与现有混淆接口的交互,不修改混淆算法和生命周期。

能力范围

API/场景 Render Token In token-only Generate Derender 重调度能力
Chat Completions 非流式 支持 Handoff、Trigger、Union 非流式 Derender 保持原能力
Chat Completions 流式 支持 Handoff、Trigger、Union Streaming Derender 单 prompt token-only relay
Completions 单 prompt 非流式 支持 Handoff、Trigger、Union 非流式 Derender 保持原能力
Completions 单 prompt 流式 支持 Handoff、Trigger、Union Streaming Derender 单 prompt token-only relay
Completions 多 prompt 非流式 支持 Handoff、Trigger、Union 批量 Derender 保持原能力
Completions 多 prompt 流式 支持 Handoff、Union fan-out 多状态 Streaming Derender 首包前整请求重试
Trigger 多 prompt 流式 可用于前置 Tokenization 原生链路 原生链路 保持原能力
Responses/Anthropic/SGLang 不进入 Render 原生链路 原生链路 保持原能力

表中的“原生链路”表示继续使用接入 Render 前的 Engine API 和响应处理,不表示请求失败。

上下游影响

客户端和客户面接口

客户面继续使用:

POST /v1/chat/completions
POST /v1/completions

客户端不需要感知 Motor 内部的 Render、Generate 和 Derender 接口。非流式响应保持完整 JSON;流式响应保持 OpenAI-compatible SSE。未开启 Render 时,请求完整沿用原链路。

当前 Render Client 只为 Chat Completions 和 Completions 配置了明确 endpoint。Responses API、Anthropic API 和 SGLang 请求不会先尝试一个不存在的 Render 路由,而是继续使用原生链路。

Coordinator

Coordinator 从 OpenAI 请求转发和调度组件,演进为 Tokenized Serving 链路的编排者。一个请求生命周期内需要管理:

  • 原始 OpenAI Request;
  • Render 后的 TokenizedRequest;
  • Prompt Token 数和 Sampling Parameters;
  • 调度、P/D leg 和 KV Transfer 上下文;
  • 非流式 GenerateResponse,或流式 Generate Chunk;
  • Streaming Derender state;
  • attempt/replay 信息;
  • 取消、超时和资源释放状态。

这些能力通过现有 Render Client、tokenization service、token-only 公共层和 response processor 承载,本阶段不引入通用 Frontend Adapter 抽象。

非流式请求需要暂存完整 GenerateResponse。流式请求只保留有界队列、状态和必要的 replay ledger,不等待完整生成结束后再响应。

Router、Scheduler 和 KV Conductor

Router 和 Scheduler 的职责不变,但支持 Render 的请求会使用 Render 返回的最终 Prompt Token IDs。

Prompt Token 数不再通过文本长度推测;KV Conductor 查询也使用最终 Token IDs。Chat Template 中加入的系统提示词、角色标记和工具定义都能被纳入 KV Cache 匹配,从而提高亲和性调度的准确性,并避免流控统计与 Engine 实际 Token 消耗不一致。

Render 的价值不仅是完成 Tokenization,更重要的是为 Motor 调度和 Engine 提供唯一、真实的 Token 视图。

KV Conductor 原有 prefix hash 计算逻辑继续复用,无需修改 Scheduler 策略。输入 Token IDs 的变化可能影响 KV 命中和实例选择结果,这是预期行为,不属于调度策略变更。

Engine 和 token-only 接口

Token-only 请求通过以下接口发送:

POST /inference/v1/generate

非流式时返回完整 GenerateResponse,流式时返回 GenerateResponse Chunk 流。Motor 保留 Engine 所需的 P/D dispatch、KV Transfer、优先级和请求元数据,不因接入 Render 丢失原有 Coordinator 与 Engine 交互能力。

旧 OpenAI Engine 链路仍作为未开启 Render、不支持 API,以及受控能力回退场景的承载方式。当前方案不要求立即删除 Engine 中所有 tokenizer 和 OpenAI API 能力。

API 变更

客户面 API

客户面 OpenAI API 路径不变:

POST /v1/chat/completions
POST /v1/completions

Coordinator 与 Render Sidecar

POST /v1/chat/completions/render
POST /v1/completions/render
POST /v1/chat/completions/derender
POST /v1/completions/derender
GET  /health

Render 接收原始 ChatCompletionRequest 或 CompletionRequest,并返回供 Engine 消费的 GenerateRequest 信息。

非流式 Derender 接收完整 GenerateResponse;Streaming Derender 接收单个 Generate Chunk 和上一份 stream_state。Render Sidecar 不保存请求级 Session。

Completions 支持单 prompt 和多 prompt。多 prompt 场景下,Coordinator 保持 Rendered Request、GenerateResponse、choice index 和 Prompt Token 统计之间的对应关系。

Coordinator 与 Token Engine

POST /inference/v1/generate

同一接口承载非流式和流式 token-only Generate。是否流式由请求参数和响应读取方式决定,不另外增加一套 Engine endpoint。

配置

"render_config": {
  "enable": true,
  "image_name": "vllm-render-cpu:v0.28.0-arm64",
  "endpoint": {
    "host": "127.0.0.1",
    "port": 8100
  },
  "timeout_ms": 5000,
  "renderer_num_workers": 4
}

配置说明:

  • enable:显式开启 Render;
  • image_name:非空时使用独立 Render CPU 镜像,为空时复用 Motor 服务镜像;
  • endpoint:Sidecar 模式通常使用 127.0.0.1:8100
  • timeout_ms:单次 Render 或 Derender HTTP 请求超时,默认 5000 ms。流式场景中每个 Derender Chunk 单独应用该超时,并非整条 SSE 共用 5 秒;
  • renderer_num_workers:控制 Render worker 数量,默认值为 4,高并发场景可增加。

Render worker 数在部署时默认与 inference_workers_config.num_workers 保持一致,避免多个 Coordinator inference worker 竞争单个 Renderer 形成排队瓶颈。模型路径和 served model name 从 Engine 配置读取,用户无需在 YAML 中重复维护。
此处vllm官方流式derender未复用wokers线程池,导致流式压测时出现性能劣化,已向vllm社区提出issue,见https://github.com/vllm-project/vllm/issues/57350

启用 KV Cache affinity 时建议同时开启 Render,让 KV 查询和 Engine 推理使用相同 Token IDs。但 Render 不因亲和性功能自动开启,避免使用不支持 Streaming Derender 的旧 vLLM 版本时导致请求失败。

版本要求

  • Render Token In:vLLM >= 0.23.0;
  • 完整非流式 Render/Derender:vLLM >= 0.24.0;
  • Streaming Derender:vLLM >= 0.27.0, 建议使用版本 >= 0.27.1。

Render、Derender 和 Engine 必须使用一致的 Model、Tokenizer、Chat Template、Special Token、Tool Parser 和 Reasoning Parser 配置。当前通过部署配置保证版本配套,尚未实现 revision/capability 自动协商。

技术方案

Coordinator 内部结构

本阶段不引入通用 Frontend Adapter,而是在 Render 相关目录中直接实现 vLLM Client 和公共处理能力:

motor/coordinator/render/
├─ models.py                内部 Render/Derender 数据模型
├─ vllm_render_client.py    Render、Derender 和健康检查 HTTP Client
├─ tokenization_service.py  Token In、校验、本地 tokenizer fallback、预算同步
├─ response.py              非流式响应处理
└─ streaming_response.py    流式状态、Derender、batch merge、背压和取消

Router 侧通过 token-only 公共模块完成 TokenizedRequest 到 EngineRequest 的转换:

motor/coordinator/router/token_only.py

Handoff、Trigger 和 Union 的策略实现只保留拓扑相关生命周期,不重复实现 Render 协议细节。Trigger 的现有入口由公共层提供薄兼容,后续 Trigger 日落时可以删除包装而不影响 Render 核心能力。

请求生命周期

非流式请求:

Request Context
├─ original OpenAI request
├─ tokenized request(s)
├─ prompt token count
├─ routing / leg context
├─ complete GenerateResponse(s)
└─ timeout and cancel state

流式请求额外维护:

StreamingRenderSession
├─ state per prompt/choice
├─ attempt snapshots
├─ committed semantic output token IDs
├─ usage / finish metadata
├─ bounded output queue
└─ producer cancellation state

Render 失败发生在推理调度之前,不占用 Engine retry。Generate 失败复用已有实例熔断、重调度和 relay。Derender 失败发生在响应转换阶段,不重新执行推理。

客户端断开后,Coordinator 取消正在执行的 Generate 和 Derender producer,等待任务退出,并通过现有 Router 生命周期释放 P/D 或 Union 状态和流控配额。

故障处理

故障域 典型场景 处理方式
Render Token In 连接失败、超时、明确不支持、响应无 Token IDs 回退延迟加载的本地 tokenizer
Render 请求校验 400/422、非法参数、上下文超限 保留请求错误,不通过 fallback 改变语义
token-only Engine 能力 /inference/v1/generate 返回 404/501 未提交输出且未开启混淆时回退原生 Engine API
Engine 执行 Prefill/Decode 连接失败、5xx、实例退出 复用现有 Engine 熔断、重调度和 relay
Derender 请求校验 400/422 或非法 stream state 返回对应错误和 detail,不重新推理
Derender 能力缺失 404/501 返回明确响应阶段错误,不重新推理
Derender 不可用 连接失败、超时、5xx 映射为 502/504,结束响应,不重新推理
客户端断开 SSE 消费端关闭连接 取消并等待关联 Generate/Derender producer

Render、Engine 和 Derender 是不同故障域。Derender 错误不消耗 P/D retry,也不上报 Engine circuit breaker。当前不对 Derender 增加自动重试;后续如果增加,只能针对相同 Chunk 和相同 previous state 做有限短重试,并保持成功后才提交状态。

新的 Render 可用性判断只影响新请求是否尝试 Token In,不能中断已经进入 Streaming Derender 的在途请求。

可观测性

请求链路需要能够区分以下阶段:

OpenAI Request
    -> Render
    -> Flow Control
    -> KV Query
    -> Schedule
    -> Generate
    -> Derender
    -> Client Response

当前通过日志记录:

  • Render 成功:request ID、model、prompt length、latency、tokenizer_source=render
  • Render fallback:request ID、reason、latency、fallback=local_tokenizer
  • token-only Prefill/Decode/Union:request ID、instance、phase 和结果;
  • Streaming Derender:request ID、prompt/choice、attempt 和错误类型;
  • relay:已提交 Token 数、剩余预算和新旧实例;
  • 多 prompt 取消:失败子流、被取消 sibling 和客户端断开原因。

测试方案

功能测试

功能测试需要覆盖:

  • 非流式 Chat Completions 和 Completions 完整闭环;
  • 流式 Chat Completions 和 Completions 的逐 Chunk Derender;
  • 单 prompt 和多 prompt;
  • Stop Tokens、Logprobs、Finish Reason 和 Usage;
  • Reasoning、Tool Call、Unicode 和 Structured Output;
  • 多 prompt choice index、Usage 合并和唯一 [DONE]
  • 单 prompt Engine 故障后的 token-only relay;
  • 客户端断开、队列背压、单路失败和 sibling cancellation;
  • 开启和关闭 Token 混淆。

一致性测试

使用同一组请求分别调用:

A. 原生 vLLM OpenAI API
B. Render -> Generate -> Derender

对比:

  • Prompt Token IDs;
  • 最终文本和流式 Chunk 拼接结果;
  • 输出 Token IDs;
  • Finish Reason 和 Stop Reason;
  • Prompt、Completion 和 Total Tokens;
  • Logprobs;
  • Content、Reasoning 和 Tool Calls 的拆分结果;
  • 错误码和错误类型。

对于确定性推理应要求结果完全一致。对于非确定性推理,可以固定生成 Token IDs,单独验证 Derender 的输出一致性。

故障测试

故障测试覆盖:

  • Render 启动时不可达和运行中停止;
  • Render Token In 超时;
  • Render 返回非法结构;
  • Derender 404、校验错误、超时和 5xx;
  • prompt relay;
  • 多 prompt 某一子流失败;

每类故障都要验证不会遗留流控配额、P/D 请求状态、Request Context、后台 producer 或未关闭的 HTTP stream。

性能测试

在相同模型、请求集、并发度和拓扑下分别关闭和开启 Render,观察:

  • Coordinator、Render、Prefill 和 Decode 的 CPU 使用率;
  • Render Tokenization latency;
  • TTFT、TPOT、E2E latency 和 P99;
  • 系统吞吐和 P/D 设备利用率;
  • 长 Prompt、复杂 Chat Template 和高并发场景的收益。

非流式 E2E 包含完整 Derender;流式 TTFT 包含首个 Generate Chunk 和首个 Streaming Derender RPC,二者需要拆分观察。

验收标准

非流式能力

流式能力

总结

阶段一已经打通非流式 Render、Generate 和 Derender,使 Coordinator 能够在调度前获得最终 Prompt Token IDs,并验证 Token 流控、KV Cache 亲和性调度和 token-only Engine 的组件边界。

当前阶段在同一数据模型上补齐了流式 Generate 和 Streaming Derender,并实现多 prompt fan-out/merge、单 prompt token-only relay、状态提交与回滚、异常取消和 Token 混淆适配。接入前后,客户面 API、Scheduler 契约和原有调度职责保持不变;Render 新增的是前后处理协议和相应故障域,不扩展 Motor 原本不具备的调度或多 prompt replay 能力。

likedislike
ascend-robotascend-robot成员
8月13日 添加了label:rfc
Yyilunh
8月13日 修改了issue 的描述
Yyilunh
8月13日 修改了issue 的描述
wangyang
wangyang成员
8月14日 评论:

👋 您好,感谢向 mindie-motor 提交 Issue!
🎉 我们已收到您的反馈,感谢你对开源社区的支持!

📅 处理时效 维护团队将在工作日 24 小时内查看并回复您的问题。
🔍 自助排查(推荐优先查看) 在等待回复期间,您可以先查阅仓库README以及历史 Issue 中相似问题的解决方案,多数问题可快速解决。
💡 为了更快定位问题,请您确保 Issue 包含:

清晰的问题描述
可复现的操作步骤
相关日志、截图或环境信息
我们会尽快跟进,感谢您的理解与配合!

likedislike
Yyilunh
8月14日 修改了issue 的描述
此处折叠了17条事件消息 查看更多
ascend-robotascend-robot成员
4 天前 添加了label:resolved