默认的 .entity(…) 调用只是请求模型输出符合JSON Schema规范的内容,并无法强制模型一定遵守。当模型返回多余字段、缺失必填字段,或者在JSON前后附带大段自然文本描述时,解析器就会抛出异常。
处理格式异常输出最简单的方案就是:检测错误,然后重试。Spring AI 只需在 EntityParamSpec 配置器上开启一个开关即可自动完成这套逻辑:
|
1 2 3 4 |
ActorsFilms films = chatClient.prompt() .user("Generate the filmography for a random actor.") .call() .entity(ActorsFilms.class, spec -> spec.validateSchema()); |
自修复重试循环工作原理
配置 spec -> spec.validateSchema() 会开启一套自修复重试循环:
- 大模型返回响应结果;
- Spring AI 使用目标类型对应的JSON Schema校验这份响应;
- 如果校验通过,直接返回强类型对象;
- 如果校验失败,会把具体校验错误信息(例如:
缺失必填字段actor、期望数组类型,但得到字符串)追加到用户提示词中,重新发起请求。默认最多重试3次。
注意: 每一次重试,模型都能看到上一轮具体出错原因,并不是无意义的盲目重试:模型明确知道哪里不符合规范,可以针对性修正输出。
该能力由 StructuredOutputValidationAdvisor(结构化输出校验切面)提供。开启 validateSchema() 时该切面会自动注册,开发者无需手动装配任何组件,只需要打开这个配置开关即可。
⚠️ 开启 validateSchema() 之后不支持流式调用:该切面需要拿到完整响应报文才能执行校验。
自定义校验切面(Advisor)
StructuredOutputValidationAdvisor 默认最大重试次数为3,使用Spring AI内置的 JsonMapper。 如果你需要自定义行为:调高重试次数、传入预先定义好的Schema、更换JSON映射器,可以手动构建实例并注册到 ChatClient。手动注册的切面会覆盖系统自动注册的版本。
|
1 2 3 4 5 6 7 8 |
var validationAdvisor = StructuredOutputValidationAdvisor.builder() .outputType(ActorsFilms.class) .maxRepeatAttempts(5) .build(); ChatClient chatClient = ChatClient.builder(chatModel) .defaultAdvisors(validationAdvisor) .build(); |
切面有二选一的配置方式,不能同时使用:
outputType:根据Java类型自动推导生成JSON Schema;outputJsonSchema:直接传入预先准备好的Schema字符串。
|
1 2 3 |
var validationAdvisor = StructuredOutputValidationAdvisor.builder() .outputJsonSchema(myConverter.getJsonSchema()) .build(); |
核心行为特性
- 根据输出类型自动推导JSON Schema,或者直接使用外部传入的Schema字符串;
- 按照 JSON Schema DRAFT_2020_12 规范校验大模型返回结果;
- 校验失败自动重试,默认最多3次;
- 重试阶段会把校验错误信息追加到提示词,引导模型自行修正输出;
- 累加每一轮重试的Token消耗,最终返回的
ChatResponse会统计全部多次请求的总用量,而不是仅统计最后一次调用(参考文档:多步骤流程下累计Token用量); - 支持接入自定义
JsonMapper。
想要了解该切面背后递归Advisor的内部机制,查阅 StructuredOutputValidationAdvisor 文档。
和厂商原生结构化输出组合使用
validateSchema() 属于响应侧的兜底防护:拿到模型返回结果之后检测错误,出错再重试。 useProviderStructuredOutput() 属于互补的请求侧约束,在向模型发起请求时就告诉服务端强制遵循Schema。两者可以叠加同时启用:
|
1 2 3 4 5 6 |
ActorsFilms films = chatClient.prompt() .user("Generate the filmography for a random actor.") .call() .entity(ActorsFilms.class, spec -> spec .useProviderStructuredOutput() .validateSchema()); |
这种组合方式对于厂商存在边界缺陷的场景尤其有用。例如 Ollama 的推理模型,有时会先输出大段推理思考文本,而不是直接输出JSON。详见文档【已知限制】章节。