【Langchain大语言模型开发教程】基于Langchain的私人助手

终于学习完了Langchain框架的核心内容,最后基于langchain技术实现一个个人知识库助手的小项目,将这些内容串联起来,在实际中进行应用。

工具清单:

1、langchain框架

2、chroma向量数据库

3、embedding模型(bge-large-zh-v1.5、bge-large-en-v1.5)

4、rerank模型(bge-reranker-v2-m3)

5、gradio前端框架

6、LLM(GLM4系列模型)

1、数据读取与数据库构建

我们使用langchain提供的文本加载器来进行各种类型数据的加载;

def file_loader(file, loaders):
    if isinstance(file, tempfile._TemporaryFileWrapper):
        file = file.name
    if not os.path.isfile(file):
        [file_loader(os.path.join(file, f), loaders) for f in os.listdir(file)]
        return
    file_type = file.split('.')[-1]
    if file_type == 'pdf':
        loaders.append(PyMuPDFLoader(file))
    elif file_type == 'md':
        pattern = r"不存在|风控"
        match = re.search(pattern, file)
        if not match:
            loaders.append(UnstructuredMarkdownLoader(file))
    elif file_type == "txt":
        loaders.append(TextLoader(file))
    elif file_type == "docx":
        loaders.append(Docx2txtLoader(file))
    return

得到数据后,由于Token上下文的限制,LLM不能一次将整个文档全都读入,我们需要将文档进行切分得到chunk;langchain为我们提供了多种文本分割器,每个文本分割器都根据 chunk_size (块大小)和 chunk_overlap (块与块之间的重叠大小)进行分割;

  • chunk_size 指每个块包含的字符或 Token的数量;
  • chunk_overlap 指两个块之间共享的字符数量,用于保持上下文的连贯性,避免分割丢失上下文信息;

 这里我们选择使用RecursiveCharacterTextSplitter,按字符串分割文本,递归地尝试按不同的分隔符进行分割文本;

我们得到分割后的文本后,我们就可以将数据存储到数据库中了,所有我们这里就需要一个embedding模型,embedding可以将文本映射到一个高维的向量空间,实现一个文本到数据的映射,而相似的文本编码后得到的向量就会相近;

最后我们初始化数据库,并将数据可持久化到硬盘,方便我们后续直接加载;

def create_db(files=DEFAULT_DB_PATH, persist_directory=DEFAULT_PERSIST_PATH, embeddings="bge-large-zh-v1.5"):

    if files == None:
        return "can't load empty file"
    if type(files) != list:
        files = [files]
    loaders = []
    [file_loader(file, loaders) for file in files]
    docs = []
    for loader in loaders:
        if loader is not None:
            docs.extend(loader.load())
    # 切分文档
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=150)
    split_docs = text_splitter.split_documents(docs)

    print(len(split_docs))
    if type(embeddings) == str:
        embeddings = get_embedding(embedding=embeddings)
    # 加载数据库
    vectordb = Chroma.from_documents(
        documents=split_docs,
        embedding=embeddings,
        persist_directory=persist_directory  # 允许我们将persist_directory目录保存到磁盘上
    )

    vectordb.persist()
    return vectordb

2、 构建RAG问答链

由了刚刚构建的数据库,我们这里直接将其接入到问答流程;

这里我们需要传入一些必要的参数,然后加载一下我们的向量数据库,并根据该向量数据库构建一个检索器,作为我们的基本检索器,我们使用rerank模型将检索到的chunk进行进一步的精排,从而提升查询结果相关性;

    def __init__(self, model: str, temperature: float = 0.0, top_k: int = 4, api_key: str = None, file_path: str = None,
                 persist_path: str = None, embedding=None, template=default_template_rq):

        print(persist_path)

        self.model = model
        self.temperature = temperature
        self.top_k = top_k
        self.file_path = file_path
        self.persist_path = persist_path
        self.api_key = api_key
        self.embedding = embedding
        self.template = template
        self.vectordb = get_vectordb(self.file_path, self.persist_path, self.embedding)

        self.prompt = PromptTemplate(input_variables=["context", "question"], template=self.template)

        self.retriever = self.vectordb.as_retriever(search_type="similarity",
                                                    search_kwargs={'k': 10})

        self.reranker = HuggingFaceCrossEncoder(model_name="../embedding/BAAI/bge-reranker-v2-m3",
                                                model_kwargs={'device': 'cuda'})

        print(f"向量库中存储的数量:{self.vectordb._collection.count()}")

接下来就是我们的问答环节,用户发出一个问题,我们根据用户指定的一些参数(temperature、top-k等)来初始化我们的大语言模型和重排器,我们使用langchain框架提供的RetrievalQA链来构建我们的问答链;

    def answer(self, question: str = None, temperature=None, top_k=4):
        """"
        核心方法,调用问答链
        arguments: 
        - question:用户提问
        """

        if len(question) == 0:
            return ""

        if temperature is None:
            temperature = self.temperature

        if top_k is None:
            top_k = self.top_k

        llm = model_to_llm(self.model, temperature, self.api_key)

        compressor = CrossEncoderReranker(model=self.reranker, top_n=top_k)

        compression_retriever = ContextualCompressionRetriever(base_compressor=compressor,
                                                               base_retriever=self.retriever)
        # 自定义 QA 链
        qa_chain = RetrievalQA.from_chain_type(llm=llm,
                                               retriever=compression_retriever,
                                               return_source_documents=True,
                                               chain_type_kwargs={"prompt": self.prompt})

        result = qa_chain({"query": question})
        answer = result["result"]
        source_documents = result["source_documents"]
        answer = re.sub(r"\\n", '<br/>', answer)
        return answer, source_documents

 以上就是我们问答助手的整个流程,接下来我们可以使用gradio框架快速的搭建起我们的交互界面;

我们问一个问题:

如何保持持续行动(刻意学习中的一个内容)

 

这是我们在数据库中检索到的内容: 

[Document(page_content='些工作量。平时要更加严格地要求自己了。我有时候时间充裕,一天会多\n写两篇文章,即使某天真的安排不出时间,也全然不会有“完蛋,我中断\n了”的感觉。树挪死,人挪活,持续行动的核心在于持续稳定。\n成长会里有位朋友在某知名通信企业任职,有一次谈到加班,她\n说:“很多公司的加班是今天做昨天的事情,或者今天做今天还没完成的事\n情,反正加班是因为做不完事情,而我们的加班是因为今天要把明天的事\n情做完,这个月把下个月的事情做完,所以能够永远赶在竞争对手前面。”\n另外一个例子是,在成长会的一些练习小组里每天都会有任务。往往\n出现这样一种情况:有一些成员从周一到周五,没有按时完成对应的任\n务,于是拖到周六,用一天的时间把所有作业都补完。很多人会觉得这好\n像挺正常,平常都做不完,周末再补,其实这是大部分人碌碌无为的原\n因。\n换一个场景你可能就会觉得这很荒谬,我们平常吃饭,每天每餐都要\n吃,到了点要吃,饿了就要吃。你会不会说我从周一到周五不吃饭,我周\n六一天把前面五天的饭全吃了?你没法五天不吃饭,你也无法一天补吃原\n来五天的饭。做事情就像吃饭一样,每天都要吃饭,于是这会变成一个常\n规项目。持续行动也应该如此。', metadata={'author': 'Scalers', 'creationDate': "D:20201006233519+00'00'", 'creator': 'calibre 3.39.1 [https://calibre-ebook.com]', 'file_path': '../knowledge_db/刻意学习.pdf', 'format': 'PDF 1.4', 'keywords': '', 'modDate': '', 'page': 91, 'producer': 'calibre 3.39.1 [https://calibre-ebook.com]', 'source': '../knowledge_db/刻意学习.pdf', 'subject': '', 'title': '刻意学习', 'total_pages': 235, 'trapped': ''}), Document(page_content='持续的事情,或者你也就走出了两三步,就迫不及待地想分享一些“牛×的\n方法”“秘籍成果”。\n这就像传销一样。一个人做了一点点事情,先把自己感动了,然后开\n始把这种感动转换成文字,营造了一个“感动场”,营造因为自己感动而产\n生的幻象。然后这个文字传播出去,立即引发一群人的感动和对牛×的憧\n憬,于是这群人在什么都没有做的情况下,便开始了新一轮的情绪高潮,\n高潮过后继续传播……于是这种氛围从公众号蔓延到社群……但是一轮一\n轮下来,你还是什么都没有做,那只是一群人产生了一些精神垃圾,然后\n找到另一群人接盘而已。这就跟开着印钞机印钞票,增发货币,是一个道\n理。\n当然,在持续行动中,我们需要正向反馈。每到一个关键节点,我们\n停下来整理整理思绪,甚至给自己一个小小的庆贺,这的确是有必要的。\n因为本质上这是一种正向反馈,有利于形成正循环。所以你看我过去每逢\n一百天都会有那么一篇总结文章,但是你也能看出越到后面的文章,我的\n心态越平和。\n如果我们在持续行动和持续进步,如果我们是在持续成长,那我们现\n在所取得的成绩,在未来都不值一提。如果我们每天都是在怀念自己以前', metadata={'author': 'Scalers', 'creationDate': "D:20201006233519+00'00'", 'creator': 'calibre 3.39.1 [https://calibre-ebook.com]', 'file_path': '../knowledge_db/刻意学习.pdf', 'format': 'PDF 1.4', 'keywords': '', 'modDate': '', 'page': 53, 'producer': 'calibre 3.39.1 [https://calibre-ebook.com]', 'source': '../knowledge_db/刻意学习.pdf', 'subject': '', 'title': '刻意学习', 'total_pages': 235, 'trapped': ''}), Document(page_content='因。\n换一个场景你可能就会觉得这很荒谬,我们平常吃饭,每天每餐都要\n吃,到了点要吃,饿了就要吃。你会不会说我从周一到周五不吃饭,我周\n六一天把前面五天的饭全吃了?你没法五天不吃饭,你也无法一天补吃原\n来五天的饭。做事情就像吃饭一样,每天都要吃饭,于是这会变成一个常\n规项目。持续行动也应该如此。\n在我刚开始写作的时候,总会担心万一有什么不可抗拒的事情,让我\n中断了写作,于是纠结不已。后面我发现这只是无谓的担心。生活中有很\n多人总是在为没有发生的事情担心,但也仅仅是担心却不做预案。以前我\n也是如此,后来才发现,不管发生什么,总是可以想办法在一天的繁忙中\n找出时间来做事情。\n白天再忙,总要起床,起床后的一段时间不会有人打扰你,于是你可\n以再起早一些,就有了属于自己的时间了;睡觉之前也有不被打扰的时\n间,这个时间也可以用来做事,只是你需要有足够的体力让自己不会感到\n困乏,于是体育锻炼的重要性就显现出来了。\n这么一来,我总能找到一些时间干自己的事情,于是就更加安心地持\n续行动了。在我持续写作的时间里,有人和我说自己想多做一些事情,却', metadata={'author': 'Scalers', 'creationDate': "D:20201006233519+00'00'", 'creator': 'calibre 3.39.1 [https://calibre-ebook.com]', 'file_path': '../knowledge_db/刻意学习.pdf', 'format': 'PDF 1.4', 'keywords': '', 'modDate': '', 'page': 91, 'producer': 'calibre 3.39.1 [https://calibre-ebook.com]', 'source': '../knowledge_db/刻意学习.pdf', 'subject': '', 'title': '刻意学习', 'total_pages': 235, 'trapped': ''})]

这是我们调用大语言模型后的答案:

保持持续行动需要:1. 严格自我要求,确保每天稳定完成任务,避免堆积工作。2. 提前规划,像吃饭一样将行动变成日常习惯,不断前行。3. 正向反馈,适时庆祝关键节点,形成正循环。谢谢你的提问! 

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是饿梦啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值