基于langchain实现LLM基础RAG金融知识问答

本文详细介绍了如何基于LangChain框架,利用BM25算法构建RAG知识库问答系统,包括数据处理、模型组件搭建和实际应用示例,展示了如何通过大模型如LLM进行文本检索和回答生成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、基础RAG流程

1、首先加载需要的外部知识文本文件,利用合适的分割方式将文本划分为多个文本块,建立索引并构建外部文本知识库;

2、基于合适的检索算法来构建检索器组件,用于获取与用户查询最相关的文本块;

3、结合用户查询和相关的文本块生成大模型的Prompt提示,最终输入到大模型并返回答案结果。

二、LangChain框架简介

LangChain是一个集成的开发框架,用于驱动LLM进行应用程序的开发。具体详情自行阅读官方文档

三、利用langchain框架实现基础的RAG知识库问答

本案例是基于上市公司的年度报告数据,构建知识库文档,利用BM25算法从文档中检索查询相关的文本片段,嵌入到LLM提示中,引导LLM生成相应的回答。

1、数据获取和处理

数据的获取和预处理请查看本人之前发布的一篇文章,点击此链接即可跳转至相关页面。

2、导入相关的第三方库

import pandas as pd
import numpy as np
import re
import jieba

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.retrievers import BM25Retriever, TFIDFRetriever
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA

3、加载报告文档

text_path = './test_contents.txt'
loader = TextLoader(text_path, encoding='utf-8')
docs = loader.load()

 4、自定义文档分割组件,划分文档块

class ReportTextSplitter(CharacterTextSplitter):
    """自定义报告文本分割器"""
    def __init__(self, sentence_size: int = 250, **kwargs):
        super().__init__(**kwargs)
        self.sentence_size = sentence_size

    def split_text(self, text: str):
        split_rules = '\n第[一二三四五六七八九十]|\n[一二三四五六七八九十]|\n[123456789]、'
        ls = re.split(split_rules, text)
        for ele in ls:
            if len(ele) > self.sentence_size:
                ele1_ls = ele.split("\n([一二三四五六七八九十])|\n\([一二三四五六七八九十]\)|\n([123456789])")
                for ele_ele1 in ele1_ls:
                    if len(ele_ele1) > self.sentence_size:
                        l = list(range(0, len(ele_ele1), self.sentence_size))
                        r = list(range(self.sentence_size, len(ele_ele1)+self.sentence_size, self.sentence_size))
                        ele2_ls = [ele_ele1[l:r] for l, r in zip(l, r)]
                        ele_id = ele1_ls.index(ele_ele1)
                        ele1_ls = ele1_ls[:ele_id] + [i for i in ele2_ls if i] + ele1_ls[ele_id + 1:]
                idx = ls.index(ele)
                ls = ls[:idx] + [i for i in ele1_ls if i] + ls[idx + 1:]
        return ls


chunk_size = 500  # 设置文本块最大长度
splitter = ReportTextSplitter(sentence_size=chunk_size)
split_docs = splitter.split_documents(docs)

5、构建检索器组件

本方案利用BM25检索算法来查找相似的文本块内容,我们利用langchain框架提供的BM25Retriever检索组件来构建我们的检索器,代码如下:

def cut_words(text):
    """
    利用jieba库进行文本分词
    """
    return jieba.lcut(text)


# k是检索器返回的相关文本块数量,preprocess_func是文本的预处理函数
retriever = BM25Retriever.from_documents(split_docs, preprocess_func=cut_words, k=3)

6、定义ChatGLM大模型接口调用

如果你有OpenAI的API key,可以使用OpenAI的大模型;若没有,可以去ChatGLM大模型官网申请相应的大模型接口应用API key,新用户注册可以免费领取文本tokens数量使用。具体的申请操作流程自行查询相关文章介绍,此处不再赘述。
申请完成后,根据如下代码构建大模型接口调用:

import time
import jwt

def generate_token(apikey: str, exp_seconds: int):
    """
    动态生成token令牌,设置令牌有效时间
    """
    try:
        id, secret = apikey.split(".")
    except Exception as e:
        raise Exception("invalid apikey", e)

    payload = {
        "api_key": id,
        "exp": int(round(time.time() * 1000)) + exp_seconds * 1000,
        "timestamp": int(round(time.time() * 1000)),
    }

    return jwt.encode(
        payload,
        secret,
        algorithm="HS256",
        headers={"alg": "HS256", "sign_type": "SIGN"},
    )


token_key = "****************************"  # 此处设置自己申请的token key
llm = ChatOpenAI(
    model_name="glm-4",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4",
    openai_api_key=generate_token(token_key, exp_seconds=3000),
    streaming=False,
    temperature=0.01  # 此参数设置大模型的幻觉程度大小,一般知识库问答设置小一些,回答要准确
)

7、生成Prompt提示模板(可自行根据需求定义)

qa_sys_prompt = """你是文档问答系统高级专家。根据以下原则、步骤及示例,你可以通过在文档中查找相关内容并仿造示例进行输出。
原则:
1、输出一定在提供的文档内容中,且只能根据提供的文档内容来问答问题;
2、直接回答问题,不需要列出中间的思考过程,不需要拓展,不需要输出"根据提供的文档内容"等前缀语句;
3、同一个问题,文本中有不同的答案,需要综合起来回答;
4、问题答案在文档中是分点/条、系列措施等列举时,需要全部列出不要遗漏;
5、若无法在文档中找到相关内容,请直接回答文档中未提及相关的问题内容即可,不需要做任何其他补充,也不要出现"因此"等字符做解释。

步骤:
1、判断下面问题中是否有多个问题,若存在多个问题,将问题拆分出多个问题;
2、根据拆分出来的问题,精确定位文档中的内容,可能存在一个或多个位置,存在多个时需要都找出来;
3、根据定位出来的位置前后内容,回答每个问题;
4、优先根据文档原文来回答每个问题;若没有,再概况总结回答。

示例:
下面将提供示例文档内容、问题及输出,仿照样例输出
文档内容:(四)研发机构设置
本公司设科学委员会和研发中心负责产品研发。科学委员会由本公司首席科
学家及核心技术人员组成,主要负责研发战略的制定、研发项目立项评审、考核
与项目重大变更等。
问题:云南沃森生物技术股份有限公司负责产品研发的是什么部门?
输出:云南沃森生物技术股份有限公司负责产品研发的是科学委员会和研发中心部门。

现在开始,根据下面的文档内容,回答问题:
文档内容:{context}
问题:{question}
"""

qa_prompt = ChatPromptTemplate.from_template(qa_sys_prompt)

8、构建问答链,生成答案

基于langchain框架的RetrievalQA组件建立知识库文档问答链,最后调用生成答案。代码如下:

# return_source_documents参数表示是否需要输出检索到的文本块内容
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=retriever, return_source_documents=True, 
                                       chain_type_kwargs={"prompt": qa_prompt})

question = '公司2019年营业外收入是多少元?'
qa_chain.invoke(question)

 生成的结果如下图所示:

其中,query为用户输入的查询文本,result是模型最终的回答结果,source_documents则是检索到的文本块内容信息。

以上就是基础的RAG知识库问答实现代码,希望能够帮助到大家,后续会继续更新大模型RAG系列的相关技术文章。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值