快速结论:这个 RFC 讨论的是“流式 derender”(streaming derender)在 disaggregated serving 里的实现设计,不是某个已报错的运行时故障。如果你的场景是交互式 P/D 服务(例如 llm-d 按 token 返回给终端客户端),优先排查两件事:一是 derender 侧是否误用了批量 derender 端点,二是是否为了流式输出去逐块调用 tokenizer.decode(),而不是使用增量解析和 stream_state 回传。
适用环境:Issue 讨论涉及 vLLM 的 distributed/scale-out 部署(/inference/v1/generate、/v1/chat/completions/derender、/v1/completions/derender)、render server(GPU-free)、增量 detokenizer、reasoning/tool parsers、SSE 生产端。性能数据仅来自文中引用的测试:1×A100-40G、Qwen3-8B、hermes 与 qwen3 parser。Issue 未给出操作系统或 Python/CUDA 版本。
最快修复方案:暂无确认的一步修复方案。Issue 中已合入的 #50550 提供了流式 chat derender 的思路:每块在一个全新 parser 上通过 parse_delta 重放,客户端回传 stream_state(含 output_token_ids 与 output_chunk_lens),请求需携带 prompt_token_ids。可优先对照这些行为检查。可优先尝试:若 renderer_num_workers 仍为默认值 1,可评估调大 worker 数,因为 #57528 已将 _detokenize_delta 移到 renderer executor;该调整是否生效取决于你的具体版本是否包含对应修复。
注意事项:流式 derender 是无状态设计,SSE 输出被明确拒绝,最终 usage chunk 由客户端负责。一处一个 choice;若出现 prompt + 历史 + delta 超过 max_model_len、缺少 prompt_token_ids、parser 未配置 chat_request 等情况,服务端会以 400 关闭请求。已知限制:流式 logprobs 尚未覆盖 parsed 路径;gpt-oss 未设置 --reasoning-parser 时两条路径都不解析;stop strings 仍可能出现在 derender 文本中(#57052)。
问题场景
使用 vLLM 的 disaggregated serving / scale-out 架构时,生产端 /inference/v1/generate 在 request.stream 为真时已返回 SSE StreamingResponse,每步携带 delta token IDs;但消费端要拿到 OpenAI 风格的 chat/completion 流式响应,就必须在 derender 侧做增量 detokenize 与 reasoning/tool 解析。若直接用批量 derender 端点(消费完整 GenerateResponse)或对每个 chunk 单独 decode,就会出现流式输出无法打通、空格/多字节字符异常、tool call 解析不完整等问题。
报错原文
[RFC]: Streaming Derender for Disaggregated Serving
a naive per chunk tokenizer.decode() produces wrong spacing and can split multi-byte characters (see #44620)
原因分析
可能原因:流式 derender 与批量 derender 的输入类型不同——前者消费 GenerateStreamResponse 的 delta,后者消费完整 GenerateResponse,因此不能作为批量端点上的一个普通参数复用之。其次,增量 detokenization 和 streaming parser 都是有状态的:IncrementalDetokenizer 依赖 prefix_offset/read_offset 与历史 token,逐块无状态 decode 会导致错误间距甚至把多字节字符切断。Issue 选择 Option A:服务端把更新后的状态随每个 chunk 返回,客户端在下一块原样回传,保持服务端无状态。
环境排查
- 确认生产端是否已使用
/inference/v1/generate的流式路径,以及stream/stream_options是否从/render正确转发。 - 确认 derender 侧调用的是
/v1/chat/completions/derender或/v1/completions/derender的stream: true模式,而不是批量端点。 - 确认请求携带
prompt_token_ids,且每个 chunk 只有一个 choice。 - 确认每个 chunk 都回传了上一块返回的
stream_state(含output_token_ids、output_chunk_lens)。 - 确认 chat parser 已配置并能拿到
chat_request;使用 gpt-oss 等模型时确认已设置--reasoning-parser。 - 确认
renderer_num_workers取值是否与并发流量匹配,并核对所用 vLLM 版本是否包含 #57528。 - 检查 prompt + 历史 + delta 的 token 总量是否超过
max_model_len。
解决步骤
- 按生产端 SSE 的 delta 粒度逐块调用流式 derender:输入为
DerenderChatStreamRequest或DerenderCompletionStreamRequest,输出为{ "chunk": ..., "stream_state": ... },不要传入完整GenerateResponse。 - 服务端实现上使用增量 detokenizer 与增量 parser,不要对每个 chunk 直接调用
tokenizer.decode();每块在全新 parser 上通过parse_delta重放,保持与/v1/chat/completions一致的reasoning/content/tool_callsdelta。 - 保留生产端的 chunk 边界:
DerenderStreamState同时携带output_chunk_lens,多 token 步(如 spec decode)在一次parse_delta调用中重放。 - 客户端循环:每次把上一块返回的
stream_state回传到下一块;n > 1视为 N 条独立流;最终 usage chunk 由客户端自行发送。 - 若对延迟敏感,可评估将 detokenization 与解析下放到 decode pool(#56851 方向),而不是在 render tier 上做重放;无状态消费方(RL、批量、自行做后处理的网关)则可继续使用流式 derender。
- 可优先尝试:若流式 plain 路径在并发下被单个 worker 卡住,调大
renderer_num_workers,前提是版本已包含 #57528。
验证方法
用 test_derender_parity.py 风格的对拍验证:覆盖 plain、reasoning、tool、reasoning + tool 四类 chunked 路径,包含一个多 token chunk 和一个仅含 finish 的最终 chunk,比对流式 derender 输出与同一 token 序列在 /v1/chat/completions 下的 delta 是否一致。交互场景还可关注端到端 p50 延迟与 renderer worker 饱和度。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[Feature / Question]:: /v1/systemone rejects image inputs on standard multimodal VLMs with "this model does not take image inputs"](https://www.chat-gpts.plus/wp-content/uploads/2026/10/62804-096dcaf8-768x403.jpg)

