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
  • 深层嵌套;
  • oneOfanyOfallOf
  • 递归类型;
  • 正则;
  • 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/

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