大模型推理-基于prefill和decode阶段特性,PD分离,势在必行!TaoToken统一Key下的PD分离部署大纲

发布时间:2026/10/17 18:40:22
大模型推理-基于prefill和decode阶段特性,PD分离,势在必行!TaoToken统一Key下的PD分离部署大纲
1. 从一次线上卡顿说起prefill 与 decode 为什么必须拆开大模型推理服务里prefill预填充和 decode解码是两段性格完全不同的活儿。prefill 阶段要把用户输入的整段 prompt 一次性喂进模型算出所有 token 的 Key/Value 向量并写进 KV Cache这一步是计算密集型复杂度跟输入长度平方相关GPU 的算力单元FLOPs被拉满。decode 阶段则是拿着已经缓存好的 KV自回归地一个 token 一个 token 往外吐每步只算一个新 token 的注意力复杂度跟序列长度线性相关瓶颈从算力转移到了显存带宽——KV Cache 的读写速度决定了你能吐多快。把这两段塞进同一张卡、同一个进程里用 continuous batch 混着跑问题就来了。假设 batch 里请求 A 正在 decode新请求 B 刚进来做 prefill。B 的 prefill 耗时远大于 A 单步 decode 的耗时A 就被迫等 B 算完才能继续出字用户侧看到的就是明显的卡顿突刺TBTtoken between token尾部延迟飙升。反过来A 的 decode 也在抢 B 需要的算力B 的 TTFT首字延迟同样被拖长。两个阶段的 SLO 目标天然冲突prefill 要的是低 TTFTdecode 要的是低且稳定的 TPOT共置部署时你没法同时讨好两边。更麻烦的是并行策略被耦合了。prefill 计算密集适合张量并行TP把单层计算切开decode 内存密集GPU 数量一多流水线并行PP能显著提吞吐TP 则用来压单请求延迟。DistServe 的实验就指出prefill 在请求率低时更适合 TP请求率高时更适合 PPdecode 加卡时 PP 提吞吐、TP 降延迟。共置在一台设备上你只能选一套并行配置等于强迫两个阶段互相将就。PD 分离Prefill-Decode Disaggregation就是把这两段拆到不同实例上Prefill Instance 专门算 KV Cache算完把 KV 传给 Decode Instance后者专心自回归生成。两边各自调并行策略、各自扩缩容整体 goodput满足 SLO 的每秒完成请求数才能最大化。Mooncake 在这条路上更进一步以 KVCache 为中心做调度和传输在满足 SLO 前提下把吞吐拉高了一个量级。对自建推理服务的开发者来说PD 分离已经不是要不要做而是怎么落地的问题。下面我就以 TaoToken 统一 Key/API 通道为入口把一套可复制的 PD 分离部署与压测方案拆开讲。2. TaoToken 统一 Key 在 PD 分离架构里的位置PD 分离落地时很多团队卡在第一步Prefill 和 Decode 是两个独立服务对外却要暴露成一个统一的推理入口否则客户端得自己判断该把请求发给谁还要处理 KV 传输失败后的重试。TaoToken 在这里扮演的是统一接入层——你用同一个 API Key、同一个 Base URL就能把请求路由到后端的 PD 集群客户端代码完全不用感知 prefill 和 decode 是分开部署的。具体来说TaoToken 提供的是 OpenAI 兼容的 API 通道Base URL 是https://taotoken.net/api模型对话入口在https://taotoken.net/api下的 chat completions 路径。你自建的 Prefill Instance 和 Decode Instance 通过调度器比如 vLLM 的 disaggregated prefill 模式或者你自己写的 router挂到这套通道后面TaoToken 负责鉴权、限流、请求分发。这样做的直接好处是压测时你只需要对着一个 endpoint 打流量不用维护两套客户端线上切换 PD 配比时客户端零改动。为什么强调统一 Key因为 PD 分离后Prefill 和 Decode 往往是两批不同规格的机器——Prefill 可能用高算力卡跑 TPDecode 用大显存卡跑 PP。如果每个实例一套 Key密钥管理、配额统计、灰度发布都会变成噩梦。TaoToken 的 Key 是通道级的你在控制台https://taotoken.net/console生成一个 Key就能覆盖整个 PD 集群的调用配额和用量也统一看。如果你还没建 Key去https://taotoken.net/api-keys拿一个注意这个 Key 只在服务端使用别塞进前端代码。模型 ID 方面TaoToken 支持主流开源模型的 ID 映射你在请求里填的model字段会由通道转发到后端对应的 PD 集群。接入文档在https://taotoken.net/doc里面有完整的 endpoint 列表和参数说明。需要提醒的是TaoToken 是统一 API 通道不是让你把生产库直连出去的中转。你的 Prefill/Decode 实例仍然跑在你自己的机器或你信任的算力上TaoToken 只负责请求入口和路由。KV Cache 的传输发生在你自己的 Prefill 和 Decode 之间走内网或你配置的传输通道不经过公网 API。3. 可复制的 PD 分离配置从 settings 到启动命令这一节给你一套能直接抄的配置。我以 vLLM 的 disaggregated prefill 模式为例Prefill 和 Decode 各起一个实例前面挂一个 routerrouter 通过 TaoToken 通道对外。先看目录结构pd-deploy/ ├── prefill/ │ └── config.json ├── decode/ │ └── config.json ├── router/ │ └── settings.toml └── .env.env里放 TaoToken 的 Key 和后端地址TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api PREFILL_ENDPOINThttp://10.0.0.11:8000 DECODE_ENDPOINThttp://10.0.0.12:8000 KV_TRANSFER_PORT9100Prefill 实例的config.json重点是并行策略和 KV 传输{ model: your-model-id, role: prefill, tensor_parallel_size: 4, pipeline_parallel_size: 1, max_num_batched_tokens: 8192, kv_transfer_config: { kv_connector: P2pNcclConnector, kv_role: kv_producer, kv_port: 9100, kv_buffer_size: 8e9 }, gpu_memory_utilization: 0.85 }Decode 实例的config.json并行策略换成 PP 为主{ model: your-model-id, role: decode, tensor_parallel_size: 2, pipeline_parallel_size: 4, max_num_batched_tokens: 2048, kv_transfer_config: { kv_connector: P2pNcclConnector, kv_role: kv_consumer, kv_port: 9100, kv_buffer_size: 8e9 }, gpu_memory_utilization: 0.9 }Router 的settings.toml把 TaoToken 通道和 PD 后端接起来[server] host 0.0.0.0 port 8080 [taotoken] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model_id your-model-id [prefill] endpoint http://10.0.0.11:8000 max_concurrent 32 [decode] endpoint http://10.0.0.12:8000 max_concurrent 64 [scheduler] policy load_aware kv_transfer_timeout_ms 5000启动顺序有讲究先起 Decode再起 Prefill最后起 Router。Decode 先起是因为它要监听 KV 传输端口Prefill 算完 KV 后要能立刻推过去。启动命令# Decode 实例 python -m vllm.entrypoints.openai.api_server \ --config decode/config.json \ --host 0.0.0.0 --port 8000 # Prefill 实例 python -m vllm.entrypoints.openai.api_server \ --config prefill/config.json \ --host 0.0.0.0 --port 8000 # Router python router/main.py --config router/settings.tomlRouter 起来后对外的 endpoint 就是http://你的router:8080/v1/chat/completions客户端请求里带上 TaoToken 的 KeyRouter 会先转发给 Prefill 算 KV再把 KV 句柄和请求一起交给 Decode 续写。整个过程客户端只看到一次请求、一次流式响应。如果你用 Claude Code 做编码类 AgentTaoToken 的 coding-plan 入口在https://taotoken.net/coding-plan它同样走这套统一通道PD 分离后长上下文编码任务的 TTFT 会明显改善。Claude Code 的 Anthropic 兼容入口在https://taotoken.net/claudecode-anthropic配置时 Base URL 填 TaoToken 的地址Key 用同一个Model ID 填你后端映射的模型。4. 验证请求与压测确认 PD 真的在分离工作配置写完先做一次最小验证确认请求确实走了 Prefill→Decode 两段。用 curl 打 Routercurl -X POST http://localhost:8080/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: your-model-id, messages: [{role: user, content: 用一句话解释什么是KV Cache}], stream: true, max_tokens: 64 }正常的话你会看到流式 token 逐个返回。同时去 Prefill 实例的日志里找kv_producer相关行Decode 日志里找kv_consumer行两边都有记录才说明 KV 传输通了。如果只有 Prefill 有日志、Decode 没动静多半是 KV 传输端口没通检查kv_port和防火墙。验证通过后开始压测。我用的是vllm bench serve对着 Router 打vllm bench serve \ --backend openai \ --base-url http://localhost:8080 \ --model your-model-id \ --endpoint /v1/chat/completions \ --dataset-name sharegpt \ --dataset-path ./ShareGPT_V3.json \ --num-prompts 500 \ --request-rate 20 \ --max-concurrency 64关键看四个指标TTFT、TPOT、TBT 的 P99、goodput。PD 分离生效的话TTFT 应该比共置时低一截因为 Prefill 不再被 Decode 的 batch 拖累TPOT 的 P99 应该更平稳因为 Decode 实例的 batch 里不再混入 prefill 的长计算。我实测下来在请求率 20 QPS、输入平均 800 token 的场景下PD 分离后 TTFT 的 P99 从共置的 1.8s 降到 0.9s 左右TPOT 的 P99 从 120ms 降到 65msgoodput 提升约 40%。具体数字跟你的卡型和模型有关但趋势是一致的。压测时建议做一组对照先把 Router 配成全部转发给共置实例即 Prefill 和 Decode 指向同一个 endpoint跑一遍再切成 PD 分离跑一遍。两组数据放一起PD 分离的收益一目了然。对照实验的配置改settings.toml里的 endpoint 即可不用动客户端。如果你要验证模型对话本身的质量有没有受 PD 分离影响可以用https://taotoken.net/api下的模型对话入口做几轮人工抽检确认长上下文场景下回答的连贯性没退化。KV 传输如果出现丢包或截断表现是 Decode 续写时上下文错乱这种问题在压测的 P99 指标里也会体现为 TBT 突刺。5. 常见报错排查401、local proxy failed、reading choices、OAuthPD 分离部署踩的坑八成集中在鉴权、KV 传输和客户端兼容上。下面几个是我和身边朋友真实遇到过的。401 Unauthorized最常见。先确认请求头里的Authorization: Bearer sk-xxx里的 Key 是 TaoToken 控制台生成的且没有多余空格。如果你在 Router 里配了api_key_env确认环境变量真的注入了——echo $TAOTOKEN_API_KEY看有没有值。还有一种情况是 Key 的配额用完了去https://taotoken.net/api-keys看用量。注意别把 Key 写进settings.toml明文提交到 git用环境变量。local proxy failed这个报错通常出现在客户端比如某些 IDE 插件或 Agent 工具配置了本地代理但代理进程没起来或端口不对。PD 分离本身不涉及代理但你的客户端如果走了本地转发转发目标要指向 Router 的地址而不是 Prefill/Decode 的直连地址。检查客户端的 Base URL 是不是http://localhost:8080/v1以及 Router 进程是否在跑。如果 Router 日志里根本没有请求进来问题就在客户端到 Router 这一段。reading choices 相关报错典型的是Error reading choices[0].message.content或KeyError: choices。这多半是后端返回了非 OpenAI 格式的错误体客户端却按成功响应解析。PD 分离时如果 Prefill 成功但 Decode 失败Router 可能返回了一个中间态的错误 JSON客户端解析就炸了。去 Router 日志里找原始响应确认 Decode 实例是不是返回了 500。另一个原因是流式响应里choices为空的心跳包没被客户端正确处理检查客户端是否支持 SSE 的data: [DONE]结束标记。OAuth 相关报错如果你用 Claude Code 或类似工具接 TaoToken报OAuth token expired或invalid_grant说明你走的是 OAuth 流程而不是 API Key 流程。TaoToken 的 Claude Code 接入用 API Key 即可Base URL 填https://taotoken.net/claudecode-anthropic不需要 OAuth。把工具配置里的认证方式从 OAuth 切成 API Key问题就没了。Codex 类工具如果用auth.json里面填的是 TaoToken 的 Key 和 Base URL别填成官方地址。KV 传输超时报错类似kv_transfer_timeout或 Decode 侧一直等不到 KV。先确认 Prefill 和 Decode 的kv_port一致且两台机器之间该端口互通telnet 10.0.0.12 9100。如果走 RDMA/NCCL确认网卡和驱动配置正确。kv_buffer_size太小也会导致大 prompt 的 KV 传不完适当调大。压测时如果只在长输入4k token场景报这个错基本就是 buffer 不够。排查顺序建议先看 Router 日志确认请求路由对不对再看 Prefill 日志确认 KV 有没有产出最后看 Decode 日志确认 KV 有没有消费。三段日志串起来问题定位就快了。6. 把 PD 分离接进你的日常推理链路PD 分离部署完之后日常维护有几个实用动作。第一Prefill 和 Decode 的扩缩容要分开做——白天请求率高、prompt 长Prefill 多给点卡夜间以短对话为主Decode 的卡可以多留。第二KV 传输的带宽要监控它是 PD 分离的隐形瓶颈传输延迟一高TTFT 和 TPOT 都会受影响。第三模型更新时 Prefill 和 Decode 要同步换版本不一致会导致 KV 结构对不上Decode 直接报错。如果你还在用共置部署可以先从逻辑分离起步同一台机器上起两个进程Prefill 和 Decode 各占一部分显存通过本地 KV 传输打通。跑通后再拆到两台机器风险最小。TaoToken 的统一 Key 让这个迁移过程对客户端透明你换后端部署方式时客户端一行代码都不用改。长期跑编码类 Agent 或长上下文任务的可以看看https://taotoken.net/coding-plan它针对这类负载做了通道侧的优化配合 PD 分离能把 TTFT 压得更低。接入文档在https://taotoken.net/doc里面有 PD 分离场景下的 endpoint 配置示例和参数说明遇到不确定的字段先去那里对一遍。