TATECHATLAS
◎ 简体中文
人工智能

为什么 JSON Schema 验证对 LLM 输出至关重要

这是一份技术指南,解释了为什么来自大语言模型 (LLM) 的语法正确 JSON 对于生产系统来说是不够的,以及 JSON Schema 如何提供必要的结构和类型保证。

本文内容

先解析模型响应,再根据明确的模式验证所得对象,然后才能使用。JSON 解析检查格式是否可读,不会要求应用所需的字段或类型。用 required 指定必填字段,用 type 指定值类型;需要拒绝多余字段时,设置 additionalProperties: false。将无效响应作为错误处理。这些检查保证的是已声明的结构,不保证回答真实,也不涵盖所有业务规则。

思维模型:语法与模式

为了解决 LLM 输出不可靠的问题,必须区分语法 (Syntax) 与模式 (Schema)。语法是指 JSON 格式本身的规则:每个左括号必须有对应的右括号,且键必须包含在双引号中。像 Python 的 json.loads() 这样的解析器仅检查这些规则。

然而,模式定义了数据的语义含义和结构。虽然语法确保了文件是可读的,但模式确保了内容是可用的。LLM 可以很容易地生成一个语法上完美但逻辑上毫无用处的 JSON 对象,因为它未能提供应用程序所需的特定信息。

示例:null、缺失字段和有效字符串

假设有一个需要用户个人资料的应用程序。系统要求 'email' 字段必须为字符串类型。LLM 可能会生成如下 JSON:

输入 JSON: {"name": "John Doe", "email": null}

在这种情况下,该 JSON 在语法上是完美的。解析器会成功将其转换为 Python 字典。然而,如果你的代码尝试对 email 字段调用.split('@'),应用程序会因为收到的是 NoneType 而不是字符串而抛出 AttributeError 异常并崩溃。

通过应用一个将 'email' 定义为必需字符串的 JSON Schema,验证步骤可以在数据到达业务逻辑之前就捕获这个错误。

# Install in your environment: python -m pip install jsonschema
import json
from jsonschema import Draft202012Validator

schema = {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "email": {"type": "string"}
    },
    "required": ["name", "email"],
    "additionalProperties": False
}
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
samples = [
    '{"name": "Ada", "email": null}',
    '{"name": "Ada"}',
    '{"name": "Ada", "email": "ada@example.com"}'
]
for text in samples:
    data = json.loads(text)
    print("accepted" if validator.is_valid(data) else "rejected")
# Expected illustrative output:
# rejected
# rejected
# accepted

预期结果

三个输入的预期示意输出依次为 rejected、rejected、accepted。第一个对象有 email,但 JSON null 转为 Python None,不符合 string 类型。第二个缺少 email,因此被 required 拒绝。第三个符合此模式。"not-an-email" 也会通过,因为示例只验证字符串类型,不验证邮箱地址。在 Python jsonschema 中,仅声明 format 不会启用格式检查;需要时应配置格式检查器。

诊断:为什么 LLM 会验证失败

LLM 验证失败有几个原因。首先,它们可能会产生幻觉字段名,例如使用 'user_email' 而不是预期的 'email'。其次,它们可能难以处理复杂类型,例如在需要数字 10 时返回字符串 '10'。第三,如果提示词 (Prompt) 模糊不清,它们可能会完全忽略某些字段。最后,它们可能会包含非标准值,如 NaN 或 Infinity,虽然某些解析器可能接受这些值,但它们并不符合严格的 JSON 规范 (RFC 7159)。

常见错误

一个常见的错误是假设只要解析器没有抛出错误,数据就是安全的。另一个错误是未能考虑到 'null' 值;在 JSON 中,一个键可以存在但其值为 null,这与键不存在有着本质的区别。开发人员还经常忘记限制属性的数量,导致 LLM 返回庞大且臃肿的对象,在处理过程中消耗过多的内存和 CPU。

验证决策标准

先明确应用契约:必填字段以及明确的类型。若要拒绝额外字段,使用 additionalProperties: false;properties 本身并不禁止它们。只有存在具体要求时才添加边界或模式。解析前应另行限制响应大小。Python json.loads 默认接受 NaN 和 Infinity;若要求严格 JSON,可用 parse_constant 抛出错误。这些结构检查不能代替权限控制或业务规则验证。

范围限制

JSON Schema 验证是一种结构化检查,而非逻辑检查。它可以确保 'price' 字段是一个数字,但它无法确保价格是正确的,也无法确保它与你数据库中的价格一致。此外,如果 LLM 生成了一个数 GB 大小的 JSON 字符串,模式验证无法防御资源耗尽攻击 (DoS);你必须在开始解析之前对输入字符串设置大小限制。

需求总结

为了有效地实施此方案,请确保你拥有:1. 为每个预期的 LLM 响应定义了 JSON Schema。2. 一个验证库(例如 Python 中的 jsonschema)。3. 一套处理验证错误的策略(例如重试 LLM 提示词或向用户返回错误)。4. 输入大小限制,以防止在初始解析阶段发生内存耗尽。

检查清单

  • 模式是否定义了所有必需的字段?
  • 类型(字符串 vs 数字)是否得到了明确强制?
  • 在解析之前是否对输入字符串大小进行了限制?
  • 解析器是否配置为处理或拒绝 NaN/Infinity?

JSON Schema 无法验证业务逻辑的一致性(例如,确保 'start_date' 在 'end_date' 之前)或内容的真实准确性。

参考来源

  1. Python: json ↗
  2. JSON Schema: objects and required properties ↗
  3. Python jsonschema: installation and format checking ↗
返回顶部 ↑