AI 返回的 JSON,先查重复键再往下传

阿哲 Lv.2 2026/10/21 21:04:38 浏览 1

下面这段有两个 summary,Python 默认读取时不会报错,前一个值直接没了:

import json

text = '{"summary":"第一版","summary":"第二版","tags":[]}'
print(json.loads(text))
# {'summary': '第二版', 'tags': []}

如果这是准备存下来的 AI 摘要,重复字段就这么被悄悄覆盖了。默认读取还接受 NaN、Infinity 这些非标准常量。能读成 Python 对象,离符合你要的字段约定还差一步。

可以把这一步放在接收端:先拒绝重复键和非标准常量,再检查字段。这里做了一个只用标准库的小工具,Python 3.9+ 能运行。

下载完整示例包 strict-json-result-check.zip

压缩包里有完整脚本、正常和错误样例、验证程序及中文说明。单独的脚本也可以下载:下载 strict_result.py

解压后先跑一次

进入解压目录:

python3 strict_result.py good.json

正常样例长这样:

{
  "summary": "这是一份用于演示结构校验的本地样例。",
  "tags": ["JSON", "Python"]
}

应该输出 VALID,退出码为 0。这个例子的字段约定很小:

  • 顶层只允许 summary 和 tags,两个都要有。
  • summary 是字符串,去掉首尾空白后长 1–120 个字符。
  • tags 是 0–5 个字符串组成的数组,每项去空白后长 1–20 个字符,不能重复;比较区分大小写。

换成重复键样例再试:

python3 strict_result.py bad-duplicate.json

这次退出码为 2,错误写到 stderr:

INVALID: duplicate object key: 'summary'

实际使用时,把文件名换成已提取出来的业务 JSON 文本即可。脚本不自动删 Markdown 围栏,也不会悄悄补括号;返回格式不对就报告给调用方处理。输入限 64 KiB、UTF-8,超过上限直接拒绝。

严格读取的关键在这两个钩子

import json

def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError(f"duplicate key: {key}")
        result[key] = value
    return result

def reject_constant(token):
    raise ValueError(f"nonstandard constant: {token}")

data = json.loads(text, object_pairs_hook=unique_object,
                  parse_constant=reject_constant)

object_pairs_hook 在对象变成字典之前检查键,因此能发现嵌套对象中的重复项。parse_constant 用来拒绝 NaN 和正负 Infinity。只设 strict=True 解决不了这两个问题;allow_nan=False 是编码时的选项,也不能直接拿来当 loads() 的解码参数。

上面是核心用法;完整文件另外处理了字节上限、UTF-8、错误位置和字段校验。它只验这份摘要结构,不是通用 JSON Schema 工具。检查时去空白不会改写原文件,VALID 也不代表摘要事实正确、标签选得合理。

附件还带了一组验证:

python3 verify.py

预期输出 PASS 32/32。这组用例已在 Python 3.12.14 实际运行,并从压缩包解压后再跑过,包含重复键、非标准常量、错误类型、多余字段、围栏、超长输入和深层嵌套。输入是本地构造的样例,没调用模型。

如果接口支持严格结构化输出,优先用它约束返回结构;拒绝响应和截断要在提取业务文本前按接口协议处理,这个脚本不负责。这里适合普通 JSON 文本、历史文件或不同客户端导出的结果,在交给下游前再验一次自己的字段约定。

参考:Python 官方 json 模块文档;OpenAI Structured Outputs 指南,2026 年 10 月 21 日核对。

评论 0

发表评论

登录后参与讨论

登录

暂无评论