它更像一个远程服务:会超时,会返回空,会把推理过程混进正文,也会凭手感补一个数字。
接第一个模型的时候我把它当函数用:给 messages,拿 content。后来发现它更像一个远程服务,会超时,会返回空,会把推理过程写进正文,也会在额度不够的时候只给半句话。
和模型输出格式有关的故障我记了二十多条,翻完一遍,大部分是我这边没做防护。
01推理过程会漏进正文
推理模型通常有两个通道:reasoning 和 content。有些供应商在流式模式下会把 reasoning 也推过来,如果你的代码只做「拼接所有 delta」,用户就会看到一段英文思考过程。
更麻烦的是它长得像正文,能绕过按长度做的健康提醒豁免。有一条记录就是这样:英文推理穿过了长度检查,直接发到了聊天里。
协议里给了两个字段,代码就得分开处理。
02max_tokens 太小,等于让它只思考不回答
实测过一个模型:max_tokens 给 1500 以下,额度会全部烧在思考链上,content 返回空字符串,finish_reason 也是空。
单看返回值,很像「角色故意不回」。判断有效输出之前,先看 completion_tokens 有没有撞上限。
这条后来变成了固定检查:空 content 加高 completion_tokens,是截断,不是拒答。
还有一种更难防的:它在没调用任何工具的情况下给你一个看着很具体的数字。有一次问一个只有调用工具才查得到的分数,第一轮它直接回了「才 56」,真实值是 85。它什么都没查,那个数是顺手感来的,改到第三版才对。
03结构化输出不是可选项
还有一类:模型把工具调用写成原始文本,或者把编号列表直接当正文发出去。用户看到的是本该被解析掉的标记。
所以输出侧至少要有四道闸:
- 结构校验:该是 JSON 的地方解析失败就重试,不要把半截 JSON 当正文。
- 长度与格式闸门:超过阈值、含代码块、含编号列表,按渠道决定要不要清洗。
- 内容白名单:语音标签、表情标签只允许已知的几个。黑名单总有漏的。
- 降级链:主模型不可用时落到备选,并且这条链要定期演练,从没跑过的分支,真用到时多半是坏的。
04流式和同步是两条路
同一个模型,流式请求默认可能不返回 usage,同步请求返回。于是成本统计里只有一半的调用有 token 数。
同一个模型,流式和非流式其实是两套实现,统计、限流、重试都得两边分别验证。
校验也分轻重。工具调用、状态变更、结构化数据严格校验;自然语言正文只做内容安全和明显异常的闸门,其余交给提示词。聊天正文要的是像人,把一句话切成符合 schema 的碎片,读起来就是机器人。