DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

结构化输出报 LengthFinishReasonError,先保留截断位置再调 token 预算

LengthFinishReasonError 表示结构化结果因输出预算结束而未完成。先读取异常保留的 completion 与结束原因,缩小任务并核对模型预算,费用和超时也要一起评估。

结构化输出报 LengthFinishReasonError,先保留截断位置再调 token 预算

用 Chat Completions 的 parse() 读取结构化结果时,可能收到:

Could not parse response content as the length limit was reached

此时该查的是结束原因和输出预算。SDK 的解析器遇到 finish_reason="length" 会抛出 LengthFinishReasonError,而不是把半个对象当作已解析结果交付。解析器实现

官方 issue #2406 用极小的 max_tokens=5 演示了这一现象。它能说明如何触发错误,但这个历史参数和预算不适合直接搬到所有模型上。原始问题

先捕获专门的异常,别只打印 str(e)

只记异常字符串,往往只能看到 token 使用量。本文采用的 openai 1.109.1 会通过异常的 completion 属性保留响应对象,可以进一步检查各个 choice。下面的诊断函数不打印内容本身,避免把用户文本写进日志:

def length_diagnostics(exc):
    completion = getattr(exc, "completion", None)
    if completion is None:
        return {"completion_available": False}
    return {
        "completion_available": True,
        "usage": (
            completion.usage.model_dump()
            if completion.usage is not None else None
        ),
        "choices": [
            {
                "index": choice.index,
                "finish_reason": choice.finish_reason,
                "content_chars": len(choice.message.content or ""),
            }
            for choice in completion.choices
        ],
    }

下面是接入位置示意;沿用项目现有的模型、消息和 Pydantic 输出类型即可:

from openai import LengthFinishReasonError

# client、request_kwargs 由现有调用代码提供。
try:
    completion = client.chat.completions.parse(**request_kwargs)
except LengthFinishReasonError as exc:
    print(length_diagnostics(exc))
    raise

异常类的实现说明流式情况下 usage 可能缺失,所以读取前要判空。不要依赖“每次一定有 token 统计”的假设。异常对象实现

确实需要查看尾部片段时,应在权限受控的测试环境使用脱敏样本。尾部只是诊断证据,不能直接作为业务结果。看到 JSON 停在某个数组里,只能说明截断位置在那里,还要结合字段数量与输出规模判断为什么写得过长。

调整顺序:先缩小任务,再给预算留余量

先检查输出设计是否真的需要长原文、完整历史或者无限增长的数组。例如只需要十条摘要,就把输入切成适当批次,并明确每批最多处理多少条;不要让一个请求承担整份大型文档的全部抽取工作。

再检查当前模型支持的预算参数。Chat Completions 常见的 max_completion_tokens 与 Responses 的 max_output_tokens 属于不同接口,不能机械替换。某些模型的预算还包含推理 token,因此“可见 JSON 很短”也不能单独证明预算充足。Chat Completions 参数参考

提高预算时要同步评估费用、超时和业务时限。不要遇到 length 就无限倍增重试;可以限制一次调整后的重试,继续失败则拆分任务或返回明确的未完成状态。

两个回归用例就能挡住常见误修

  • 失败用例:给可控短任务设置明显不足、且模型允许的预算,确认异常进入失败分支,数据库里没有落下半个结果
  • 成功用例:恢复合理预算,检查结束原因、解析结果类型及业务字段,而不是只看“没抛异常”

这两项由读者在测试环境执行,会调用模型。服务可能拒绝过小预算,出现参数错误时应先满足模型约束,不能把它算成截断用例通过。

也不要靠给残缺 JSON 补括号宣布修好。括号补齐无法恢复缺失字段、列表尾项或被截掉的字符串,反而容易把不完整信息包装成可信结果。