Prompt约束、StructuredOutputConverter、原生JSON Schema与validateSchema怎么选?

文章摘要

企业AI应用需要把模型输出用于路由、入库、审批或工具调用时,不能长期依赖自然语言解析。Spring AI提供了多种结构化输出路径:在Prompt中手写格式要求、使用StructuredOutputConverter生成格式指令和反序列化、启用Provider原生JSON Schema约束,以及通过validateSchema()进行本地校验和自修复。四种方案的可靠性、兼容性、成本和复杂度不同。本文给出分层选型方法和生产组合建议。

一、四种方案解决的问题不同

方案一:手写Prompt约束

请只输出JSON,不要输出解释。

方案二:StructuredOutputConverter

Java类型
→ 生成格式指令
→ 模型输出文本
→ 转换为Java对象

方案三:Provider原生JSON Schema

Schema作为API参数
→ Provider约束输出

方案四:validateSchema自修复

本地验证响应
→ 把错误反馈给模型
→ 有上限地重新生成

它们不是完全互斥,可以组合使用。

二、只写Prompt什么时候够用

示例:

返回JSON:
{
  "category": "string",
  "summary": "string"
}

适合:

  • 原型验证;
  • 低风险内部工具;
  • 结果只用于展示;
  • 失败后可以人工重试;
  • Provider不支持结构化输出。

优势:

  • 最简单;
  • 不依赖框架;
  • 跨模型兼容性高;
  • 调试直观。

不足:

  • 模型可能加Markdown;
  • 可能漏字段;
  • 类型无法保证;
  • Prompt越复杂越脆弱;
  • 下游代码需要大量容错。

结论:

只写Prompt适合原型
不适合关键业务协议

三、StructuredOutputConverter适合什么场景

Spring AI的Converter可以根据Java类型生成Schema或格式说明,再把返回文本转换为对象。

示例:

record CustomerIntent(
        String intent,
        double confidence
) {
}

调用:

CustomerIntent intent = chatClient.prompt()
        .user("判断客户意图")
        .call()
        .entity(CustomerIntent.class);

优点:

  • 与Java领域对象直接衔接;
  • 减少手写JSON模板;
  • 支持List、Map和泛型;
  • 跨Provider;
  • 适合Spring项目。

不足:

  • 默认仍可能是Prompt级约束;
  • 不保证模型一定满足格式;
  • 复杂Schema容易失败;
  • 需要完整响应后才能解析。

适合:

  • 信息提取;
  • 分类;
  • 报告摘要字段;
  • 普通工作流输出;
  • 多Provider兼容层。

四、Provider原生结构化输出的优势

原生结构化输出把JSON Schema放到模型API参数中,而不是只写进Prompt。

优点:

  • 约束更强;
  • Prompt更干净;
  • 输出通常更稳定;
  • 减少Markdown包裹;
  • 更适合机器消费。

Spring AI示意:

CustomerIntent intent = chatClient.prompt()
        .user(message)
        .call()
        .entity(
                CustomerIntent.class,
                spec -> spec.useProviderStructuredOutput()
        );

适合:

  • 工作流路由;
  • API返回对象;
  • 规则判断;
  • 自动审批前置结果;
  • 大批量信息提取。

五、原生JSON Schema为什么不是万能方案

不同Provider支持的Schema子集不同。

常见限制:

  • 顶层数组;
  • $ref;
  • 深层嵌套;
  • oneOf、anyOf、allOf;
  • 递归类型;
  • 正则;
  • Map动态键;
  • 复杂泛型。

因此,跨Provider应用要采用Schema最小公共集:

object
string
number
integer
boolean
array
required
additionalProperties
简单enum

不要把Java完整领域模型原样暴露给模型。

六、validateSchema解决什么问题

即使Provider宣称支持原生结构化输出,也可能存在:

  • 模型版本边界行为;
  • 推理文本混入;
  • Schema子集不完整;
  • Provider兼容层问题;
  • 自建模型不稳定。

validateSchema()属于响应侧安全网:

收到响应
→ 本地Schema验证
→ 失败则反馈具体错误
→ 重新生成

适合:

  • 关键结构;
  • 模型偶发格式漂移;
  • 本地模型;
  • 多Provider路由;
  • 升级灰度期。

七、四种方案对比

维度 Prompt约束 Converter 原生JSON Schema validateSchema
接入复杂度 低 低 中 中
格式可靠性 低 中 高 高
Provider兼容 高 高 中 高
额外调用 无 无 无 失败时增加
流式支持 文本可流式 entity不流式 取决于调用 通常不流式
类型映射 手工 自动 配合Converter 配合Converter
适合生产关键流程 不推荐 需校验 推荐 推荐作为安全网

八、生产项目推荐组合

低风险展示

Converter

例如:

  • 推荐标签;
  • 页面摘要;
  • 普通内容分类。

中风险工作流

Provider原生结构化输出
+本地业务校验

例如:

  • 工单分类;
  • 售前需求提取;
  • 任务分派。

高风险流程

Provider原生JSON Schema
+validateSchema
+业务规则校验
+人工审批

例如:

  • 退款建议;
  • 合同风险;
  • 权限变更;
  • 对外正式承诺。

九、Schema设计的五条原则

1. 字段少

结构化输出不是报告全文。

2. 枚举明确

LOW, MEDIUM, HIGH

不要使用开放字符串表达核心状态。

3. 文本字段有限长

避免模型把整篇分析塞进reason。

4. 不使用深层嵌套

建议控制在两到三层。

5. 协议与领域模型分离

定义AI专用DTO:

record AiRiskDecision(...)

再映射到业务对象,不要让模型直接构造数据库实体。

十、业务校验不能被Schema替代

Schema可以验证:

字段存在
类型正确
枚举合法
结构符合要求

Schema无法判断:

订单是否属于用户
金额是否超限
文档是否过期
审批人是否有权限
日期是否符合业务规则

因此需要第二层:

public void validateBusiness(
        RefundDecision decision,
        Order order
) {
    if (decision.amount().compareTo(order.paidAmount()) > 0) {
        throw new BusinessValidationException(
                "退款金额超过实付金额"
        );
    }
}

十一、什么时候不应该自动修复

以下情况不适合让模型继续尝试:

  • 输入本身缺少关键事实;
  • 用户无权限;
  • 业务状态已变化;
  • Provider预算已耗尽;
  • 输出包含潜在攻击;
  • 高风险结果存在冲突。

自动修复只解决格式问题,不解决事实和权限问题。

十二、模型路由中的结构化输出能力

多模型路由时,应登记:

supports_native_structured_output
supported_schema_features
max_schema_size
supports_top_level_array
reasoning_output_behavior

路由不能只按价格和智能水平,还要看输出协议能力。

例如:

分类任务
→ 选择结构化输出稳定的小模型

复杂分析
→ 强模型生成证据
→ 小模型整理结构

有时“强模型一次完成全部工作”并不是成本和稳定性最优方案。

十三、如何测试结构化输出

测试集需要覆盖:

  • 正常输入;
  • 空输入;
  • 超长输入;
  • 中英文混合;
  • 枚举边界;
  • 日期歧义;
  • 数值单位;
  • Prompt Injection;
  • 缺少事实;
  • Provider切换。

指标:

first_attempt_success_rate
repair_success_rate
average_attempts
schema_failure_rate
business_validation_failure_rate
cost_per_valid_object
p95_latency

最终应关注:

每1000个有效对象的总成本

而不是单次模型价格。

十四、选型决策树

只做原型或展示?
→ Prompt约束或Converter

需要稳定Java对象?
→ Converter

Provider支持JSON Schema且流程关键?
→ 原生结构化输出

偶发格式失败需要自动恢复?
→ validateSchema

涉及业务动作?
→ 再加业务校验和人工审批

总结

四种方案的合理分工是:

Prompt约束
→ 最广泛兼容

StructuredOutputConverter
→ Java类型映射

Provider原生JSON Schema
→ 请求侧强约束

validateSchema
→ 响应侧校验和自修复

企业项目通常不应只选其中一种,而应根据风险组合使用,并始终保留业务校验、观测和失败降级。

延伸阅读

如果你正在关注企业级AI应用、Spring AI、RAG、Agent与MCP工程化落地,欢迎访问 智元界:

https://www.zyentor.com/

智元界将持续分享可运行的技术实战、架构设计、问题排查与企业应用案例。