LLM 吐出了 JSON,程序就能放心接了吗

以文章分类为例,写一个不用第三方依赖的校验器,区分格式、业务规则和事实错误。

目录

让模型“只返回 JSON”,通常是把它接进程序的第一步。可 JSON 能解析,和结果能使用,中间还隔着几道门。

假设要给博客文章打分类。模型返回了 {"category": "电影", "confidence": 1.2}。语法没问题,但站点只允许技术、汽车、摄影三类,置信度也不应该超过 1。直接存进去,后面的分类页面才会发现问题。

把合同写在程序一侧

这里约定三个字段:category 是允许的分类,confidence 是 0 到 1 之间的有限数值,needs_review 是布尔值。无法判断时使用“待定”,并进入人工复核。模型的自报置信度只是一个字段,不是经过校准的正确概率。

下面的 Python 示例仅负责校验,不调用模型、不修改文件。使用标准库即可运行:

import json
import math

ALLOWED = {"技术", "汽车", "摄影", "待定"}

def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("重复字段: " + key)
        result[key] = value
    return result

def validate(raw):
    if len(raw) > 10000:
        raise ValueError("输出过长")
    item = json.loads(raw, object_pairs_hook=unique_object)
    if not isinstance(item, dict):
        raise ValueError("顶层必须是对象")
    if set(item) != {"category", "confidence", "needs_review"}:
        raise ValueError("字段不完整或存在多余字段")
    if not isinstance(item["category"], str):
        raise ValueError("分类必须是字符串")
    if item["category"] not in ALLOWED:
        raise ValueError("未知分类")
    score = item["confidence"]
    if type(score) not in (int, float):
        raise ValueError("置信度必须是数值")
    if not 0 <= score <= 1 or not math.isfinite(score):
        raise ValueError("置信度超出范围")
    if type(item["needs_review"]) is not bool:
        raise ValueError("复核标记必须是布尔值")
    if item["category"] == "待定" and not item["needs_review"]:
        raise ValueError("待定分类需要复核")
    return item

print(validate(
    '{"category":"摄影","confidence":0.8,"needs_review":false}'
))

为什么不直接用 isinstance(score, int)?Python 的布尔值也是整数的子类,这样可能把 true 当作合法分数。重复字段也要留意,默认解码行为可能保留后一个值;示例用 object_pairs_hook 把它变成错误。相关行为可以查 Python json 文档

拒绝比自动猜一个值更省事

假如模型写了 "confidence": "很高",自动转换成 0.9 看起来很体贴,实际上替它编了一条新信息。返回明确错误,让上层决定重试或复核,比静默修补可靠。

重试时可以给模型“confidence 必须是数值”这样的简短反馈,但仍然要限制次数。第一次格式不对,第二次分类不存在,第三次仍然失败,就留在待处理队列里。不要为了让流水线显示全绿,默认填一个“技术”。

如果应用使用结构化输出接口,也仍需要处理拒答、截断和服务错误。接口对结构的约束不能证明分类符合文章内容。

校验通过以后,还有一件事没检查

一篇讲胶片扫描的文章,被分到“汽车”,完全可以通过上面的代码。所以评估时还要留一组人工确认的文章,检查业务正确率。

我会优先保存边界例子:车载摄像头是汽车还是摄影?用语言模型整理照片属于技术还是摄影?这些分歧可能来自分类规则本身,不一定是模型能力差。

先约定按文章主要问题分类,还是允许多标签,再修改数据结构。如果人自己都没达成一致,让模型输出再规整的 JSON 也解决不了。