/api/generate silently ignores think when format is set; /api/chat does not

当你在 Ollama 中对思考型模型(如 qwen3、gpt-oss)同时使用 /api/generate 并设置 think: true 和 format (结构化输出/JSON)时,思考过程会被静默忽略,导致返回答案错误或干脆不是合法 JSON,而 /api/chat 的同参数请求正常。优先改用

快速结论:当你在 Ollama 中对思考型模型(如 qwen3、gpt-oss)同时使用 /api/generate 并设置 think: true 和 format(结构化输出/JSON)时,思考过程会被静默忽略,导致返回答案错误或干脆不是合法 JSON,而 /api/chat 的同参数请求正常。优先改用 /api/chat 端点;如果必须用 /api/generate,先取消 format 或关闭 think。

适用环境:Issue 中确认的环境包括 macOS,Ollama 0.32.5(后续评论确认 0.32.9 仍存在),测试模型为 qwen3:0.6b 和 gpt-oss:20b。其他平台和模型未在 Issue 中逐一验证。

最快修复方案:Issue 中明确验证过的变通方案是:当需要使用 format 且模型支持思考时,改用 /api/chat 端点发送同样的请求。/api/generate 侧暂无确认的一步修复方案(Issue 中讨论的 deferral 修复方向只是计划,尚未有已验证的发布版本)。

注意事项:把 think 从 true 改成具体等级(如 medium)并不能绕过问题,这是评论中明确说明的。Issue 返回的是 HTTP 200 加看似合理的文本,不会报错,容易误以为请求成功,需要自行校验 JSON 是否合法以及 thinking 字段是否为空。

问题场景

用户在 Ollama 0.32.5(及后续 0.32.9)中,对支持 thinking 的模型调用 /api/generate,同时在请求体里设置了 think(布尔或等级)和 format(JSON Schema 或 "json")。典型复现场景是让模型做数学题(17 × 23)并返回结构化 JSON,或让模型仅输出合法 JSON(如 {"ok":true})。同一个模型、同一段 prompt、同一个 seed,只切换端点:/api/generate 出现异常,/api/chat 正常。

补充观察:不使用 format 时,/api/generate 的 thinking 输出正常(约 3764 字符),说明触发条件是 think 与 format 同时出现。使用带 builtin parser 的模型(如 gpt-oss:20b)时,故障更严重:推理文本本身被塞进 response,结果完全不是合法 JSON,thinking 为 null。

报错原文

/api/generate silently ignores think when format is set; /api/chat does not

/api/generate  think=true format=set -> thinking=    0 chars  response={"answer": "user_answer"}
/api/chat      think=true format=set -> thinking= 1650 chars  content ={ "answer": "391" }

Still present in 0.32.9, and on a model with a builtin parser the failure mode is worse than a degraded answer: the reasoning text itself lands in response, so the result is not valid JSON at all and thinking is null.

/api/generate  think=medium format=json -> thinking = None       response = 'The user says: ":", "    }'
/api/chat      think=medium format=json -> thinking= 328 chars  content  = '{"ok":true}'

注意:该问题不会返回 HTTP 错误码,接口以 HTTP 200 返回,因此不会抛出异常,只能通过比对 thinking 字段和 response/content 内容发现。

原因分析

根据 Issue 作者的分析,根因在服务端路由处理逻辑不一致:

  • ChatHandler 在有 thinking 状态时,会把 grammar(结构化输出约束)推迟到 thinking 结束之后再应用,走的是 structuredOutputsState 的双请求路径。相关逻辑大意是:当 req.Format != nil、结构化输出状态为 None、且模型具备 CapabilityThinking 时,先把 currentFormat 置为 nil,让模型先自由推理,之后再应用格式约束。
  • GenerateHandler 同样有 builtinParser 和 thinkingState,但缺少这段等价的推迟逻辑,直接把 Format: req.Format 透传给 Completion。

结果是:/api/generate 从第一个 token 起就套用 JSON grammar,模型没有被留出推理空间,只能填出一个占位答案(例如 "user_answer"),同时 thinking 为空。对于带 builtin parser 的模型,约束方式不同,推理文本直接落入 response,导致非法 JSON。

可能原因(Issue 中未最终定论的部分):GenerateHandler 与 ChatHandler 在结构化输出与 thinking 的状态机处理上未对齐,具体到代码改动是否只需移植 deferral,评论中表示还在做 focused pass,尚未合并。

环境排查

  • 确认 Ollama 版本:Issue 中 0.32.5 和 0.32.9 均复现,如使用这两个或相近版本,命中该问题的可能性高。
  • 确认模型:支持 thinking 的模型才触发,Issue 中验证过 qwen3:0.6b 和 gpt-oss:20b。
  • 确认请求参数:是否同时出现 think(true 或等级)和 format(Schema 或 "json")。两者只出现其一时不触发。
  • 确认端点:同一组参数下 /api/generate 与 /api/chat 表现是否不同。
  • 确认响应:检查 thinking 字段是否为空/null,response/content 是否为预期 JSON。
  • 操作系统:Issue 中记录的是 macOS,其他平台未在 Issue 中确认。

解决步骤

  1. 如果业务允许切换端点:把请求从 /api/generate 改到 /api/chat。这是 Issue 评论中明确给出的 workaround,适用于所有「支持 thinking 的模型 + 设置了 format」的组合。
  2. 如果必须使用 /api/generate:暂时不要在同一个请求里同时使用 format 与 think。要么去掉 format 让 thinking 正常输出,再自行解析模型返回;要么保留 format 但关闭 thinking(think: false 或不传 think)。Issue 中未验证后一种组合在所有模型上的准确性,属于可优先尝试的规避手段。
  3. 不要尝试把 think: true 改成具体等级(如 think: "medium")。评论中已明确验证这种写法在 0.32.9 下同样无效。
  4. 如果需要在 /api/generate 上彻底修复:关注上游对 GenerateHandler 移植 /api/chat 两阶段格式处理的进展。Issue 中作者及评论者表示会先补 request 级别的回归测试,再开 draft PR,属于尚未合并的代码改动,不要直接套用非官方补丁到生产环境。
  5. 如果正在用旧版本,Issue 中没有给出「升级到某版本即可修复」的结论,因此不建议把升级作为首选修复手段;升级后仍需按上面的验证方法确认。

验证方法

用同一组 prompt、seed、模型和参数分别请求两个端点,对比 thinking 与结构化输出:

curl -s localhost:11434/api/generate -d '{
  "model":"qwen3:0.6b","prompt":"What is 17 * 23?","stream":false,"think":true,
  "format":{"type":"object","properties":{"answer":{"type":"string"}},"required":["answer"]},
  "options":{"seed":1}}' | jq '{thinking,response}'

curl -s localhost:11434/api/chat -d '{
  "model":"qwen3:0.6b","messages":[{"role":"user","content":"What is 17 * 23?"}],
  "stream":false,"think":true,
  "format":{"type":"object","properties":{"answer":{"type":"string"}},"required":["answer"]},
  "options":{"seed":1}}' | jq '{thinking:.message.thinking,content:.message.content}'

修复成功的判定标准:/api/generate 的 thinking 不再为空且长度合理,response 是符合 schema 的合法 JSON(例如 {"answer":"391"}),而不是占位字符串或推理文本。对 gpt-oss:20b 这类带 builtin parser 的模型,还应用 format:"json" 和 think:"medium" 跑一遍,确认 response 能被 JSON 解析器成功解析、thinking 非 null。

另需注意:Issue 返回的是 HTTP 200,不能只看状态码判断成功,必须检查响应体内容。

参考来源

ollama/ollama #17544

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 25534

发表回复

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