第2章 RAG技术与应用

1. 大模型应用开发的三种模式

Thinking: Prompt vs RAG vs Fine-tuning 什么时候使用?

2. 什么是 RAG ?

RAG(Retrieval-Augmented Generation)

  • 检索增强生成,是一种结合信息检索(Retrieval)和文本生成(Generation)的技术
  • RAG技术通过实时检索相关文档或信息,并将其作为上下文输入到生成模型中,从而提高生成结果的时效性和准确性。

2.1. RAG 的优势是什么?

  • 解决知识时效性问题: 大模型的训练数据通常是静态的,无法涵盖最新信息,而RAG可以检索外部知识库实时更新信息
  • 减少模型幻觉: 通过引入外部知识,RAG能够减少模型生成虚假或不准确内容的可能性
  • 提升专业领域回答质量: RAG能够结合垂直领域的专业知识库,生成更具专业深度的回答
  • 生成内容的溯源(可解释性)

2.2. RAG 的核心原理与流程

Step1:数据预处理,构建索引库

  • 知识库构建:收集并整理文档、网页、数据库等多源数据,构建外部知识库
  • 文档分块:将文档切分为适当大小的片段(chunks),以便后续检索。分块策略需要在语义完整性与检索效率之间取得平衡
  • 向量化处理:使用嵌入模型(如BGE、M3E、Chinese-Alpaca-2等)将文本块转换为向量,并存储在向量数据库中

Step2:检索阶段

  • 查询处理:将用户输入的问题转换为向量,并在向量数据库中进行相似度检索,找到最相关的文本片段
  • 重排序:对检索结果进行相关性排序,选择最相关的片段作为生成阶段的输入

Step3:生成阶段

  • 上下文组装:将检索到的文本片段与用户问题结合,形成增强的上下文输入
  • 生成回答:大语言模型基于增强的上下文生成最终回答
划重点:RAG 本质上就是重构了一个新的 Prompt!

3. NativeRAG

NativeRAG的步骤:

  • Indexing => 如何更好地把知识存起来。
  • Retrieval => 如何在大量的知识中,找到一小部分有用的,给到模型参考。
  • Generation => 如何结合用户的提问和检索到的知识,让模型生成有用的答案。
划重点:上面三个步骤虽然看似简单,但在 RAG 应用从构建到落地实施的整个过程中,涉及较多复杂的工作内容!

4. LangChain快速搭建本地知识库检索

4.1. 环境准备

  1. 本地安装好 Conda 环境 conda create -n chapter-2 python=3.11 ipykernel

  2. 推荐使用阿里大模型平台百炼:https://bailian.console.aliyun.com/

  3. 百炼平台使用

    • 注册登录
    • 申请api key

4.2. 搭建流程

  1. 文档加载,并按一定条件切割成片段
  2. 将切割的文本片段灌入检索引擎
  3. 封装检索接口
  4. 构建调用流程:Query -> 检索 -> Prompt -> LLM -> 回复

安装依赖

pip install pypdf2
pip install dashscope
pip install langchain
pip install langchain-openai
pip install langchain-community
pip install faiss-cpu

demo1

import os
import logging
import pickle
from PyPDF2 import PdfReader
from langchain.chains.question_answering import load_qa_chain
from langchain_openai import OpenAI, ChatOpenAI
from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.callbacks.manager import get_openai_callback
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from typing import List, Tuple

def extract_text_with_page_numbers(pdf) -> Tuple[str, List[int]]:
    """
    从PDF中提取文本并记录每行文本对应的页码
    
    参数:
        pdf: PDF文件对象
    
    返回:
        text: 提取的文本内容
        page_numbers: 每行文本对应的页码列表
    """
    text = ""
    page_numbers = []

    for page_number, page in enumerate(pdf.pages, start=1):
        extracted_text = page.extract_text()
        if extracted_text:
            text += extracted_text
            page_numbers.extend([page_number] * len(extracted_text.split("\n")))
        else:
            logging.warning(f"No text found on page {page_number}.")

    return text, page_numbers

def process_text_with_splitter(text: str, page_numbers: List[int], save_path: str = None) -> FAISS:
    """
    处理文本并创建向量存储
    
    参数:
        text: 提取的文本内容
        page_numbers: 每行文本对应的页码列表
        save_path: 可选,保存向量数据库的路径
    
    返回:
        knowledgeBase: 基于FAISS的向量存储对象
    """
    # 创建文本分割器(递归字符文本分割器),用于将长文本分割成小块
    text_splitter = RecursiveCharacterTextSplitter(
        separators = ["\n\n", "\n", ".", " ", ""],
        chunk_size = 512,
        chunk_overlap = 128,
        length_function = len,
    )

    # 分割文本
    chunks = text_splitter.split_text(text)
    # logging.debug(f"Text split into {len(chunks)} chunks.")
    print(f"文本被分割成 {len(chunks)} 个块。")
        
    # 创建嵌入模型,OpenAI嵌入模型,配置环境变量 OPENAI_API_KEY
    # embeddings = OpenAIEmbeddings()

    # 调用阿里百炼平台文本嵌入模型,配置环境变量 DASHSCOPE_API_KEY
    embeddings = DashScopeEmbeddings(
        model = "text-embedding-v4"
    )
    # 从文本块创建知识库
    knowledgeBase = FAISS.from_texts(chunks, embeddings)
    print("已从文本块创建知识库...")
    
    # 存储每个文本块对应的页码信息
    page_info = {chunk: page_numbers[i] for i, chunk in enumerate(chunks)}
    knowledgeBase.page_info = page_info

    # 如果提供了保存路径,则保存向量数据库和页码信息
    if save_path:
        # 确保目录存在
        os.makedirs(save_path, exist_ok=True)
        
        # 保存FAISS向量数据库
        knowledgeBase.save_local(save_path)
        print(f"向量数据库已保存到: {save_path}")
        
        # 保存页码信息到同一目录
        with open(os.path.join(save_path, "page_info.pkl"), "wb") as f:
            pickle.dump(page_info, f)
        print(f"页码信息已保存到: {os.path.join(save_path, 'page_info.pkl')}")
    
    return knowledgeBase

def load_knowledge_base(load_path: str, embeddings = None) -> FAISS:
    """
    从磁盘加载向量数据库和页码信息
    
    参数:
        load_path: 向量数据库的保存路径
        embeddings: 可选,嵌入模型。如果为None,将创建一个新的DashScopeEmbeddings实例
    
    返回:
        knowledgeBase: 加载的FAISS向量数据库对象
    """
    # 如果没有提供嵌入模型,则创建一个新的
    if embeddings is None:
        embeddings = DashScopeEmbeddings(
            model="text-embedding-v4"
        )
    
    # 加载FAISS向量数据库,添加allow_dangerous_deserialization=True参数以允许反序列化
    knowledgeBase = FAISS.load_local(load_path, embeddings, allow_dangerous_deserialization=True)
    print(f"向量数据库已从 {load_path} 加载。")
    
    # 加载页码信息
    page_info_path = os.path.join(load_path, "page_info.pkl")
    if os.path.exists(page_info_path):
        with open(page_info_path, "rb") as f:
            page_info = pickle.load(f)
        knowledgeBase.page_info = page_info
        print("页码信息已加载。")
    else:
        print("警告: 未找到页码信息文件。")
    
    return knowledgeBase

# 读取PDF文件
pdf_reader = PdfReader('./浦发上海浦东发展银行西安分行个金客户经理考核办法.pdf')
# 提取文本和页码信息
text, page_numbers = extract_text_with_page_numbers(pdf_reader)
text

demo1 执行结果

'百度文库  - 好好学习,天天向上  \n-1 上海浦东发展银行西安分行  \n个金客户经理管理考核暂行办法  \n \n \n第一章  总   则 \n第一条   为保证我分行个金客户经理制的顺利实施,有效调动个\n金客户经理的积极性,促进个金业务快速、稳定地发展,根据总行《上\n海浦东发展银行个人金融营销体系建设方案(试行)》要求,特制定\n《上海浦东发展银行西安分行个金客户经理管理考核暂行办法(试\n行)》(以下简称本办法)。  \n第二条   个金客户经理系指各支行(营业部)从事个人金融产品\n营销与市场开拓,为我行个人客户提供综合银行服务的我行市场人\n员。 \n第三条   考核内容分为二大类, 即个人业绩考核、 工作质量考核。\n个人业绩包括个人资产业务、负债业务、卡业务。工作质量指个人业\n务的资产质量。  \n第四条   为规范激励规则,客户经理的技术职务和薪资实行每年\n考核浮动。客户经理的奖金实行每季度考核浮动,即客户经理按其考\n核内容得分与行员等级结合,享受对应的行员等级待遇。  \n 百度文库  - 好好学习,天天向上  \n-2 第二章  职位设置与职责  \n第五条   个金客户经理职位设置为:客户经理助理、客户经理、\n高级客户经理、资深客户经理。  \n第六条   个金客户经理的基本职责:  \n(一)   客户开发。研究客户信息、联系与选择客户、与客户建\n立相互依存、相互支持的业务往来关系,扩大业务资源,创造良好业\n绩; \n(二)业务创新与产品营销。把握市场竞争变化方向,开展市场\n与客户需 求的调研,对业务产品及服务进行创新;设计客户需求的产\n品组合、制订和实施市场营销方案;  \n(三)客户服务。负责我行各类表内外授信业务及中间业务的受\n理和运作,进行综合性、整体性的客户服务;  \n(四)防范风险,提高收益。提升风险防范意识及能力,提高经\n营产品质量;  \n(五)培养人材。在提高自身综合素质的同时,发扬团队精神,\n培养后备业务骨干。  \n 百度文库  - 好好学习,天天向上  \n-3 第三章  基础素质要求  \n第七条   个金客户经理准入条件:  \n(一)工作经历:须具备大专以上学历,至少二年以上银行工作\n经验。  \n(二)工作能力:熟悉我行的各项业务,了解市场情况,熟悉各\n类客户的金融需求,熟悉个人理财工具,有一定的业务管理和客户管\n理能力。  \n(三)工作业绩:个金客户经理均应达到相应等级的准入标准。\n该标准可根据全行整体情况由考核部门进行调整。  \n(四)专业培训:个金客户经理应参加有关部门组织的专业培训\n并通过业务考试。  \n(五)符合分行人事管理和专业管理的要求。  \n第四章  个人业绩考核标准  \n第八条   个金客户经理个人业绩以储蓄季日均、季有效净增发卡\n量、季净增个贷余额 三项业务为主要考核指标,实行季度考核。具体\n标准如下:  \n \n    \n类别 行员级别 考核分值 准入标准  \n储蓄业务 个贷业务 卡业务 \n客户经理助理  5 90 300万  500张 \n4 95  百度文库  - 好好学习,天天向上  \n-4 3 100  \n2 105  \n1 110  \n客户经理 5 115 300万  500张 \n4 120  \n3 125  \n2 130  \n1 135  \n高级客户经理  5 140 500万 800万  \n4 145  \n3 150  \n2 155  \n1 160  \n资深客户经理  5 165 500万 800万  \n4 170  \n3 175  \n2 180  \n1 185  \n说明: 1.储蓄业务(季日均余额)为各类个金客户经理考核进入的最低标准。   \n2.卡业务(季新增发有效卡量)为见习、 D类、初级客户经理进入的最低标准。  \n3.有效卡的概念:每张卡月均余额为 100元以上。  \n4.个贷业务(季新增发放个贷)为中级以上客户经理考核进入的最低标准。  \n5.超出最低考核标准可相互折算,折算标准: 50万储蓄 =50万个贷 =50张有效卡 =5分(折算以 5分为单位)  \n \n 百度文库  - 好好学习,天天向上  \n-5 第五章  工作质量考核标准  \n第九条   工作质量考核实行扣分制。工作质量指个金客户经理在\n从事所有个人业务时出现投诉、差错及风险。该项考核最多扣 50分,\n如发生重大差错事故,按分行有关制度处理。  \n(一)服务质量考核:   \n1、工作责任心不强,缺乏配合协作精神;扣 5分 \n2、客户服务效率低,态度生硬或不及时为客户提供维护服务,\n有客户投诉的 ,每投诉一次扣 2分 \n3、不服从支行工作安排,不认真参加分(支)行宣传活动的,\n每次扣 2分; \n4、未能及时参加分行(支行)组织的各种业务培训、考试和专\n题活动的每次扣 2分; \n5、未按规定要求进行贷前调查、贷后检查工作的,每笔扣 5分; \n6、未建立信贷台帐资料及档案的每笔扣 5分; \n7、在工作中有不廉洁自律情况的每发现一次扣 50分。 \n(二)个人资产质量考核:  \n当季考核收息率 97%以上为合格,每降 1个百分点扣 2分;不\n良资产零为合格,每超一个个百分点扣 1分。 \nA.发生跨月逾期,单笔不超过 10万元,当季收回者,扣 1分。 \nB.发生跨月逾期, 2笔以上累计金额不超过 20万元,当季收回\n者,扣 2分;累计超过 20万元以上的,扣 4分。 百度文库  - 好好学习,天天向上  \n-6 C.发生逾期超过 3个月,无论金额大小和笔数,扣 10分。 \n \n第六章  聘任考核程序  \n第十条   凡达到本办法第三章规定的该技术职务所要求的行内职\n工,都可向分行人力资源部申报个金客户经理评聘。  \n第十一条   每年一月份为客户经理评聘的申报时间,由分行人力\n资源部、个人业务部每年二月份组织统一的资格考试。考试合格者由\n分行颁发个金客户经理资格证书,其有效期为一年。  \n第十二条   客户经理聘任实行开放式、浮动制,即:本人申报  —\n— 所在部门推荐  —— 分行考核  —— 行长聘任  —— 每年考评\n调整浮动。   \n第十三条   特别聘任:  \n(一)经分行同意录用从其他单位调入的个金客户经理,由用人\n单位按 D类人员进行考核, 薪资待遇按其业绩享受行内正式行员工同\n等待遇。待正式转正后按第十一条规定申报技术职务。  \n(二)对为我行业务创新、工作业绩等方面做出重大贡献的市场\n人员经支行推荐、分行行长 批准可越级聘任。  \n第十四条   对于创利业绩较高,而暂未入围技术职务系列,或所\n评聘技术职务较低的市场人员,各级领导要加大培养力度,使其尽快百度文库  - 好好学习,天天向上  \n-7 入围,并由所在行制定临时奖励办法。  \n \n第七章   考核待遇  \n第十五条   个人金融业务客户经理的收入基本由三部分组成: 客\n户经理等级基本收入、业绩奖励收入和日常工作绩效收入。  \n客户经理等级基本收入是指客户经理的每月基本收入, 基本分为\n助理客户经理、客户经理、高级客户经理和资深客户经理四大层面,\n在每一层面分为若干等级。  \n客户经理的等级标准由客户经理在上年的业绩为核定标准, 如果\n客户经理在我行第一次进行客户经理评级, 以客户经理自我评价为主\n要依据,结合客户经理以往工作经验,由个人金融部、人事部门共同\n最终决定客户经理的等级。  \n助理客户经理待遇按照人事部门对主办科员以下人员的待遇标\n准;客户经理待遇按照人事部门对主办科员的待遇标准;高级客户经\n理待遇按照人事部门对付科级的待遇标准; 资深客户经理待遇 按照人\n事部门对正科级的待遇标准。  \n业绩奖励收入是指客户经理每个业绩考核期间的实际业绩所给\n与兑现的奖金部分。  \n日常工作绩效收入是按照个金客户经理所从事的事务性工作进\n行定量化考核,经过工作的完成情况进行奖金分配。该项奖金主要由\n个人金融部总经理和各支行的行长其从事个人金融业务的人员进行\n分配,主要侧重分配于从事个金业务的基础工作和创新工作。  百度文库  - 好好学习,天天向上  \n-8 第十五条   各项考核分值总计达到某一档行员级别考核分值标\n准,个金客户经理即可在下一季度享受该级行员的薪资标准。下一季\n度考核时,按照已享受行员级别考核折算比值进行考核,以次类推。  \n第十六条   对已聘为各级客户经理的人员,当工作业绩考核达不\n到相应技术职务要求下限时,下一年技术职务相应下调 。 \n第十七条   为保护个人业务客户经理创业的积极性,暂定其收入\n构成中基础薪点不低于 40%。 \n \n第八章  管理与奖惩  \n第十八条   个金客户经理管理机构为分行客户经理管理委员会。\n管理委员会组成人员:行长或主管业务副行长,个人业务部、人力资\n源部、风险管理部负责人。  \n第十九条   客户经理申报的各种信息必须真实。分行个人业务部\n需对其工作业绩数据进行核实,并对其真实性负责;分行人事部门需\n对其学历、工作阅历等基本信息进行核实,并对其真实性负责。  \n第二十条   对因工作不负责任使资产质量产生严重风险或造成损\n失的给予降级直至开 除处分,构成渎职罪的提请司法部门追究刑事责\n任。 \n 百度文库  - 好好学习,天天向上  \n-9 第九章  附    则 \n第二十一条   本办法自发布之日起执行。  \n第二十二条   本办法由上海浦东发展银行西安分行行负责解释和\n修改。  \n '

demo2

print(f"提取的文本长度: {len(text)} 个字符。")

# 处理文本并创建知识库,同时保存到磁盘
save_dir = "./vector_db"
knowledgeBase = process_text_with_splitter(text, page_numbers, save_path=save_dir)
    
# 处理文本并创建知识库
knowledgeBase = process_text_with_splitter(text, page_numbers)
knowledgeBase

demo2 执行结果

提取的文本长度: 3881 个字符。
文本被分割成 10 个块。
已从文本块创建知识库...
向量数据库已保存到: ./vector_db
页码信息已保存到: ./vector_db\page_info.pkl
文本被分割成 10 个块。
已从文本块创建知识库...





<langchain_community.vectorstores.faiss.FAISS at 0x160a195d6d0>

demo3

# 设置查询问题
# query = "客户经理被投诉了,投诉一次扣多少分"
query = "客户经理每年评聘申报时间是怎样的?"
if query:
    # 执行相似度搜索,找到与查询相关的文档
    docs = knowledgeBase.similarity_search(query)
    
    # 初始化对话大模型
    chatLLM  = ChatOpenAI(
        # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
        api_key = os.getenv("DASHSCOPE_API_KEY"),
        base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1",
        model = "deepseek-v3"
    )
    
    # 加载问答链
    chain = load_qa_chain(chatLLM, chain_type="stuff")

    # 准备输入数据
    input_data = {"input_documents": docs, "question": query}

    # 使用回调函数跟踪API调用成本
    with get_openai_callback() as cost:
        # 执行问答链
        response = chain.invoke(input=input_data)
        print(f"查询已处理。成本: {cost}")
        print(response["output_text"])
        print("来源:")

    # 记录唯一的页码
    unique_pages = set()

    # 显示每个文档块的来源页码
    for doc in docs:
        text_content = getattr(doc, "page_content", "")
        source_page = knowledgeBase.page_info.get(
            text_content.strip(), "未知"
        )

        if source_page not in unique_pages:
            unique_pages.add(source_page)
            print(f"文本块页码: {source_page}")

demo3 执行结果

查询已处理。成本: Tokens Used: 1293
	Prompt Tokens: 1238
		Prompt Tokens Cached: 0
	Completion Tokens: 55
		Reasoning Tokens: 0
Successful Requests: 1
Total Cost (USD): $0.0
根据第十一条规定,客户经理每年评聘的申报时间是**每年一月份**。分行人力资源部和个人业务部会在**每年二月份**组织统一的资格考试,考试合格者将获得由分行颁发的个金客户经理资格证书,该证书的有效期为一年。
来源:
文本块页码: 1

demo4

# 设置查询问题
query = "客户经理被投诉了,投诉一次扣多少分?"
# query = "客户经理每年评聘申报时间是怎样的?"
if query:
    # 从磁盘加载向量数据库
    loaded_knowledgeBase = load_knowledge_base("./vector_db", embeddings)
    # 使用加载的知识库进行查询
    docs = loaded_knowledgeBase.similarity_search(query)
    
    # 加载问答链
    chain = load_qa_chain(chatLLM, chain_type="stuff")

    # 准备输入数据
    input_data = {"input_documents": docs, "question": query}

    # 使用回调函数跟踪API调用成本
    with get_openai_callback() as cost:
        # 执行问答链
        response = chain.invoke(input=input_data)
        print(f"查询已处理。成本: {cost}")
        print(response["output_text"])
        print("来源:")

    # 记录唯一的页码
    unique_pages = set()

    # 显示每个文档块的来源页码
    for doc in docs:
        text_content = getattr(doc, "page_content", "")
        source_page = knowledgeBase.page_info.get(
            text_content.strip(), "未知"
        )

        if source_page not in unique_pages:
            unique_pages.add(source_page)
            print(f"文本块页码: {source_page}")

demo4 执行结果

向量数据库已从 ./vector_db 加载。
页码信息已加载。
查询已处理。成本: Tokens Used: 1281
	Prompt Tokens: 1201
		Prompt Tokens Cached: 0
	Completion Tokens: 80
		Reasoning Tokens: 0
Successful Requests: 1
Total Cost (USD): $0.0
根据提供的上下文内容,客户经理被投诉一次扣2分。具体依据如下:

**工作质量考核标准第九条(一)第2项明确规定:**  
"客户服务效率低,态度生硬或不及时为客户提供维护服务,有客户投诉的,每投诉一次扣2分。"

因此,若客户经理因服务质量问题被投诉,每次将被扣除2分。
来源:
文本块页码: 1

小结:

1. PDF文本提取与处理

  • 使用PyPDF2库的PdfReader从PDF文件中提取文本在提取过程中记录每行文本对应的页码,便于后续溯源
  • 使用RecursiveCharacterTextSplitter将长文本分割成小块,便于向量化处理

2. 向量数据库构建

  • 使用OpenAIEmbeddings / DashScopeEmbeddings将文本块转换为向量表示
  • 使用FAISS向量数据库存储文本向量,支持高效的相似度搜索为每个文本块保存对应的页码信息,实现查询结果溯源

3. 语义搜索与问答链

  • 基于用户查询,使用similarity_search在向量数据库中检索相关文本块
  • 使用文本语言模型和load_qa_chain构建问答链将检索到的文档和用户问题作为输入,生成回答

4. 成本跟踪与结果展示

  • 使用get_openai_callback跟踪API调用成本
  • 展示问答结果和来源页码,方便用户验证信息

5. 三大阶段有效提升RAG质量方法

5.1. 数据准备阶段

5.1.1. 常见问题

  • 数据质量差:
    企业大部分数据(尤其是非结构化数据)缺乏良好的数据治理,未经标记/评估的非结构化数据可能包含敏感、过时、矛盾或不正确的信息。
  • 多模态信息:
    提取、定义和理解文档中的不同内容元素,如标题、配色方案、图像和标签等存在挑战。
  • 复杂的PDF提取:
    PDF是为人类阅读而设计的,机器解析起来非常复杂。

5.1.2. 如何提升数据准备阶段的质量?

  • 构建完整的数据准备流程
  • 采用智能文档技术

(1)构建完整的数据准备流程

  1. 数据评估与分类
  • 数据审计:全面审查现有数据,识别敏感、过时、矛盾或不准确的信息。
  • 数据分类:按类型、来源、敏感性和重要性对数据进行分类,便于后续处理。
  1. 数据清洗
  • 去重:删除重复数据
  • 纠错:修正格式错误、拼写错误等
  • 更新:替换过时信息,确保数据时效性
  • 一致性检查:解决数据矛盾,确保逻辑一致
  1. 敏感信息处理
  • 识别敏感数据:使用工具或正则表达式识别敏感信息,如个人身份信息
  • 脱敏或加密:对敏感数据进行脱敏处理,确保合规。
  1. 数据标记与标注
  • 元数据标记:为数据添加元数据,如来源、创建时间等
  • 内容标注:对非结构化数据进行标注,便于后续检索和分析
  1. 数据治理框架
  • 制定政策:明确数据管理、访问控制和更新流程
  • 责任分配:指定数据治理负责人,确保政策执行
  • 监控与审计:定期监控数据质量,进行审计

(2)智能文档技术

阿里文档智能:https://www.aliyun.com/product/ai/docmind?spm=a2c4g.11174283.0.0.bfe667a8tIVMdG

微软 LayoutLMv3:https://www.microsoft.com/en-us/research/articles/layoutlmv3/

5.2. 知识检索阶段

5.2.1. 常见问题

  • 内容缺失:
    当检索过程缺少关键内容时,系统会提供不完整、碎片化的答案 => 降低RAG的质量
  • 错过排名靠前的文档:
    用户查询相关的文档时被检索到,但相关性极低,导致答案不能满足用户需求,这是因为在检索过程中,用户通过主观判断决定检索“文档数量”。理论上所有文档都要被排序并考虑进一步处理,但在实践中,通常只有排名top k的文档才会被召回,而k值需要根据经验确定。
  • 不在上下文中:
    从数据库中检索出包含答案的文档,但未能包含在生成答案的上下文中。这种情况通常发生在返
    回大量文件时,需要进行整合以选择最相关的信息。

5.2.2. 如何提升知识检索阶段的质量?

  • 通过查询转换澄清用户意图:明确用户意图,提高检索准确性。
  • 采用混合检索和重排策略:确保最相关的文档被优先处理,生成更准确的答案。

(1)通过查询转换澄清用户意图

  • 场景:用户询问 “如何申请信用卡?”

  • 问题:用户意图可能模糊,例如不清楚是申请流程、所需材料还是资格条件。

  • 解决方法:通过查询转换明确用户意图。

  • 实现步骤:

    • 意图识别:使用自然语言处理技术识别用户意图。例如,识别用户是想了解流程、材料还是资格。

    • 查询扩展:根据识别结果扩展查询。例如:

      • 如果用户想了解流程,查询扩展为“信用卡申请的具体步骤”
      • 如果用户想了解材料,查询扩展为“申请信用卡需要哪些材料”
      • 如果用户想了解资格,查询扩展为“申请信用卡的资格条件”
    • 检索:使用扩展后的查询检索相关文档

  • 示例:

    1. 用户输入:“如何申请信用卡?”
    2. 系统识别意图为 流程,扩展查询为 信用卡申请的具体步骤
    3. 检索结果包含详细的申请步骤文档,系统生成准确答案

(2)混合检索和重排策略

  • 场景:用户询问“信用卡年费是多少?”

  • 问题:直接检索可能返回大量文档,部分相关但排名低,导致答案不准确。

  • 解决方法:采用混合检索和重排策略。

  • 步骤:

    1. 混合检索:结合关键词检索和语义检索。比如:关键词检索:“信用卡年费”。
    2. 语义检索:使用嵌入模型检索与“信用卡年费”语义相近的文档。
    3. 重排:对检索结果进行重排。
    4. 生成答案:从重排后的文档中生成答案。
  • 示例:

    1. 用户输入:“信用卡年费是多少?”
    2. 系统进行混合检索,结合关键词和语义检索。
    3. 重排后,最相关的文档(如“信用卡年费政策”)排名靠前。
    4. 系统生成准确答案:“信用卡年费根据卡类型不同,普通卡年费为100元,金卡为300元,白金卡为1000元。”

5.3. 答案生成阶段

5.3.1. 常见问题

  • 未提取:
    答案与所提供的上下文相符,但大语言模型却无法准确提取。这种情况通常发生在上下文中存在过多噪音或相互冲突的信息时。
  • 不完整:
    尽管能够利用上下文生成答案,但信息缺失会导致对用户查询的答复不完整。格式错误:当prompt中的附加指令格式不正确时,大语言模型可能误解或曲解这些指令,从而导致错误的答案。
  • 幻觉:
    大模型可能会产生误导性或虚假性信息。

5.3.2. 如何提升答案生成阶段的质量?

  • 改进提示词模板
  • 实施动态防护栏

(1)改进提示词模板

场景 原始提示词 改进后的提示词
用户询问“如何申请信用卡?” “根据以下上下文回答问题:如何申请信用卡?” “根据以下上下文,提取与申请信用卡相关的具体步骤和所需材料:如何申请信用卡?”
用户询问“信用卡的年费是多少?” “根据以下上下文回答问题:信用卡的年费是多少?” “根据以下上下文,详细列出不同信用卡的年费信息,并说明是否有减免政策:信用卡的年费是多少?”
用户询问“什么是零存整取?” “根据以下上下文回答问题:什么是零存整取?” “根据以下上下文,准确解释零存整取的定义、特点和适用人群,确保信息真实可靠:什么是零存整取?”

如何对原有的提示词进行优化?

可以通过 DeepSeek-R1QWQ 的推理链,对提示词进行优化:

  • 信息提取:从原始提示词中提取关键信息。
  • 需求分析:分析用户的需求,明确用户希望获取的具体信息。
  • 提示词优化:根据需求分析的结果,优化提示词,使其更具体、更符合用户的需求。

(2)实施动态防护栏

动态防护栏(Dynamic Guardrails)是一种在生成式AI系统中用于实时监控和调整模型输出的机制,旨在确保生成的内容符合预期、准确且安全。它通过设置规则、约束和反馈机制,动态地干预模型的生成过程,避免生成错误、不完整、不符合格式要求或含有虚假信息(幻觉)的内容。

在RAG系统中,动态防护栏的作用尤为重要,因为它可以帮助解决以下问题:

  • 未提取:确保模型从上下文中提取了正确的信息。
  • 不完整:确保生成的答案覆盖了所有必要的信息。
  • 格式错误:确保生成的答案符合指定的格式要求。
  • 幻觉:防止模型生成与上下文无关或虚假的信息。

场景1:防止未提取

用户问题:“如何申请信用卡?”

  • 上下文:包含申请信用卡的步骤和所需材料。

  • 动态防护栏规则:检查生成的答案是否包含“步骤”和“材料”。如果缺失,提示模型重新生成。

  • 示例:

    • 错误输出:“申请信用卡需要提供一些材料。”
    • 防护栏触发:检测到未提取具体步骤,提示模型补充。

场景2:防止不完整

用户问题:“信用卡的年费是多少?”

  • 上下文:包含不同信用卡的年费信息。

  • 动态防护栏规则:检查生成的答案是否列出所有信用卡的年费。如果缺失,提示模型补充。

  • 示例:

    • 错误输出:“信用卡A的年费是100元。”
    • 防护栏触发:检测到未列出所有信用卡的年费,提示模型补充。

场景3:防止幻觉

用户问题:“什么是零存整取?”

  • 上下文:包含零存整取的定义和特点。

  • 动态防护栏规则:检查生成的答案是否与上下文一致。如果不一致,提示模型重新生成。

  • 示例:

    • 错误输出:“零存整取是一种贷款产品。
    • 防护栏触发:检测到与上下文不一致,提示模型重新生成。

如何实现动态防护栏技术?

事实性校验规则,在生成阶段,设置规则验证生成内容是否与检索到的知识片段一致。例如,可以使用参考文献验证机制,确保生成内容有可靠来源支持,避免输出矛盾或不合理的回答。

如何制定事实性校验规则?

当业务逻辑明确且规则较为固定时,可以人为定义一组规则,比如:

  • 规则1:生成的答案必须包含检索到的知识片段中的关键实体(如“年费”、“利率”)。
  • 规则2:生成的答案必须符合指定的格式(如步骤列表、表格等)。
  • 实施方法:
    • 使用正则表达式或关键词匹配来检查生成内容是否符合规则。
    • 例如,检查生成内容是否包含“年费”这一关键词,或者是否符合步骤格式(如“1. 登录;2. 设置”)。

6. RAG在不同阶段提升质量的实践

  • 数据准备环节,阿里云考虑到文档具有多层标题属性且不同标题之间存在关联性,提出多粒度知识提取方案,按照不同标题级别对文档进行拆分,然后基于Qwen14b模型和RefGPT训练了一个面向知识提取任务的专属模型,对各个粒度的chunk进行知识提取和组合,并通过去重和降噪的过程保证知识不丢失、不冗余。最终将文档知识提取成多个事实型对话,提升检索效果;
  • 知识检索环节,哈啰出行采用多路召回的方式,主要是向量召回和搜索召回。其中,向量召回使用了两类,一类是大模型的向量、另一类是传统深度模型向量;搜索召回也是多链路的,包括关键词、ngram等。通过多路召回的方式,可以达到较高的召回查全率。
  • 答案生成环节,中国移动为了解决事实性不足或逻辑缺失,采用FoRAG两阶段生成策略,首先生成大纲,然后基于大纲扩展生成最终答案。

7. QA

如果LLM可以处理无限上下文了,RAG还有意义吗?

  • 效率与成本:LLM处理长上下文时计算资源消耗大,响应时间增加。RAG通过检索相关片段,减少输入长度。
  • 知识更新:LLM的知识截止于训练数据,无法实时更新。RAG可以连接外部知识库,增强时效性。
  • 可解释性:RAG的检索过程透明,用户可查看来源,增强信任。LLM的生成过程则较难追溯。
  • 定制化:RAG可针对特定领域定制检索系统,提供更精准的结果,而LLM的通用性可能无法满足特定需求。
  • 数据隐私:RAG允许在本地或私有数据源上检索,避免敏感数据上传云端,适合隐私要求高的场景。
  • 结合LLM的生成能力和RAG的检索能力,可以提升整体性能,提供更全面、准确的回答。