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

- 姓名
- 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) 中看起来不错,但在生产环境中却是噩梦,原因有三:
- 幻觉伪装:LLM 非常擅长在错误的时候听起来很自信。在一大段文本中,一个错误的日期或价格很难通过编程方式捕捉到。
- 下游失败:如果你的应用程序需要触发工作流(例如,“如果合同在 2026 年之前到期,则发送警报”),从自然语言段落中解析该逻辑非常容易出错。
- 缺乏问责制:你无法轻松地对一段话进行单元测试。但是,你可以对具有特定类型的 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) |
成功的专家建议
- 保持模式精简:不要试图一次提取 50 个字段。契约中的字段越多,LLM 失去焦点的可能性就越大。将复杂的文档拆分为多个提取步骤。
- 利用 DeepSeek-V3 的成本优势:对于高吞吐量的提取任务,通过 n1n.ai 使用 DeepSeek-V3 可以在保证性能的同时,成本仅为其他顶尖模型的一小部分。
- 优雅降级策略:如果模型未能返回有效的 JSON,请实现重试逻辑,切换到像 OpenAI o3 这样逻辑推理能力极强的模型来调试提取过程。
- Prompt 提示工程:在系统提示词中明确指出“如果信息不存在,请返回 null,不要编造”。
总结
让 LLM 漫无边际闲聊的时代已经结束了。对于企业级应用来说,模式就是契约。通过强制执行类型化答案,你可以确保你的 RAG 流水线不仅是一个华丽的搜索引擎,而是一个可靠的数据处理引擎。今天就开始利用 n1n.ai 的多模型能力,构建你的契约式 AI 流水线吧。
在 n1n.ai 获取免费 API 密钥。