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

ChatCompletion object is not subscriptable:请求成功了,取结果的代码还停在旧版

Chat Completions 完整响应是模型对象时,用属性读取替代旧式字典下标;下游必须接收字典时再用 model_dump() 转换,并分别处理文本、拒绝和工具调用。

ChatCompletion object is not subscriptable:请求成功了,取结果的代码还停在旧版

如果异常落在下面这种表达式上:

text = response["choices"][0]["message"]["content"]

并提示:

'ChatCompletion' object is not subscriptable

先检查 type(response)。OpenAI Python 1.x 及其后续客户端通常返回 ChatCompletion 模型对象,旧式字典下标读取不再适用。官方讨论 #1150 记录了同类取值问题。原始讨论

一行修正,然后处理没有文本的情况

直接读取文本可以改为:

text = response.choices[0].message.content

但如果下一行立刻 text.strip(),工具调用场景可能继续报 NoneType 的错误。实际业务最好把“文本”“拒绝”“工具调用”作为不同分支处理。下列分支代码以 openai 1.109.1 为基线,较早的 1.x 版本不能据此假定存在相同字段:

def classify_first_message(response):
    if not response.choices:
        return "empty", None
    message = response.choices[0].message
    if message.refusal:
        return "refusal", message.refusal
    if message.tool_calls:
        return "tool_calls", message.tool_calls
    if message.content is None:
        return "empty", None
    return "text", message.content

这个函数只是一个示例分流策略:有工具调用时优先交给工具处理。如果你的产品允许同一消息同时展示文本并处理工具,应按业务调整,不要无意丢弃其中一部分。收到工具调用也不等于获得了执行外部操作的授权。

如果明确只接受普通文本,可以在调用方检查类别,再决定是否去掉首尾空白。不能用 str(None) 把缺失内容变成字面量 "None" 塞给用户。

下游确实只收字典时,在边界转换

payload = response.model_dump()
text = payload["choices"][0]["message"]["content"]

model_dump() 用于得到 Python 字典;model_dump_json() 返回 JSON 字符串,二者不要混用。Pydantic 的序列化文档明确区分了 Python 对象形式和 JSON 形式。Pydantic 序列化说明

只在需要交给旧组件、序列化器或消息队列的边界转换即可。若一个函数里一会儿按属性读取、一会儿按字典读取,后续排查会变得更困难。可以统一函数的输入约定,让类型检查更容易发现遗漏。

用本地对象检查,不用再扣一次 API 额度

下面构造的是测试数据,不是模型真实返回。代码不会联网,可先验证“属性读取”和“转字典读取”是否一致:

from openai.types.chat import ChatCompletion

response = ChatCompletion(
    id="local-fixture",
    object="chat.completion",
    created=0,
    model="local-fixture",
    choices=[{
        "index": 0,
        "finish_reason": "stop",
        "message": {"role": "assistant", "content": "OK"},
    }],
)
assert response.choices[0].message.content == "OK"
assert response.model_dump()["choices"][0]["message"]["content"] == "OK"

再把测试消息的 content 改为 None,检查自己的分支能否正常返回。给 tool_calls 和 refusal 各加一个测试,比只验证一条文本更容易发现下一处问题。

本文适用于 Chat Completions 的完整响应对象。流式调用拿到的分块、Responses API 的输出以及第三方网关自行包装的结果,结构可能不同。修复这类取值错误不需要改到 /completions 端点。SDK 响应模型与迁移说明