开发人员现在可以通过将 Zod schema 接入 Vercel AI SDK 或 Anthropic 的 tool-use API,来确保 LLM 返回的 JSON 符合预定义的结构,从而消除模型因添加意外字段而导致的运行时崩溃。

对具体防护措施的需求在 1 月份变得显而易见,当时一个部署到生产环境的分类器在完美运行三周后,开始返回第二个 “explanation” 键。由于代码预期只有一个字段,这个额外的键触发了异常——而无需进行任何代码部署。这一事件说明了一个更广泛的问题:大多数教程在 JSON.parse(response) 处就停止了,假设模型会遵守提示词中的 schema。实际上,LLM 经常会“跑偏”——改变大小写、添加字段或将输出包裹在 Markdown 代码块中——从而导致静默数据损坏或直接失败。

为什么原始 JSON 解析是不安全的

LLM 的训练目标是提供帮助,而不是唯命是从。一个要求

{ "category": "string" }

的提示词并不能将模型绑定到该精确结构上。即使是编写良好的提示词,也可能被模型的内部启发式算法覆盖,尤其是在 temperature 设置鼓励创造力,或者下游指令促使其进行详细阐述时。结果就是产生一段看起来像 JSON 但偏差足以破坏严格结构解析器的文本流。

当这种不匹配进入生产代码时,代价是立竿见影的:抛出异常、请求失败,并可能引发下游错误连锁反应。在大型服务中,这些分钟级的停机时间会转化为收入损失和用户信任度的下降。

Zod + Vercel AI SDK:三步安全网

Zod 是一个 TypeScript 优先的 schema 验证器,可以描述模型应该输出的精确数据结构。结合 Vercel AI SDK 的 Output.object 助手,验证会在模型生成响应后自动进行。

  1. 定义 schema – 编写一个镜像所需 JSON 的 Zod 对象。对于简单的分类器,它可能是 z.object({ category: z.string() });对于复杂的发票提取器,schema 可以嵌套对象、数组和辨析联合类型 (discriminated unions)。
  2. 将其传递给 SDK – 使用 Output.object(schema) 包装 schema。SDK 会注入一段提示词,告诉模型输出一个符合 schema 的 JSON 块,并使用 Zod 的 safeParse 解析结果。
  3. 处理失败safeParse 返回一个结果对象而不是抛出异常。如果解析失败,将错误反馈给模型并重试。可以指示模型根据具体的验证消息来修正输出,从而将大多数边缘情况转化为自我修复循环。

由于 SDK 在一处完成了提示、解析和重试逻辑,开发人员可以用一个单一的、经过类型检查的调用,取代以往零散的字符串处理。

Anthropic tool use:强制结构化输出

当直接使用 Anthropic 的 API 时,通过“工具调用 (tool use)”也可以实现同样的保证。工具被定义为一个函数,其输入 schema 使用 JSON Schema 表示;只有当 Anthropic 的模型能够满足该 schema 时,才会调用该工具。通过将 tool_choice 设置为 "any"(或特定的工具名称),可以强制模型返回结构化块,而不是自由格式的文本。

工作流程与 Vercel 的方法类似:

  • 编写 Zod schema。
  • 将其转换为用于工具定义的 JSON Schema 负载。
  • 在请求中包含该工具,并要求模型调用它。
  • 使用 zod.safeParse 解析工具的响应。

如果模型仍然产生格式错误的数据,同样适用“带反馈的重试”模式。

当验证仍然失败时

即使实施了 schema 强制执行,偶尔仍会出现不匹配的情况。原因包括:

  • 模型幻觉:模型可能会生成看起来像 JSON 但包含语法错误的字符串。
  • 提示词泄露:之前的对话轮次可能会泄露格式化指令,从而覆盖了 schema 请求。
  • 版本差异:较新的模型版本有时会改变它们解释工具调用的方式。

建议的缓解措施是轻量级的重试循环。在解析失败时,代码会发送后续提示词,例如:“你上次的输出不是有效的 JSON。它包含了……请仅返回 schema 中定义的字段。”由于验证错误是明确的,模型可以在无需人工干预的情况下进行自我修正。

性能与成本考量

添加 Zod 验证引入的 CPU 开销微乎其微——对于典型的负载,safeParse 操作在微秒级即可完成。网络延迟保持不变;只有在极少数失败的情况下,重试才会产生额外的往返开销。在实践中,成功防止一次异常所带来的收益,远大于请求时间略微增加所带来的成本。

反方观点:强制执行 Schema 是否过度了?

一些开发者认为,严格的 Schema 会限制模型的灵活性,尤其是在新字段可能提供有价值的上下文时。这是在安全性与开放性之间的权衡。在任务关键型服务(如支付处理、身份验证、合规报告)中,可预测性至关重要。在探索性原型中,较为宽松的方法或许可以接受,但即使在这种情况下,设置一个最小限度的防护(例如 z.object({}).passthrough())也能在不丢弃有用扩展的情况下,捕获灾难性的解析错误。

后续关注点

  • SDK 演进:Vercel 的 AI SDK 路线图包含了内置的重试策略和更丰富的错误报告,这将进一步简化修复循环。
  • 工具标准化:随着越来越多的供应商采用工具使用(tool-use)规范,可能会出现跨供应商的 Schema 验证器,从而减少对特定供应商适配器的需求。
  • 社区模式:开源库正开始将 Zod Schema 与提示词模板(prompt templates)捆绑在一起,使“Schema 优先”的工作流成为一种可复用的资产。

核心总结

通过将 Zod Schema 视为模型不可逾越的契约,开发者可以从脆弱的 JSON.parse 权宜之计,转向一种确定性的流水线——在这种流水线中,意外字段会导致受控的验证失败,而不是导致生产环境崩溃。Vercel 的 Output.object 助手与 Anthropic 的工具使用机制相结合,将 LLM 从不可预测的文本生成器转变为可靠的数据提供者,让团队能够专注于业务逻辑,而不是无休止地调试边缘情况。