我以为"让模型返回 JSON"是最简单的一个活
后端有个小功能,要把一段文字交给大模型解析成结构化数据。我接这个活的时候想的是"这个最快"。
结果它是整个项目里我唯一没修好的东西。
第一次请求,后端返回:
Overstats API 错误: 500 - shiqu_llm_not_configured
它说我没配 LLM。那就配上,填 key,选模型。再请求,变成:
502 - shiqu_llm_failed ... 返回内容不是合法 JSON
这次模型调通了,是它返回的东西后端解析不了。
我用的第一个模型是 deepseek-v4-flash。我查了一下,它不支持强制 JSON 输出——你要求它返回 JSON,它会给一段"看着像 JSON 的文字"。听起来就是它了。
换成硅基流动上的 Qwen2.5-7B-Instruct,还是 502。
到这里我判断问题在解析环节。最可能的形态是模型给返回值裹了一层 Markdown 代码块:
```json
{ "区": true }
```
它觉得这样更"友好",后端的 json.loads() 直接崩。这也解释了为什么换模型没用——很多模型默认都会这么干。
我后来想到该怎么防
约束那一头:选明确支持结构化输出参数的模型,别指望所有模型都听话;提示词里写死"只返回纯 JSON,不要解释、不要 Markdown 代码块";超时调大(我调到 600 秒,慢模型出结构化内容更慢)。
解析那一头:在 json.loads 之前先剥掉代码块围栏,把开头的 ```json 和结尾的 ``` 去掉再解析。
还有一条是我这次最缺的:解析失败的时候,把原始返回内容打进日志。
我全程只能猜,就是因为日志里只有"不是合法 JSON"这一句,没有那段内容本身。如果有原文,看一眼就知道该剥什么了。
这功能到现在还放着,我暂时改用不依赖 LLM 的那几个指令。
同一时期我还在配图源插件,撞上一个同类的事。插件面板一直提示 "Pixiv Token 未配置",我去拿 token:
gppt login -j
输出第一行就是:
Cached token expired; refreshing ...
给出新的 access_token 和 refresh_token,我复制进插件——还是不行。中间命令行飞快闪过一句:
Reusing the cached token
它在复用本地缓存里的旧 token。我反复重试、反复复制,复制来复制去还是失效的那一版。直接拿 refresh_token 手动去换新的也不通:
指定されたエンドポイントは存在しません
"指定的端点不存在"——接口已经变了。
最后我把这个 token 清空,只用主图源。少一个回退源,但少一堆麻烦。
这两件事我放在一起记,是因为性质一样:模型答应给你 JSON 是约定,第三方给你长期 token 也是约定。这两种约定都会静默失效——不报错,不提醒,只是安静地不工作。
所以我后来给任何依赖外部服务的环节加的第一样东西,是让它能"证明自己还活着":一个健康检查,或者一条带原始返回的日志。比事后翻记录猜原因省事太多。