第 19 章 未解问题与路线图
MoE 大模型低成本部署框架第 19 章 未解问题与路线图
CHAPTER 19

第 19 章 未解问题与路线图

13 分钟阅读 · 3,717 字

第 8 部的最后一章(全书的正文在第 9 部 20/21 章收尾):
现在还不知道什么、下一步按什么顺序做、哪些路已经走过并否决。
权威来源:docs/项目背景与问题总览.md §5 问题清单 / §6 可观测性盲区 / §9 已否决 / §10 路线图,
加 2026-10-19 当天的增量(来自 docs/reports/ 与 ~/docs/notes/)。
状态列是整理补充的,问题本身出自 iwiki〈实训项目总览〉page-HESSCxaq(用户亲笔)。


19.1 十个问题的现状(截至 2026-10-20 北京)

# 问题 状态(含 10-19 增量) 本书章节
1 prefill 全局串行(最大瓶颈):serve 层一次只准一个请求读提示词;254K ≈ 81–91 s,8 并发 TTFT 可达 4.7 分钟;停车快照 4.7 G > 8 GiB → 全天驱逐 2508 次 → 整段重读 未修。四个旋钮已列(12.7),需停服窗口 09、12
2 1M 散落事实召回弱:BABILong 2/15、生产 300 轮 3/7;会自信答错;NIAH 15/15 ≠ 通用 1M 未试 YaRN 外推;且 10-19 起 RAG 关闭、>262,144 直接 400 → 当前连"1M 会话"这个前提都不成立,14.8 那批精度数字不可复现 05、14.8
3 并行批槽瑕疵:并发下 timings 偶发缺失/错挂;惩罚类采样参数在批槽不生效 timings/惩罚仍未修;但 10-21 起批槽链有了每窗口统计(STRATA_STEP_STATS),并发窗口的耗时/接受率第一次可测 10、10.3.1
4 长上下文不吐正文(只有 thinking) ✅ 主因已修(特殊 token 转义,快道 10-18、普道 10-19 18:58 都吃上);残余 B 模式未根因 18.4、18.6
5 该调 tool 却没调 同一主因(工具 JSON 在特殊 token 处被截断,未完成的调用被 parser 丢弃 frontend.py:560-575 #211);A 类已修,B 类(plan-then-EOS)仍在 18.4、18.6
6 图片 token 开销不确定(常从 100k 直接超窗) ✅ 机制已定位 + 已加缓解:strata-vision GPU 编码、单图 ≤1024 token(实测 33 / 324 / 996);没有文件级自动压缩;文档侧 10-19 晚加了网关抽取(PDF/DOCX/XLSX/CSV,按 256K 剩余预算头尾截断,documents.enabled=false 可回滚) 08、14.9
7 重复动作:一直调用同一个 tool 未修。bd7e62ed 已定位(路径幻觉 + 无停止条件 + 计划-执行断裂,非引擎截断),产出的是 agent 规则而不是代码 18.5
8 原生网页监控不监控并行 ✅ 2026-10-15 完成(Vue3 并发监控台) 03
9 并发小、推理速度慢 10-21 增:自适应验证窗口(--mtp-varlen)已上两条 lane —— 与固定 L 持平,证明"调窗口宽度"这条路到顶了(强制 L=2 的对照也只 +2~5%)。天花板仍是本机双实例聚合 ~145–170 tok/s,要达标只能加卡或动 prefill(#1) 10、10.3.1、14.3
10 RAG 服务可能不稳定;需更多 1M 方案 10-19 已把 RAG 关掉(rag_enabled=false,纯透传,有效 256K)→ 问题从"RAG 不稳"变成"要不要重建 RAG / 还是扩原生窗口"的路线抉择 05、19.4

一句话读这张表:#4/#5/#6/#8 有实质进展(都在网关与 serve 层,重启代价低);
#1/#3/#9 全在引擎层,一条都没动——这正是"下一步主线 = 改造推理引擎"的原因。

19.2 2026-10-19 这一天改变了什么

变化 影响
RAG 关闭(rag_enabled=false),引擎退到纯透传 有效窗口从"1M 组装"变成 262,144 硬上限;>262,144 直接 400;14.8 全套 1M 精度数字不可复现
窗口预检 + 400 改写 + fit_max_tokens(网关侧) 用户不再看到 #545 这类内部错误码;超长请求本地拒绝、不进引擎、不浪费 GPU;不再压 max_tokens=1 盲重试
文档抽取上线(rag-proxy/doc_extract.py) PDF/DOCX/XLSX/XLS/CSV/文本自动识别与抽取,扫描 PDF 走页面图,按剩余预算头尾截断
特殊 token 转义部署到两条 lane 事故 3 类早停在两条通道都消失(prompt_escapes 计数随真实流量增长)
计数口径判定:引擎 usage 为权威计费口径 客户端 CC 中文低估 ~2.4×、网关上界估算英文高估 ~3.3×、中继 256,000 vs 引擎 262,144 三处不对齐(中继侧待转交)
两次真实重启(rag-proxy、strata-a/b) 16 章 §16.7 的时间口径冲突(报告 15:33 vs supervisord 16:41)就是这次留下的

19.3 盲区清单:改造引擎前必须知道"哪些信息今天拿不到"

先别误以为全黑——已经能看到的:网关 /concurrency(每个在途请求的状态/TTFT/实时速度 + 两通道 GPU)、
/health(counters / sessions / block_rebuilds / embed_cache / 上游可用性)、
引擎 /metrics(单路状态 + 八项硬件指标 + 最近 12 次 timings)、/v1/status(模型/窗口/批槽数/GPU/RAM)、
引擎日志(prompt N = R reused + F read、批窗口、命中率)。

关键限制:/concurrency 是代理视角(“这个请求在飞”),不是引擎内部槽位视角。

想看什么 为什么看不到 方向
4 个批槽里哪个在跑、每槽进度 引擎状态模型是单路的:serve/server.py 的 status dict 会被并发请求互相覆盖;GET /slots 永远只返回 1 个槽;in_flight 是聚合数 引擎侧维护槽位表 → /slots 数组或结构化 INFO 行(要改引擎 + 重启)
批槽 MTP 链状态(是否启用、每槽草稿/接受、chain_off 降级) ⚠️ 2026-10-21 部分解决:STRATA_STEP_STATS 现每窗口一行 JSONL(slots/rows/drafts/accepted/step_ms/keeps/ctx),analyze_steps.py 汇总;但仍没有 HTTP 出口,也没有 chain_off 事件与逐槽进度 引擎加结构化输出(剩余部分)
逐槽 / 逐层专家命中率 只有请求结束时一个全局 hit rate 按槽输出计数
prefill 排队详情(队列长度、谁在等、预计 TTFT) 排队发生在 serve 层但不暴露 加排队观测
会话停车占用与驱逐 只在引擎日志(evictions=),/health 没有 暴露停车槽占用/驱逐计数
并行下的 timings 偶发缺失/错挂(#3)→ 监控页的复用率/命中率并发时不可信 上游修复或本地加固
引擎每请求内容留存 api_monitor 默认关(生产 404),开启有内存与隐私代价 按需 --api-monitor
图片展开后的实际 token 数 引擎展开多少不可见(#6) 引擎输出图片 token 数
网关侧长会话状态 全内存无持久化,重启即清空 需要持久化设计

这张表是改造引擎的输入:#3/#4/#6/#9 现在没有足够观测手段去定位,
所以 goal.md 把"补观测"列为当前主线三件事之一,并明确"没有观测就没法定位问题"。

19.4 路线图与优先级

goal.md 的当前主线(改造引擎要做的三件事)与总览 §10 的优先级合并后:

优先级 事项 为什么排这里 代价
P0 补可观测性:批槽表、MTP 链状态、逐槽命中率、prefill 排队、停车驱逐、图片 token 它是其余所有问题的前置;不做就只能猜 改引擎 + 重启(6–13 分钟/lane)
P1 治 prefill 全局串行:可并行 / 可恢复的 prefill 读 最大瓶颈(#1),TTFT 的量级差异全在这里 引擎改动 + 停服窗口;短期可先调四个旋钮
P1 TTFT 四旋钮择机实施:conversation-cache-mib 8192→24000+、prompt-cache-every 16384→4096、普道准入、组装预算封顶 已列未做,收益明确(12 章),风险是主机 RAM(现 176/216 GiB 悬顶) 只改配置,但要重启 lane
P2 批槽能力:drafter 批量化;解开 GMAX=8 对 8 路以上的限制 并发天花板(#9)的一半在这里;另一半是硬件 引擎改动,必须过逐 token 一致性
P2 1M 能力路线抉择:原生窗口扩到 1M(显存/内存代价)vs 检索增强 10-19 关掉 RAG 后,这条从"优化"变成"二选一" 决策成本 > 实现成本
P3 高速通道(既定 = 插队式,非抢占式) 已定方案,排在长上下文之后 引擎 + 网关
P3 扩展模型:异模型双通道(卡 A 跑小快模型)、适配更多 MoE、争取第三张卡 阶段 ③;#9 的另一半只能靠加卡 需要新权重与新窗口

三层递进(实训主线):① 学习项目(读懂链路与引擎源码、能复现压测)→ ② 改造推理引擎(当前) → ③ 扩展模型。

19.5 已否决 / 不再走的路(别重复踩)

方案 结论 原因
同型号单卡实例扩吞吐 曾否决,后翻案为现生产(dual) 当时 solo 9.3 tok/s,真根因是 CPU 专家池抢核,钉核即解决(18.2)
双卡各跑 dense + 专家共享(对称共享专家池) 不建议推进 引擎不支持对称共享:--peer-device 是主从专家缓存层,且与 --layer-split 互斥(11 章)
Q2_0 量化 排除 质量不可接受;若做真·快通道可重新评估
Coder 变体 不建议 中文明显变差,本服务中文为主
抢占式高速通道 不选 代价是在途请求被取消;选了插队式(忙时降级为批速度)
“换更小量化冲并发目标” 不可达 本机聚合天花板 ~145–170 tok/s,达标只能加卡
让网关做健康感知 failover 未实施(不是否决,是待办) 需要重启 rag-proxy;手册已写方案(16 章 §16.4)

19.6 按"重启代价"分层的具体待办

这一节把上面所有待办按落在哪一层重排——因为在本项目里,层决定了能不能快速做(15 章 §15.9)。

层 重启代价 待办
网关 rag-proxy 秒级(但清空内存会话/嵌入缓存) ① 健康感知 failover;② 组装保留"任务锚点"(首条用户任务 + 下一步目标 + 用户约束)——事故 5 的任务卡实验证明有效;③ 观察 window_guard_rejects 分布;④ 中继窗口 256,000 vs 引擎 262,144 的对齐(待转交中继方)
serve(Python) 1–13 分钟(模型重载) ① fit_max_tokens 的行为级验证(需 ~230k token 边缘请求,70 s+ prefill,未在现网跑);② B 模式(plan-then-EOS)的可观测补丁:stops_while_reasoning 已有计数,需要更细的 parser state 记录
引擎配置(不改代码) 1–13 分钟 四个 TTFT 旋钮(19.4 P1);--api-monitor 按需
引擎 C++ 编译 + 6–13 分钟/lane P0 观测(槽位表 / MTP 链 / 逐槽命中率 / 停车驱逐 / 图片 token);P1 可并行 prefill;P2 批槽 drafter 与 GMAX
不改代码,改 agent 0 事故 4/5 的规则:绝对路径先验证、两次无新信息就总结、"说要写"必须真的调工具、按章拆任务并 Write 落盘

观察:10-19 一天能修完窗口守卫事故、文档抽取、特殊 token 部署,是因为它们全落在秒级与分钟级两层;
而 #1/#3/#9 从 10-06 拖到今天,是因为它们全落在引擎层且缺观测。
这不是优先级问题,是代价结构问题——路线图的设计应该显式利用这个结构。

19.7 本章小结

  • 十个问题里 4 个已实质解决、1 个已定位未修、5 个未动;未动的全在引擎层。
  • 10-19 的 RAG 关闭是一个路线级动作:它把"1M 靠组装"这条路线暂时中止,
    让"扩原生窗口 vs 重建检索"变成必须显式选择的分叉。
  • 补观测排在治 prefill 之前,理由不是它更重要,而是没有观测就无法判断 prefill 改动是否有效。
  • 已否决清单里最容易被重新踩的是两条:对称共享专家池(引擎不支持)与
    换更小量化冲并发(天花板是硬件)。
  • 排期时先问"这个改动落在哪一层":能放进网关的不要放进 serve,能放进 serve 的不要放进引擎——
    这条经验是本项目十月份全部产出的元规则。

19.8 2026-10-21 增补:根因定位完成,路线换成 M0–M4

§19.4 那张表写于"要改造引擎"这个决心之前,按"问题"排优先级。逐行读完 SSEStraBed 之后,
优先级应该按机制重排——因为 #1(prefill 串行)与 #9(并发慢)原来是两个问题,
现在知道它们是同一个窗口机制的两面(20 章)。改造手册见 21 章,这里只记变化本身。

① 三个新事实改变了对 #9 的排序

原判断 10-21 复核
"并发慢"的一大半是 GMAX=8 的天花板 不是。8 行预算确实卡,但 4 路时 L_max 已经掉到 2(generate.cpp:5781);先卡住的是预算分配,不是行数上限
MTP 是并发的主要杠杆 只在贪心路径上是。网页默认 temperature 0.6 ⇒ sl.sampled ⇒ 整组退回逐 token(generate.cpp:5823)。采样流量完全没吃到投机
CPU 专家 76% ⇒ 应该加 worker 不能加。taskset -c 0-15 钉死 16 核,13 worker 已饱和(21.2 的否决记录)

⇒ #9 的第一优先从"扩 kernel"变成"改一个默认值"+“扩一个常量”(贪心默认、kStagingBlobs 16→32)。

② 新增一个此前没列进问题清单的硬上限

kStagingBlobs = 16(verify.hpp:357):一帧最多 16 个未命中专家走 PCIe 快档,其余全落 CPU。
它不是"问题清单 #N",因为没有任何文档提过它——这类"代码里存在、清单里没有"的上限,
应该定期用 grep constexpr 扫一遍(19.3 的盲区清单该补这一列)。

③ #1 的性质变了:从"要不要并行化 prefill"变成"把已有旁路拓宽"

read_windows(generate.cpp:6554)已经在生产上跑"用解码窗口读 prompt",只是 --short-read 64 限死。
所以 #1 的短期动作是放开一个参数,长期动作是把 sp.run 从同步调用改成可推进的状态机(M3)。
这比原路线图里"源码级重构批 prefill"的估算低一档。

④ 路线表(取代 §19.4 的排序,内容不冲突)

里程碑 一句话 是否改代码 判据
M0 网页默认贪心;pcie-frac;STRATA_PF_FUSED=1;--short-read 512 否(改配置/环境变量/前端默认值) 4 槽 chain 每请求 ≥100 tok/s
M1 kStagingBlobs 16→32;命中率 是(一个常量) 命中 +5pp;CPU experts 5.38→≤3.5
M2 拆每层门铃(预取 / 双窗口流水 / 零拷贝) 是(核心改动) GPU-reach wait 23%→<10%
M3 decode 与 prefill 同窗 是(重构) 解码最长停顿 4.9s→≤0.5s
M4 行预算 8→16/32(可能不需要) 是(三处 kernel) 8 槽每请求 ≥60% 单路

到 2026-10-21 为止,M0–M4 一步未实施;19.1 表里 #1/#3/#9 仍是"未修"。
这一节的价值不是进度,而是把"未修"从"不知道从哪下手"变成了"知道先动哪一行"。

全部章节