让大模型稳定地输出 JSON

623 阅读3分钟

在写了一个开源版本的 Manus(我也复刻了一个 Manus,带高仿 WebUI 和沙盒),发现很多 issue 是各种各样的模型无法稳定地输出 JSON,而导致了运行出错:

由于我是用 Deepseek 和 GPT-4o 这类高级的模型进行调试运行的,所以还没有发现这类问题,换了几个其它模型后,特别是小模型后,发现这类问题特别严重,如何解决呢?

方法一: 提示词 + JSON format

Deepseek 官方给出的说明如下(JSON Output | DeepSeek API Docs):

添加图片注释,不超过 140 字(可选)

基本是使用 JSON 提示词描述 + JSON 样例(Few shot)+ JSON response_format 来约束大模型的 JSON 输出。

如何用比较好地用提示词描述 JSON 字段呢?网络上比较好的实践是用 Typescript 或者 Yaml 格式描述(LLM生成Json结构化数据的几种方案个人目前认为最好的方式依然是`TypeScript约束Prompt + Yaml格 - 掘金),当然简单地就直接用列表描述就好。

给大模型提供几个 JSON 样例特别重要,因为你所描述的 JSON 结构不一定能准确地让大模型知道。

最后,再结合 API 级别的 JSON Format 输出,它的实现原理是 Constrained Decoding,即在预测下一字符时,把不符合 JSON 格式的丢掉,基本在高级模型可以非常稳定地输出 JSON 格式。

但是,在小模型或者硅基流动上面一些乱七八糟的模型,这一套基本都不是很稳定,它才不管你是不是 JSON Format 输出,那怎么办呢?第一想到的是用 Function Call。

方法二:使用 Function Call

模型的 Function Call 的参数是以 JSON 格式给出,那我们能不能定义一个 Function Call,然后把我们想要的 JSON 格式在函数参数中描述,让大模型调用时,我们就直接拿参数的 JSON 即可。

然而,还是太 Naiive 了,发现小模型连函数参数都不一定给出正确的 JSON 格式。

那还是直面问题吧!

方法三:JSON 提取 + 修复

大概总结了一下,那些大模型输出的 JSON 问题:

  • JSON 输出问题:
  • 被包含在 ``` 的 Markdown 语法块中
  • 包含其它的输出。
  • JSON 语法问题:
  • 逗号错误
  • 引号错误
  • 双引号没有转义

第一类问题,网上有很多提词教程,如:不要输出其它、输出第一个字符为[或者{,这些都只能降低概率,不然彻底解决这类问题,特别是小模型指令遵循能力低。所以,还是通过正则表达式之类的方法直接把 JSON 提取出来。

第二类问题,可以直接让 Cursor、Deepseek 等帮你写一个 JSON 纠正的代码,把 JSON 纠正。

方法四:大模型兜底

JSON 问题是解不完的,最终可以直接反用大模型帮你把错误的 JSON 转换成正确的 JSON,提示词参考如下:

Please extract and fix the JSON from the following text. Return only valid JSON without any explanation or markdown formatting.

Input text:
{text}

Requirements:
1. Extract any JSON-like content from the text
2. Fix common JSON formatting issues (missing quotes, trailing commas, etc.)
3. Return only the valid JSON, no additional text
4. If multiple JSON objects exist, return the most complete one
5. If no valid JSON can be extracted or fixed, return null

JSON:

随说某些大模型输出 JSON 不稳定,但是在提示词少,且目的明确的情况下,还是可以帮你较稳定地完成提取和转换的。

鸡尾酒疗法

虽然上面讲了各种方法,但是每种方法都不是独立的,每个方法环环相扣。实际应用中,可以将以上方法混合使用,把能兜的底都兜住。

参加代码:github.com/Simpleyyt/a…