用langChang构建智能回答系统
课程传送:LangChain 实战课 - 掘金小册 - 掘金小册 (juejin.cn)
项目及实现框架
项目:“易速鲜花”内部员工知识问答系统
项目简介:
该系统替代内部员工学习手册,替代了冗余的文档,将零碎的信息集中起来。该系统可以基于最新的员工手册回答员工的各种疑问。
开发框架:
下图描述了LangChain框架实现知识文档系统的整体框架。
数据源(Data Sources):数据可以有多种,包括PDF的非结构化数据,SQL中的结构化数据,Python,java代码。
大模型应用(Application,即LLM App):以大模型为逻辑引擎,生成我们所需要的回答。
用例:(Use-Cases):大模型生成的回答可以构建出QA/聊天机器人等系统。
核心实现机制:数据处理管道
具体流程分为下面5步。
- Loading:文档加载器把Documents 加载为以LangChain能够读取的形式。
- Splitting:文本分割器把Documents 切分为指定大小的分割,我把它们称为“文档块”或者“文档片”。
- Storage:将上一步中分割好的“文档块”以“嵌入”(Embedding)的形式存储到向量数据库(Vector DB)中,形成一个个的“嵌入片”。
- Retrieval:应用程序从存储中检索分割后的文档(例如通过比较余弦相似度,找到与输入问题类似的嵌入片)。
- Output:把问题和相似的嵌入片传递给语言模型(LLM),使用包含问题和检索到的分割的提示生成答案。
数据的准备和载入
在AI练中学中PDF,word,和txt格式的各种文件和具体代码已经给出,方便我们直接运行。代码如下:
import os
os.environ["OPENAI_API_KEY"] = '你的Open AI API Key'
# 1.Load 导入Document Loaders
from langchain.document_loaders import PyPDFLoader
from langchain.document_loaders import Docx2txtLoader
from langchain.document_loaders import TextLoader
# 加载Documents
base_dir = '.\OneFlower' # 文档的存放目录
documents = []
for file in os.listdir(base_dir):
# 构建完整的文件路径
file_path = os.path.join(base_dir, file)
if file.endswith('.pdf'):
loader = PyPDFLoader(file_path)
documents.extend(loader.load())
elif file.endswith('.docx'):
loader = Docx2txtLoader(file_path)
documents.extend(loader.load())
elif file.endswith('.txt'):
loader = TextLoader(file_path)
documents.extend(loader.load())
我们需要利用OpenAI的两种不同模型做以下两件事:
- 用OpenAI的Embedding模型为文档做嵌入。
- 调用OpenAI的GPT模型来生成问答系统中的回答。
文本的分割
将加载的文本分割成更小的块,以便进行嵌入和储存。 具体代码
# 2.Split 将Documents切分成块以便后续进行嵌入和向量存储
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)
chunked_documents = text_splitter.split_documents(documents)
向量数据库储存
下面引用老师关于嵌入的介绍
词嵌入(Word Embedding)是自然语言处理和机器学习中的一个概念,它将文字或词语转换为一系列数字,通常是一个向量。简单地说,词嵌入就是一个为每个词分配的数字列表。这些数字不是随机的,而是捕获了这个词的含义和它在文本中的上下文。因此,语义上相似或相关的词在这个数字空间中会比较接近。 举个例子,通过某种词嵌入技术,我们可能会得到: "国王" -> [1.2, 0.5, 3.1, ...] "皇帝" -> [1.3, 0.6, 2.9, ...] "苹果" -> [0.9, -1.2, 0.3, ...] 从这些向量中,我们可以看到“国王”和“皇帝”这两个词的向量在某种程度上是相似的,而与“苹果”这个词相比,它们的向量则相差很大,因为这两个概念在语义上是不同的。 词嵌入的优点是,它提供了一种将文本数据转化为计算机可以理解和处理的形式,同时保留了词语之间的语义关系。这在许多自然语言处理任务中都是非常有用的,比如文本分类、机器翻译和情感分析等。
向量数据库
向量数据库,也称为矢量数据库或者向量搜索引擎,是一种专门用于存储和搜索向量形式的数据的数据库。在众多的机器学习和人工智能应用中,尤其是自然语言处理和图像识别这类涉及大量非结构化数据的领域,将数据转化为高维度的向量是常见的处理方式。这些向量可能拥有数百甚至数千个维度,是对复杂的非结构化数据如文本、图像的一种数学表述,从而使这些数据能被机器理解和处理。然而,传统的关系型数据库在存储和查询如此高维度和复杂性的向量数据时,往往面临着效率和性能的问题。因此,向量数据库被设计出来以解决这一问题,它具备高效存储和处理高维向量数据的能力,从而更好地支持涉及非结构化数据处理的人工智能应用。
信息提取与向量数据库
-
向量数据库:将内部文档存储为向量,以便进行信息检索。
-
信息提取:通过将问题转换为向量,并与数据库中的向量比较,提取最相关信息。
-
向量比较方法:
- 欧氏距离:测量向量间直线距离,适用于关心大小差异的场景。
- 余弦相似度:测量向量间方向相似性,适用于文本等语义比较。
选择相似度度量标准
- 欧氏距离:适用于数据大小差异重要的场景,如物品推荐系统。
- 余弦相似度:适用于文本数据和高维稀疏数据,如信息检索和文本分类。
问答系统构建
- 目标:建立问答系统,从语义上理解和比较问题的答案。
- 推荐方法:使用余弦相似度作为度量标准,适合文本数据的语义匹配。
RetrievalQA 链的实现
-
LLM(Large Language Model) :负责回答问题。
-
Retriever:根据问题检索相关文档,找到具体的“嵌入片”。
-
代码实现:
python import logging from langchain.chat_models import ChatOpenAI from langchain.retrievers.multi_query import MultiQueryRetriever from langchain.chains import RetrievalQA logging.basicConfig() logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO) llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vectorstore.as_retriever(), llm=llm) qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever_from_llm)
问答系统的UI实现
-
Flask应用:接收用户问题,生成答案,并通过网页展示。
-
代码实现:
python from flask import Flask, request, render_template app = Flask(__name__) @app.route('/', methods=['GET', 'POST']) def home(): if request.method == 'POST': question = request.form.get('question') result = qa_chain({"query": question}) return render_template('index.html', result=result) return render_template('index.html') if __name__ == "__main__": app.run(host='0.0.0.0', debug=True, port=5000) -
HTML关键代码:
html复制 <body> <div class="container"> <div class="header"> <h1>易速鲜花内部问答系统</h1> <img src="{{ url_for('static', filename='flower.png') }}" alt="flower logo" width="200"> </div> <form method="POST"> <label for="question">Enter your question:</label><br> <input type="text" id="question" name="question"><br> <input type="submit" value="Submit"> </form> {% if result is defined %} <h2>Answer</h2> <p>{{ result.result }}</p> {% endif %} </div> </body>
总结
我们先把本地知识切片后做Embedding,存储到向量数据库中,然后把用户的输入和从向量数据库中检索到的本地知识传递给大模型,最终生成所想要的回答。