优化 AWS Bedrock 结构化输出: JSON Schema 设计指南

作者
  • avatar
    姓名
    Nino
    职业
    Senior Tech Editor

在大语言模型(LLM)的应用开发中,获取格式正确的响应往往与获取准确的内容同样重要。 AWS Bedrock 最近推出了“受限解码”(Constrained Decoding)功能,该功能可以从推理层面保证模型输出严格符合预定义的 JSON Schema。然而,许多开发者存在一个误区:认为只要 Schema 校验通过,输出的数据质量就一定高。

事实上, JSON Schema 不仅仅是一个结构契约,它更是一个高权重的“提示词”(Prompt)。字段名称、描述信息、属性顺序以及枚举值(Enums)都会直接引导模型的注意力机制。通过 n1n.ai 调用 Claude 3.5 Sonnet 等顶级模型时,深刻理解“Schema 即提示词”的设计哲学,是实现生产级可靠性的关键。

AWS Bedrock 如何强制执行结构化输出

与传统的“先生成后校验”(即生成后再通过正则或第二次 LLM 调用来修复错误 JSON)不同, AWS Bedrock 的受限解码是在推理过程中实现的。当你提交一个 Schema 时, Bedrock 会执行以下步骤:

  1. 校验: 验证 Schema 是否符合 JSON Schema Draft 2020-12 标准。
  2. 编译: 将 Schema 编译成一种专门的语法(Grammar)。首次运行可能需要几分钟时间。
  3. 缓存: 编译后的语法会在每个账户下缓存 24 小时,以加速后续调用。
  4. Token 掩码: 在模型逐个生成 Token 的过程中, Bedrock 会修改 Token 的对数概率(Logprobs)。任何会导致违反 Schema 的 Token 都会被物理遮掩(Masked),使得模型在逻辑上无法产出不符合规范的 JSON。

以下是使用 Python SDK (boto3) 的基础实现代码:

import boto3, json

# 专业建议:使用 n1n.ai 可以在不同区域和模型间快速切换测试性能
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")

schema = {
    "type": "object",
    "properties": {
        "customer_name": {"type": "string"},
        "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
    },
    "required": ["customer_name", "sentiment"],
    "additionalProperties": False,  # AWS Bedrock 强制要求设置为 False
}

response = bedrock.converse(
    modelId="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
    messages=[{
        "role": "user",
        "content": [{"text": "分析这段文字:'我非常喜欢这个产品!' —— Sarah"}],
    }],
    inferenceConfig={"maxTokens": 256},
    outputConfig={
        "textFormat": {
            "type": "json_schema",
            "structure": {
                "jsonSchema": {
                    "schema": json.dumps(schema),
                    "name": "sentiment_analysis",
                }
            },
        }
    },
)

data = json.loads(response["output"]["message"]["content"][0]["text"])
# 输出结果:{"customer_name": "Sarah", "sentiment": "positive"}

原则一:描述性命名即语义引导

LLM 是按顺序生成 Token 的。当模型写下 "customer_full_name": 这个键名时,这串特定的 Token 序列就成为了生成后续值的上下文。像 Claude 3.5 Sonnet(可通过 n1n.ai 访问)这样在海量代码和文档上训练过的模型,对字段名具有极强的先验理解。

应尽量避免使用 field1val 这种模糊的名称。相反,使用 product_rating_out_of_five 这样具有明确含义的名称。字段名越具描述性,模型预测后续数值 Token 的准确度就越高。

原则二:Description 是嵌入式指令

在传统的 JSON Schema 中, description 字段通常被视为给人类看的元数据。但在 LLM 应用中,它们是实时的指令。 PARSE 研究系统表明,通过优化字段描述(结合其他优化手段),可以将提取准确率提高 64.7% 以上。

例如,在提取客服工单时,不要只定义一个 severity 字段,而应像这样定义:

{
  "severity": {
    "type": "string",
    "enum": ["low", "medium", "high", "critical"],
    "description": "low 表示视觉或轻微问题; medium 表示性能下降; high 表示核心功能损坏; critical 表示数据丢失或系统宕机"
  }
}

通过将业务逻辑直接编码进 Schema,你可以显著减少系统提示词(System Prompt)的复杂度,提高指令遵循的一致性。

原则三:字段顺序的力量(推理优先)

这是处理复杂任务时最重要的技巧。由于 LLM 是顺序生成字段的,因此 Schema 中属性出现的顺序决定了模型的“思考顺序”。你应当始终将“推理”或“分析”字段放在最终“结论”字段之前。

如果你先要求模型输出布尔值 is_fraudulent(是否欺诈),模型必须在处理证据之前就给出答案。如果你在布尔值之前放置一个 risk_analysis 字符串字段,模型在编写分析过程的同时就在进行“思考”,从而得出更准确的最终判断。这实际上是将“思维链”(Chain of Thought)直接嵌入到了数据结构中。

原则四:利用可空类型(Nullable)防止幻觉

如果一个字段被标记为 required(必填),但源文本中确实没有相关信息,模型为了满足 Schema 约束,很可能会编造(幻觉)一个值。为了防止这种情况,请使用可空类型:

{
  "company_name": {
    "type": ["string", "null"],
    "description": "如果文本中提到了公司名称则返回,否则返回 null"
  }
}

这给了模型一个安全的“出口”,在 RAG(检索增强生成)流程中能显著降低幻觉率。

原则五:枚举(Enums)的机械与语义约束

枚举值不仅在机械层面限制了输出(模型无法产出集合之外的 Token),还在语义层面告诉了模型存在哪些类别。 AWS Bedrock 官方建议尽可能使用枚举来提高准确性。如果不使用枚举,你可能会得到 "positive""Positive""good" 等各种不统一的回复,给下游业务逻辑带来巨大麻烦。

企业级扩展的最佳实践

在生产环境中扩展 LLM 应用时,请注意以下技术细节:

  • Token 限制: 如果 maxTokens 设置过小,导致 JSON 在闭合前被截断,输出将变为非法格式。务必预留足够的 Token 空间。
  • 额外属性: AWS Bedrock 要求所有对象必须设置 "additionalProperties": false。这是为了确保语法状态机的确定性。
  • 扁平化设计: 尽量保持 Schema 结构扁平。深层嵌套(超过 3 层)会增加推理延迟,并可能导致模型丢失上下文注意力。

通过将这些 Schema 设计策略与 n1n.ai 提供的多模型聚合能力相结合,你可以确保结构化输出不仅格式正确,而且在语义上高度可靠,为后续的自动化流程打下坚实基础。

n1n.ai 获取免费 API 密钥。