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

responses.parse 同时传了 text_format 和 text.format,为什么还没请求就报 TypeError

responses.parse 不接受 text_format 与 text.format 两个格式入口同时存在。Pydantic 解析保留 text_format,手写 schema 改用 create,并检查封装是否重新填回冲突字段。

responses.parse 同时传了 text_format 和 text.format,为什么还没请求就报 TypeError

同一个调用里既传 Pydantic 类型,又复制了手写 schema:

TypeError: Cannot mix and match text.format with text_format

这里要修的是调用参数组合。responses.parse() 会根据 text_format 生成输出格式;若 text 字典里同时出现 format,SDK 无法接受两个格式入口,就在发送请求之前抛错。

openai 1.109.1 的官方源码包含这一显式检查,以下示例以该版本接口为基线。本文不推断它最早在哪个发行版本加入;请先记录项目实际安装的 openai.__version__。Responses 客户端源码

想拿到 Pydantic 对象,就只保留 text_format

下面使用一个最小抽取对象。模型名和密钥从环境变量读取;运行会发送真实请求并可能计费。

import os
from pydantic import BaseModel
from openai import OpenAI

class Ticket(BaseModel):
    subject: str

with OpenAI(api_key=os.environ["OPENAI_API_KEY"]) as client:
    response = client.responses.parse(
        model=os.environ["OPENAI_MODEL"],
        input="工单标题:导出任务超时",
        text_format=Ticket,
    )
    if response.status != "completed":
        raise RuntimeError("响应未完成,不能写入业务结果")
    ticket = response.output_parsed
    if ticket is None:
        raise RuntimeError("没有可用的结构化结果,请检查拒绝等分支")
    assert isinstance(ticket, Ticket)
    print(ticket.subject)

删的是冲突的 text.format。如果 text 中还有模型支持的其他配置,不必把整个配置对象全删掉。封装函数最好把输出类型作为唯一来源,别让调用者同时从另一个字典传入格式。

想自己管理 JSON Schema,就走 create 并校验

手写格式放进 Responses 的 text.format。下面只构造参数,不联网:

text_config = {
    "format": {
        "type": "json_schema",
        "name": "ticket",
        "strict": True,
        "schema": {
            "type": "object",
            "properties": {"subject": {"type": "string"}},
            "required": ["subject"],
            "additionalProperties": False,
        },
    },
}

在现有代码里将它作为 client.responses.create(..., text=text_config) 的参数,不再传 text_format。收到结果后,先判断状态和拒绝,再校验相应的输出文本;可以用 Ticket.model_validate_json(...) 做类型校验。模型支持的 schema 子集仍以接口文档为准。结构化输出指南

不要把 Chat Completions 的 response_format 原样搬过来。这里的格式对象直接放在 text.format,不是再包一层同名的 Chat Completions 字段。两个端点的参数结构不一样,复制配置时需要连同调用入口一起看。

参数明明删了,怎么还在报

最容易漏掉的是二次合并:调用处移除了 schema,公共封装又从默认配置里填回来了。检查真正传给 SDK 的最终参数,但只记录字段名和类型,别把完整输入、密钥或用户内容写进日志。

可以在自己的封装边界加一个小检查:

def check_format_entry(kwargs):
    text = kwargs.get("text") or {}
    if "text_format" in kwargs and "format" in text:
        raise ValueError("只允许一个输出格式入口")

check_format_entry({"text_format": Ticket})
check_format_entry({"text": text_config})

这个应用侧检查约定“未使用的参数直接省略”。不要给 text_format 填 None 作为关闭开关;明确不使用时,从 kwargs 删除它,避免把无效值继续传入 SDK。

验收时分别检查类型入口、手写入口和双入口三种情况。前两种应进入各自的正确调用流程;双入口应在本地被拒绝。仅消除 TypeError 还不够,结构化结果必须经过完成状态、拒绝分支和字段校验后才能进入业务。