初步进阶 - 大模型API调用返回值详解
基础预备知识
1.1 什么是 API 返回值
客户端向大模型服务商服务器发送 HTTP/HTTPS 请求,服务器运算完成后传回的数据,统称为 API 返回值。
我们在 Python、Java、JavaScript 中使用的官方 SDK,本质只是封装了网络请求、JSON 序列化、对象映射。SDK 提供的response对象,内部承载的就是服务器下发的原始 JSON。开发者可以选择直接解析原始 JSON,或者使用 SDK 封装好的属性取值。
无论使用哪一种开发方式,底层原始 JSON 结构永远是标准。看懂原生 JSON,就不会被不同语言 SDK 的封装差异迷惑。
1.2 两种响应模式底层通信原理
1.2.1 非流式响应(stream=false)
通信模型:标准 HTTP 短连接。 完整流程:
- 客户端发起 POST 请求;
- 服务端接收请求,将 prompt 送入大模型;
- 模型完整生成全部输出文本;
- 服务端组装完整 JSON 数据包,一次性返回;
- HTTP 连接关闭。
优势:数据一次性到达,处理逻辑简单; 劣势:需要等待模型生成全部内容,首包延迟高,不适合聊天交互场景。
1.2.2 流式响应(stream=true)
通信模型:基于 SSE(Server-Sent Events)服务器推送长连接。 完整流程:
- 客户端发起 POST 请求,声明开启流式;
- HTTP 长连接持续保持;
- 模型每生成一小段文本,立刻封装为分片数据包实时推送;
- 客户端持续循环接收分片;
- 模型生成结束,推送结束标记分片,连接关闭。
优势:首字输出速度快,实现打字机动态效果,聊天产品标准方案; 劣势:需要循环迭代分片,手动拼接完整文本,代码容错逻辑更多。
1.3 关键名词定义
- Token:大模型处理文本的最小单位,中文大致 1 个 Token≈1.5 个汉字,输入、输出 Token 均为计费依据;
- Chunk:流式模式下,服务器推送的单条分片数据包;
- delta:增量差分数据,流式分片专属字段,代表本次新增生成内容;
- finish_reason:生成终止原因,用来判断 AI 回答停止输出的真实诱因;
- choices:候选回答数组,接口支持同时生成多条回答,绝大多数业务仅使用第 0 条结果。
模式一:非流式响应(stream=False)完整结构详解
当请求参数stream设置为 false 或者不传递该参数时,接口启用非流式模式。服务器等待文本全部生成完毕,返回单一完整 JSON 对象。
2.1 标准原始 JSON 样板
{
"id": "chatcmpl-8k72jd9sk123abc",
"object": "chat.completion",
"created": 1756889231,
"model": "glm-4-flash",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "大模型API通过网络请求远程调用云端模型,不需要本地部署大模型。"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 26,
"total_tokens": 50
}
}
2.2 顶层一级字段逐字段深度解析
2.2.1 id
字段含义:本次对话请求全局唯一标识符。 数据类型:字符串。 业务作用:
- 日志追踪:程序记录日志时保存 id,在平台后台查询对应调用记录;
- 故障排查:出现回答异常、内容拦截、计费异常时,提供给厂商客服定位具体请求;
- 分布式系统中用于请求去重、链路追踪。
注意:每一次 API 调用都会生成全新 id,连续多轮对话的 id 互不相同。
2.2.2 object
字段含义:响应类型标记。 固定取值:chat.completion。 作用:区分接口类别,用于框架内部路由、数据解析判断。在兼容 OpenAI 协议的所有接口中,非流式对话响应 object 永远为此值。
2.2.3 created
字段含义:响应生成时间戳。 数据类型:整数,Unix 时间戳(秒级)。 可以通过代码转换为标准年月日时间,用于统计调用耗时、记录对话发生时间。
2.2.4 model
字段含义:实际执行本次推理的模型名称。 很多开发者会忽略这个字段。 场景价值:当配置存在路由分发、模型自动降级策略时,可以校验实际运行模型是否为预期模型,防止配置错误导致调用廉价 / 高价模型。
2.3 核心数组:choices
choices 是一个数组。协议设计初衷支持一次性生成多条不同回答(n 参数控制候选数量)。绝大多数业务场景 n=1,数组内仅存在一条元素,固定读取choices[0]。
2.3.1 index
候选回答序号,n=1 时恒等于 0。开启多候选生成时,序号依次递增。日常开发几乎不需要使用。
2.3.2 message 对象(非流式专属核心对象)
message 内部包含两个子字段:role、content。
- role 固定取值:
assistant。 语义代表:本条消息由大模型 AI 产生。 回顾请求参数 messages 数组三大角色:
- system:系统提示词,设定 AI 规则;
- user:人类用户提问;
- assistant:AI 模型回复。 多轮对话开发时,我们拿到这条 message 后,需要组装同样结构放入下一轮 messages,维持对话上下文。
- content 业务最核心字段,保存 AI 完整回答文本。这是非流式模式下获取 AI 输出的唯一文本来源。
2.3.3 finish_reason(工程关键字段)
代表模型停止生成文本的原因,是线上业务必不可少的判断依据,支持以下标准枚举值:
- stop 正常终止。模型自主完成全部回答,没有受到外部限制,输出文本完整。
- length 达到请求参数
max_tokens设置的最大输出上限,模型被强制截断文本。 业务处理建议:检测到此值,需要提示用户内容过长,回答被截断,或者自动精简上文上下文。 - content_filter 输出内容触发服务商内容安全审核策略,输出被拦截,文本大概率不完整或者为空。 业务处理建议:友好提示用户当前提问存在敏感内容,无法生成回答。
- null 非流式模式下不会出现 null,仅存在于流式中间分片。
2.2 usage 计费统计模块
usage 模块是成本管控核心,所有 Token 统计数据存放于此。
- prompt_tokens 提示词消耗 Token 总数。包含 system 提示词、全部历史对话、当前用户输入文本。
- completion_tokens AI 输出内容消耗 Token。
- total_tokens 本次请求输入 + 输出 Token 总和。
落地应用场景:
- 自建计费系统,面向内部用户或者外部客户按量收费;
- 设置额度预警,当累计 token 到达阈值主动停止调用;
- 优化 prompt,持续精简文本,降低调用成本。
2.3 非流式标准取值范式
原始 JSON 取值伪代码: data["choices"][0]["message"]["content"] 各类 SDK 封装后对象取值形式: response.choices[0].message.content
2.4 非流式模式典型边界场景
场景 1:content_filter 触发,content 为空字符串; 场景 2:触发 length 截断,文本结尾语句不完整; 场景 3:网络异常返回 4xx/5xx HTTP 错误,不存在完整响应 JSON。
模式二:流式响应(stream=True)完整结构详解
开启 stream=True 后,服务器不再返回单一完整 JSON,持续推送多条 SSE 分片数据包(chunk)。这里出现新手最大认知误区:很多开发者直接套用非流式取值方式,读取message.content,程序直接抛出异常。
铁律:流式分片数据包内不存在 message 对象,只有 delta 增量对象。
3.1 普通分片标准 JSON 样板(生成过程中推送)
{
"id": "chatcmpl-8k72jd9sk123abc",
"object": "chat.completion.chunk",
"created": 1756889232,
"model": "glm-4-flash",
"choices": [
{
"index": 0,
"delta": {
"content": "大模型API"
},
"finish_reason": null
}
]
}
3.2 结束分片样板(模型生成完成最后一条数据包)
{
"id": "chatcmpl-8k72jd9sk123abc",
"object": "chat.completion.chunk",
"created": 1756889235,
"model": "glm-4-flash",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": "stop"
}
]
}
3.3 顶层一级字段解析
id、created、model 字段含义和非流式完全一致。 唯一区别字段:object 流式分片固定取值:chat.completion.chunk。 通过 object 字段可以在底层框架中区分当前数据包是完整响应,还是流式分片。
3.4 choices 数组内部结构详解
3.4.1 index
和非流式定义一致,多候选场景使用,常规业务恒为 0。
3.4.2 delta(流式专属增量对象)
delta 全称增量差分。含义:当前这一轮分片,模型新生成的文本,不包含历史所有内容。 重点特征:
- 一条分片内 delta.content 通常只有几个汉字、词语;
- 需要开发者在代码中定义字符串变量,循环累加所有分片内容,才能拼接出完整回答;
- delta 可能为空对象
{},结束分片经常出现 delta 无 content; - delta.content 有可能为 null。
很多新手报错根源:没有做空值判断,直接读取 content 进行字符串拼接。当 content 为 null,字符串与 null 合并直接触发程序异常。
3.4.3 finish_reason
中间持续生成的分片:finish_reason = null; 最后结束分片:finish_reason 填充 stop /length/content_filter。 业务开发规范:不要依靠分片关闭连接判断生成结束,优先识别 finish_reason 非 null 作为终止标志。
3.5 流式取值逻辑标准流程
- 定义空字符串变量 reply,用于保存完整回答;
- 持续循环接收每一条 chunk 分片;
- 安全校验:判断 choices 数组是否存在、长度是否大于 0;
- 获取 delta 对象;
- 判断 delta.content 不为空且不为 null;
- 将增量文本追加至 reply,同时实时输出实现打字机效果;
- 循环结束后,reply 变量存储 AI 完整回答文本;
- 将完整回答组装为
{"role":"assistant","content":reply}存入对话上下文。
3.6 新手高频致命错误汇总
错误 1:在循环 chunk 中使用chunk.choices[0].message.content 原因:流式数据包不存在 message 对象,直接属性访问报错。 错误 2:不判空,直接读取 delta.content,遇到 null 程序崩溃。 错误 3:把单条分片的 delta 内容直接当做完整回答,丢失绝大多数文本。
流式与非流式返回结构系统性对比
| 对比维度 | 非流式 stream=False | 流式 stream=True |
|---|---|---|
| 底层传输方式 | 单次 HTTP 响应,短连接 | SSE 长连接,持续推送分片 |
| 顶层 object 值 | chat.completion | chat.completion.chunk |
| 文本载体对象 | message | delta |
| 文本字段 | message.content | delta.content |
| 数据获取方式 | 一次取值即可 | 循环迭代分片,手动拼接文本 |
| finish_reason 状态 | 响应内直接存在有效值 | 中间分片为 null,仅最后分片填充结果 |
| 适用场景 | 后台批量处理、数据分析、离线任务 | 在线聊天、人机交互、前端实时展示 |
| 首字延迟 | 较高 | 较低 |
| 代码复杂度 | 低 | 较高,需要增加容错与拼接逻辑 |
返回值常见枚举状态深度业务解读
5.1 finish_reason 全场景业务处理方案
- stop 正常结束。可以直接使用完整回答,存入对话上下文,继续下一轮提问。
- length 达到输出 token 上限,文本截断。 推荐策略: 方案 A:提示用户「回答过长,内容已截断,可以精简问题继续提问」; 方案 B:程序自动裁剪历史对话上下文,减少 prompt 长度,扩大可用输出空间。
- content_filter 内容安全拦截。两种细分情况:用户输入违规、AI 输出违规。 推荐策略:不展示残缺内容,返回标准化友好提示,同时记录日志用于审计。
5.2 HTTP 层面错误响应(不属于正常返回 JSON)
很多学习者只关注成功场景的返回结构,忽略异常 HTTP 状态码。当接口鉴权失败、余额不足、限流、模型不存在时,服务器不会返回上文标准 JSON,而是返回错误结构体。 常见状态码: 401:API 密钥错误、密钥过期、权限不足; 429:调用频率超限,触发限流; 400:请求参数错误(模型名错误、消息格式非法); 500:服务商服务内部异常。
错误响应示例(不同厂商格式略有差异,但都包含错误信息)
{
"error": {
"code": "invalid_api_key",
"message": "提供的API Key不正确"
}
}
工程规范:代码必须捕获 HTTP 异常,区分业务正常响应与错误响应,不能假定接口一定返回标准对话 JSON。
返回值工程开发规范(生产环境强制要求)
6.1 空值安全校验规范
- 非流式场景 读取
choices[0]前,建议判断 choices 数组长度大于 0,防止极端空返回; - 流式场景双重校验 ① 判断 chunk.choices 是否存在且非空; ② 判断 delta.content 不为 None、非空字符串。
伪代码安全模板:
reply = ""
for chunk in response:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
if delta.content:
reply += delta.content
6.2 多轮对话上下文组装规范
无论流式还是非流式,想要实现 AI 记住历史对话,必须手动维护 messages 列表。 流程:
- 获取完整 AI 回答文本;
- 构造消息
{"role":"assistant","content":"完整文本"}; - append 存入全局 message 数组;
- 下一轮 API 调用携带完整 messages。
重要提醒:不能使用分片内零散 delta 片段直接存入上下文,必须使用拼接完成的完整文本。
6.3 Token 用量监控规范
非流式可以直接读取 usage; ⚠️ 巨大缺陷:流式标准分片数据包不会携带 usage 字段! 痛点说明:绝大多数厂商流式响应结束分片不会返回 token 统计数据。 解决方案: 方案 1:使用第三方 Token 计算器,本地统计文本 token 数量; 方案 2:调用结束后主动查询平台账单接口获取消耗; 方案 3:业务上流式模式不做细粒度实时统计,采用定时汇总方式。
这是非常容易踩坑的知识点:很多开发者以为流式同样能拿到 usage,调试时发现所有 chunk 均无 usage 对象。
6.4 日志记录规范
建议每次调用存储以下返回信息: 请求 id、模型名称、finish_reason、输入文本摘要、输出文本摘要、token 消耗、调用时间。出现问题可以快速复盘。
更多推荐
所有评论(0)