Qwen-JSON:把结构化输出变成一个可训练的问题

记录 Qwen-JSON 的目标、底座、GRPO + LoRA 训练思路和结构化输出评估方法,并区分模型卡报告与独立复现实验。

Qwen-JSON:把结构化输出变成一个可训练的问题

很多模型都能在提示词里说“请只输出 JSON”,但真正把它接进程序之后,问题会立刻变得具体:字段有没有缺失,类型是不是正确,外层有没有多余文字,答案本身是不是可信。

所以结构化输出不只是提示词问题,也可以被拆成数据、奖励和评估问题。

这个公开模型是什么

我的 Freakz3z/Qwen-JSON 模型仓库 把目标集中在 JSON 结构化输出上。模型卡将它描述为 RL-Struct,并列出这些关键信息:

Base Model:Qwen/Qwen3-4B-Instruct-2507
Training:GRPO + LoRA
Tasks:JSON Recipes、GSM8K-JSON、ToolUse
License:Apache-2.0

它的基本思路是使用 GRPO 和多维奖励,让模型同时关注结构、格式、有效性、正确性和长度,而不是只依靠 constrained decoding 在推理阶段强行限制 token。

模型卡中的结果是项目页面报告的实验结果。本文不把这些数字当成我在本地重新复现过的结论。

JSON 可靠性至少有五层

一个看起来像 JSON 的字符串,距离“可用”还有很远。可以按下面的顺序检查:

1. 能否被 JSON parser 解析
2. 顶层对象和字段类型是否正确
3. 必填字段是否完整
4. 字段值是否满足任务约束
5. 最终答案是否正确且长度合理

例如:

{"name": "番茄鸡胸肉", "nutrition": "Calories: 380"}

它可能完全符合语法,但 nutrition 里的数值未必正确,也可能缺少数据集要求的其他字段。因此格式奖励只能是起点,不能代替语义评估。

为什么选择 GRPO

对于同一个 prompt,模型可以采样多条回答:

回答 A:无法解析
回答 B:JSON 可解析,但字段缺失
回答 C:结构完整,答案正确
回答 D:结构完整,但答案错误

如果验证器能给这些回答打分,就可以在组内形成相对优势,让模型提高类似 C 的回答概率。

GRPO 相比 PPO 的一个重要区别,是不必额外维护 critic 来估计 value。它不代表训练没有成本:同一个问题需要生成多条回答,奖励函数和采样策略也会直接影响训练信号。

多维奖励怎么拆

一个简单的奖励设计可以是:

JSON 可解析       -> +1
Schema 验证通过   -> +1
必填字段完整       -> +1
答案正确           -> +2
额外文本           -> -1
无意义冗长         -> -0.5

这种分层设计比直接写一个黑盒总分更容易排查问题。训练日志中应该保留每个分项,否则总 reward 上升时,很难知道模型到底学会了什么。

奖励还需要防止几个常见投机:

  • 只输出固定模板,不处理问题内容
  • 用冗长文字提高碰到关键词的概率
  • 通过边界格式绕过解析器
  • 在训练任务上高分,换一种问法就失败

页面中报告了什么

当前模型卡的性能表列出:

| 方法 | Structural Acc. | JSON Validity | Content Acc. | | --- | ---: | ---: | ---: | | GPT-3.5 Zero-shot | 45.5% | 82.1% | 88.0% | | LLaMA-3-8B SFT | 78.2% | 85.4% | 86.0% | | RL-Struct | 89.7% | 92.1% | 84.5% |

这张表很有意思的一点是:结构准确率和内容准确率不是同一个指标。一个方法可能更会输出合法结构,却没有同步提高内容正确率。

这也是我觉得结构化输出值得单独研究的原因:不能只看“能不能解析”,还要继续问“解析出来的东西有没有用”。

一条实际使用路径

模型卡提供了 Transformers、llama.cpp、vLLM、Ollama 等使用方向。对于本地测试,可以先从最小调用开始:

from transformers import pipeline

pipe = pipeline("text-generation", model="Freakz3z/Qwen-JSON")
messages = [
    {"role": "user", "content": "请输出一份简单的 JSON 食谱"},
]
result = pipe(messages)
print(result)

推理时仍然应该在应用层做 JSON parse、schema validation 和失败重试。模型训练得更可靠,不等于下游程序可以取消校验。

从模型到系统

一个结构化输出模型真正接入工具调用或业务系统后,链路更接近:

用户请求
-> 模型生成
-> JSON 解析
-> Schema 校验
-> 业务规则校验
-> 执行动作或返回结果

任何一层失败,都应该有明确的 fallback,而不是把一段未经验证的文本直接传给下游系统。

对于 ToolUse 场景,还需要额外校验工具名、参数类型、权限和高风险操作。结构正确只是安全边界的一部分。

这个项目留下的问题

Qwen-JSON 这个方向仍然有几个值得继续观察的问题:

  1. 结构奖励变高后,内容准确率是否同步提高
  2. 模型能否迁移到没有见过的 schema
  3. 复杂嵌套 JSON 是否会让错误率重新上升
  4. GRPO 的采样成本和推理收益是否值得
  5. 结构化训练会不会损害普通对话能力

这些问题不能只靠模型卡上的一个总分回答,需要把数据、验证器、采样参数和评估集一起公开,才能判断方法是否真正稳定。

结语

Qwen-JSON 对我来说更像一个明确的实验对象:把“模型输出不稳定”拆成可以观察的结构、格式、语义和长度问题,再用 GRPO 把部分验证逻辑变成训练信号。

模型不会因为看到“只输出 JSON”就自动理解什么叫可靠。可靠性最终要落在可执行的 parser、schema、验证器和失败处理上。

参考:Freakz3z/Qwen-JSON 模型卡DeepSeekMath 论文TRL GRPOTrainer 文档