← 文章

把 LLM 当不可信外部服务

约 3 分钟#LLM#工程

它更像一个远程服务:会超时,会返回空,会把推理过程混进正文,也会凭手感补一个数字。

目录

接第一个模型的时候我把它当函数用:给 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 的碎片,读起来就是机器人。

相关阅读