千笔-AIWritePaper · https://www.aiwritepaper.com多轮 Agent 最容易翻车的不是「忘记要求 JSON」而是把提示词里的格式约定当成契约却从不给 Runner 一个可校验的output_type。官方 Agents中文智能体写得很直默认输出是纯文本str传入output_type后模型走 Structured Outputs最终输出必须是该类型且没有未完成的 tool calls。本文按工程笔记写法钉死类型契约、可跑片段、校验失败回退与生产禁区。示例模型名写作gpt-4o以你账号可用快照与官方文档为准。图上方默认 str vs output_type中部最终输出判定与 error_handlers下方生产禁区对照。目标说明读完你应能独立完成五件事用一句话说清output_type把「希望像 JSON」升级成「Runner 只接受该类型实例」。解释最终输出判定所需类型的输出且没有tool calls否则循环继续或失败。写出可跑片段Pydantic 模型 Agent(output_type...) 打印final_output字段。会用error_handlers{invalid_final_output: ...}回退对象仍须通过同一 schemahandler不重试模型、不重放 tool 副作用。列出生产禁区用 schema 当鉴权、把 handler 回退写成业务成功、无审计直接写库、提示词格式与output_type双轨互撕、把校验失败当「模型创意」。规格钉死对照官方 Agents / Running agents默认无output_type→str。类型面Pydantic / dataclass / list / TypedDict 等可被 TypeAdapter 包装的类型。结构化传入output_type 启用 structured outputs而不是靠「请输出 JSON」碰运气。失败面校验失败可走invalid_final_output未配置时非空校验失败继续抬ModelBehaviorError。与 tools有 tool calls 时不算最终输出类型契约不能代替 tool 审批。适用边界适合上 output_type下游要入库/渲染的字段固定日程、工单、评分卡、抽取表。需要把「模型偶尔多写一段解释」从成功路径剔除。已有 Pydantic 模型想让 Agent 直接吐同构对象。想把失败从进程崩溃改成受控占位对象仍带 schema。多 Agent 交接时接收方只认结构化字段不认散文。不该指望它单独搞定业务授权字段合法 ≠ 用户有权执行该操作。提示词政治正确schema 通过不代表内容合规该上的 guardrails 仍要上。无限开放域问答强行结构化会逼模型编造字段开放域更适合str 后处理闸门。把 TypeAdapter 支持面当成「任意 Python 对象都行」不能序列化进 structured outputs 的类型不要硬挂。用 handler 回退掩盖系统性提示错误回退是断路器不是长期正确答案。风险提示schema 过宽大量 Optional等于退回散文schema 过窄又会抬高invalid_final_output频率。handler 返回值会再走同一output_type校验——返回str或残缺 dict 会二次失败。生产里若把「校验通过」自动触发付款/删库缺口在授权层不在 SDK。步骤与机制1. 输出契约对照机制谁约束形状失败时典型用途默认str提示词软约束格式漂移难测聊天、解释output_typeBaseModelSDK structured outputs校验失败/异常或 handler工单、抽取仅提示「输出 JSON」无硬契约看起来像 JSON 实则漂移原型演示勿进生产invalid_final_outputhandler你返回同类型回退不重试、不重放副作用受控降级tools 类型先跑完 tools 再谈终答有 tool calls 不算终答查数后再结构化2. 可跑Pydantic 结构化输出先pip install openai-agents并导出OPENAI_API_KEY。importasynciofrompydanticimportBaseModel,FieldfromagentsimportAgent,Runner MODELgpt-4o# 占位以账号可用快照为准classTicketDraft(BaseModel):title:strseverity:strField(descriptionlow|mid|high)next_action:strasyncdefmain():agentAgent(nameTicketAgent,instructions根据用户描述输出工单草稿不要额外解释。,modelMODEL,output_typeTicketDraft,)resultawaitRunner.run(agent,登录页偶发 500影响约两成用户需要今晚排查网关。,)ticketresult.final_outputassertisinstance(ticket,TicketDraft)print(ticket.title,ticket.severity,ticket.next_action)asyncio.run(main())验收final_output是TicketDraft实例而不是「看起来像 JSON 的字符串」。3. 可跑校验失败时的受控回退官方 Running agents 说明error_handlers支持invalid_final_output。handler 返回的应用回退会再按同一output_type校验返回None表示放弃恢复。fromagentsimportAgent,Runner,RunErrorHandlerInputdefon_invalid_final_output(data:RunErrorHandlerInput[None])-TicketDraft:# 断路器占位对象不是「模型其实答对了」returnTicketDraft(title未解析请人工重填,severitymid,next_action缩小描述后重试本条不得自动建单,)agentAgent(nameTicketAgent,instructionsReturn a structured ticket.,modelMODEL,output_typeTicketDraft,)resultRunner.run_sync(agent,随便聊聊天气,# 易偏离 schema 的输入用于烟测error_handlers{invalid_final_output:on_invalid_final_output},)print(result.final_output)烟测清单见_w/output-type-smoke-checklist.md。把「走进 handler」记成失败样本而不是成功发布。4. 与 tools 共存时的边界结构化输出不取消 tool 循环模型若仍发出 tool calls就不算最终输出。常见误读是「已经声明 output_type所以第一轮一定会停」。正确顺序是需要的工具跑完 → 再产出匹配类型的终答。副作用工具仍要needs_approvalschema 漂亮不能代替审批。生产禁区生产禁止只靠提示词要 JSON必须output_type或等价硬校验否则回归测试不稳定。禁止把 schema 校验当鉴权severityhigh合法 ≠ 允许重启生产集群。禁止把 handler 回退当业务成功回退对象要打标如标题前缀、单独状态机禁止静默建单。禁止无审计写库结构化字段入库要带run_id、模型快照、提示词版本。禁止 schema 与提示词双轨互撕提示词要求 Markdown 报告同时output_type只要三个字段——等于制造invalid_final_output。禁止在 handler 里重放付款/删库官方语义就是不重放 tool 副作用你自行补调用等于绕开断路器。可验证清单官方 Agents / Running agents 链接可打开。本地跑通至少 1 次 typedfinal_output。准备 1 条故意偏题输入观察异常或 handler。handler 返回值与output_type同构并有「非成功」标记。文档钉点已抄进_w/output-type-docs-notes.md。生产路径审查鉴权、审计、审批与 schema 分离。踩坑以为 print 出来是 JSON 就等于结构化可能是字符串。Optional 过多字段全空也能「通过」。枚举写成自由字符串schema 过宽下游还要再解析。handler 返回 dict 却缺必填二次校验失败。测试只跑 happy path上线后第一次偏题就炸。把 ModelBehaviorError 当偶发网络错误重试可能是契约设计问题。工程落地字段设计比模型更重要结构化输出项目里一半事故来自字段语义含混。建议把每个字段写成三行笔记含义、合法例子、非法例子。例如severity若允许自由文本下游就会出现「挺严重的」若收敛为枚举low|mid|high测试可以断言。字段名避免动词化空话optimize_level改用可观察量user_impact_pct_estimate且标明是估计。与 sessions / tool-loop 文的衔接Session 管历史tool 循环管副作用output_type管终局形状。三者不要互相顶替。常见错误是「历史里已经有 JSON所以不必 output_type」——历史可被污染契约必须在终局里再验一次。回归测试最小集Happy字段齐全类型正确。缺必填应失败或进 handler。枚举越界应失败。夹带 Markdown 前言应失败证明不是靠提示词碰运气。tools 后再结构化先查数再出票确认终局类型仍对。把这五条写进 CI 的「契约烟测」比再加两句提示词更值钱。文档钉点与清单见_w/。对外沟通模板「我们启用了 Agents SDK 的output_type。成功表示字段通过 schema失败会进入受控回退且不会自动建单。业务授权在独立服务。」把这句话放进运行手册避免值班把 handler 回正当成功。与 guardrails 的分工output_type管形状input/output guardrails 管策略与安全。形状合法仍可能违规侮辱、泄密、越权指令。生产上两者并列先 guardrail再业务授权最后落库。不要指望一个 Pydantic 模型解决三层问题。对照表提示词 JSON vs output_type vs 手工解析做法稳定性可测性生产建议提示词要求 JSON低难断言仅演示模型输出后json.loads中需大量容错过渡期output_type structured outputs高可直接 isinstance默认生产路径output_type invalid handler高可测降级生产必配手工解析看似灵活实则把契约藏进一堆try/except。正式环境应把灵活留给业务层状态机而不是留给「偶尔多写的散文」。日志里应留下什么至少run_id、agent 名、模型快照名、output_type名、是否走过 invalid handler、耗时、token 粗用量。不要把 API Key 与用户隐私字段打进同一行明文日志。出现 handler 命中率升高时优先查提示词与 schema 是否互撕而不是先加机器。小队分工建议平台组维护 Agent 定义与 handler。业务组定义字段合法例子与鉴权。值班组只根据「成功 / 回退 / 异常」三态操作不解读模型散文。三组口径一致结构化输出才不会变成新的甩锅接口。从演示到生产的迁移清单把演示用的「请输出 JSON」提示词删掉或降级为注释避免双轨。为每个 Agent 声明唯一output_type禁止同一 Agent 有时 str 有时模型。配置invalid_final_output回退对象带明显前缀或状态字段。鉴权服务独立即使 severityhigh 也要查角色与工单类型。审计成功与回退分指标看板回退率突增要告警。与 tool 审批联调先批准副作用再接受结构化终局。文档把禁区六条贴进 oncall runbook。完成这七步才算从博客可跑片段迁到可值班系统。若只完成第 1–2 步就宣称「我们已结构化」仍会在第一次偏题输入时夜惊。误读官方文档的两种方式误读一以为output_type会自动重试直到成功。官方明确 handler 不重试模型。误读二以为空结构化响应与非空校验失败完全同一路径。实现细节以当前文档为准工程上两者都要有烟测不可只测一种。把误读写成 FAQ比在群里反复解释更省时间。总结output_type的价值是把输出形状从「口头约定」变成「Runner 可执行契约」。先跑通 typed 终答与 invalid 回退烟测再谈是否自动写库。去掉任何产品名读者手里仍应剩下一个 Pydantic 模型、一段可跑代码、一份生产禁区清单。