Skip to content

Fix L0 JSON output routing for reasoning models#32

Open
Xinyue-QwQc wants to merge 1 commit intoRT15548:mainfrom
Xinyue-QwQc:fix/l0-json-output-routing-upstream
Open

Fix L0 JSON output routing for reasoning models#32
Xinyue-QwQc wants to merge 1 commit intoRT15548:mainfrom
Xinyue-QwQc:fix/l0-json-output-routing-upstream

Conversation

@Xinyue-QwQc
Copy link
Copy Markdown

@Xinyue-QwQc Xinyue-QwQc commented Apr 7, 2026

AI 说明

此 Pull Request 由 AI 代表 fork 维护者发起并提交。

下面的代码修改与说明文字由 AI 辅助整理后,作为一次聚焦于根因修复的改动提交到上游仓库。

摘要

这个 PR 修复的是:在支持 reasoning / thinking 的模型上,L0 锚点提取有时会失败的根本原因。

具体问题是什么

在部分 L0 提取场景里,模型实际上已经完成了任务,但返回结果会变成:

  • message.content 是空的
  • 真正的 JSON 结果却出现在 reasoning_content 或 thinking 输出里

于是最终表现出来就是:

  • 锚点提取失败
  • 但失败并不是因为模型没做完
  • 而是因为结构化结果没有出现在提取逻辑原本预期的输出通道里

根因分析

这个问题本质上是一个“输出路由(output routing)问题”,而不只是一个 JSON 解析问题。

当前的 L0 提取请求只向模型发送了:

  • 一条要求“严格输出 JSON”的 system 指令
  • 一条包含 <round> 内容的 user 消息

这种请求形态实际上仍然把“如何回答”这件事交给了模型自己决定。

对于支持 reasoning / thinking 的模型或供应商接口,常见行为会变成:

  1. 先在 reasoning / thinking 通道里规划结构
  2. 把真正的 JSON 先放进 reasoning 通道
  3. assistant.content 留空,或者只留下不完整内容

也就是说,虽然 prompt 里要求了“只输出 JSON”,但请求结构本身并没有足够强地约束模型必须把 JSON 通过正常的 assistant.content 通道输出出来。

为什么这个 PR 是从根上修

1. 重新引入 assistant JSON prefill

这个 PR 在生成前重新加入了一段 assistant 预填充:

{"anchors":[

这样做的意义是:

  • 原本是“请你输出 JSON”
  • 现在变成了“请你继续补完这个 JSON”

对于 chat-completions 风格接口来说,这种约束更强。

这样可以明显降低模型把内容先路由到 reasoning 通道里的概率,并把输出重新拉回到提取逻辑原本期待的 assistant.content 中。

换句话说,这不是在事后容错,而是在生成阶段就把模型引导回正确的输出路径。

2. 让 Qwen3 的 thinking 关闭判断变成大小写无关

当前代码只有在模型名包含精确大小写 Qwen3 时,才会发送 enable_thinking = false

但真实使用场景里,模型名可能来自:

  • 不同 provider
  • 不同代理层
  • 用户手动填写
  • 自定义 OpenAI-compatible 后端

这些地方给出的模型标识不一定总是相同大小写。

所以把这个判断改成大小写无关后,可以更稳定地对 qwen3 变体关闭 thinking,进一步减少它走 reasoning-first 输出路径的概率。

为什么这个 PR 不包含 reasoning_content 解析兼容

因为那更像是“针对症状做兜底”,而不是“修掉根因”。

解析 reasoning_content 当然可以在某些下游场景里提升兼容性,但如果在上游直接这么做,会带来几个问题:

  • 让 L0 提取继续依赖 provider 特定字段
  • 默认接受“输出走错通道”这件事,而不是把它纠正回来
  • 让这条提取链路在不同供应商下变得更脆弱

所以从上游角度看,更干净的修法是:

  • 让模型重新把 JSON 输出到预期的 assistant.content 通道
  • 而不是默认把 reasoning 通道视作主输出来源

因此,这个 PR 有意只包含“根因修复”:

  • 为 L0 提取重新加入 assistant JSON prefill
  • 把 qwen3 的 thinking 关闭判断改成大小写无关

并且不包含 reasoning_content 的解析兜底逻辑。

验证

  • npm run lint
  • garbled text check passed
  • eslint passed

补充说明

这是我第一次向上游项目提交 PR,所以可能还没有完全符合项目习惯或维护者预期。如果这个 PR 的内容、格式或处理方式有需要调整的地方,我会非常愿意继续修改。感谢阅读和包容。

@Hhhhenrry-HA
Copy link
Copy Markdown
Collaborator

感谢这次的 PR,也谢谢你把思路和根因分析写得这么完整,我这边看得很清楚。

这次里面关于 reasoning / thinking 模型输出路由的判断方向,我觉得是有价值的,尤其你提到的这类现象,在一些模型上确实会出现。

不过这版我这边暂时不会直接合,主要还是 assistant prefill 这一点。

我们在 2.5.1 那轮调整 L0 这条链路的时候,其实也碰到过类似问题,后来排下来发现:有些 OpenAI-compatible API / 代理层对 prefill 的支持并不稳定,表现会不太一致。也因为这个原因,后面我们是有意把这类 prefill 方案拿掉的,尽量把 L0 保持在更通用的 system + user 结构上,避免不同接口下又出现新的兼容性分歧。

所以这次从通用性考虑,assistant prefill 这条路我这边就先不准备重新引入了。

不过还是很感谢你认真拆这个问题,也谢谢你把 PR 整理得这么完整。像里面 Qwen3 判断这类兼容性细节,我这边会再单独看看。如果你后面还有别的想法,也欢迎继续提。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants