发生了什么
通过 llama.cpp 运行 Gemma 4 进行工具调用的用户一直遭遇持续性错误:返回工具结果后出现 HTTP 400/500 崩溃,或者模型忽略结果并发出第二次工具调用而非继续执行。一位 Reddit 用户利用 ChatGPT 作为调试助手阅读并修补 C++ 源代码,将故障追溯到 llama.cpp 代码库中 common/chat.cpp 内的四个具体漏洞。
核心问题在于 Gemma 4 使用的工具调用消息格式与 OpenAI 风格 API 不同。llama.cpp 流水线未能在正确的阶段将 OpenAI 风格的 assistant/tool 消息历史转换为 Gemma 原生的 tool_responses 格式,且一个 JSON 解析漏洞导致任何非 JSON 工具输出都会直接崩溃。
受影响的代码路径位于 common_chat_templates_apply_jinja()、common_chat_try_specialized_template() 和 workaround::gemma4_model_turn_builder 中——这是为绕过 Jinja 模板限制而添加到 llama.cpp 中的专用 Gemma 4 处理层。
技术深度解析
已识别出四个不同的漏洞:
漏洞 1:转换发生得太晚
在 common_chat_templates_apply_jinja() 中,从 OpenAI 风格的 tool 角色消息到 Gemma 风格 tool_responses 的转换发生在提示词差异(prompt diff)和生成提示词推导路径运行之后。这意味着 Gemma 从未看到其预期的格式。修复方案是将转换提前到函数中,在任何提示词差异逻辑执行之前进行。
漏洞 2:重复转换
在 common_chat_try_specialized_template() 中,相同的 Gemma 工具响应转换运行了第二次。由于消息已在第一轮中转换,这破坏了消息结构。修复方案是从该代码路径中移除冗余的转换。
漏洞 3:缺少空内容字段
在 workaround::gemma4_model_turn_builder::build() 中,合成的助手消息被发出时完全缺少 content 字段。Gemma 4 的聊天模板要求显式指定空字符串作为内容("content": ""),而不是省略该字段。如果没有它,模板渲染器会产生格式错误的回合边界。
漏洞 4:强制解析工具输出的 JSON(严重崩溃)
最严重的漏洞位于 workaround::gemma4_model_turn_builder::collect_result() 中。代码无条件地对每个工具结果字符串调用 json::parse()。如果您的工具返回 JSON,这可以工作,但遇到任何纯文本输出时会立即崩溃,例如:
[DIR] Components
[FILE] README.md
[FILE] package.json修复方案很简单:检查结果是否已经是 JSON 值;如果不是,则将其保留为纯字符串。与将所有工具输出标准化为字符串的 OpenAI 函数调用规范不同,Gemma 4 的流水线内部处理混合类型——但前提是代码没有先强制解析它们。
作为对比,Ollama 和 LM Studio 等框架实现了自己的 Gemma 4 聊天模板包装器,并不共享此 llama.cpp 特定流水线,因此这些前端用户可能不会遇到相同的崩溃。
谁应该关注
这影响任何通过 llama.cpp 直接运行 Gemma 4 或使用以 llama.cpp 为后端的前端(包括某些配置的 Open WebUI 和 llama-cpp-python)的开发人员或研究人员。这些漏洞 specifically 由工具使用流程触发:如果您仅进行标准聊天补全而不进行工具调用,则不会遇到这些问题。
在 Gemma 4 之上构建代理流水线、编码助手或文件系统工具的后台工程师是主要受众。任何返回非 JSON 输出的工具——shell 命令、目录列表、纯文本日志——都会遭遇 JSON 解析崩溃。正在评估 Gemma 4 与 GPT-4o 或 Claude 用于本地工具使用任务的团队应注意,llama.cpp 的基准测试结果可能因这些漏洞而人为降低,而非反映模型能力。
本周行动指南
在官方补丁发布到 llama.cpp 之前,请手动应用修复:
- 克隆 llama.cpp:
git clone https://github.com/ggerganov/llama.cpp - 打开
common/chat.cpp并定位common_chat_templates_apply_jinja()——将 Gemmatool_responses转换块移至提示词差异逻辑之上。 - 在
common_chat_try_specialized_template()中,移除或保护重复的 Gemma 转换,使其仅运行一次。 - 在
gemma4_model_turn_builder::build()中,确保合成的助手消息包含content: ""。 - 在
gemma4_model_turn_builder::collect_result()中,将 JSON 解析包裹在 try/catch 或类型检查中,并回退为将结果保留为原始字符串。 - 重新构建:
cmake -B build && cmake --build build --config Release -j$(nproc) - 在 llama.cpp GitHub 仓库跟踪上游问题,并关注解决 Gemma 4 工具调用处理的 PR。