先把当时的情况摆出来。instructor 1.3.2 配 openai 1.40,client.chat.completions.create(response_model=Order, max_retries=3) 返回的对象里 items 是空列表,日志里能看到 ValidationError,但异常被吞掉当成成功返回。
这类报错背后常见的原因有几处,逐个排除就行。模型把 JSON 包在 markdown 代码块里,解析层直接跳过。另一种情况是response_model 里有默认空列表的字段,校验通过但内容为空。
少数情况下是重试时把错误信息拼进 messages,角色写成了 system。不容易想到的是,未开启 strict 模式,模型自由发挥字段名。
先关掉封装看原始返回内容
curl https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"把订单抽成 JSON"}],"response_format":{"type":"json_object"}}'
接着打开调试日志确认重试次数与最后一次报错
python -c "import logging;logging.basicConfig(level=logging.DEBUG);print('instructor debug on')"
再往下用 strict 的 json_schema 模式约束字段
python -c "print({'response_format':{'type':'json_schema','json_schema':{'name':'order','strict':True}}})"
剩下的就是在业务侧加一条非空校验,空列表直接重试
python -c "print({'retry_if': 'len(result.items) == 0'})"
怎么算真的好了,看的是同一批现象。抽取结果里 items 至少一条,字段名与模型定义一致,连续二十条样本没有再出现空对象。
补一句。默认值会让校验形同虚设,空列表和空字符串都能通过,从模型到业务之间最好再加一层内容级校验,别把“解析成功”当成“抽取成功”。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
