登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

模型输出为什么会被截断以及怎样读取完成原因

来源:17golang原创

时间:2026-10-26 02:45:26 277浏览 收藏

模型回答只返回了半段、JSON 少了一个括号,或者流式输出停在代码中间时,先不要把问题归结为“模型突然失灵”。对于 OpenAI Chat Completions,关键字段是 choices[0].finish_reason;对于 Responses API,应该读取 status 和 incomplete_details.reason。其中 length 或 max_output_tokens 才表示触达输出上限,stop 或 completed 则表示正常完成。

官方参考:https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create

Responses 参考:https://developers.openai.com/api/reference/resources/responses/methods/create

排查顺序应是:先确认接口类型,再读取最终状态字段,最后根据原因决定是否提高输出预算、继续生成或处理工具调用。只看已经生成的文本长度,不能判断回答是否完整。
  • Chat Completions:检查 choices[0].finish_reason,流式请求要等最后一个 chunk。
  • Responses API:检查 status;当状态是 incomplete 时继续读 incomplete_details.reason。
  • 结构化输出:遇到 length 或 max_output_tokens 时,不要直接解析可能不完整的 JSON。

先确认你读取的是哪一种响应对象

“完成原因”不是所有 SDK 都放在同一个位置。旧代码常见的 Chat Completions 返回对象有 choices 数组,每个 choice 包含消息和 finish_reason;新的 Responses API 直接在响应对象上给出 status,不完整时再从 incomplete_details 读取细因。

因此第一步不是加大 token 参数,而是确认请求到底调用了哪个端点。把两种字段混读,往往会得到空值,然后误判为网络超时。

Chat Completions 请求上限与 choices 完成原因的静态关系说明图
图1:结构说明图,展示 Chat Completions 的请求上限、choice 消息与完成原因之间的静态关系,不是运行截图。

Chat Completions 怎样读取 finish_reason

非流式响应可以直接读取第一个 choice。下面的代码把“正常结束”“达到长度上限”“工具调用”和“内容过滤”分开处理;示例只展示判断逻辑,不把任何示例输出冒充成实际运行结果。

from openai import OpenAI

client = OpenAI()
response = client.chat.completions.create(
    model="gpt-6-astra",
    messages=[{"role": "user", "content": "用三点说明如何处理分页异常。"}],
    max_completion_tokens=600,
)

choice = response.choices[0]
reason = choice.finish_reason

if reason == "stop":
    # stop 表示自然结束或命中了请求中的 stop 序列
    print(choice.message.content or "")
elif reason == "length":
    # length 表示达到请求设置的最大生成预算,内容可能不完整
    raise RuntimeError("输出达到长度上限,不能当作完整答案")
elif reason == "tool_calls":
    # tool_calls 表示模型交回工具调用,应用应先处理工具结果
    print("等待处理工具调用")
elif reason == "content_filter":
    # 内容过滤可能让结果缺少可直接使用的正文
    raise RuntimeError("响应因内容过滤而未完整生成")
else:
    # 兼容 SDK 或服务新增的结束原因,先记录原值再决定策略
    raise RuntimeError(f"未处理的 finish_reason: {reason}")

判断重点是 finish_reason 而不是 message.content 是否非空。content 有值,只能说明服务返回了一段文本;当原因是 length 时,这段文本仍可能停在半个 JSON、半条命令或未闭合的 Markdown 代码块中。

如果使用 stream=True,中间 chunk 的 finish_reason 通常仍是 null。应用应持续拼接内容,直到收到带有最终原因的 chunk,再决定是否把结果写入业务数据库。

Responses API 怎样读取 incomplete_details

Responses API 的判断方式不同:先看响应的 status,只有状态为 incomplete 时再查看 incomplete_details.reason。官方参考中,这个原因可以包括 max_output_tokens、max_messages、content_filter 或 steered。

Responses API 完成状态与 incomplete_details 原因的静态关系说明图
图2:结构说明图,展示 Responses API 的 status、输出对象和 incomplete_details.reason 关系,不是实际接口截图。
from openai import OpenAI

client = OpenAI()
response = client.responses.create(
    model="gpt-6-astra",
    input="把下面的接口说明整理成结构化 JSON:分页参数、错误码和重试建议。",
    max_output_tokens=800,
)

if response.status == "completed":
    # completed 才进入正常的业务解析路径
    print(response.output_text)
elif response.status == "incomplete":
    details = response.incomplete_details
    reason = details.reason if details else "unknown"
    # 先记录原因,再决定是否续写或调整预算
    if reason == "max_output_tokens":
        raise RuntimeError("输出预算不足,不能直接解析为完整 JSON")
    if reason == "content_filter":
        raise RuntimeError("输出被内容过滤打断")
    raise RuntimeError(f"Responses 未完成:{reason}")
else:
    # 对未知状态保留原值,避免把新状态误认为成功
    raise RuntimeError(f"未处理的 response.status: {response.status}")

Responses API 的 max_output_tokens 计算的是模型可生成的总输出预算,包含不可见的推理 token;所以“可见文字不多却 incomplete”并不矛盾。遇到 max_output_tokens 时,应结合实际任务提高预算、缩短输入或拆分任务,而不是盲目重复同一个请求。

为什么看起来像截断:按结束原因排查

读取到的原因通常说明什么处理动作
stop自然结束或命中 stop 序列进入正常解析,但仍按业务 schema 处理异常 JSON
lengthChat Completions 达到生成上限检查 max_completion_tokens,不要把半截内容当成功
tool_calls模型返回工具调用,而不是最终答案执行或拒绝工具调用,再把结果交回模型
max_output_tokensResponses 达到总输出预算提高预算、减少输入或拆分任务
content_filter生成被内容过滤中断记录状态并走安全处理分支,不要拼接成完整答案
流式最终原因为空可能还没有收到最后一个事件,或连接中断区分“未结束”和“已结束”,保存连接状态与已收集片段

尤其要注意流式响应:最后一个事件以前的 null 不是成功,也不是截断,它只表示生成仍在进行。只有收到终止事件或明确的传输错误,应用才能判断这次流是否完整。

生产环境的处理手册

  1. 先记录原始结束字段。在日志中保存接口类型、模型、请求预算、finish_reason 或 incomplete_details.reason,不要只保存拼接后的文本。
  2. 再判断是否允许解析。结构化 JSON、代码和需要闭合标记的模板,只有正常结束时才进入解析;长度上限时保留片段供诊断。
  3. 最后选择恢复动作。长度不足可以调整预算或拆分任务;工具调用要完成工具链;内容过滤和未知状态应进入人工或安全策略分支。

一个实用的落库字段至少包括 api_kind、status、finish_reason、incomplete_reason、output_text 和 is_complete。其中 is_complete 应由结束原因计算,不应由“文本长度大于零”推断。

常见问题

finish_reason 是 stop 就一定代表 JSON 合法吗?

不一定。它只说明生成达到自然停止点或命中 stop 序列;JSON 是否合规则仍要交给 JSON 解析器或结构化输出处理。

流式输出最后没有 finish_reason 怎么办?

先把它视为未完成,检查是否收到了服务端终止事件以及客户端连接错误。不要因为已经收到若干 token 就把结果标记为成功。

为什么 max_output_tokens 很大仍然不完整?

Responses API 的预算包含可见输出和推理 token;长输入、复杂推理或结构化任务都可能消耗更多预算。应结合状态字段判断,必要时缩短任务或拆分输出。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>