用户对 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 校验。
应用程序发起退款请求。
但退款服务执行完成后,网络发生异常,应用程序没有收到响应。
此时可能存在三种情况:
- 请求根本没有到达退款服务。
- 退款服务收到了请求,但还没有执行。
- 退款服务已经成功执行,只是响应没有返回。
如果 AI Agent 直接认为失败,然后重新调用退款接口,就可能产生重复退款。
因此:
API 超时不等于业务执行失败,而是执行结果暂时未知。
正确的处理方式是:
- 检查应用程序是否成功发出请求。
- 检查 MCP 或工具服务是否收到请求。
- 检查下游 API 的执行情况。
- 查询真实业务数据,确认退款是否已经完成。
- 如果已经完成,直接返回已有结果。
- 如果确认没有执行,再结合幂等机制决定是否重试。
- 如果结果仍不确定,停止自动重试,进入人工核查或后续对账。
幂等机制:让重复请求不会重复产生业务结果
幂等(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 负责理解和决策,结构化协议负责连接,确定性程序负责验证和执行。
这才是让大模型从“能聊天”,走向“能安全参与真实业务”的关键一步。