登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

结构化输出怎样处理模型缺少可选字段

来源:17golang原创

时间:2026-10-19 22:33:23 288浏览 收藏

结构化输出里“模型没返回可选字段”不一定是失败。更稳妥的处理是:先在 schema 中把真正不可缺少的字段放进 required,让可选字段可以合法省略;解析 JSON 后,再由应用层统一补默认值,并单独检查核心字段的类型和业务条件。这样既不会为了追求固定键集合而把 schema 设得过窄,也不会把缺字段、null 和空字符串混成一种情况。

Hugging Face 的结构化输出文档可作为接口设计参考:https://huggingface.co/docs/inference-providers/en/guides/structured-output。本文只讨论 schema 与缺失字段的处理,不讨论具体模型效果、提示词评测或训练。

最实用的分工是:schema 负责约束“能不能算合法结构”,normalize 负责补齐“业务对象需要的默认形状”,业务层再决定缺少核心字段时是否拒绝。
  • 必填字段:缺少就不能继续,例如对象主键或正文摘要。
  • 可选字段:缺少仍可处理,例如标签、置信度或扩展说明。
  • null:字段出现了,但值为空;它是否允许要由类型和业务规则单独决定。

一、先把“缺少”分成三种状态

我在接入结构化输出时,最容易踩的坑是把“键不存在”当成“键存在但值为空”。例如下面三个对象看起来都像“没有标签”,但后续含义不同:

响应形态含义建议动作
{}字段没有出现可选字段可补默认值,必填字段拒绝
{"tags": null}字段出现但值为空先确认 schema 是否允许 null
{"tags": []}字段出现且明确为空集合通常可直接当作无标签

JSON Schema 的 properties 只描述属性的形状,默认并不会让属性自动变成必填;只有列入 required 的键才必须出现。与此同时,null 也不是“缺失”的别名,字符串字段不能因为缺失就自然接受 null。

结构化输出 schema 中 required、可选字段、null 与模型响应对象的静态关系说明图
图1:schema 约束层、响应对象和可选字段之间的边界说明图,不是模型运行截图。

二、用 properties 与 required 表达真实契约

假设业务需要标题和摘要,但标签、来源和置信度可以缺省,那么 schema 不应该为了“看起来整齐”把所有键都放进 required。下面是一个简化的 JSON Schema。JSON 本身不支持注释,因此字段意图放在代码块后的文字中说明。

{
  "type": "object",
  "properties": {
    "title": { "type": "string", "minLength": 1 },
    "summary": { "type": "string", "minLength": 1 },
    "tags": { "type": "array", "items": { "type": "string" } },
    "source": { "type": "string" },
    "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
  },
  "required": ["title", "summary"],
  "additionalProperties": false
}

title 和 summary 是下游展示所需的核心字段,所以放入 required。tags、source 和 confidence 没有列入其中,表示它们可以不出现;如果出现,仍需符合各自的类型。某些 provider 对严格 schema、额外属性和可选字段的支持边界不同,实际调用时应以所选模型和 provider 的文档为准。

如果调用接口支持 JSON Schema,可以把这份 schema 放进结构化响应格式;如果只支持 grammar,也应保持同一个契约思路:必填字段由语法约束,可选字段由后处理归一化。

import json
from typing import Any

# 这是结构化响应格式的示意;具体 provider 的字段名以其接口文档为准。
response_format = {
    "type": "json_schema",
    "json_schema": {
        "name": "ArticleMeta",
        "schema": schema,
        "strict": True,
    },
}

# 解析前只处理 JSON 语法,不把缺失的可选键误当成空字符串。
data: dict[str, Any] = json.loads(model_text)
print(data.get("tags"))

三、解析后做一次 normalize,而不是反复改提示词

schema 解决的是结构边界,不能替代业务对象的默认值策略。一个简单的归一化函数可以把“缺少 tags”变成空列表,把“缺少 confidence”变成明确的默认值,同时把核心字段缺失和 null 单独抛出。

from typing import Any

REQUIRED_KEYS = {"title", "summary"}
OPTIONAL_DEFAULTS = {
    "tags": [],
    "source": "unknown",
    "confidence": 0.0,
}

def normalize_output(raw: Any) -> dict[str, Any]:
    # 先确认顶层确实是对象,避免列表或字符串被当成字典继续处理。
    if not isinstance(raw, dict):
        raise ValueError("结构化输出顶层必须是对象")

    data = dict(raw)
    missing = REQUIRED_KEYS - data.keys()
    if missing:
        # 核心字段缺失属于契约错误,不能靠默认值掩盖。
        raise ValueError(f"缺少核心字段: {sorted(missing)}")

    for key, default in OPTIONAL_DEFAULTS.items():
        # 只为“键不存在”补默认值,不覆盖调用方明确传入的 null。
        if key not in data:
            data[key] = list(default) if isinstance(default, list) else default

    if data["title"] is None or data["summary"] is None:
        # null 与缺失不同;核心文本为 null 时交给业务层拒绝。
        raise ValueError("核心文本字段不能为 null")
    if not isinstance(data["tags"], list):
        # 可选字段出现时仍要检查类型,不能因为它不是 required 就放过错误值。
        raise ValueError("tags 必须是数组")
    return data

这里有一个容易忽略的细节:默认值最好在函数内部重新创建可变对象,避免多个请求共享同一个列表。对于 source 这类展示字段,可以采用保守默认值;对于会影响计费、权限或模型路由的字段,则宁可保持缺失并交给业务层决定。

结构化输出从响应对象到归一化对象的检查点与字段默认值静态关系说明图
图2:响应解析、默认值映射、核心字段检查和业务对象之间的静态关系说明图,不是运行结果截图。

四、把工作流固定成四个检查点

为了让问题容易定位,我会把一条调用链拆成四个责任边界:

  1. schema 检查:确认必填键、类型、长度和额外属性策略与业务契约一致。
  2. 解析检查:只确认返回内容能被 JSON 解析,不在这里偷偷填充业务默认值。
  3. 归一化检查:补可选默认值,记录哪些键是补出来的,并拒绝核心键缺失或为 null。
  4. 业务写入检查:继续检查枚举、数值范围、权限和数据库约束,避免把“结构合法”误当成“业务可信”。

如果模型经常省略一个本来应该稳定出现的字段,先看它到底是“可选字段设计不清”,还是 provider 没有按预期支持当前 schema。不要一遇到缺键就无限重试;重试只能处理偶发生成问题,不能修复错误的契约。

五、常见误区与速查表

现象优先判断处理建议
可选键偶尔不出现是否真的需要固定键集合保留为 optional,解析后补默认值
键出现但值为 nullschema 是否允许 null单独定义 nullable 语义,不等同于缺失
核心键缺失响应是否违反契约拒绝或进入有限重试,不用默认值掩盖
多出未知键provider 是否支持额外属性约束按接口能力选择拒绝、保留或过滤

最后可以记住一句话:schema 管合法形状,normalize 管稳定形状,业务校验管可用结果。三层职责分开后,模型少返回一个真正可选的字段就只是一次可记录的默认值补齐,不会变成整条链路的异常。

常见问题

把所有字段放入 required 能不能避免缺字段?

可以让缺字段更容易被判错,但会把本来允许省略的信息变成硬约束,也可能降低不同模型和 provider 的兼容性。只有下游无法工作的字段才应放入 required。

JSON Schema 的 default 会自动补值吗?

不要把 default 当成自动修复器。它可以表达设计意图,但真正补值仍应由解析后的归一化代码或明确支持该语义的框架负责。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>