事情本身不复杂,线索都在日志里。拿 6 条客服问答做 gpt-4o-mini 微调试水,创建 job 时直接 400,原文 training file must contain at least 10 examples,可文件用预览工具打开格式完全正常。

第一反应是「messages 结构不完整,缺 assistant 回合的样本不计入条数」,验证下来不成立。

这类报错背后常见的原因有几处,逐个排除就行。训练集最少 10 条样本,少于下限在创建阶段就被拒。另一种情况是文件里的空行被当成无效样本,实际条数比看到的多算少。

少数情况下是不同模型对应的样本下限不一样,小模型的要求反而更高。

先统计文件里非空的样本条数

python -c "import json;print(sum(1 for l in open('train.jsonl') if l.strip()))"

然后逐条打印角色序列确认结构完整

python -c "import json;[print(i,[m['role'] for m in json.loads(l)['messages']]) for i,l in enumerate(open('train.jsonl')) if l.strip()]"

再往下把角色不全的样本单独挑出来

python -c "import json;print([i for i,l in enumerate(open('train.jsonl')) if l.strip() and [m['role'] for m in json.loads(l)['messages'][:2]]!=['system','user']])"

补齐后用 fine-tune 用途重新上传

curl https://api.openai.com/v1/files -H "Authorization: Bearer $OPENAI_API_KEY" -F purpose=fine-tune -F file=@train.jsonl

恢复的判定其实很直接。文件详情里 bytes 与本地一致,创建 job 返回 200 且 status 进入 queued,日志里不再出现 at least 10 examples,trained_tokens 大于零。

提醒一点。样本条数和请求条数是两个概念,微调按有效对话计算,空行和结构残缺的行会悄悄被丢掉,凑数前先按结构过滤一遍。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。

作者 9823189