停止在 RAG 中返回文本:通过类型化答案契约防止幻觉

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

在检索增强生成 (RAG) 的世界里,我们多年来一直在完善“检索”部分。我们优化向量数据库,实验混合搜索,并微调重排序器 (Reranker)。然而,“生成”部分——即大语言模型 (LLM) 与用户对话的最后一公里——仍然是充满非结构化字符串的“西部荒野”。如果你正在构建企业级文档智能系统,从 RAG 流水线返回原始文本已不再被接受。它是幻觉、下游解析错误和系统脆弱性的主要根源。

为了构建健壮的系统,我们必须从“聊天式 AI”转变为“契约式 AI”。这是通过 类型化答案契约 (Typed Answer Contract, TAC) 实现的。通过为每个响应定义严格的模式 (Schema),我们将 LLM 从创意作家转变为结构化数据提取器,使其结果可验证、可测试且可靠。利用 n1n.ai 等平台,开发人员可以无缝切换 DeepSeek-V3 和 Claude 3.5 Sonnet 等高性能模型,以在大规模环境下执行这些契约。

问题所在:RAG 中的“文本黑洞”

标准的 RAG 流水线通常以类似这样的提示词结束:“根据背景信息,回答用户的问题。”然后 LLM 生成一段文字。虽然这在演示 (Demo) 中看起来不错,但在生产环境中却是噩梦,原因有三:

  1. 幻觉伪装:LLM 非常擅长在错误的时候听起来很自信。在一大段文本中,一个错误的日期或价格很难通过编程方式捕捉到。
  2. 下游失败:如果你的应用程序需要触发工作流(例如,“如果合同在 2026 年之前到期,则发送警报”),从自然语言段落中解析该逻辑非常容易出错。
  3. 缺乏问责制:你无法轻松地对一段话进行单元测试。但是,你可以对具有特定类型的 JSON 对象进行单元测试。

解决方案:模式即契约

类型化答案契约将 JSON 模式中的每个字段视为流水线向模型提出的特定问题。我们不再问“这份文件说了什么?”,而是要求模型填充一个 Pydantic 模型。这迫使模型对其知识进行分类,并在信息缺失时明确说明。

在使用 n1n.ai 时,你可以利用支持跨多个供应商结构化输出的统一 API 端点,确保无论你使用 OpenAI o3 还是 DeepSeek-V3,契约都保持有效。

实战指南:构建类型化 RAG 流水线

让我们看看如何使用 Python 和 Pydantic 实现这一点。我们将为保险文档提取任务定义一个契约。

第一步:定义契约

from pydantic import BaseModel, Field, validator
from typing import List, Optional

class PolicyAnalysis(BaseModel):
    policy_holder: str = Field(..., description="被保险实体的全名")
    coverage_amount: float = Field(..., description="美元计的最大责任限额")
    exclusions: List[str] = Field(default_factory=list, description="未涵盖的具体场景列表")
    is_expired: bool = Field(..., description="如果当前日期已超过到期日期,则为 True")
    confidence_score: float = Field(..., description="模型内部置信度,范围 0 到 1")

    @validator('confidence_score')
    def check_confidence(cls, v):
        if v < 0.5:
            raise ValueError('置信度过低,无法进行自动化处理')
        return v

第二步:通过 n1n.ai 执行

使用 n1n.ai API,我们可以将此模式发送给像 DeepSeek-V3 这样强大的模型。使用像 n1n.ai 这样的聚合器的优势在于,如果一个模型无法遵守模式,可以自动回退到 Claude 3.5 Sonnet。

import requests
import json

# n1n.ai API 配置
API_KEY = "YOUR_N1N_API_KEY"
URL = "https://api.n1n.ai/v1/chat/completions"

def get_structured_rag_response(context, question):
    payload = {
        "model": "deepseek-v3",
        "messages": [
            {"role": "system", "content": "你是一名法律分析师。请严格按照模式提取数据。"},
            {"role": "user", "content": f"背景内容: {context}\n\n问题: {question}"}
        ],
        "response_format": {"type": "json_object"},
        "schema": PolicyAnalysis.schema() # 传递 Pydantic 模式
    }

    headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
    response = requests.post(URL, json=payload, headers=headers)
    # 解析并验证返回的 JSON
    return PolicyAnalysis.parse_raw(response.json()['choices'][0]['message']['content'])

高级技巧:引入“证据”字段

为了真正消除幻觉,请在模式中为每个数据点添加一个 evidence(证据)字段。这会强制 LLM 直接从检索到的背景文本中引用原文。如果模型找不到直接的引用,它就不应该填充该字段。

class EvidenceField(BaseModel):
    value: str
    source_quote: str = Field(..., description="证明此值的背景原文片段")
    page_number: int

这种方法将 RAG 从“生成式”转变为“抽取式”,极大地提高了金融和医疗等严苛行业的准确性。通过 n1n.ai 调用的 DeepSeek-V3 在处理这类长上下文抽取任务时表现尤为出色。

对比分析:文本型 RAG vs. 类型化契约 (TAC)

功能特性文本型 RAG类型化答案契约 (TAC)
输出格式自然语言(散文)经验证的 JSON
校验方式人工或 LLM 二次评估Pydantic / Schema 级硬校验
幻觉风险高(隐藏在文字中)低(必须匹配模式和引用)
系统集成需要正则或复杂的解析逻辑原生程序化集成
模型选择任何模型具备强 JSON 支持的模型 (DeepSeek, Claude)

成功的专家建议

  1. 保持模式精简:不要试图一次提取 50 个字段。契约中的字段越多,LLM 失去焦点的可能性就越大。将复杂的文档拆分为多个提取步骤。
  2. 利用 DeepSeek-V3 的成本优势:对于高吞吐量的提取任务,通过 n1n.ai 使用 DeepSeek-V3 可以在保证性能的同时,成本仅为其他顶尖模型的一小部分。
  3. 优雅降级策略:如果模型未能返回有效的 JSON,请实现重试逻辑,切换到像 OpenAI o3 这样逻辑推理能力极强的模型来调试提取过程。
  4. Prompt 提示工程:在系统提示词中明确指出“如果信息不存在,请返回 null,不要编造”。

总结

让 LLM 漫无边际闲聊的时代已经结束了。对于企业级应用来说,模式就是契约。通过强制执行类型化答案,你可以确保你的 RAG 流水线不仅是一个华丽的搜索引擎,而是一个可靠的数据处理引擎。今天就开始利用 n1n.ai 的多模型能力,构建你的契约式 AI 流水线吧。

n1n.ai 获取免费 API 密钥。