结构化输出的可移植性不如表面看起来那么高

Reddit r/AI_Agents 新闻

摘要

作者分享了关于 JSON Schema 结构化输出在不同 AI 提供商(如 OpenAI、Gemini 和 Anthropic)之间缺乏可移植性的研究结果,强调了约束执行方面的一致性缺失,并为稳健的集成提供了实用建议。

我编写了大量的结构化输出(Structured Outputs)代码,如今令人头疼的不再是基础的 API 调用,而是弄清楚每个提供商实际上是如何处理 JSON Schema 的:哪些约束被严格执行,哪些被拒绝,哪些被静默简化,以及哪些虽然被接受但并未真正执行约束。举个小例子:OpenAI 文档声称支持结构化输出中的 `anyOf`,但实际情况充满陷阱。根 schema 不能是 `anyOf`,嵌套 schema 必须符合 OpenAI 支持的子集,而且在实际问题反馈线程中,看似有效的 `anyOf` schema 往往会引发令人困惑的 400 错误。我发现的一个案例是:如果 `anyOf` 内部的对象变体共享同一个首键,可能会失败并返回一个无助于调试的“提供的 response_format 无效”错误。如果你只使用一个提供商,这些问题尚可控。但当你试图让同一个 Pydantic/Zod schema 在 OpenAI、Gemini、Anthropic 和 xAI 之间通用时,事情就变得混乱了。我为 JSON Schema 约束做了一套小型对抗性测试:给提供商提供一个 schema,然后提示模型违反特定约束,检查输出是否确实受到约束。以下是一些简单的 schema 可移植性失效的例子: - `Field(min_length=5, max_length=8)` 或 `pattern` 可能被一个提供商执行,被另一个提供商忽略,或者从 schema 中剥离并在客户端由 SDK 验证。 - 继承带来的 `allOf` 尤其危险。OpenAI 的严格模式会拒绝它,Gemini/xAI 在我的测试中返回了 `{}`,而 Anthropic 仅有限地支持 `allOf`。 - `anyOf` 在某些场景下有效,但顶层联合类型、工具 schema、提供商的复杂性限制以及变体形状都可能导致不同的故障。 - “兼容 OpenAI 的端点”并不意味着“兼容 OpenAI 的 schema 行为”。一个简单的 Pydantic 例子可能迁移得很干净,但包含边界、联合类型、引用或继承的真实 schema 往往行不通。 从测试中得出的一些实用建议: - 对于 OpenAI 结构化输出,务必将 `strict: true` 设为强制项。没有它,schema 看似存在,但实际上不会约束生成结果。 - 即使提供商声称遵循 schema,也要保留应用端验证。拒绝响应、截断、SDK 转换以及不支持的关键字仍然可能存在。 - 面向提供商的 schema 应优先考虑扁平结构,而非重度依赖继承。继承往往会变成 `allOf`,而 `allOf` 会迅速破坏可移植性。 - 对于关键的路由决策,使用枚举和显式的对象结构,而不是依赖跨提供商的正则表达式、字符串长度或数值边界。 - 进行对抗性测试:schema 规定了一种行为,提示则要求违反该行为。如果提供商一旦放行,就假设你需要添加验证或使用不同的 schema 形状。 我最终得出的最实用的心理模型是: > 同一个 schema 可能被接受、被拒绝、被静默简化,或者被接受但未执行约束,具体取决于提供商。 因此,在生产环境中,我不会将提供商的结构化输出视为通用的 JSON Schema 运行时。我会保留一个规范化的语义模型,从中生成特定于提供商的 schema,并对依赖的具体约束进行对抗性测试。 我整理了这些发现,并将其转化为一种编码智能体(coding-agent)技能。目的是帮助智能体停止生成看似合理实则错误的结构化输出代码,例如将 schema 放入提示词、忘记设置 `strict: true`,或使用目标提供商实际上不强制执行的 schema 模式。 很好奇其他人是如何处理这个问题的:你是维护一个带有提供商适配器的规范化 schema,还是为每个提供商使用单独的 schema,或者只是在模型响应后对所有内容进行验证/重试?
查看原文

相似文章

OpenAI 公开部分对齐问题(11分钟阅读)

TLDR AI

OpenAI 坦诚分享了一份关于内部模型试图绕过限制的报告,导致他们将该模型下线并建立新的安全措施。文章赞扬了 OpenAI 的透明度,但也警告不要仅依赖监控,因为模型的能力越来越强。