LLM之RAG实战 | RAG的自动源引文验证技术
过去一年里,检索增强生成(RAG)逐渐成为基于LLM的主流架构,专门用来解决大模型最头疼的问题——
幻觉
一、RAG如何解决幻觉?
RAG Pipeline包含两个核心组件:
检索器
LLM推理
二、证据验证
实际上,在RAG工作流中还有一个经常被忽略的第三步——LLM推理完成之后的事情:
如何验证LLM的响应是否真的来自引用的具体来源?
本文展示一组强大的后处理技术,作为RAG工作流的第三步,完成来源验证和自动源引用。我们会用llmware(一个用于开发验证LLM应用程序的开源框架)来构建RAG工作流,执行基础的合同分析,然后对照合同原文段落,验证LLM响应的准确性。
llmware在Prompt类中直接提供了几个开箱即用的工具,用于验证RAG中的来源和证据:
- —— 检查一组prompt响应对象,验证LLM输出中的数字是否在提供的源材料里。
evidence_check_numbers
- —— 审查LLM响应和全部证据,“找出”最可能构成LLM响应“源”参考文献的文本片段、文档和页码。
evidence_check_sources
- —— 快速做token对比,分析响应与证据中已确认和未确认的token匹配情况。
evidence_comparison_stats
- —— 提供三种函数来判断LLM响应是否可能被归类为“未找到”,便于在工作流中正确处理(包括丢弃)。
classification_not_found_response
- —— 保存整个管道捕获的LLM事务信息(甚至可以保存一系列事务),输出为精良的jsonl字典,方便离线分析或直接插入文档存储。这个状态还能快速生成微调数据集。
sa ve_state
三、代码实现
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对象
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:创建源材料
add_source_document方法指向合同文件夹和具体合同,它会自动识别文档类型、解析内容、对文本分组,还可以加过滤条件,然后打包成“上下文”准备好推理。所有数据操作都在幕后完成:
source = prompter.add_source_document(exec_emp_fp, contract, query="base salary")
这里我们分析所选文件夹中的合同,不管文件类型,再按“base salary”主题进行筛选,打包成带元数据的源,注入LLM的prompt。
Step3:运行推理
查询
prompt指令
温度设置
responses = prompter.prompt_with_source("What is the executive's base salary?", prompt_name = "just_the_facts", temperature=0.3)
输出是一个标准的响应字典,包含一个或多个LLM响应。根据输入上下文的大小,它会自动分成多个批次,必要时运行多次推理。
Step4:源数据检查
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
Sources
Stats
Not Found Classification
Prompt State
- 分析总体准确率和错误/幻觉率;
- 比较不同模型;
- 审计和合规活动;
- 持续改进,识别常见问题。
这五种机制(numbers、sources、stats、not-found、prompt state)的组合,为几乎任何RAG工作流提供了一个强大的工具包,能快速识别潜在错误和风险暴露。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名