跳到主要内容
版本:2.0.1

模式验证与自我纠正 :: Spring AI 参考

Schema 验证与自我纠正

默认情况下,.entity(…​) 调用请求模型生成符合 schema 的 JSON,但无法强制其遵守。当模型返回额外字段、遗漏必需字段,或将 JSON 包裹在文本中时,解析器会抛出异常。

处理格式错误的输出的最简单方法是检测并重新尝试。Spring AI 通过在 EntityParamSpec 消费者上设置单个开关即可自动执行此操作:

ActorsFilms films = chatClient.prompt()
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorsFilms.class, spec -> spec.validateSchema());

自校正循环如何运作​

spec → spec.validateSchema() 消费者启用了一个自纠正的重试循环:

  1. 模型进行响应。

  2. Spring AI 针对目标类型的 JSON Schema 验证响应内容。

  3. 如果验证通过,你将获得一个强类型的记录。

  4. 如果验证失败,验证错误(例如,"缺少必需字段 actor"、"期望 array,但得到 string")会被追加到用户提示中,并重新发起调用——默认最多尝试 3 次。

模型在每次重试时都会看到具体的错误,因此第二次尝试并非盲目重试:模型知道问题所在,并可以对其进行修正。

结构化输出自纠正循环

这由 StructuredOutputValidationAdvisor 提供支持,它是一个在调用 validateSchema() 时自动注册的递归顾问。您无需进行任何额外配置;该开关即为完整的配置项。

备注

当 validateSchema() 处于激活状态时,不支持流式传输。顾问(advisor)需要完整的响应内容才能对其进行验证。

自定义 Advisor​

StructuredOutputValidationAdvisor 默认设置为 3 次重试尝试,并使用 Spring AI 默认的 JsonMapper。如需进行自定义——例如增加尝试次数、提供预定义的 schema 或使用其他 mapper——请自行创建实例并将其注册到 ChatClient 上。显式注册的 advisor 将替代自动注册的 advisor:

var validationAdvisor = StructuredOutputValidationAdvisor.builder()
.outputType(ActorsFilms.class)
.maxRepeatAttempts(5)
.build();

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(validationAdvisor)
.build();

Advisor 可通过 outputType(自动推导 schema)或 outputJsonSchema(预置的 schema 字符串)进行配置;这两个选项互斥。

var validationAdvisor = StructuredOutputValidationAdvisor.builder()
.outputJsonSchema(myConverter.getJsonSchema())
.build();

关键行为:

  • 从预期输出类型推导 JSON Schema,或接受预提供的 Schema 字符串。

  • 使用 JSON Schema DRAFT_2020_12 对 LLM 响应进行验证。

  • 当验证失败时重试调用(默认:最多尝试 3 次)。

  • 在重试尝试中,将验证错误消息添加到提示中,以帮助模型自我纠正。

  • 累积所有验证尝试中的 token 使用量,因此返回的 ChatResponse 报告的是所有重试的累计使用情况,而不仅仅是最后一次尝试的情况(参见多步骤流程中的累计使用情况)。

  • 可选支持自定义 JsonMapper。

有关此顾问背后递归顾问机制的详细信息,请参阅 StructuredOutputValidationAdvisor。

与 Provider 原生输出结合​

validateSchema() 是一个响应端的安全网——它在事后捕获无效输出并重试。useProviderStructuredOutput() 是一种互补的请求端约束。这两者可以自然组合使用:

ActorsFilms films = chatClient.prompt()
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorsFilms.class, spec -> spec
.useProviderStructuredOutput()
.validateSchema());

这在处理提供商的边缘情况时特别有用,尤其是在原生强制机制仅部分生效的情况下——例如,Ollama 推理模型可能会输出纯文本推理轨迹,而非 JSON。参见 已知限制。