文章

Structured Outputs + Tool Calling:生产 Agent 为什么需要两阶段约束设计

从 OpenAI、Anthropic、vLLM 与最新研究出发,拆解结构化输出、工具调用和约束解码的工程边界,说明为什么生产级 Agent 应采用两阶段约束设计,避免 JSON Schema 抑制工具调用。

为什么结构化输出不是简单的格式化问题

很多 LLM 应用在 Demo 阶段只需要一句提示词:「请用 JSON 返回」。到了生产环境,这种方式会迅速暴露问题:字段缺失、枚举非法、嵌套结构错误、Markdown 包裹 JSON、工具调用参数不稳定、最终结果无法被后端安全解析。

Structured Outputs 的价值在于,它不只是提示模型「像 JSON 一样输出」,而是在生成阶段约束输出必须符合 schema。对表单抽取、订单查询、Agent 工具调用、自动化审批、RAG 结构化答案来说,这比普通 prompt 更接近工程接口。

但新的问题也随之出现:如果系统既要求模型调用工具,又要求模型输出严格 JSON,应该把这两件事放在同一次生成里吗?答案通常是否定的。

核心结论:工具调用和最终结构化输出要分层

一个生产级 Agent 通常有两类输出:

Action output: 模型选择工具、填写参数、触发外部系统
Final output:  模型根据证据生成稳定结构化结果

这两类输出的目标不同。

  • 工具调用关注的是 action selection:是否需要查数据库、调用哪个 API、参数怎么填、失败后是否重试。
  • 结构化输出关注的是 contract compliance:最终结果是否满足 JSON Schema、字段是否完整、类型是否正确、枚举是否合法。

如果把两者强行塞进同一个 schema-constrained decoding 过程,模型可能为了满足最终 JSON 结构而减少工具调用意愿。近期关于开源模型的研究把这种现象称为 tool suppression:schema 合规率看起来很高,但工具执行能力被压低。

核心知识结构

可以把这个主题拆成 6 个工程模块。

1. JSON Mode

JSON mode 解决的是「输出是不是合法 JSON」。它不能可靠保证 required 字段、enum、嵌套结构和业务规则都符合要求。

2. Structured Outputs

Structured Outputs 解决的是「输出是否符合 JSON Schema」。它更适合直接对接后端接口、数据库写入、UI 渲染和工作流节点。

3. Constrained Decoding

Constrained decoding 会在每一步生成时限制可选 token,使输出只能沿着合法 grammar 或 schema 继续生成。它比事后解析和 retry 更强,但也会引入约束开销。

4. Function Calling

Function calling 是模型与外部系统交互的动作层。它的重点不是最终文本格式,而是工具选择、参数填写、执行顺序和错误恢复。

5. Tool Schema

工具 schema 不是普通 API 文档。它会影响模型如何理解工具边界、参数含义、使用时机和返回结果。

6. Two-Pass Execution

两阶段执行把「工具行动」和「最终结构化回答」拆开:第一阶段允许模型完成工具调用,第二阶段再用 schema 约束最终输出。

为什么单次强约束容易出问题

假设你要做一个订单客服 Agent。用户问:

帮我查一下上个月未按时送达的订单,并按订单号、原因、建议动作返回 JSON。

系统有两个目标:

  1. 调用订单查询工具,拿到真实订单数据
  2. 按固定 JSON Schema 输出结果

错误设计通常是这样:

User task + Tool definitions + Final JSON Schema -> One model call under strict schema

这种设计看起来简单,但容易产生 4 类问题。

问题一:模型优先满足最终 schema

当最终 schema 已经要求输出 ordersreasonnext_action 等字段时,模型可能直接生成结构化结果,而不是先调用工具获取证据。

问题二:工具调用 token 与 schema token 竞争

在部分 guided decoding 实现中,schema 会被编译成 grammar 或 token mask。如果 mask 只允许最终 JSON 结构继续生成,工具调用相关 token 可能变得不可达或低优先级。

问题三:评测指标会误导

如果只看 JSON 是否 valid,系统可能得到很高分。但这不能说明工具真的被调用,也不能说明结果来自真实数据。

问题四:失败恢复困难

工具调用失败、参数缺失、权限不足、外部 API 超时,都需要 action 层恢复。如果生成过程被最终 schema 强约束,恢复路径会变窄。

推荐架构:Transparent Two-Pass Execution

更稳的方式是把 Agent 分成两个阶段。

第一阶段:工具执行阶段

目标是完成事实获取和动作执行。

  • Input: 用户任务 + 可用工具 + 工具使用规则
  • Output: 工具调用、工具结果、压缩证据、错误状态
  • Constraint: 约束工具参数,但不要强制最终业务 JSON

这一阶段关注:

  • 是否需要工具
  • 调用哪个工具
  • 参数是否完整
  • 工具结果是否足够
  • 失败是否需要重试或降级

第二阶段:最终结构化输出阶段

目标是把证据转成稳定的业务结果。

  • Input: 用户任务 + 第一阶段证据 + 最终 JSON Schema
  • Output: 符合 schema 的最终 JSON
  • Constraint: 严格 schema-constrained decoding

这一阶段关注:

  • required 字段是否完整
  • enum 是否合法
  • 数组和嵌套对象是否符合结构
  • 无证据字段是否明确为空或标记 unknown
  • 是否可被后端直接解析

一个可落地的伪代码结构:

async function runAgentTask(userInput: string) {
  const actionTrace = await runToolPhase({
    userInput,
    tools: [searchOrders, getShipmentDetail, createSupportTicket],
    policy: {
      requireEvidenceBeforeFinalAnswer: true,
      maxToolCalls: 5,
      compressToolResults: true,
    },
  });

  const finalJson = await runStructuredFinalPhase({
    userInput,
    evidence: actionTrace.compactedEvidence,
    schema: LateDeliveryOrderReportSchema,
    strict: true,
  });

  validate(finalJson, LateDeliveryOrderReportSchema);
  logTrace({ userInput, actionTrace, finalJson });
  return finalJson;
}

这段结构的重点不是具体 SDK,而是边界:工具执行阶段不背最终 JSON 的结构压力,最终输出阶段不再决定是否调用工具

Schema 设计也会影响模型行为

很多团队以为 schema 只是后端校验规则。实际上,schema 的字段名、description、enum 命名和嵌套方式都会向模型传递隐含指令。

例如下面两个字段,行为暗示并不一样:

{ "status": "string" }
{
  "delivery_risk_level": {
    "type": "string",
    "enum": ["low", "medium", "high", "unknown"],
    "description": "Risk level based only on verified shipment evidence. Use unknown when evidence is insufficient."
  }
}

第二种写法更适合生产,因为它把业务含义、证据边界和 fallback 规则都写进 schema。

工具定义比工具数量更重要

工具调用失败很多时候不是模型能力问题,而是工具面设计问题。

一个生产工具定义至少应说明:

  • 工具做什么
  • 什么时候应该用
  • 什么时候不应该用
  • 每个参数是什么意思
  • 参数格式和业务限制是什么
  • 返回结果包含什么
  • 返回结果不包含什么
  • 调用失败后如何处理

工具返回也要控制长度。不要把原始日志、HTML、大段 JSON 全部返回给模型。更合理的是返回高信号结构:

{
  "order_id": "O-9182",
  "delivery_status": "late",
  "promised_date": "2026-05-12",
  "actual_date": "2026-05-15",
  "evidence": ["carrier_scan_delayed", "warehouse_dispatch_late"],
  "recommended_action": "issue_shipping_credit"
}

评测不能只看 schema 合规率

Structured Outputs 很容易让团队产生一个错觉:只要 JSON 通过校验,系统就可靠了。

生产评测至少要拆成 4 层:

评测维度检查内容
Format Compliance输出是否是合法 JSON,是否通过 schema validation
Tool Invocation Quality是否在需要时调用工具,是否没有在不需要时滥用工具
Argument Accuracy工具参数是否来自用户任务,时间范围、ID、枚举和过滤条件是否正确
Evidence Faithfulness最终 JSON 中的结论是否来自工具结果,而不是模型补全

一个简化评测样例:

task: "Find May orders that were fulfilled but delivered late."
expected_behavior:
  must_call_tools:
    - search_orders
    - get_shipment_detail
  must_not_fabricate_orders: true
  final_schema_valid: true
  evidence_required_for_each_order: true
metrics:
  - schema_valid_rate
  - tool_call_recall
  - tool_argument_accuracy
  - evidence_faithfulness
  - end_to_end_success_rate

如果只看 schema_valid_rate,你可能会把一个「会编 JSON 但不会查数据」的 Agent 误判为可靠。

生产落地 Checklist

上线前建议检查:

  • 是否区分工具调用阶段和最终结构化输出阶段
  • 是否避免把最终业务 JSON Schema 强行套在 action selection 上
  • 是否明确每个工具的使用边界
  • 是否为复杂参数提供 schema-validated examples
  • 是否控制工具返回长度,只保留高信号字段
  • 是否对最终输出使用 schema validation
  • 是否设计无证据字段的 fallback,例如 unknownnullevidence_missing
  • 是否记录完整 trace:用户输入、工具调用、参数、工具结果、最终 JSON
  • 是否同时评测 schema 合规率和工具调用召回率
  • 是否为 open-weight model 单独评测 tool suppression 风险
  • 是否在不同 serving backend 上验证 structured output 行为
  • 是否禁用或谨慎使用与 schema 不兼容的并行工具调用模式

结论

Structured Outputs 解决的是「最终结果能不能被系统稳定解析」,Tool Calling 解决的是「模型能不能正确行动并获取证据」。这两个能力都很重要,但它们不应该被粗暴合并成一次强约束生成。

更稳的生产设计是:先行动,再约束,再验证——让 Agent 在工具阶段完成检索、查询、执行和证据收集,让模型在最终阶段按 JSON Schema 输出稳定结果,再用 schema validation、evidence check 和 trace 监控闭环。

对工程团队来说,关键原则是:不要把 Structured Outputs 当成万能可靠性补丁。它应该约束最终接口契约,而不是替代工具规划、证据获取和 Agent 评测。

常见问题

Structured Outputs 和 JSON mode 有什么区别?
JSON mode 主要保证输出是有效 JSON,但不保证字段、枚举和嵌套结构完全符合 schema;Structured Outputs 会按 JSON Schema 约束生成,更适合生产系统对接。
为什么工具调用和最终 JSON 输出不建议混在同一个约束里?
工具调用需要模型先决定是否行动,最终 JSON 输出需要模型严格遵循 schema。两类约束同时启用时,部分模型或解码后端可能优先满足 schema,导致工具调用被抑制。
两阶段约束设计适合哪些场景?
适合订单查询、工单诊断、RAG 问答、数据抽取、自动化运营等既需要调用工具获取证据,又需要输出稳定结构化结果的 Agent 应用。