Eval bug: mistral4 empty output on Metal for prompts over ~300 tokens (clean GGUF, with and without flash attention)

在 Apple Silicon(Metal 后端)上用 llama-server 加载 Mistral Small 4 119B(mistral4 架构)GGUF 时,提示词一长(约 300 token 以上)输出就变成空字符串, finish_reason 却是 length ,实际生成的全是特殊

快速结论:在 Apple Silicon(Metal 后端)上用 llama-server 加载 Mistral Small 4 119B(mistral4 架构)GGUF 时,提示词一长(约 300 token 以上)输出就变成空字符串,finish_reason 却是 length,实际生成的全是特殊 token。优先排查 n_ubatch-ub)是否大于等于 32,而不是先怀疑 prompt 长度或 flash attention。

适用环境:macOS + Metal 后端;Apple M5 Max 128GB、M5 Max 64GB、M4 Max 128GB 均已复现;llama.cpp build ac4cdde(Docker Model Runner latest-metal 镜像,Docker Desktop 4.82.0 / DMR v1.2.5),并在 DMR v1.2.6 / build 72874f5 复现;模型为 Mistral Small 4 119B 的 UD-IQ3_XXS 与 Q4_K_M GGUF。

最快修复方案:启动 llama-server 时加 -ub 31(或更低),例如 -ub 31-ub 16。这是 Issue 中实测验证过的唯一有效规避手段:固定同一段 619 token 提示词,-ub 31 能正常输出,-ub 32 立刻变空。

注意事项:该方案只降低 prefill(提示词处理)速度,生成速度不受影响,因为 decode 是单 token、始终走安全路径。Issue 明确限定“一个模型、一种量化、仅 Metal”,未直接检查 logits,因此 NaN 只是与 #26223 的推断吻合,未独立确认。上游是否已在 master 修复未在 Issue 中确认。

问题场景

用户在 macOS(Apple Silicon)上用 llama-server(Metal 后端)加载 Mistral Small 4 119B GGUF,通过 OpenAI 兼容接口 /v1/completions/v1/chat/completions 发起请求。短提示词(约 6 token)在关闭 flash attention 时能正常续写;一旦提示词变长(约 300 token 以上,或任意聊天模板生成的数百 token 提示),返回的 text / content 为空字符串,而 finish_reasonlength,即明明生成了 max_tokens 个 token 却没有可见内容。原始报告还提到该问题在 -fa off 甚至 --fit off 下依旧存在。

报错原文

Eval bug: mistral4 empty output on Metal for prompts over ~300 tokens (clean GGUF, with and without flash attention)

text: "", finish_reason: length
all 60 generated tokens are id 31

原因分析

根据后续复现,真正的触发变量不是提示词长度,而是 n_ubatch-ub):请求会被切成大小为 n_ubatch 的 micro-batch,默认 -ub 512 时,-ub >= 32 就会失败,-ub <= 31 则正常。同一段 619 token 提示词下,-ub 31 输出 2+2 equals 4.-ub 32 直接为空。

这与 #26223 的分析一致:ne21_mm_id_min 为 32,所以 -ub <= 31 时每个 micro-batch 都走 mul_mv_id 路径,而会把操作数收窄为 f16 的 mul_mm_id kernel 根本不会被执行。另一个补充说明是 flash attention 并非触发因素——-ub 16 单独就足以恢复正常,原始报告中 -fa off 的表现差异更可能来自 llama-cli 与 llama-server 的批处理方式不同。此外,长提示词失败时模型会退化成每个 token 都输出 id 31(词表中为 <SPECIAL_31>,CONTROL 类型),服务端会把这些控制 token 从响应文本里剥掉,所以表现为空输出而非重复文本。

环境排查

  • 确认后端为 Metal、系统为 macOS,硬件为 Apple Silicon(M4/M5 Max 均可复现)。
  • 确认 llama.cpp 构建版本(Issue 中为 build ac4cdde / 72874f5,来自 Docker Model Runner 镜像)。
  • 确认模型为 mistral4 架构:Mistral Small 4 119B UD-IQ3_XXS 或 Q4_K_M GGUF。
  • 确认启动参数中的 -ub(n_ubatch)取值;未显式指定时即为默认 512,位于失败区间。
  • 确认 -fa--fit 设置:Issue 已证明它们不是本次问题的变量。
  • 确认 GGUF 完整性:报告者用 gguf-py 检查 181/181 张 F32/F16/BF16 张量均干净,全局 max |x| = 6.78,故不是文件损坏。

解决步骤

  1. 把 llama-server 的 micro-batch 上限压到 31 或更低,例如:llama-server -m <模型>.gguf -c 2048 -b N -ub 31 --parallel 1,或用 -ub 16 进一步留出余量。
  2. 保持 flash attention 为默认值即可,无需因为该问题关闭 FA(Issue 已说明 FA 不是变量)。
  3. 用一段稳定长度的提示词做对照测试:同一提示词下切换 -ub 31-ub 32,确认边界落在 31/32 之间。
  4. 若使用 Docker Model Runner,需在其底层 llama.cpp 构建中应用同样参数;上游修复未在 Issue 中确认,Issue 曾请求提供 commit pointer。

验证方法

用长提示词(如 619 token 或更长)连续请求,观察响应 text/content 不再为空,finish_reason 不再是因空输出导致的 length。更直接的判据是切换 -ub-ub 31 能返回连贯文本,-ub 32 复现空输出,说明命中了同一失败边界。也可在 --verbose 下确认不再出现所有生成 token 都是 id 31 的情况。

参考来源

ggml-org/llama.cpp #25722

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 23777

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注