📦 GitHub: https://github.com/geekchow/deepseek-v41-flash-inference-guide
你在哪里 :最后一站。所有组件的内部都已经看过,现在把它们缝成一个连续的故事。
读完本文你将知道:编程 Agent 第三轮请求从发出到收到工具调用的每一跳------跨越哪个边界、携带什么数据、组件内部做了什么、产出了什么。
1. 带数据标注的全景图
三种形式,按需选用:
- 交互式逐跳演示 :06-walkthrough-interactive.html(浏览器直接打开,可逐步前进 / 后退,并切换"D-17 宕机"变体对比状态变化);
- 静态全景(含所有数据标注) :draw.io 源文件 diagrams/walkthrough-annotated.drawio(用 diagrams.net 打开);
- 时序版:下图。
下图回答:示例请求在每一跳上携带的具体数据是什么。
Decode Prefill VE Prefill LB 3FS Entrypoint Recipe Agent Decode Prefill VE Prefill LB 3FS Entrypoint Recipe Agent #mermaid-svg-Z45StpcJfsJv42q9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Z45StpcJfsJv42q9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Z45StpcJfsJv42q9 .error-icon{fill:#552222;}#mermaid-svg-Z45StpcJfsJv42q9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z45StpcJfsJv42q9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z45StpcJfsJv42q9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z45StpcJfsJv42q9 .marker.cross{stroke:#333333;}#mermaid-svg-Z45StpcJfsJv42q9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z45StpcJfsJv42q9 p{margin:0;}#mermaid-svg-Z45StpcJfsJv42q9 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Z45StpcJfsJv42q9 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Z45StpcJfsJv42q9 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Z45StpcJfsJv42q9 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Z45StpcJfsJv42q9 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Z45StpcJfsJv42q9 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Z45StpcJfsJv42q9 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Z45StpcJfsJv42q9 .sequenceNumber{fill:white;}#mermaid-svg-Z45StpcJfsJv42q9 #sequencenumber{fill:#333;}#mermaid-svg-Z45StpcJfsJv42q9 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Z45StpcJfsJv42q9 .messageText{fill:#333;stroke:none;}#mermaid-svg-Z45StpcJfsJv42q9 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Z45StpcJfsJv42q9 .labelText,#mermaid-svg-Z45StpcJfsJv42q9 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Z45StpcJfsJv42q9 .loopText,#mermaid-svg-Z45StpcJfsJv42q9 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Z45StpcJfsJv42q9 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Z45StpcJfsJv42q9 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Z45StpcJfsJv42q9 .noteText,#mermaid-svg-Z45StpcJfsJv42q9 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Z45StpcJfsJv42q9 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Z45StpcJfsJv42q9 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Z45StpcJfsJv42q9 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Z45StpcJfsJv42q9 .actorPopupMenu{position:absolute;}#mermaid-svg-Z45StpcJfsJv42q9 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Z45StpcJfsJv42q9 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Z45StpcJfsJv42q9 .actor-man circle,#mermaid-svg-Z45StpcJfsJv42q9 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Z45StpcJfsJv42q9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop 约 220 步 Messages JSON thinking + edit_file + 截图 1 100,000 token max_tokens=65536 2 哈希链匹配 3 L1 = 96,896 4 SWA 目录:D-17 有快照 命中 96,896 5 未命中 3,104,64--256K 档 6 配额最低的 DP rank 7 截图 1280×800 8 图像 embedding 9 757 块,GDR 逐层换入 10 编码器算 3,104 token 解码器重放 140 token 11 新块 758--781 12 Block KV + SWA KV 13 DSpark:草稿 5 验证约 3.6,接受约 3.2 14 新 token 15 token ID 16 thinking_delta / input_json_delta 17 新块 + SWA@100,608 18 stop_reason=tool_use 19
为保持可读,图中省略了 Decode LB(在 Prefill 与 Decode 之间)和 VE LB(在 Prefill 与 VE 之间),它们的决策见下文第 ③④⑥ 跳。
2. 逐跳叙述
① 客户端 → deepseek-recipe:协议变成 token
进 :一个 POST /v1/messages 请求,stream: true,思考模式开启,带 edit_file 工具定义、前两轮历史(含历史推理和 DSML 工具调用)、本轮工具结果(终端报错,里面恰好有 '<|User|>')、一张 1280×800 截图和一句指令。
内部 (详见 recipe 深潜):
MessagesRequest.convert→ 统一 Conversation;模型名解析为 V4.1 模板;温度 1.0、top_p 1.0。render_conversation→ prompt 文本 +special_ranges。系统段写入推理强度、DSML 工具调用说明和工具 schema;历史轮次原样保留推理内容,保证与第 2 轮序列逐 token 对齐;结尾<|Assistant|><think>。- 分段编码:特殊区间直接映射 ID(如
<|User|>= 128803),普通文本段只用基础词表批量并行编码。报错里的'<|User|>'被编成普通 token------伪造控制 token 失败。 resolve_max_tokens(100000, context_limit)→ 65,536。
出:100,000 个 token ID + 采样参数。
② Entrypoint:96,896 个 token 不必重算
内部 (详见 Entrypoint 与 KV 存储):
- 命中上限
L − 134 = 99,866。 - 对本地 CHECK 角色的 kvcache-store 做哈希链前缀匹配(只查元数据):第 2 轮留下的 757 个块全部命中 →
L1 = 96,896。这一步在路由前完成,无法隐藏。 - 查 SWA 内存目录:区间
[96,768, 96,896]内,D-17 上有位置 96,896 的快照 → 命中 = 96,896。
出:请求 + 命中长度 96,896(未命中 3,104)。
③ Prefill LB:按"算力时间"而不是"请求个数"分配
内部 (详见 负载均衡器族):
- 总上下文 100,000 → 64--256K 档。
- 该档中剩余配额最多的引擎主动拉取这个请求;没有前缀亲和------任何引擎都能从 3FS 读到这 757 块。
- 组内交给配额负载最低的 DP rank。
- 单卡内 FIFO;用
cost(q = 3104, c = 96896)对照本步时间预算(DP 组内 all-reduce MIN 后的值)求解 chunk 大小:3,104 < 32,768 且预算足够 → 一个 chunk 完成。
④ VE:截图变成 embedding
prefill 引擎遇到 <|image|> 占位,经 VE LB 随机选一台 VE 机器、交给 patch 最少的 GPU(按假设 patch_size = 16,本图 80 × 50 = 4,000 个 patch)。VE 卡执行 pad → ViT → downsample → aligner,返回 embedding 填入占位处。CPU 侧预处理已由 MultimodalProcessor 异步完成。
⑤ Prefill Service:只算 3,104 个 token
内部 (详见 Prefill 篇):
- KV 换入:Fetch 进程从 3FS 读 757 块(每块同时向 11 个节点发请求、收齐 10 个即解码)进主机共享内存;引擎换入线程以 GDR 逐层搬进 HBM,与计算重叠。按 890 B/token 估算约 86 MB。
- SWA 拉回:从 D-17 经 400G VLAN 拉回 1,415,040 字节(20 层 × 134 token × 528 B)。
- 编码器层 :对 3,104 个新 token 前向,MoE 部分在 EP4 上由 EPLB 均衡(实测
moe_lb≈ 1.07)。 - 解码器层 :只对 prompt 最后 140 个 token 做 SWA Bounded Replay,窗口截断在段内,产出 decode 用的近似解码器 SWA KV,不进前缀缓存------prefill 总计算量接近减半的来源。
- 落盘:新增第 758--781 块(覆盖到 99,968)写入 3FS;SWA 快照存于 99,968,登记目录。
⑥ Decode LB → Decode Service:长请求去"轻"的卡
Decode LB 同时看每张卡的并发数和 KV 总长:我们的请求 KV 约 10 万 token,属于"长请求",被送到 KV 总长最小的卡。Block KV 与 SWA KV 经同一条 P→D 路径传过去。
⑦ Decode Service:约 220 步生成约 700 个 token
- EP64DP64:每卡 7 个专家(384 + 64 冗余),每步 MoE 访存大幅降低;所在 rank 约 60 个并发请求。
- 每步:草稿头猜 5 个 token + 置信度 → GPU 上的 TileLang kernel 按全批期望收益分配验证名额(平均约 3.58 个)→ 主模型批量验证,平均接受约 3.21 个。
- CPU 的
schedule(N+1)/post-process(N−1)与 GPU 的forward(N)/sample(N)完全重叠,GPU 无空洞。 - 约 700 ÷ 3.2 ≈ 220 步;以每用户约 246 token/秒计,约 2.8 秒。
⑧ deepseek-recipe → 客户端:token 流变成协议事件
StreamProcessor 收到一串 InferenceChunk.tokens(每个约 3 个 token):
- 先发
message_start,usage里input_tokens = 3104、cache_read_input_tokens = 96896------第 ② 步的命中结果原样反映在账单上; - 推理段输出
thinking_delta;推理里用普通 token 写出的</think>仍属推理内容; - 遇到真正的
</think>(ID 128822)→ 关闭 thinking 块;遇到|DSML|(ID 128825)开启的invoke→ 工具名一出现就发content_block_start {type: tool_use, name: edit_file}; - 参数按 DSML 边界直接转成 JSON 片段,逐个发
input_json_delta;不完整的 UTF-8 字符和被切断的标签由缓冲与增量 KMP 处理; - EOS →
message_delta {stop_reason: tool_use}→message_stop。
(以上事件序列可用 examples/recipe_stream_demo.py 在本地复现。)
⑨ 收尾:为第 4 轮铺路
decode 期间每跨过一个 128 边界就在显存内拷贝一次 SWA 窗口;请求结束(序列约 100,700)时用 100,608 处的拷贝落存到 decode 机器内存并登记目录;新块写入 3FS。Agent 执行 edit_file 后发起第 4 轮,命中长度最多可达 100,608。
变体回放:D-17 宕机
第 ② 步 SWA 目录查不到可用快照 → 命中 L1 − 128 = 96,768;第 ③ 步配额按 3,232 个 token 计算;第 ⑤ 步编码器层把命中前缀的最后 128 个 token 与 3,104 个新 token 一起前向,那 128 个 token 只重建 SWA KV、Block KV 原样复用。其余各跳不变。代价:多算 128 个 token。
3. 数字账本
| 量 | 值 | 出处 |
|---|---|---|
| prompt 长度 L | 100,000 | 示例设定 |
| 命中上限 | 99,866(L − 134) | 命中策略 |
| Block KV 命中 L1 | 96,896(757 块) | 第 2 轮长 97,000 |
| SWA 快照位置 | 96,896 | ⌊96,999/128⌋×128 |
| 未命中 / 实算 | 3,104(变体 3,232) | |
| 换入 KV 体积 | ≈ 86 MB | 96,896 × 890 B |
| SWA 快照体积 | 1,415,040 B | 20 × 134 × 528 |
| 解码器重放 | 140 token | KEEP_TOKENS |
| prefill 后新增块 | 第 758--781 块 | ⌊100,000/128⌋ = 781 |
| decode 步数 | ≈ 220 | 700 ÷ 3.2 |
| 生成耗时 | ≈ 2.8 秒 | 700 ÷ 246 |
| 账单 | input 3,104 + cache_read 96,896 | recipe usage |
| 下一轮可命中 | 最多 100,608 | ⌊100,699/128⌋×128 |
4. 回到 Why
第 1 章提出的痛点,在这一个请求里都被解决了:
- 长且重复的 Agent 输入:10 万 token 只算了 3,104 个------共享 3FS 前缀缓存 + 精确的命中策略;
- 缓存贵且与均衡冲突:Block KV 放独立存储集群,任何 prefill 都能命中,均衡器只管算力;SWA KV 只存 1.35 MiB 单副本,丢了只多算 128 token;
- prefill 与 decode 脾气相反:prefill 用 EP4 小组按时间预算切 chunk,decode 用 EP64 大组 + DSpark 吃满访存带宽;
- MoE 负载不均:EPLB 把最慢卡压到平均值的 1.07--1.09 倍;
- 多协议与安全:recipe 让 Messages 客户端得到与官方一致的行为,并且无法通过文本伪造控制 token;
- 多厂商硬件:同一套 DeepInfer 引擎跑在英伟达 SM100 和昇腾 950 上。
这就是单集群每天 155 万亿 token、每 GPU·小时 38 美元背后的完整链条。
📚 返回专栏目录