模型输出校验:确保 LLM 结果符合预期格式和范围

FreeGuideOnline 最新 2026-06-29

模型输出校验:确保 LLM 结果符合预期格式和范围

什么是模型输出校验

模型输出校验是指在获得大语言模型(LLM)的响应后,对内容的结构、类型、取值范围、必填字段等进行自动化检查与修正的过程。其目标是让不可控的自然语言输出变成可以被下游系统安全消费的可靠数据。

无论你是构建聊天机器人、数据提取管道还是代码生成系统,没有校验的输出就像没有安检的机场——看似方便,实则充满风险。

为什么输出校验如此重要

LLM 本质上是概率模型,即使使用精细的提示工程,仍然会出现以下问题:

  • 格式漂移:要求返回 JSON,却得到了 Markdown 包裹的代码块。
  • 字段缺失或多余:缺少关键字段,或额外添加了不需要的内容。
  • 类型错误:需要整数的字段返回了字符串 "123"
  • 超出范围:评分字段要求 1-5 分,结果返回了 6 分或 null
  • 内容不安全:包含注入代码、不当言论或敏感信息。
  • 幻觉数据:引用了不存在的 ID 或参数。

校验层将这些不可预测性转化为可控的失败,使系统即便在模型表现不佳时也能优雅降级,而不是直接崩溃。

常见输出问题汇总

问题类型 示例
格式包装 期望 {"name":"Alice"},实际返回 ```json\n{"name":"Alice"}\n```
JSON 语法错误 尾部多余逗号、未转义引号、注释
键名错误 name 写成了 Namefull_name
类型与约束 价格返回 "-5" 或邮箱格式错误
内容截断 长回答被 token 限制切断,JSON 不完整
重复/冗余 模型重复输出同一句话或字段
幻觉引用 引用了不存在的法律条文或 API 参数

校验的三大类型

1. 格式校验 —— 提取并验证结构

目标:确保输出可以被正确解析为目标格式(JSON、XML、CSV等)。

常见步骤

  1. 去除包裹:清理 Markdown 代码块、前后空白、无关解释文本。
  2. 语法修复:处理尾部逗号、单引号、注释(使用容错解析器)。
  3. 解析验证:尝试解析,若失败则尝试自动修复或进入重试逻辑。

代码示例(Python 基础清理)

import re
import json

def clean_and_parse_json(raw_output: str):
    # 去除 Markdown 代码块标记
    cleaned = re.sub(r'^```(?:json)?\s*|\s*```$', '', raw_output.strip())
    # 尝试直接解析
    try:
        return json.loads(cleaned)
    except json.JSONDecodeError:
        # 可在此加入自动修复逻辑:去除尾部逗号、单引号替换等
        # 若修复失败,返回 None 或抛出异常以触发重试
        return None

2. 内容校验 —— 检查字段与类型

目标:确保 JSON 模式(Schema)符合预期,所有必要字段存在且类型正确。

常用工具

  • JSON Schema:声明式定义数据结构,使用 jsonschema 库校验。
  • Pydantic:Python 数据模型库,提供自动解析、类型转换和校验。
  • TypeScript 类型守卫:在前端进行运行时校验。

Pydantic 校验示例

from pydantic import BaseModel, Field, EmailStr, validator

class UserProfile(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    age: int = Field(..., ge=18, le=120)
    email: EmailStr
    score: float = Field(..., ge=0.0, le=100.0)

    @validator('name')
    def name_must_not_be_empty(cls, v):
        if not v.strip():
            raise ValueError('name cannot be empty')
        return v.strip()

# 使用方式
try:
    profile = UserProfile.parse_raw(llm_response)
except Exception as e:
    # 记录错误并触发重试或降级
    print(f"校验失败: {e}")

3. 边界与范围校验 —— 确保语义合理

目标:超越结构,检查数据是否在业务逻辑允许的范围内。

典型检查项

  • 数值范围(如评分 1-5)
  • 字符串枚举(如“低、中、高”)
  • 日期逻辑(开始时间 < 结束时间)
  • 列表长度限制(结果不超过 10 条)
  • 外部引用一致性(如 ID 是否存在于数据库中)
  • 内容安全扫描(检测 PII、攻击性文本)

组合校验示例

def validate_summary(data: dict):
    # 格式已经由 Pydantic 校验
    # 业务边界:摘要长度不超过 200 单词
    if len(data['summary'].split()) > 200:
        return False, "摘要单词数超出限制"
    # 敏感信息检测(示例正则)
    if re.search(r'\b\d{3}-\d{2}-\d{4}\b', data['summary']):
        return False, "摘要可能包含社会保障号码"
    return True, "校验通过"

实用校验技术与工具

文本输出后处理策略

  • 正则表达式提取:从混合文本中提取结构化片段。
  • JSON 修复库:如 json_repair(Python),可自动修复不完整的 JSON。
  • 自定义解析器:对于非标准格式(如特定 DSL)编写专用解析函数。

结构化输出强制技术

更好的办法是在设计提示时就让模型输出可校验的结构

  • 函数调用 / 工具使用:强制模型生成符合 function schema 的 JSON 参数(OpenAI、Anthropic 等均支持)。
  • JSON 模式约束:部分推理平台提供 response_format 参数直接限制输出 JSON 模式(如 GPT-4 的 JSON mode)。
  • Constrained Decoding:使用 llama.cpp 的 grammar 或 Outlines 库的引导生成,从 token 级别保证格式正确。

但即使使用这些技术,校验后处理仍然是必要的——模型仍可能在允许的 schema 内填入无效内容。

重试与降级策略

校验不是一次性的,而应设计成带反馈的循环:

  1. 记录失败详情:字段名、错误原因、原始输出
  2. 智能重试:将错误信息反馈给模型,要求其修正后重新生成(最多 2-3 次)。
  3. 降级处理:如果重试均失败,提供默认值、返回模拟数据或触发人工审核。

重试提示模板

你的上一次输出不符合以下 schema:
{schema_errors}
请修正并仅返回正确的 JSON 对象,不要包含解释。
失败输出:
{previous_bad_output}

实战:构建可靠的输出校验管道

以下是一个端到端的 Python 管道示例,整合了解析、模式校验和重试逻辑:

import json
from jsonschema import validate, ValidationError
from tenacity import retry, stop_after_attempt, wait_exponential

DESIRED_SCHEMA = {
    "type": "object",
    "properties": {
        "company": {"type": "string"},
        "revenue": {"type": "number", "minimum": 0},
        "employees": {"type": "integer", "minimum": 1}
    },
    "required": ["company", "revenue"]
}

def extract_json(text):
    # 简单提取;生产环境可使用 json_repair 库
    start = text.find('{')
    end = text.rfind('}')
    if start != -1 and end != -1:
        return text[start:end+1]
    return text

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
def get_validated_output(prompt, llm_func):
    raw = llm_func(prompt)
    json_str = extract_json(raw)
    data = json.loads(json_str)
    validate(instance=data, schema=DESIRED_SCHEMA)
    return data

# 使用
try:
    result = get_validated_output("提取公司信息的提示", my_llm_call)
except Exception as e:
    result = {"company": "未知", "revenue": 0, "employees": 1}  # 安全降级

最佳实践

  1. 永远不要信任模型输出:即便使用了严格模式,也需进行校验。
  2. 校验尽量贴近输出:解析后立即校验,避免错误数据污染后续逻辑。
  3. 错误信息要具体:失败时清晰指出哪个字段不符合哪种约束,便于模型自我纠错。
  4. 分离结构校验与业务校验:先用 Schema 保证结构,再用业务函数检查逻辑。
  5. 使用成熟的校验库:不要重复造轮子,Pydantic、JSON Schema、Zod(JS)都久经考验。
  6. 记录与监控:将校验失败率、常见错误类型作为监控指标,持续优化提示和校验规则。
  7. 考虑“温和”解析:对于年龄字段,可以接受字符串 "25" 并转换为整数,而不是直接拒绝。Pydantic 的 coerce 模式对此很有帮助。
  8. 设置全局超时与预算:重试会消耗 token,为整个管道设置最大尝试次数和总成本阈值。

总结

模型输出校验是将 LLM 从“演示级”能力提升为“生产级”可靠组件的关键一环。它不仅仅是一堆正则表达式——而是由提取、解析、模式验证、业务规则检查和智能重试组成的健壮管道。开始时你可以从简单的 JSON 清洗入手,逐步引入类型校验和语义检查,最终形成一套自愈能力强的输出处理中间层。正确的校验能让你自信地说:模型可能犯错,但我的系统永远知道如何应对。