【LLM实战】RAG初体验,两种实现方式
every blog every motto: You can do more than you think.
https://blog.csdn.net/weixin_39190382?type=blog
0. 前言
【LLM实战】RAG初体验,两种实现方式
# !pip install pypdf2
# !pip install dashscope
# !pip install langchain
# !pip install langchain-openai
# !pip install langchain-community
# !pip install faiss-cpu
1. 数据预处理
- 将pdf文件转换为txt文件
- 分块
- 生成向量
- 保存向量
文档:点击下载文档
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, Tupledef 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_textpage_numbers.extend([page_number] * len(extracted_text.split("\n")))else:logging.warning(f"No text found on page {page_number}.")return text, page_numbersdef 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_KEYembeddings = DashScopeEmbeddings(model = "text-embedding-v2")# 从文本块创建知识库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 knowledgeBasedef 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-v2")# 加载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_infoprint("页码信息已加载。")else:print("警告: 未找到页码信息文件。")return knowledgeBase# 读取PDF文件
pdf_reader = PdfReader('./浦发上海浦东发展银行西安分行个金客户经理考核办法.pdf')
# 提取文本和页码信息
text, page_numbers = extract_text_with_page_numbers(pdf_reader)
text
'百度文库 - 好好学习,天天向上 \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 '
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
提取的文本长度: 3881 个字符。
文本被分割成 10 个块。
已从文本块创建知识库...
向量数据库已保存到: ./vector_db
页码信息已保存到: ./vector_db\page_info.pkl
文本被分割成 10 个块。
已从文本块创建知识库...<langchain_community.vectorstores.faiss.FAISS at 0x1e4e100c650>
2.查询
2.1 方法一:OpenAI接口
# 设置查询问题
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}")
C:\Users\13010\AppData\Local\Temp\ipykernel_16536\3842879030.py:17: LangChainDeprecationWarning: This class is deprecated. See the following migration guides for replacements based on `chain_type`:
stuff: https://python.langchain.com/docs/versions/migrating_chains/stuff_docs_chain
map_reduce: https://python.langchain.com/docs/versions/migrating_chains/map_reduce_chain
refine: https://python.langchain.com/docs/versions/migrating_chains/refine_chain
map_rerank: https://python.langchain.com/docs/versions/migrating_chains/map_rerank_docs_chainSee also guides on retrieval and question-answering here: https://python.langchain.com/docs/how_to/#qa-with-ragchain = load_qa_chain(chatLLM, chain_type="stuff")查询已处理。成本: Tokens Used: 1255Prompt Tokens: 1201Prompt Tokens Cached: 0Completion Tokens: 54Reasoning Tokens: 0
Successful Requests: 1
Total Cost (USD): $0.0
根据提供的考核标准:在服务质量考核部分第2条明确规定:
"客户服务效率低,态度生硬或不及时为客户提供维护服务,有客户投诉的,每投诉一次扣2分。"因此,客户经理被投诉一次将被扣2分。
来源:
文本块页码: 1
2.2 方法二:Tongyi接口
from langchain_community.llms import Tongyi# 设置查询问题
# query = "客户经理被投诉了,投诉一次扣多少分?"
query = "客户经理每年评聘申报时间是怎样的?"
if query:# 示例:如何加载已保存的向量数据库# 注释掉以下代码以避免在当前运行中重复加载# 创建嵌入模型embeddings = DashScopeEmbeddings(model="text-embedding-v2")# 从磁盘加载向量数据库loaded_knowledgeBase = load_knowledge_base("./vector_db", embeddings)# 使用加载的知识库进行查询docs = loaded_knowledgeBase.similarity_search(query)# 初始化对话大模型DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY"),llm = Tongyi(model_name="deepseek-v3", dashscope_api_key=DASHSCOPE_API_KEY)# 加载问答链chain = load_qa_chain(llm, 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}")
向量数据库已从 ./vector_db 加载。
页码信息已加载。
查询已处理。成本: Tokens Used: 0Prompt Tokens: 0Prompt Tokens Cached: 0Completion Tokens: 0Reasoning Tokens: 0
Successful Requests: 1
Total Cost (USD): $0.0
客户经理每年评聘申报时间是**每年一月份**。
来源:
文本块页码: 1