第 19 章 未解问题与路线图
第 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 仍是"未修"。
这一节的价值不是进度,而是把"未修"从"不知道从哪下手"变成了"知道先动哪一行"。