先看现象。让模型输出结构化字段,返回内容以三个反引号加 json 开头,json.loads 抛 Expecting value: line 1 column 1;同一提示词在另一个模型上不带代码块,导致解析分支要写两套。
真正麻烦的地方在于,几个原因的现象几乎一样。模型训练时见过大量 markdown 示例,习惯性包代码块。还有一种是提示词只说了输出 JSON,没禁止代码块。
不容易想到的是,没用 response_format 约束,输出完全自由。
还有一种情况,流式拼接时把首尾标记一起写进了缓冲区。
动手时先把原始返回落盘,确认首字符到底是什么
python -c "import json;print(repr(open('raw.txt').read()[:60]))"
随后用正则剥掉代码块围栏再解析
python -c "import re,json;s=open('raw.txt').read();s=re.sub(r'^```(?:json)?|```$','',s.strip(),flags=re.M);print(json.loads(s))"
接着改用 json_object 模式让服务端保证是纯 JSON
curl https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $KEY" -d '{"model":"gpt-4o-mini","response_format":{"type":"json_object"},"messages":[{"role":"system","content":"只输出 JSON 对象"},{"role":"user","content":"抽出字段"}]}'
解析失败时记样本并走一次修复重试
python parse_with_repair.py --in out.jsonl --retry 1
改完先别撤,确认这几项。一千条样本的解析成功率到 99.8%,失败样本都带原始文本可回查,前端不再出现空白卡片。
剥围栏只能救急,根治要靠 response_format 或 json_schema,同时保留原始文本落盘,否则出错时无从分析。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。
