用 response_format 的 json_schema 抽取合同要素,约 3% 的请求返回空对象,HTTP 状态 200,finish_reason 是 length,把 max_tokens 从 256 提到 1024 后不再出现。

一开始怀疑是「思考模型的推理内容也占输出额度」,查了一圈,方向不对。

原因不止一个,得分开看。输出被截断在 JSON 中间 解析器返回空对象。也可能是max_tokens 按平均长度设得太紧。

少数情况下是客户端把解析失败吞掉返回空字典。

第一件事是看 finish_reason 与 usage

python -c "import json;r=json.load(open('r.json'));print(r['choices'][0]['finish_reason'],r['usage'])"

再往下把 max_tokens 提到 1024 再跑同一批

python extract.py --max-tokens 1024 --in contracts.jsonl

然后让解析失败直接抛错而不是返回空

python -c "import json;print(json.loads('{}'))"

剩下的就是统计截断率与字段完整率

python report.py --log extract.jsonl --metric truncation

当时还开着另一个排查窗口,两边日志对照着看才定位到。

同一份数据我导出了两份,一份留底,一份拿来试。

别只看报错消失,验证要多做一步。空对象比例降到零,finish_reason 全部是 stop,字段完整率与人工抽查一致。

结构化输出遇到空对象先看 finish_reason,length 就是额度不够,别急着怀疑 schema。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189