← 大模型应用基础

模型输出解析失败时,应该填默认值继续,还是直接报错?

中等 结构化信息抽取 · 第 3 / 3 问 更新于 2026/09/29
大模型应用结构化输出JSON 解析错误处理数据质量
本题落地项目AI智能简历优化系统

简化版

看这个字段错了会不会变成一条「看起来正常的假数据」。分数、金额、状态这类会被存下来、被统计、被拿去做决策的关键字段,解析失败就直接报错:给出能操作的提示,附上模型原文片段,写失败日志,不覆盖上一次成功的结果,让用户重试。只影响展示、而且有安全保守值的字段(比如优先级拿不准时归到「中」),可以归一或置空,但要同时保存模型的原始返回,方便事后核对。最忌讳的是给关键字段填 0、空串或第一个枚举值然后照常入库:一条被填成 0 分的记录和一次真实的低分,在数据库里长得一模一样,事后谁也分不清。

详细版

先把「解析失败」拆成三类,处理方式不同:

失败类型例子关键字段非关键字段
语法失败清理代码围栏、截取花括号之后仍不是合法 JSON报错,附原文片段,允许重试同左,整段都解析不了就没有字段可兜底
结构失败必需字段缺失、数组变成了字符串报错置空,由页面或人工补
语义失败枚举值不在范围内、名称对不上系统数据报错或转人工归一到保守值,或者保持原值不改

判断一个字段算不算「关键」,问四个问题:下游会不会拿它计算或决策;默认值能不能和真实值区分;错了用户能不能一眼看出来;失败了能不能低成本重试。前两个问题只要有一个答「会」「不能」,就不要用默认值兜底。

失败时的动作也有固定顺序:先校验、再落库,校验不过就不动旧数据;报错信息写清原因和下一步;原始输出进日志。

完整版教学

一、默认值的代价:假数据混进真数据

模型输出解析失败后填一个默认值继续跑,表面上接口不报错了,实际上把问题从「这次没结果」变成了「这次有一个错结果」。后者更难处理,因为它不会触发任何告警。

用一组数字感受它对统计的影响。假设某岗位有 100 条面试评分,其中 95 条解析成功、平均 78 分,另外 5 条解析失败:

失败记为 0 分:  (95 × 78 + 5 × 0) / 100 = 7410 / 100 = 74.1 分
失败直接报错:   95 条有效记录,平均 78 分;5 次失败在日志里,可以重试

平均分被拉低了将近 4 分,而看报表的人完全不知道这 4 分从哪来。用户这边更糟:看到自己得了 0 分,只会以为自己答得太差,想不到是模型没按格式返回。

记忆钩子:报错是「这次没有结果」,默认值是「这次有一个错结果」。前者可以重试,后者会一直留在数据里。

二、三类失败分开处理

语法失败是整段文本解析不了。先做确定性的清理:去掉代码围栏、截出最外层的花括号或方括号、忽略 JSON 后面多出来的说明文字。清理完还解析不了,就没有任何字段可以兜底,只能报错。

结构失败是 JSON 合法,但缺了必需字段,或者字段类型不对。这时要看缺的是哪个字段:缺了分数、分类这种业务依赖的字段,就报错;缺了备注、建议这种展示字段,可以置空。

语义失败是字段都在、类型也对,但值不可用:优先级写成了「较高」,分类名在系统里查不到,日期写成了「下周五」。这一类最适合用代码归一,因为规则是确定的,而且不需要猜。

模型原文
  └─ 清理(围栏、客套话、尾部说明)
       └─ 语法解析 ──失败──▶ 报错 + 原文片段 + 失败日志
            └─ 结构校验 ──缺关键字段──▶ 报错
                 └─ 语义校验 ──非关键字段不合法──▶ 归一 / 置空 / 保持原值
                      └─ 落库(这一步之前不动任何旧数据)

清理代码围栏、截取花括号这些确定性修复怎么做,见「大模型输出的 JSON 不合法时应该如何修复?」。

三、用四个问题判断能不能兜底

问题答案结论
下游会拿这个字段计算、排序或决策吗?会不能兜底
默认值和真实值在库里能区分吗?不能不能兜底
值错了,用户能一眼看出来吗?看不出倾向报错
失败后重试的成本高吗?低报错让用户重试

分数、金额、库存、诊断结论、审批状态,前两问基本都是「会」「不能」。工单优先级拿不准时归到「中」是另一种情况:它会影响排队顺序,但「中」是一个保守的中间值,客服接单后还能改,所以可以归一,同时把模型原本给的值另存一份。

四、报错要报得有用

直接报错不等于把异常栈甩给用户。一条好的解析失败提示包含三样东西:

  1. 发生了什么:AI 没有按要求的 JSON 格式返回,这次没有生成结果;
  2. 下一步做什么:重试一次;反复出现时去检查 Prompt 模板是否被改动;
  3. 排查线索:模型实际返回内容的前一两百个字符。

第三条最有价值。返回的是一段中文说明,说明模型没按格式输出,多半是 Prompt 出了问题;返回的是半截 JSON,说明输出撞上了长度上限;返回的是空内容,问题在模型配置。同一句「解析失败」,靠这段原文就能分出三种原因。

同时要写一条失败日志:用的哪个模型、哪个模板、请求内容、错误原因、耗时。没有失败日志,就看不到失败率。

五、失败不能破坏上一次的成功

很多写法是「先删旧结果,再插新结果」。如果解析失败发生在删除之后,用户上一次好不容易生成的结果也没了。

错误顺序:删除旧知识点 → 调模型 → 解析失败 → 抛异常   结果:新的没有,旧的也没了
正确顺序:调模型 → 解析 → 校验非空 → 删除旧知识点 → 插入新知识点

「解析出 0 条」也要按失败处理。模型返回了一个合法的空数组,语法、结构都没问题,但对「提取知识点」这个任务来说就是没有结果,不能拿一个空结果去覆盖旧结果。

六、可以兜底时怎么兜

非关键字段的兜底有三种做法,按可逆程度排序:

做法适用例子
保持原值不改字段本来就有值,模型的值对不上分类名匹配不上系统分类,就不改工单原来的分类
置空,让人补没有可靠的推断依据用户没说出行人数,就留空让用户自己填
归一到保守值有明确的中间档,事后可改优先级不在高 / 中 / 低里时归到「中」

三种做法都要把模型的原始返回另存下来。这样事后统计「模型有多少次给了非法值」时有据可查,发现规则写错时也能重新归一。

注意「置空」和「填 0」不是一回事。空值在统计时会被跳过或单独计数,0 会被当成一个真实的数参与计算。

七、重试的边界

报错之后可以让用户重试,也可以在后端自动重试一次,但要守住两条:

  • 自动重试次数要小。 一到两次足够,格式问题多半是偶发的;反复失败说明模板或模型有问题,再试只是多花钱。
  • 修复不能编造关键值。 让模型「把上面的 JSON 修好」时,它可以补一个右括号,但不能补一个分数。关键字段缺失时,修复的结果不可信,应该重新生成而不是修复。

八、常见误区与追问

  • 误区:接口不报错就是好接口。 用默认值吞掉解析失败,接口是不报错了,数据却错了,而且没人知道。
  • 误区:空结果不算失败。 任务要求产出内容时,一个合法的空数组同样是失败,不能用它覆盖上次的结果。
  • 误区:兜底值随便选一个枚举。 兜底值要选保守的中间值,而且要保存模型原值,否则事后无法核对。
  • 误区:报错信息越简短越好。 只说「解析失败」无法排查,附上原文片段才能区分是模板问题、截断还是配置问题。
  • 误区:置空和填 0 等价。 0 会参与计算,空值会被统计跳过,两者对报表的影响完全不同。
  • 追问:解析失败后先删旧数据有什么问题? 失败发生在删除之后时,新结果没生成,旧结果也丢了,应该先解析校验,再替换。
  • 追问:所有字段都要这么严格吗? 不需要,按「会不会被计算决策、默认值能不能区分」划分关键字段,非关键字段可以归一或置空。

九、加强记忆

解析失败的处理记「关键字段报错、非关键字段归一、任何时候不动旧数据」。关键字段是会被计算、统计、决策的字段,失败就报错,提示里写清下一步并附上模型原文片段,同时写失败日志。非关键字段按「保持原值、置空、归一到保守值」兜底,并保存模型原始返回。先解析校验再替换旧结果,空结果也算失败。

项目实战落地

项目里怎么做的

《AI智能简历助手》的诊断评分要解析出 score、level、analysis。清理代码围栏和客套话之后仍然解析失败时,直接抛出业务异常,不再退回成「0 分 / 未评级」:

} catch (Exception e) {
    // 所以这里直接报错,并把模型实际返回的开头附在提示里,方便对照 Prompt 模板调整。
    throw new CustomException("5008", "AI没有按要求的JSON格式返回评分结果,无法解析出分数。"
            + "可以重试一次,若反复出现请到【Prompt模板】检查评分模板是否被改动。"
            + "模型实际返回:" + StrUtil.maxLength(StrUtil.blankToDefault(modelContent, ""), 100));
}

报错时评分记录不会写入 resume_ai_record,这次就是没有结果。

另外两个项目可以对照着看:

  • 《AI个人学习计划助手》的知识点提取,解析出 0 条时直接报「AI本次没有提取到知识点」,不往下执行「先删除该资料的旧知识点、再插入新结果」,上一次提取成功的知识点因此保留;失败时把翻译好的原因写进 AI 调用日志。
  • 《AI智能客服与工单处理系统》的工单分类,缺分类或优先级时报错;优先级不在高、中、低里时归到「中」;分类名对不上系统分类时不改工单原来的分类;模型原始给出的分类名和优先级另存在 ai_category_name、ai_priority 里。

为什么这样取舍

  • 评分失败不存 0 分。 存成 0 分虽然不报错,但会往记录表里写一条假成绩:用户看到 0 分只会以为自己简历太差,想不到是模型没按格式返回;一条 0 分记录和一次真实的低分评价在数据库里长得一模一样,事后谁也分不清。
  • 能救的先救,救不回来才报错。 解析前的清理和解析失败的报错是配套的:代码围栏、客套话这类能靠清理救回来的情况不报错,清理之后还不行才报错。
  • 0 条不删旧数据。 下一步会先删旧知识点,这时继续往下走,这次失败的提取会把上次成功的结果一起清空。

面试官还会追问

  • 模型返回的 JSON 能解析,但里面没有 score 字段,这时又是怎么处理的?
  • 诊断评分为什么用同步调用等完整结果,而不像问题分析那样流式输出?
  • 评分记录里为什么要保存当时输入模型的简历文本?

学完《AI智能简历助手》,上面这些追问你都会迎刃而解。

本题落地项目小试牛刀AI智能简历优化系统基于大模型完成简历上传解析、AI评分、问题诊断、优化建议、内容改写、Word下载、模拟面试题和生成记录管理,覆盖从简历诊断到求职准备的完整流程,并沉淀Prompt模板和改写策略。SpringbootSpringAILLMPromptSSE源码+SQL喂饭学习教程配套面试文档环境安装文档项目运行文档 学习这个项目 也可以学AI个人学习计划与资料助手小试牛刀 查看项目 也可以学AI智能客服与工单处理系统登峰造极 查看项目