尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring AI调用entity()时JSON解析失败?Markdown包裹、字段缺失与Schema不兼容完整排查

Spring AI调用entity()时JSON解析失败?Markdown包裹、字段缺失与Schema不兼容完整排查 文章摘要Spring AI的ChatClient.call().entity()可以把模型输出直接转换为Java对象但生产项目经常遇到JSON前后带解释文字、被Markdown代码块包裹、必填字段缺失、枚举值不匹配、日期格式错误或Provider不支持完整JSON Schema等问题。默认结构化输出本质上仍可能是“Prompt约束文本解析”并不天然保证100%成功。本文从目标类型设计、原始响应留存、Provider原生结构化输出、Schema校验与自修复、流式限制和错误分级六个方面给出完整排查方案。一、典型错误表现代码OrderRiskresultchatClient.prompt().user(prompt).call().entity(OrderRisk.class);可能出现JsonParseException MismatchedInputException UnrecognizedPropertyException InvalidFormatException Cannot deserialize value of type模型实际返回以下是分析结果 json { riskLevel: HIGH, reason: 客户连续逾期 }人能看懂但Jackson看到的不是纯JSON。 ## 二、先保留原始响应 很多项目只调用 java .entity(OrderRisk.class)一旦失败只看到反序列化异常看不到模型到底返回了什么。排查时先改为StringrawchatClient.prompt().user(prompt).call().content();log.debug(structured.raw{},sanitize(raw));然后再手工转换BeanOutputConverterOrderRiskconverternewBeanOutputConverter(OrderRisk.class);OrderRiskresultconverter.convert(raw);生产环境不要记录敏感原文可以记录响应哈希长度前后少量脱敏片段错误类型模型和Prompt版本Schema版本。三、目标Java类型是否适合生成错误类型设计publicclassOrderRisk{privatefinalRiskLevelriskLevel;privatefinalBigDecimalscore;privatefinalLocalDateTimecreatedAt;publicOrderRisk(...){...}}可能存在无默认构造字段没有GetterJackson配置不一致时间格式不明确枚举值约束不清晰非静态内部类循环引用。推荐优先使用RecordpublicrecordOrderRisk(RiskLevelriskLevel,doublescore,Stringreason,ListStringevidenceIds){}结构越简单模型和反序列化器越稳定。四、枚举值为什么最容易失败定义publicenumRiskLevel{LOW,MEDIUM,HIGH}模型可能返回{riskLevel:高风险}或{riskLevel:high}这都会造成枚举转换失败。Prompt中应明确riskLevel只能取LOW、MEDIUM、HIGH之一。 不得输出中文值、缩写或其他值。业务层仍应对未知值进行错误处理而不是偷偷映射为默认值。五、日期和数字格式需要收敛日期不推荐让模型自由输出2026年8月2日上午 08/02/26 2 Aug 2026推荐ISO-8601 2026-08-02T08:30:0008:00如果只是业务日期可以使用LocalDate并要求YYYY-MM-DD数字金额不要让模型返回1,200元 约1.2K 人民币1200结构化字段使用{amount:1200.00,currency:CNY}六、额外字段导致失败怎么办模型返回{riskLevel:HIGH,reason:连续逾期,analysis:详细推理过程}而Java类型没有analysis。两种策略严格模式禁止额外字段任何漂移都视为错误。适合工作流路由工具参数订单操作财务数据审批结果。宽松模式忽略未知字段。适合非关键展示兼容旧模型渐进迁移。企业系统建议对关键结构使用严格模式避免模型偷偷扩展协议。七、默认entity()并不是强制保证结构化输出存在两个层次。Prompt约束把JSON Schema或格式指令加入Prompt → 模型尽量遵守 → 客户端解析优点Provider兼容广大多数模型可用。缺点可能输出解释文字可能漏字段可能多字段可能输出无效JSON。Provider原生结构化输出JSON Schema作为API级约束发送 → Provider约束模型输出可靠性通常更高但存在Provider支持差异模型版本差异JSON Schema子集限制顶层数组限制复杂递归结构不支持。八、启用Provider原生结构化输出Spring AI 2.0可以在目标模型支持时启用原生结构化输出OrderRiskresultchatClient.prompt().user(prompt).call().entity(OrderRisk.class,spec-spec.useProviderStructuredOutput());也可以在ChatClient级启用相应参数。但要注意打开开关 ≠ 所有Provider都严格支持全部Schema上线前必须对具体Provider模型API版本Schema顶层结构进行实测。九、使用validateSchema()自修复当输出不符合Schema时可以让Spring AI把具体校验错误反馈给模型并重新生成OrderRiskresultchatClient.prompt().user(prompt).call().entity(OrderRisk.class,spec-spec.validateSchema());也可以组合.entity(OrderRisk.class,spec-spec.useProviderStructuredOutput().validateSchema())逻辑Provider约束输出 → 本地Schema再次校验 → 失败后携带错误重试这种方式比盲目重试更有效因为模型会看到缺少哪个字段 哪个字段类型错误 哪个值不符合枚举十、重试不是免费的结构化输出自修复会增加Token延迟模型费用并发占用Provider限流压力。需要记录structured_output_attempts schema_validation_failure_count structured_output_total_tokens structured_output_latency_ms repair_success_rate如果大量请求都需要第二次或第三次修复说明问题不应只靠重试解决。应检查Schema是否过于复杂模型是否适合Prompt是否冲突字段描述是否不清是否选择了不稳定的推理模式Provider原生支持是否完整。十一、不要用一个超大对象承载全部结果错误Schema客户分析 风险判断 行动计划 邮件正文 审批意见 工具参数一次返回几十个嵌套字段失败率会快速上升。推荐拆成第一步分类 第二步提取字段 第三步生成建议 第四步生成文本关键路由对象保持小而稳定publicrecordIntentDecision(IntentTypeintent,doubleconfidence,booleanrequiresHumanReview){}十二、顶层数组为什么容易踩坑部分Provider的原生结构化输出对顶层数组支持有限。不推荐ListOrderRisk推荐包装publicrecordOrderRiskList(ListOrderRiskitems){}JSON{items:[{riskLevel:HIGH,score:0.91}]}这种结构更容易跨Provider迁移。十三、流式输出不能直接entity().entity()需要完整响应才能解析因此通常用于.call()流式.stream()返回的是文本片段不能在每个Chunk上完成完整JSON反序列化。如果必须流式展示结构化任务建议流式发送进度事件 → 模型完整生成 → 服务端聚合 → Schema校验 → 最终发送result事件不要尝试把半截JSON当成完整对象解析。十四、BeanOutputConverter升级后Schema变化Spring AI 2.0中BeanOutputConverter的JSON Schema生成逻辑与工具调用Schema进一步对齐。升级时需要检查Kotlin可选属性是否仍在required中JsonProperty(required false)行为LocalDateTime等format提示自定义Schema后处理扩展点新旧Schema是否兼容历史Prompt和测试集。不要只验证代码能编译还要保存并比较生成Schema差异。十五、推荐错误分级publicenumStructuredOutputError{EMPTY_RESPONSE,INVALID_JSON,SCHEMA_MISMATCH,ENUM_VALUE_INVALID,DATE_FORMAT_INVALID,PROVIDER_NOT_SUPPORTED,RETRY_EXHAUSTED}不同错误使用不同策略空响应 → 可短次数重试 Schema不匹配 → 带错误自修复 Provider不支持 → 回退Prompt模式 重试耗尽 → 转人工或返回稳定错误十六、完整排查清单□ 是否保留了脱敏后的原始响应 □ Java目标类型是否可反序列化 □ 枚举取值是否明确 □ 日期和金额格式是否固定 □ 是否存在额外字段 □ Schema是否过度复杂 □ Provider是否支持原生结构化输出 □ 是否启用了useProviderStructuredOutput □ 是否启用了validateSchema □ 是否记录了修复次数和累计Token □ 是否误在stream()中直接解析entity □ 升级后生成Schema是否发生变化总结entity()解析失败并不只是“模型偶尔不听话”而可能来自目标类型设计 Prompt约束不足 Provider能力差异 Schema不兼容 流式调用方式错误生产级方案应采用简单稳定的领域类型 Provider原生约束 本地Schema校验 有上限的错误自修复 失败降级和完整观测
返回列表