大模型 API 返回不稳定?先从这几个地方查起 ypxx.net

调用大模型 API 的时候,很多人都会碰到类似情况:明明是同一个输入,今天的回答和昨天不一样;有时候能规规矩矩返回 JSON,有时候又夹了几句解释;还有字段丢失、输出被截断、流式返回中断,甚至偶尔超时、返回空内容。

一遇到这些问题,大家很容易先想到两个原因:是不是 temperature 太高了?是不是 Prompt 没写好?这两个点当然重要,但放到真实业务里看,大模型 API 返回不稳定往往不是某一个单点造成的。它可能和模型本身的生成机制有关,也可能和请求参数、Prompt、上下文、RAG、工具调用,甚至整条 API 工程链路都有关系。

所以更实际的目标,并不是强行要求“大模型每次逐字逐句都一样”。生产环境里更重要的是:输出格式能解析、关键字段别乱变、异常可以重试、问题能够复现,线上还能监控得到。换句话说,我们要做的是把大模型输出的不稳定性控制在业务可以接受的范围内。

先判断:你遇到的到底是哪种“不稳定”?

排查之前,别急着改 Prompt。先把现象说清楚,很多问题看起来都叫“不稳定”,但背后的原因可能完全不同。

只有先把问题归类,后面才知道该从哪里下手:是生成层的问题,还是 Prompt 的问题;是结构化输出没约束好,还是 API 调用链路本身出了波动。

原因一:大模型本来就是概率生成,不是固定函数

大模型和传统函数不一样。传统函数只要输入一样,输出基本就一样;但大模型生成内容时,通常是在已有上下文的基础上预测下一个 token 的概率分布,然后再按照采样策略选出下一个 token。

这也解释了为什么同一段 Prompt,模型可能给出不同回答。影响随机性的参数主要有这些:

  • temperature:越高越发散,越低越保守;
  • top_p:控制候选 token 的累计概率范围;
  • top_k:限制候选 token 的数量;
  • presence_penalty / frequency_penalty:影响重复程度和引入新话题的倾向;
  • max_tokens:决定输出是否可能被截断;
  • stop:如果设置不当,可能让模型提前停止。

这里有个容易被忽略的点:temperature=0 只能降低随机性,不代表任何情况下都绝对一致。不同服务商的实现方式、浮点计算、并行推理、模型版本、系统 Prompt、RAG 检索结果、工具调用结果、外部数据源变化,都可能让输出发生差异。

所以,碰到大模型 API 返回不稳定时,第一步不是直接把 temperature 改成 0,而是先确认:这几次调用的完整条件,真的完全一样吗?

原因二:API 参数没固定,每次生成空间都不一样

很多线上问题表面上看是“大模型不稳定”,但仔细查才发现,每次请求的参数根本不完全一致。

建议每次请求都把这些信息记录下来,并且能做对比:

  • model 以及实际调用到的模型版本;
  • temperature、top_p、top_k;
  • max_tokens、stop;
  • seed,如果平台支持的话;
  • response_format;
  • tools 和 tool_choice;
  • 是否开启了 stream;
  • system prompt、user prompt、conversation history;
  • RAG 召回的文档 ID、排序和分数。

常见参数和稳定性的关系,大致可以这样看:

如果你用了 latest、auto 这类模型别名,也要格外小心。它们方便是方便,但背后可能有模型路由、灰度升级或者版本切换。生产环境里,更稳妥的做法是固定明确的模型版本,同时记录服务商返回的 request_id 或 trace 信息,方便后续追踪。

原因三:Prompt 太开放,模型自然会自由发挥

Prompt 越开放,模型能发挥的空间就越大。比如下面这种写法:

帮我分析这段用户反馈,返回结果。

这句话其实没有说清楚模型要扮演什么角色,也没定义分类标准、输出格式、字段范围、长度限制,更没有说明无法判断时该怎么办。模型当然可能每次用不同方式回答。

更稳的写法,应该把任务边界说清楚:

你是用户反馈分类器。

请只返回 JSON,不要解释。

字段要求:

- category:只能是 "价格"、"功能"、"售后"、"其他"

- sentiment:只能是 "正面"、"中性"、"负面"

- reason:不超过 30 字

- confidence:0 到 1 之间的小数

如果无法判断,category 返回 "其他",confidence 返回 0。

用户反馈:{{input}}

一个比较稳定的 Prompt,通常要包含这些内容:

  • 角色:告诉模型现在要充当什么功能;
  • 任务:只让它完成一件明确的事;
  • 边界:说明哪些内容不能输出;
  • 格式:明确字段、类型和枚举值;
  • 长度:限制摘要、理由、标题等内容的字数;
  • 兜底:无法判断时应该返回什么;
  • 示例:必要时可以给 few-shot,但示例之间不能互相打架。

如果一个 Prompt 既要求“严格返回 JSON”,又要求“解释原因、展示分析过程、补充建议”,模型就很容易在格式和内容之间摇摆。做结构化任务时,最好减少开放式表达,把模型的发挥空间收窄。

原因四:只靠 Prompt 要 JSON,本来就不够稳

很多时候,大家说“大模型 API 返回不稳定”,其实说的是“JSON 输出不稳定”。这个问题确实很常见,但靠一句“请返回 JSON”往往解决不了。

结构化输出大致可以分成三种处理方式。

低成本方案:用 Prompt 约束

这种方式适合原型验证,或者风险不高的场景。

只返回 JSON,不要 Markdown,不要解释。

字段必须包含:name、age、reason。

如果无法判断,字段值返回 null。

它的优点是简单、成本低,但本质上还是软约束。模型仍然可能返回字段缺失、类型错误,或者在 JSON 前后多加几句解释。

中等强度方案:JSON Mode / response_format

如果平台支持 response_format 或 JSON Mode,建议优先打开。它通常能明显减少非 JSON 文本,让返回结果更容易被程序解析。

不过也要注意,JSON Mode 更多是保证“语法上像 JSON”或者“能解析成 JSON”,不一定能保证字段含义、字段类型、枚举值完全符合你的业务要求。

高强度方案:Structured Outputs / Tools / JSON Schema

如果是生产环境里的信息抽取、分类、审核、Agent 工具调用,更建议使用 JSON Schema、Structured Outputs、Function Calling 或 Tools。

这类方式能约束得更细,比如:

  • 字段名;
  • 字段类型;
  • 必填项;
  • 枚举值;
  • 数组结构;
  • 工具入参格式。

但即便用了这些能力,也不建议跳过服务端校验。返回结果拿到后,仍然应该用 Pydantic、Zod、JSON Schema validator,或者自定义逻辑再校验一遍。解析失败时可以重试一次;如果还失败,就进入人工处理、降级规则或者异常队列。

原因五:上下文、RAG、工具调用也会带来波动

很多复杂 AI 应用的波动,其实不一定来自基础模型,而是出在上下文链路上。

在多轮对话里,如果历史消息被截断、顺序发生变化,或者不小心混入了其他用户的输入,模型输出就会变。并发场景下,如果 session 隔离没做好,还可能出现上下文串扰,这类问题排查起来更麻烦。

在 RAG 场景里,答案是否稳定,很大程度取决于检索链路:

  • query rewrite 每次是否一致;
  • 向量召回的 top_k 是否固定;
  • rerank 排序是否稳定;
  • 过滤条件有没有变化;
  • 文档版本是否更新;
  • 是否缓存了召回结果;
  • 是否记录了引用来源。

如果每次召回的文档片段都不一样,最终答案自然也很难一致。像客服问答、知识库问答这类场景,尤其要记录“这次回答到底基于哪些文档 ID”。

在 Agent 和工具调用场景里,不稳定还可能来自工具选择。工具描述不清、工具太多、tool_choice 没指定,都可能导致模型有时调用工具,有时直接回答。关键业务流程里,最好缩小工具集合,必要时直接强制调用指定工具。

原因六:API 工程链路不稳定,不一定是模型的问题

线上 API 不稳定,也可能是工程链路的问题,而不是模型生成本身的问题。

常见情况包括:

  • 429 限流;
  • 5xx 服务异常;
  • 网络 timeout;
  • 客户端重试导致二次生成有差异;
  • 流式输出中断;
  • SSE 解析不够健壮;
  • 缓存命中不一致;
  • 模型别名发生变化;
  • 服务商灰度升级;
  • 自动路由到了不同模型或节点;
  • 内容安全策略被触发;
  • finish_reason=length 导致输出被截断。

如果结果只是偶发异常,建议先看这些字段:

1. status code

2. error message

3. finish_reason

4. request_id / trace id

5. model 实际版本

6. 是否发生 retry

7. 是否 stream 中断

8. token 用量

9. latency

10. RAG 召回文档是否一致

比如,finish_reason=length 往往说明输出可能被截断;content_filter 可能表示触发了安全策略;429 更像是限流问题;502、504 则可能和服务端或网络链路有关。只盯着返回正文,不看这些元信息,基本很难定位真正原因。

快速排查表:不同现象优先看什么?

这张表想强调的是:不要把所有问题都甩给模型。很多所谓“API 返回结果不稳定”,本质上是参数、上下文、外部检索、重试机制或客户端解析不稳定。

不同场景的稳定性配置建议

信息抽取 / JSON 分类

这类任务最看重的是字段稳定、格式合法、类型正确。

建议这样做:

  • temperature 设置为 0 或 0.1;
  • top_p 适当收紧;
  • 使用 JSON Schema、Structured Outputs 或 Tools;
  • max_tokens 略高于目标 JSON 长度;
  • Prompt 里不要要求解释过程;
  • 返回后做 schema 校验;
  • 解析失败后重试一次,如果仍失败,就进入人工或降级规则。

客服问答 / 知识库问答

这类任务更关心事实一致性,以及答案来源能不能追溯。

建议:

  • temperature 控制在较低范围;
  • 固定 RAG 的召回数量和排序策略;
  • 明确要求“只基于资料回答,不确定就说不知道”;
  • 输出引用来源;
  • 没有资料时拒答,不要编造;
  • 记录本次回答使用的文档 ID。

内容生成 / 营销文案

内容生成不一定要每次逐字一致,真正重要的是风格稳定、质量可控。

建议:

  • 可以适当使用较高的 temperature;
  • 提供品牌语气、示例文案和禁用词;
  • 限定标题数量、字数和结构;
  • 一次生成多个候选,再用规则或评分模型筛选;
  • 不要把“每次完全一样”当成核心目标。

Agent / 工具调用

Agent 场景里的不稳定,通常来自步骤太长、工具太多,以及中间状态不可控。

建议:

  • 把任务拆成“计划 → 工具调用 → 结果总结”;
  • 控制每一步的输出长度;
  • 使用 Function Calling / Tools;
  • 必要时强制 tool_choice;
  • 每一步执行后都做校验;
  • 工具失败时,把错误信息回传给模型,让它修复;
  • 避免一次性让模型输出复杂长 JSON 和大段代码。

上线前如何测试大模型 API 是否足够稳定?

不要只拿一两条样例就判断系统稳不稳。上线前最好准备一套固定测试集,覆盖正常输入、边界输入、异常输入和高风险输入。

一个比较可执行的流程是:

  1. 准备 50-200 条固定测试输入;
  2. 每条输入重复调用 3-5 次;
  3. 固定模型版本、参数、Prompt 和 RAG 配置;
  4. 自动评估输出结果;
  5. 记录失败样本,并按原因分类。

可以重点统计这些指标:

  • JSON 解析成功率;
  • Schema 校验通过率;
  • 字段完整率;
  • 分类一致率;
  • 答案一致率;
  • 平均输出长度;
  • 空返回率;
  • 超时率;
  • 安全拦截率;
  • 重试后成功率;
  • P95 / P99 延迟;
  • 单次调用成本。

上线后也要保留关键日志,比如 request_id、用户会话、模型版本、Prompt、参数、RAG 文档、返回内容、错误码、finish_reason、token 用量、延迟和重试次数。没有日志,就很难复现;问题复现不了,最后只能靠猜。

关于 ClaudeAPI 等兼容接入平台的注意点

如果你是通过第三方 Claude API 兼容接入服务来调用模型,比如 ClaudeAPI 这类平台,排查思路其实还是一样的:先确认请求参数、模型版本、返回状态、超时重试,以及日志记录是否完整。

但也要注意,第三方兼容接入平台并不等同于模型官方服务。像模型支持范围、线路、价格、额度、限速、开票、企业充值或技术协助等信息,都应该以平台最新说明为准。不要默认它一定“绝对稳定”,也不要默认一定“完全不限速”。

总结:别只盯着 temperature,按四层思路排查

大模型 API 返回不稳定,通常不是一个原因造成的。比较稳妥的排查顺序是:

  1. 先确认请求是否真的完全一致;
  2. 固定模型版本,尽量避免使用不确定的别名;
  3. 降低 temperature,适当收紧 top_p;
  4. 明确 Prompt 里的任务、边界、格式和兜底逻辑;
  5. JSON 场景使用 response_format、Schema 或 Tools;
  6. 检查 finish_reason、错误码和 token 用量;
  7. 排查 RAG、工具调用、上下文是否发生变化;
  8. 设置合理的 timeout、重试、降级和缓存策略;
  9. 记录完整请求日志,保留 request_id;
  10. 建立稳定性测试集和线上监控。

说到底,大模型不是传统意义上的确定性函数。对生产系统来说,更专业的目标不是承诺“每次都完全一样”,而是让关键输出足够稳定,异常能够被发现,失败可以重试,结果可以追踪,并把大模型输出的不确定性控制在业务能接受的范围内。