结构化输出、Schema 校验与工具参数可靠性

0 阅读13分钟

用户对 AI 说:

“帮我查一下订单 20261103001 的退款进度,如果还没退,就帮我处理一下。”

大模型能够理解用户需求,也能判断需要调用什么工具。但要让程序真正执行,它还需要生成符合接口要求的参数。字段缺失、类型错误,甚至业务判断错误,都可能导致执行失败。

如何把模型理解的自然语言,转换成程序能够识别、校验和执行的数据?

一、为什么 AI 需要结构化输出?

大语言模型(LLM)擅长理解自然语言,但它的输出具有灵活性。

同一个意思,模型可能生成不同表达:

  • 订单正在退款处理中。
  • 退款申请已经提交,还没有完成。
  • 当前退款尚未到账。
  • 退款流程仍在进行。

对于人来说,这些表达都容易理解。

但对于程序来说,调用 API 需要明确的字段和数据类型。

例如,退款查询接口要求:

{
  "order_id": "20261103001",
  "include_history": true
}

模型却可能返回:

{
  "order_id": 20261103001,
  "include_history": "yes"
}

看起来只是数字和字符串的区别,但程序要求 order_id 是字符串,include_history 是布尔值。

这就可能导致参数校验失败。

结构化输出(Structured Output)就是为了解决这个问题:让模型按照预先约定的数据结构生成结果,使模型输出能够被程序稳定地读取和处理。

它解决的不是模型能不能理解用户,而是模型如何把理解结果交给程序。

这里也体现了 AI 应用与传统软件的一个区别:

传统软件通常由表单、按钮和固定流程产生结构化参数;AI 应用则需要先理解用户的自然语言,再把理解结果转换为程序可用的数据。

二、JSON 和 JSON Schema:数据格式与数据规范

要理解结构化输出,需要先区分两个概念。

1. JSON:组织和传递数据

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。

例如:

{
  "name": "张三",
  "age": 28,
  "is_member": true,
  "orders": [
    "20261103001",
    "20261103002"
  ]
}

JSON 使用键值对组织数据,可以表示字符串、数字、布尔值、数组和对象等类型。

它解决的是数据如何表示和传递的问题。

但 JSON 本身并不规定某个业务对象必须有哪些字段,也不保证字段的业务含义正确。

2. JSON Schema:描述和约束数据结构

JSON Schema 用于描述 JSON 数据应该符合什么规范。

例如,订单查询要求订单编号必须是字符串:

{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "description": "订单编号"
    },
    "include_history": {
      "type": "boolean",
      "description": "是否包含退款历史"
    }
  },
  "required": [
    "order_id"
  ],
  "additionalProperties": false
}

其中:

关键字作用
type规定数据类型
properties定义对象包含哪些字段
required指定必填字段
enum限制字段允许的取值
additionalProperties控制是否允许额外字段
description解释字段的业务含义

需要注意:定义在 properties 中的字段,不会自动成为必填字段,必须通过 required 明确指定。

例如:

{
  "type": "string",
  "enum": [
    "processing",
    "success",
    "failed"
  ]
}

表示退款状态只允许三种取值。

这样可以避免模型随意返回 refund_done、refunding_now 等未经系统定义的状态。

JSON 负责承载数据,JSON Schema 负责描述数据应该符合什么规范。

3. 结构化输出如何使用 Schema?

如果只是通过 Prompt 告诉模型:

“请严格按照 JSON 格式输出。”

模型仍然可能遗漏字段或生成不符合要求的数据。

在支持结构化输出约束的模型接口中,可以将 JSON Schema 提供给模型,让模型按照指定结构生成结果。

例如,OpenAI 的 Structured Outputs 支持通过 strict 模式约束输出符合受支持的 JSON Schema 子集。

这比单纯依赖自然语言提示更加可靠。

但要注意:不同模型平台对 JSON Schema 的支持范围可能不同,不能认为所有 Schema 关键字都能被所有模型接口支持。

三、结构化输出与 Function Calling 有什么关系?

这两个概念经常一起出现,但它们解决的问题不同。

1. Function Calling:让模型提出工具调用请求

Function Calling(工具调用)让模型能够根据用户需求选择合适的工具,并生成对应的调用请求。

例如,应用程序提前向模型提供两个工具:

get_refund_status
查询退款状态

create_refund
发起退款

用户说:

“帮我查询订单的退款进度。”

模型判断应该调用:

get_refund_status

并生成相应的参数。

2. Schema:约束工具调用参数

工具定义通常包含工具名称、用途描述和参数 Schema。

例如:

{
  "name": "get_refund_status",
  "description": "查询订单退款状态",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string"
      }
    },
    "required": [
      "order_id"
    ]
  }
}

模型在调用工具时,需要按照参数 Schema 生成请求。

两者的关系可以这样理解:

技术主要职责
工具定义告诉模型有哪些工具、用途和参数要求
Function Calling模型选择工具并生成调用请求
JSON Schema约束工具参数的数据结构
应用程序校验请求并真正执行工具

实际调用过程:

用户提出问题
    ↓
模型理解用户意图
    ↓
选择工具并生成参数
    ↓
应用程序接收调用请求
    ↓
校验参数
    ↓
调用真实 API
    ↓
返回业务结果
    ↓
模型组织最终回答

这里有一个容易混淆的地方:

模型生成了 Function Calling 请求,并不代表工具已经执行。

真正执行 API 的是应用程序。工具可以通过 API、MCP 等方式接入,Schema 不负责网络通信或业务执行。

另外,结构化输出也可以独立使用。例如,让模型把一段自然语言整理成固定格式的分类结果,不需要调用任何外部工具。

3. JSON Mode、Structured Outputs 和 Function Calling 的区别

技术主要解决的问题
JSON Mode输出合法 JSON
Structured Outputs输出符合指定 Schema
Function Calling选择工具并生成调用请求

JSON Mode 不保证输出符合指定的数据结构;Structured Outputs 可以进一步约束结构;Function Calling 则负责工具选择和调用请求。

三者可以配合使用,但不能互相替代。

四、为什么 Schema 校验通过,AI 仍然可能做错事?

现在假设模型已经生成了完全符合要求的参数:

{
  "order_id": "20261103001"
}

Schema 校验也通过了。

是不是就可以执行?

还不行。

因为数据结构正确,不代表业务事实正确,也不代表当前用户有权限操作。

实际系统至少需要关注三个层次。

1. 结构校验:数据格式是否正确?

检查:

  • JSON 是否能够正常解析。
  • 必填字段是否存在。
  • 字段类型是否正确。
  • 枚举值是否符合要求。
  • 是否出现不允许的额外字段。

2. 业务校验:数据是否符合真实业务规则?

假设订单编号格式正确,但订单实际上不存在。

或者订单已经取消,业务规则不允许退款。

这些问题无法单纯通过 Schema 判断。

必须由真实业务服务查询数据,并根据业务规则判断是否允许执行。

3. 权限校验:当前用户是否有权访问?

假设用户 A 请求查询用户 B 的订单。

即使订单编号合法、订单真实存在,也不能允许用户 A 查询。

用户身份应该来自服务端可信的登录态和权限系统,不能信任模型自行生成的 user_id、角色或租户信息。

三层校验解决的是不同问题:

校验层核心问题
结构校验数据格式正确吗?
业务校验业务条件允许吗?
权限校验当前用户有资格操作吗?

三者不能互相替代。

这也是为什么 AI 应用不能把所有判断都交给大模型。模型可以理解用户的自然语言,但真实业务事实和操作权限必须由确定性系统负责。

五、模型生成错误参数,系统应该怎么办?

即使使用结构化输出,实际应用仍然需要处理异常情况。

例如,模型收到:

“查一下订单的退款进度。”

但用户没有提供订单编号,上下文中也没有明确的订单信息。

此时模型不能随便猜一个订单编号,而应该要求用户补充信息。

再比如,模型生成了不符合 Schema 的参数。

系统可以把具体错误反馈给模型,让它有限次数地纠正:

参数校验失败:

order_id 必须为字符串,
当前返回值为数字。

请根据工具参数定义重新生成。

如果连续纠正仍然失败,就应该停止工具调用并报告错误。

不能为了完成任务而绕过 Schema 校验。

这里需要区分两类失败:

  • 模型生成失败: 参数不符合 Schema,业务 API 尚未执行。
  • 工具执行失败: 参数通过校验,但调用下游 API 时发生异常。

它们的恢复方式并不相同。

尤其是工具执行失败,需要考虑分布式系统中的可靠性问题。

六、API 超时,为什么不能让 AI 自动再调用一次?

假设用户说:

“帮我申请退款。”

模型已经正确选择 create_refund 工具,参数也通过了 Schema 校验。

应用程序发起退款请求。

但退款服务执行完成后,网络发生异常,应用程序没有收到响应。

此时可能存在三种情况:

  1. 请求根本没有到达退款服务。
  2. 退款服务收到了请求,但还没有执行。
  3. 退款服务已经成功执行,只是响应没有返回。

如果 AI Agent 直接认为失败,然后重新调用退款接口,就可能产生重复退款。

因此:

API 超时不等于业务执行失败,而是执行结果暂时未知。

正确的处理方式是:

  1. 检查应用程序是否成功发出请求。
  2. 检查 MCP 或工具服务是否收到请求。
  3. 检查下游 API 的执行情况。
  4. 查询真实业务数据,确认退款是否已经完成。
  5. 如果已经完成,直接返回已有结果。
  6. 如果确认没有执行,再结合幂等机制决定是否重试。
  7. 如果结果仍不确定,停止自动重试,进入人工核查或后续对账。

幂等机制:让重复请求不会重复产生业务结果

幂等(Idempotency)是分布式系统中非常重要的概念。

它关注的是:

同一个业务操作被重复提交时,是否会产生重复的业务副作用。

例如,系统为一次退款申请生成唯一的业务请求标识:

Idempotency-Key: refund-request-001

第一次调用时,退款服务记录这个请求标识及其处理结果。

如果同一个业务请求再次提交,并且复用了相同的幂等键,服务端就可以识别这是同一次操作,而不是一笔新的退款。

实际实现还需要保证幂等记录与业务副作用之间具备可靠的一致性,不能只在客户端生成一个 ID 就认为已经实现幂等。

这也意味着:

  • 查询类接口可以根据其安全语义决定是否重试。
  • 退款、支付、创建订单等写操作,必须考虑重复执行的风险。
  • 执行结果不确定时,先查询业务状态。
  • 无法确认状态时,停止自动重试,转人工核查或对账。

这不是 AI 独有的问题,而是分布式系统的经典可靠性问题。

但当 Agent 能够自主选择工具并发起多次调用时,这些问题会更加突出。

七、如何判断一个 AI 工具调用是否可靠?

不能只看模型是否成功返回 JSON。

一个完整的 AI 工具调用链路,至少应该从以下几个方面评估。

1. 模型理解与工具选择

  • 是否正确识别用户意图?
  • 是否选择了正确的工具?
  • 信息不足时是否主动澄清?

2. 参数生成

  • Schema 通过率。
  • 必填字段完整率。
  • 参数语义正确率。
  • 错误纠正成功率。

3. 系统执行

  • 业务校验是否正确。
  • 越权操作是否被拦截。
  • API 调用成功率。
  • 超时后的状态核实是否有效。
  • 是否发生重复业务副作用。

4. 最终结果

  • 模型最终回答是否依据真实工具返回结果?
  • 是否把未知状态说成执行成功?
  • 是否能够准确报告失败原因?

这里尤其需要注意:

Schema 通过率高,不代表工具调用成功率高。

工具调用成功,也不代表业务结果正确。

只有把模型行为、工具执行和业务结果结合起来,才能真正评估 AI 应用的可靠性。

八、真正可靠的 AI 应用,需要让模型和程序各司其职

为什么大模型已经能理解用户需求,却不能直接放心地操作业务系统?

因为理解需求和执行业务,是两种不同的能力。

大模型擅长处理自然语言、理解上下文、判断意图和选择工具。

但业务系统需要确定的数据结构、真实业务状态、用户权限和可控的执行结果。

结构化输出和 Schema,为两者之间建立了数据契约。

Function Calling,让模型能够以标准方式提出工具调用请求。

应用程序负责校验、授权和执行。

幂等、状态核实和有限重试,则负责控制执行失败后的风险。

最终可以用一句话概括:

AI 负责理解和决策,结构化协议负责连接,确定性程序负责验证和执行。

这才是让大模型从“能聊天”,走向“能安全参与真实业务”的关键一步。

参考资料

  1. OpenAI:Structured Outputs
  2. OpenAI:Function Calling
  3. JSON Schema 官方文档
  4. RFC 9110:HTTP Semantics
  5. AWS Builders' Library:Making retries safe with idempotent APIs