首页 > 教程攻略 > ai资讯 >LLM之RAG实战 | RAG的自动源引文验证技术

LLM之RAG实战 | RAG的自动源引文验证技术

来源:互联网 时间:2026-08-03 14:09:29

过去一年里,检索增强生成(RAG)逐渐成为基于LLM的主流架构,专门用来解决大模型最头疼的问题——

幻觉

。这个毛病在知识密集型场景下尤为突出,而RAG恰好能对症下药。

一、RAG如何解决幻觉?

RAG Pipeline包含两个核心组件:

检索器

负责筛选和提取相关数据,

LLM推理

则让模型基于这些过滤后的数据来回答问题。这两步对于需要利用私有知识的企业来说尤其关键——比如一堆合同、财务分析或客户支持数据。

二、证据验证

实际上,在RAG工作流中还有一个经常被忽略的第三步——LLM推理完成之后的事情:

如何验证LLM的响应是否真的来自引用的具体来源?

这才是RAG系统真正价值所在。但你得有一个集成管道,能捕获传给LLM的各类输入,再把所有推理数据整合起来,用于后处理、源验证、持续审查和审计,最终形成一个不断改进的闭环。

本文展示一组强大的后处理技术,作为RAG工作流的第三步,完成来源验证和自动源引用。我们会用llmware(一个用于开发验证LLM应用程序的开源框架)来构建RAG工作流,执行基础的合同分析,然后对照合同原文段落,验证LLM响应的准确性。

llmware在Prompt类中直接提供了几个开箱即用的工具,用于验证RAG中的来源和证据:

  • evidence_check_numbers

    —— 检查一组prompt响应对象,验证LLM输出中的数字是否在提供的源材料里。
  • evidence_check_sources

    —— 审查LLM响应和全部证据,“找出”最可能构成LLM响应“源”参考文献的文本片段、文档和页码。
  • evidence_comparison_stats

    —— 快速做token对比,分析响应与证据中已确认和未确认的token匹配情况。
  • classification_not_found_response

    —— 提供三种函数来判断LLM响应是否可能被归类为“未找到”,便于在工作流中正确处理(包括丢弃)。
  • sa ve_state

    —— 保存整个管道捕获的LLM事务信息(甚至可以保存一系列事务),输出为精良的jsonl字典,方便离线分析或直接插入文档存储。这个状态还能快速生成微调数据集。

三、代码实现

3.1 安装llmware包

pip install llmware

3.2 使用Setup()命令下拉示例文档

from llmware.setup import Setup
sample_files_path = Setup().load_sample_files()
contracts_path = os.path.join(sample_files_path, "Agreements")

本演示使用“Agreements”文件夹中的高管雇佣协议样本——当然你也可以换成自己的数据。

拿到样本合同后,就可以搭建RAG工作流了。先看每个组件,再拼起来。

Step1:创建Prompt对象

—— 在llmware中,Prompt是处理端到端prompt交互的主类。RAG的所有步骤都可以在特定Prompt中搞定:加载模型、包装和过滤源材料、应用prompt指令、还有后处理生命周期。第一步很简单,实例化一个Prompt对象并挂上模型:

prompter = Prompt().load_model("gpt-4", api_key=open_api_key)

这里我们用gpt-4,你可以直接传api_key,也可以把它放到os.environ变量里:

openai_api_key = os.environ.get("OPENAI_API_KEY","")

(多说一句,建议你试试其他模型,包括Huggingface上的开源LLM。llmware有个特点——换个模型只需改模型名称字符串和对应的api key。)

Step2:创建源材料

—— 这是RAG的关键一步。我们用add_source_document方法指向合同文件夹和具体合同,它会自动识别文档类型、解析内容、对文本分组,还可以加过滤条件,然后打包成“上下文”准备好推理。所有数据操作都在幕后完成:

source = prompter.add_source_document(exec_emp_fp, contract, query="base salary")

这里我们分析所选文件夹中的合同,不管文件类型,再按“base salary”主题进行筛选,打包成带元数据的源,注入LLM的prompt。

Step3:运行推理

—— 主要的处理步骤。把上一步准备好的源加载到prompt里,通过

查询

prompt指令

温度设置

调用LLM,拿到响应:

responses = prompter.prompt_with_source("What is the executive's base salary?", prompt_name = "just_the_facts", temperature=0.3)

输出是一个标准的响应字典,包含一个或多个LLM响应。根据输入上下文的大小,它会自动分成多个批次,必要时运行多次推理。

Step4:源数据检查

—— prompt完成后的最后一步,对响应字典做后处理。前面提到的几种来源和证据检查工具:

ev_numbers = prompter.evidence_check_numbers(responses)
ev_sources = prompter.evidence_check_sources(responses)
ev_stats = prompter.comparison_stats(responses)
not_found_status = prompter.classify_not_found_response(responses, parse_response=True, evidence_match=True, ask_the_model=False)

接下来看看每个方法的输出。

完整代码如下(也可在llmware仓库中找到):

from llmware.prompts import Prompt
from llmware.configs import LLMWareConfig
import os

os.environ["USER_MANAGED_OPENAI_API_KEY"] = "insert-your-openai-key"

def contract_analysis_w_fact_checking (model_name):

    contracts_path = "/path/to/your/sample/documents/"

    # create prompt object
    prompter = Prompt().load_model(model_name)

    research = {"topic": "base salary", "prompt": "What is the executive's base salary?"}

    for i, contract in enumerate(os.listdir(contracts_path)):

        print("nAnalyzing Contract - ", str(i+1), contract)

        print("Question: ", research["prompt"])

        # contract is parsed, text-chunked, and then filtered by "base salary'
        source = prompter.add_source_document(contracts_path, contract, query=research["topic"])

        # calling the LLM with 'source' information from the contract automatically packaged into the prompt
        responses = prompter.prompt_with_source(research["prompt"], prompt_name="just_the_facts", temperature=0.3)

        # run several fact checks
        ev_numbers = prompter.evidence_check_numbers(responses)
        ev_sources = prompter.evidence_check_sources(responses)
        ev_stats = prompter.evidence_comparison_stats(responses)
        z = prompter.classify_not_found_response(responses, parse_response=True, evidence_match=True, ask_the_model=False)

        for r, response in enumerate(responses):

            print("LLM Response: ", response["llm_response"])
            print("Numbers: ",ev_numbers[r]["fact_check"])
            print("Sources: ", ev_sources[r]["source_review"])
            print("Stats: ", ev_stats[r]["comparison_stats"])
            print("Not Found Check: ", z[r])

        # We're done with this contract, clear the source from the prompt
        prompter.clear_source_materials()

        # Sa ve jsonl report to jsonl to /prompt_history folder
        print("nupdate: prompt state sa ved at: ", os.path.join(LLMWareConfig.get_prompt_path(),prompter.prompt_id))

        prompter.sa ve_state()

运行脚本后,控制台会快速输出每个合同的分析结果,类似这样:

Analyzing Contract -1 Nyx EXECUTIVE EMPLOYMENT AGREEMENT.docx

Question:What is the executive's base salary?
LLM Response:$200,000.

Numbers:[{'fact': '$200,000,', 'status': 'Confirmed', 'text': ' ... pay Executive a base salary at the annual rate of $200,000, payable semimonthly in accordance with Employer's normal payroll practices.... ', 'page_num': '3', 'source': 'Nyx EXECUTIVE EMPLOYMENT AGREEMENT.docx'}]
Sources:[{'text': 'pay Executive a base salary at the annual rate of $200000 payable semimonthly in accordance with Employer's normal payroll practices ', 'match_score': 1.0, 'source': 'Nyx EXECUTIVE EMPLOYMENT AGREEMENT.docx', 'page_num': '4'}]
Stats:{'percent_display': '100.0%', 'confirmed_words': ['200000'], 'unconfirmed_words': [], 'verified_token_match_ratio': 1.0, 'key_point_list': [{'key_point': '$200,000.', 'entry': 0, 'verified_match': 1.0}]}
Not Found Check:{'parse_llm_response': False, 'evidence_match': False, 'not_found_classification': False}

下面详细解释每一个事实检查的作用:

Numbers

—— 审查LLM响应,识别其中的数字,然后在源材料里寻找匹配的数字值。返回一个词典,包含事实、状态,以及提供确认的源文本和页码。内置了基本的正则处理(比如去掉$、逗号等)和浮点值比较(如12.00与12视为相同),增强了鲁棒性。

Sources

—— 审查上下文中提供的所有来源,根据匹配token的密度进行统计,确定LLM输出最可能的特定来源(包括文档和页码)。

Stats

—— 一个非常实用的快速检查,也是识别潜在问题响应的最可靠方法之一。它显示按token匹配的百分比(排除停用词和基本格式项),并列出已确认和未确认的关键token。

Not Found Classification

—— 经验表明,这是RAG处理中最重要但也最容易被忽视的检查。几乎任何RAG自动化中,都会出现很多情况:特定段落被包含在上下文中,但该段落不足以回答目标问题。这时错误风险最高,模型会试图通过拼凑上下文信息来“帮忙”生成答案,即使上下文并不直接适用。此外,当工作流最期望的输出是“未找到”分类时,模型往往会长篇解释为什么不能回答,干扰后续分析。这里的“False”表示双重否定——即该事务不是“未找到”事务。

Prompt State

—— 所有经过检查的LLM响应对象都会保存在提示状态历史中(控制台输出底部的链接里)。这个.jsonl文件提供了特定LLM事务所有元数据的完整视图,可用于:

  • 分析总体准确率和错误/幻觉率;
  • 比较不同模型;
  • 审计和合规活动;
  • 持续改进,识别常见问题。

这五种机制(numbers、sources、stats、not-found、prompt state)的组合,为几乎任何RAG工作流提供了一个强大的工具包,能快速识别潜在错误和风险暴露。