使用Chainlit、Qdrant和Zephyr构建用于文档问答的大型语言模型应用程序

2023-11-23 14:51:26 浏览数 (1)

该博客介绍了一种利用Zephyr-7B Beta模型作为大型语言模型的应用,以及Langchain和Chainlit。在这里,我将调查它们各自的能力,并展示它们在开发交互式聊天应用程序中的潜力。我将概述用户界面(UI)的设计,后端处理的建立,以及创建一个完全可操作的问答应用程序所涉及的无缝集成过程。

逐步实施

•文档提交:通过上传PDF格式的必需文档开始,确保准确提交。•使用PyPDF2进行文本提取:利用PyPDF2工具从上传的PDF文档中有效地提取文本内容,确保提取的准确性。•使用HuggingFace Embeddings进行语义表示:采用OpenAIEmbeddings机制将提取的文本转换为向量化的嵌入,定量地将内容的语义本质封装起来。•在Qdrant中进行向量存储:将向量化的嵌入安全地存储在Qdrant中,建立一个有组织且可检索的语义表示库。•利用HuggingFace Embeddings进行查询向量化:使用OpenAIEmbeddings将用户查询转换为相应的向量嵌入,将查询的语义结构与向量化的领域对齐。•上下文文档检索:利用查询的向量化嵌入有效地检索具有上下文相似性的文档,同时收集相关元数据以丰富文档上下文。•重新排序上下文检索的结果:不直接返回检索到的文档,可以使用给定查询的上下文来压缩它们,以便只返回相关信息。•生成AI系统集成:将检索到的文档及其元数据传递给生成AI系统。触发先进的认知过程,根据文档中的上下文洞察力生成信息丰富的回答。•用户回应综合:通过将生成AI系统生成的综合答案回传给用户,以一个体贴且全面的回应形式恰当地回答原始查询中的问题。 这个实验展示了在创建一个复杂的问答应用程序中,实现Zephyr-7B-Beta和先进AI技术的实际应用,展示了提升用户互动和认知回应的潜力。

技术栈

•Qdrant: 存储文本嵌入向量的向量存储器。•BAAI/bge-small-en-v1.5: 提供高级语义分析的嵌入模型。•Langchain: 使用大型语言模型(LLM)进行应用开发的框架。•Chainlit: 界面构建器,方便创建类似ChatGPT的界面。•Zephyr-7B Beta: 作为应用核心组件的大型语言模型。•Cohere Re-Ranker: 通过重新排序检索到的上下文来提高相关性。利用Cohere的重新排序终端点,改善人机交互。•上下文压缩: 通过压缩和过滤基于查询上下文的文档来解决检索中的挑战。检索过程包括一个基本检索器和一个文档压缩器。 这种技术的融合为开发具有先进问答能力的交互式聊天应用提供了一个强大的框架。概述的步骤和技术栈共同为流畅高效的用户体验作出了贡献。

Zephyr-7B Beta 概览

Zephyr Beta 是一款经过精细训练的Mistral大型语言模型的第7B版本,使用了公开可访问的和合成的数据集进行训练。拥有70亿个参数的Zephyr经过在Mistral上的微调,产生了与Llama 2 70B Chat在各种基准测试(如ARC、HellaSwag、MMLU和TruthfulQA)中相当的结果。 这个模型不仅超越了它的更大的竞争对手,比如GPT-3.5 Turbo和Llama 70B,还在山羊基准测试中挑战了GPT-4的能力,同时保持了更加紧凑的形态,体积比GPT-3.5模型小25倍。 Zephyr-7B Beta和ChatGPT都共同致力于成为教育和研究的有价值助手。然而,它们在方法上存在差异。Zephyr-7B Beta展示了在广泛领域问题上的多样性,能从网页数据和技术资源中得出深入见解。相比之下,ChatGPT采用了稍微不同的训练方法,并利用了不同的数据集。 Zephyr-7B Beta的成功部分归功于直接提炼的精细调整。这包括根据选择的奖励和人工智能反馈来利用模型补全,从而使其更符合人类偏好。Zephyr-7B Beta的另一个显著特点是其分词模板的聊天模板,这有助于生成更精确的回答。

值得一提的是,Zephyr-7B Beta在现代CPU上表现出令人称赞的性能,提供满意的结果。

Chainlit概述

Chainlit,一个开源的Python软件包,通过无缝集成业务逻辑和数据加快ChatGPT类应用的开发。

主要特点包括:

•Swift构建:在几分钟内轻松集成到现有的代码库中,或者从头开始进行开发。•数据连续性:利用用户生成的数据和反馈来提高应用程序的性能。•可视化复杂推理:通过快速概览了解导致特定结果的中间步骤。•提示优化:深入探究提示,以在Prompt Playground中识别问题并进行改进。

可用的集成:

•Langchain•Haystack•LLama-Index•Langflow 在这里所示的RAG实现中,将使用Langchain框架。

解释Langchain框架

Langchain是一个免费可用的框架,简化了利用大型语言模型(LLM)开发应用程序的过程。它提供以下功能:

•标准化接口:提供了一个标准化接口,便于与各种工具进行无缝集成。•完整链路:提供了针对常见应用定制的完整链路,使AI开发人员能够利用GPT-4等LLM的强大能力,并与外部的计算资源和数据进行整合。•跨语言支持:附带了适用于Python和JavaScript的包。

向量存储器一解析

定义

矢量存储是专为高效存储和检索矢量嵌入的特殊数据库。这种专门化是至关重要的,因为传统的SQL等数据库在处理大量矢量数据方面并不精细调整。

嵌入的作用

嵌入表示数据,通常是非结构化数据,如文本,在高维空间中以数值向量格式呈现。传统的关系型数据库不适合存储和检索这些矢量表示。

矢量存储的关键特点

•高效索引:向量存储可以使用相似性算法索引和快速搜索相似的向量。•增强的检索功能:该功能允许应用程序根据提供的目标向量查询识别相关向量。

Qdrant 概述

Qdrant[1]是一个专业的矢量相似度搜索引擎,旨在通过用户友好的API提供一种供生产使用的服务。它便于存储、搜索和管理点(向量),以及附加的有效载荷。这些有效载荷作为补充信息,提高搜索的精确性,并向用户提供有价值的数据。 使用Qdrant非常简单。您可以使用Python qdrant-client,访问最新的Qdrant Docker镜像并建立本地连接,或者在准备好进行全面过渡之前,探索Qdrant的云免费套餐选项。

Qdrant高级架构

理解语义相似性

在一组文档或术语的背景下,语义相似性是一种度量指标,通过比较其含义或语义内容的相似性来衡量项目之间的距离,而不是依靠词汇的相似性。这涉及使用数学工具来评估语言单元、概念或实例之间语义关系的强度。通过这个过程获得的数值描述结果是通过比较支持它们含义或描述它们性质的信息而得到的。 很关键的是要区分语义相似性和语义相关性。语义相关性包括两个术语之间的任何关系,而语义相似性则特指「是一个」的关系。这一区别澄清了语义比较的微妙性质以及它们在各种语言和概念环境中的应用。

重新排序是什么

技术搜索过程中的重新排序涉及到一个名为 RAG 的两阶段过程:

第一阶段 — 关键词搜索

第二阶段 — 基于语义的前 K 项检索 在传统语义搜索中,采用了两步骤的方法。首先,检索机制对一组文档进行大致扫描,创建一个初步的文档列表。随后,重新排序机制将该候选文档列表重新组织。重新排序的过程通过根据特定参数重新组织结果来提高模型性能。

为什么重新排序是必要的? 大型语言模型(LLMs)的召回性能往往随着更多上下文的添加而降低,导致扩展性上下文窗口,或称为“上下文填充”。重新排名的基本概念是将文档的总数量简化为固定数量。重新排名优先考虑并重新组织记录,将最相关的项目置于顶部,然后发送给LLM。通过识别可能不在前三个结果中的记录并将它们合并成较小的集合,进而提供给LLM,重新排名证明了其无价之处。

介绍:Cohere 重新排序器

Cohere 是一家加拿大初创公司,专注于自然语言处理模型,目的是改善人机交互。他们在检索器中提供了一个重新排序的端点。这是在上下文压缩检索器中提出的概念基础上构建的。

理解上下文压缩

文档检索中的一个挑战是在数据摄入过程中,文档存储系统可能会遇到特定查询的不确定性。这往往导致相关信息被埋在包含无关文本的文档中。通过应用程序传输整个文档可能会导致LLM调用的成本增加和次优响应。

上下文压缩解决了这个问题。其核心思想是不立即将检索到的文档原样返回,而是使用给定查询的上下文对其进行压缩。这确保只传递相关信息。 在这个上下文中,“压缩”一词既指压缩各个文档的内容,也指选择性地将文档整体过滤掉。

上下文压缩的步骤

要使用上下文压缩检索器,需要以下组件:

•一个基本检索器。•一个文档压缩器。

该过程涉及以下步骤:

•“上下文压缩检索器将查询转发给基本检索器。•然后,它将初始文档提交给文档压缩器。•文档压缩器通过减少文档内容或完全删除它们来缩短文档列表。

安装所需依赖

pip install langchain qdrant-client huggingface_hub sentence-transformers PyPDF2 cohere chainlit pip install rank_bm25 pip install llama-cpp-python

创建项目文件夹

mkdir chainlit_rag

在文件夹内创建.env文件,并将以下内容添加进去

[cohere] api_key = 您的 COHERE API KEY1

您可以在以下网址注册自己并获取API密钥:https://dashboard.cohere.com/api-keys

下载模型参数文件

从以下链接下载模型文件并保存到当前工作目录中(即chainlit_rag文件夹):

https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF/blob/main/zephyr-7b-beta.Q4_K_M.gguf

代码实现

此代码使用Python实现,它是一个基于上传的PDF文档的内容进行处理和回答用户问题的系统的一部分。 文本:"它使用各种自然语言处理和检索技术。让我们分解一下关键组成部分和功能:

库和导入

•代码导入了几个库,如Langchain模块,Chainlit,PyPDF2,BytesIO,os和ConfigParser。•它使用这些库中的类和函数进行自然语言处理、文档检索和系统配置。

Cohere API密钥配置

•代码从配置文件(.env)中读取Cohere API密钥,并将其设置为环境变量。

文本分块和处理

文本:「- 使用指定参数的RecursiveCharacterTextSplitter将PDF文件中的文本拆分为块。

•代码然后处理PDF文本,为每个块创建元数据,并将其存储在Chroma向量存储中。

语言模型和检索器

•代码使用HuggingFaceBgeEmbeddings的语言模型对文本进行嵌入。•检索模型如BM25Retriever和Ensemble Retriever被用于根据用户查询检索相关文档。•Cohere Rerank组件用于重新排列检索到的文档,以提高响应质量。」

初始化和用户交互

•系统提示用户上传PDF文件,并在接收到文件后进行处理。•它设置了各种组件,包括语言模型、检索器和压缩器。•系统通知用户文档已被处理,并且他们现在可以提出问题了。

消息模板和系统初始化

•系统使用消息模板来进行系统提示和人工消息。•系统消息模板指导用户如何回答问题和请求,并在响应中返回来源信息。

WebSocket事件处理程序

•代码包括用于处理WebSocket事件的装饰器(@cl.on_chat_start和@cl.on_message)。•用户连接后,系统会初始化并等待PDF文件上传。•代码会根据用户的消息作出反应,使用定义的组件链进行处理,并将响应发送回用户。

用户会话管理

•代码利用Chainlit cl.user_session来存储和检索用户特定的数据。•它将元数据、文本和处理链存储在用户会话中以供以后使用。

最终答案呈现

•系统检索并呈现用户问题的最终答案,以及相关的源文件。•当最终答案是流式传输时,它会相应地更新用户界面。

总体来说,这段代码在处理PDF文档、检索相关信息,并通过websocket接口提供周到的用户查询答案方面起着复杂系统的编排作用。

示例代码

在chainlit_rag文件夹中创建app.py文件,并粘贴下面所示的代码。

导入所需依赖

代码语言:javascript复制

#import required libraries
from langchain.embeddings import HuggingFaceEmbeddings,HuggingFaceBgeEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Qdrant
from langchain.chains import RetrievalQAWithSourcesChain,RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import (ChatPromptTemplate,
 SystemMessagePromptTemplate,
 HumanMessagePromptTemplate)
from langchain.llms import CTransformers
from langchain.llms import LlamaCpp
from langchain.retrievers import BM25Retriever,EnsembleRetriever
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CohereRerank
from cohere import Client

#

import chainlit as cl
import PyPDF2
from io import BytesIO
from getpass import getpass

#

import os
from configparser import ConfigParser
env_config = ConfigParser()

从.env文件中获取Cohere API密钥

代码语言:javascript复制
# Retrieve the cohere api key from the environmental variables

def read_config(parser: ConfigParser, location: str) -> None:
 assert parser.read(location), f"Could not read config {location}"

#

CONFIG_FILE = os.path.join(".", ".env")
read_config(env_config, CONFIG_FILE)
api_key = env_config.get("cohere", "api_key").strip()
os.environ["COHERE_API_KEY"] = api_key

使用重叠方式将获取的文本划分为较小的片段

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000,chunk_overlap=100)

创建一个系统模板以指导LLM

代码语言:javascript复制
system_template = """Use the following pieces of context to answer the user's question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
ALWAYS return a "SOURCES" part in your answer.
The "SOURCES" part should be a reference to the source of the document from which you got your answer.

Begin!

---

{summaries}"""
messages = [SystemMessagePromptTemplate.from_template(system_template),HumanMessagePromptTemplate.from_template("{question}"),]
prompt = ChatPromptTemplate.from_messages(messages)
chain_type_kwargs = {"prompt": prompt}

Chainlit天生支持异步操作,使代理能够同时执行任务,促进多个用户与单个应用程序进行交互。这种异步编程的方法是一种有效的管理多个任务的方法,允许程序在没有中断或阻塞的情况下运行。

“async”和“await”关键字用于在Python中定义和处理异步代码。异步函数是一种协程,它是一种特殊类型的函数,可以暂停其执行并在后续恢复,同时允许其他任务运行。

Chainlit使用异步编程来高效处理事件和任务。在创建Chainlit代理时,我们经常需要定义异步函数来处理事件和执行操作。

辅助函数,用于在用户聊天会话开始时初始化任务定义。

代码语言:javascript复制
#Decorator to react to the user websocket connection event.
@cl.on_chat_start
async def init():
 files = None

等待用户上传pdf

代码语言:javascript复制
while files is None:
 files = await cl.AskFileMessage(
 content="Please upload a PDF file to begin!",
 accept=["application/pdf"],
 ).send()
 file = files[0]
 msg = cl.Message(content=f"Processing `{file.name}`…")
 await msg.send()

读取pdf

代码语言:javascript复制
pdf_stream = BytesIO(file.content)
 pdf = PyPDF2.PdfReader(pdf_stream)
 pdf_text = ""
 for page in pdf.pages:
 pdf_text  = page.extract_text()

文本切块

代码语言:javascript复制
texts = text_splitter.split_text(pdf_text)

每块增加元数据

代码语言:javascript复制
metadatas = [{"source": f"{i}-pl"} for i in range(len(texts))]

创建chroma向量存储

代码语言:javascript复制
model_id = "BAAI/bge-small-en-v1.5"
 embeddings = HuggingFaceBgeEmbeddings(model_name= model_id,
 model_kwargs = {"device":"cpu"})

#

bm25_retriever = BM25Retriever.from_texts(texts)
 bm25_retriever.k=5

将embedding存储在用户会话中

代码语言:javascript复制
cl.user_session.set("embeddings", embeddings)
 docsearch = await cl.make_async(Qdrant.from_texts)(
 texts, embeddings,location=":memory:", metadatas=metadatas
 )
 llm = LlamaCpp(streaming=True,
 model_path="zephyr-7b-beta.Q4_K_M.gguf",
 max_tokens = 1500,
 temperature=0.75,
 top_p=1,
 gpu_layers=0,
 stream=True,
 verbose=True,n_threads = int(os.cpu_count()/2),
 n_ctx=4096)
 #Hybrid Search
 qdrant_retriever = docsearch.as_retriever(search_kwargs={"k":5})
 ensemble_retriever = EnsembleRetriever(retrievers=[bm25_retriever,qdrant_retriever],
 weights=[0.5,0.5])
 #Cohere Reranker

#

compressor = CohereRerank(client=Client(api_key=os.getenv("COHERE_API_KEY")),user_agent='langchain')

#

compression_retriever = ContextualCompressionRetriever(base_compressor=compressor,
 base_retriever=ensemble_retriever,
 )

创建一个使用chroma向量存储的chain

代码语言:javascript复制
chain = RetrievalQA.from_chain_type(
 llm = llm,
 chain_type="stuff",
 retriever=compression_retriever,
 return_source_documents=True,
 )

把元数据和内容存储在用户会话中

代码语言:javascript复制
cl.user_session.set("metadatas", metadatas)
 cl.user_session.set("texts", texts)

提醒用户系统已经准备好

代码语言:javascript复制
msg.content = f"`{file.name}` processed. You can now ask questions!"
 await msg.update()
 #store the chain as long as the user session is active
 cl.user_session.set("chain", chain)

用于响应来自UI的消息的辅助函数

代码语言:javascript复制
cl.on_message
async def process_response(res):
 # retrieve the retrieval chain initialized for the current session
 chain = cl.user_session.get("chain")
 # Chinlit callback handler
 cb = cl.AsyncLangchainCallbackHandler(
 stream_final_answer=True, answer_prefix_tokens=["FINAL", "ANSWER"])
 cb.answer_reached = True
 print("in retrieval QA")
 res = await chain.acall(res, callbacks=[cb])
 print(f"response: {res}")
 answer = res["result"]
 sources = res["source_documents"]
 source_elements = []

从用户会话中获取文本内容和元数据

代码语言:javascript复制
metadatas = cl.user_session.get("metadatas")
all_sources = [m["source"] for m in metadatas]
texts = cl.user_session.get("texts")

if sources:
    found_sources = []

    # Add the sources to the message
    for source in sources:
        print(source.metadata)
        try :
            source_name = source.metadata["source"]
        except :
            source_name = ""
        # Get the index of the source
        text = source.page_content
        found_sources.append(source_name)
        # Create the text element referenced in the message
        source_elements.append(cl.Text(content=text, name=source_name))

    if found_sources:
        answer  = f"nSources: {', '.join(found_sources)}"
    else:
        answer  = "nNo sources found"

if cb.has_streamed_final_answer:
    cb.final_stream.elements = source_elements
    await cb.final_stream.update()
else:
    await cl.Message(content=answer, elements=source_elements).send()

执行 Chainlit 脚本

请执行下面的指令来执行chainlit应用。

chainlit run app.py

Chainlit 用户接口快照

快照 1

系统运行日志

in retrieval QA Batches: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 41.66it/s] response: {'query': 'What is the social stigma associated with Mental Health?', 'result': " Social stigma refers to the negative attitudes, beliefs, and behaviors that society assigns to individuals with mental illness. It can lead to exclusion, prejudice, and discrimination, which in turn can affect an individual's self-esteem, social relationships, and access to opportunities and resources. Stigma is a worldwide phenomenon and continues to be a significant barrier for people seeking help for their mental health concerns. Themes associated with mental illness and stigma include beliefs that individuals with mental illness are dangerous, unpredictable, to blame for their condition, difficult to communicate with, and have poor treatment outcomes. This can lead to personal fear in others, exclusion, prejudice, and discrimination against those with mental illness.", 'source_documents': ...... {'source': '19-pl', 'relevance_score': 0.9789956} {'source': '28-pl', 'relevance_score': 0.97631055} {'source': '22-pl', 'relevance_score': 0.9651191}

快照 2

系统运行日志

llama_print_timings: load time = 1093.50 ms llama_print_timings: sample time = 37.26 ms / 130 runs ( 0.29 ms per token, 3488.81 tokens per second) llama_print_timings: prompt eval time = 94385.15 ms / 808 tokens ( 116.81 ms per token, 8.56 tokens per second) llama_print_timings: eval time = 27183.79 ms / 130 runs ( 209.11 ms per token, 4.78 tokens per second) llama_print_timings: total time = 122202.95 ms response: {'query': 'What are the Themes associated with mental illness and stigma identifiednby adolescents?', 'result': ' According to Box 1.2 in the text, some themes associated with mental illness and stigma identified by adolescents include negative attitudes toward groups described as deviant, such as those with mental health problems, increasing with age; derogatory labels used to describe people with mental health problems or mental illness, such as "retarded," "psycho(path)," "spastic," "mental," "crazy," and "nutter"; stress, genetics, and bad childhood experiences as frequently cited causes of mental illness; and negative attitudes and reactions from other people, including professionals, towards individuals with mental health problems.', 'source_documents': ....... {'source': '28-pl', 'relevance_score': 0.9996049} {'source': '27-pl', 'relevance_score': 0.99843913} {'source': '30-pl', 'relevance_score': 0.9777138}

声明

本文由山行翻译整理自:https://nayakpplaban.medium.com/building-an-llm-application-for-document-q-a-using-chainlit-qdrant-and-zephyr-7efca1965baa,主要用于技术分享与学习使用,感兴趣的请点赞、收藏、关注。

引用

•https://docs.chainlit.io/get-started/overview•https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF•https://qdrant.tech/documentation/•https://github.com/ggerganov/llama.cpp•https://python.langchain.com/docs/get_started/introduction•https://huggingface.co/BAAI/bge-small-en-v1.5

References

[1] Qdrant: https://qdrant.tech/documentation

0 人点赞