LangChain实战课—“易速鲜花”本地部署
此项目为初步了解LangChian作为一个基于大语言模型的应用开发框架,其功能的强大。用于深入学习LangChain(包括每一个组件)之前。
项目及实现框架
项目名称:“易速鲜花”内部员工知识库问答系统。
项目介绍:“易速鲜花”作为一个大型在线鲜花销售平台,有自己的业务流程和规范,也拥有针对员工的SOP手册。新员工入职培训时,会分享相关的信息。但是,这些信息分散于内部网和HR部门目录各处,有时不便查询;有时因为文档过于冗长,员工无法第一时间找到想要的内容;有时公司政策已更新,但是员工手头的文档还是旧版内容。
基于上述需求,我们将开发一套基于各种内部知识手册的 “Doc-QA” 系统。这个系统将充分利用LangChain框架,处理从员工手册中产生的各种问题。这个问答系统能够理解员工的问题,并基于最新的员工手册,给出精准的答案。
开发框架:整个框架分为三个部分:
数据源(Data Sources):数据可以有很多种,包括PDF在内的非结构化的数据(Unstructured Data)、SQL在内的结构化的数据(Structured Data),以及Python、Java之类的代码(Code)。在这个示例中,我们聚焦于对非结构化数据的处理。 大模型应用(Application,即LLM App):以大模型为逻辑引擎,生成我们所需要的回答。 用例(Use-Cases):大模型生成的回答可以构建出QA/聊天机器人等系统。实现机制:大模型应用作为“服务器”,根据用例在数据源中找到答案并返回给用户。
核心实现机制: 这个项目的核心实现机制是下图所示的数据处理管道(Pipeline)。
在管道的每一步中,LangChain都提供了相关工具,这就是LangChain的强大之处。
具体流程是:
加载文件(Loading):文档加载器把Documents 加载为以LangChain能够读取的形式。 切分文件(Splitting):文本分割器把Documents 切分为指定大小的分割,我把它们称为“文档块”或者“文档片”。 嵌入及存储(Storage):将上一步中分割好的“文档块”以“嵌入”(Embedding)的形式存储到向量数据库(Vector DB)中,形成一个个的“嵌入片”。 检索(Retrieval):应用程序从存储中检索分割后的文档(例如通过比较余弦相似度,找到与输入问题类似的嵌入片)。 返回结果(Output):把问题和相似的嵌入片传递给语言模型(LLM),使用包含问题和检索到的分割的提示生成答案。前三步为准备数据源,第四步是将用户提出的问题同样切分等操作得到一个向量,再与数据源中的向量数据库通过余弦相似度等找出类似的问题答案。【这些都是数据分析的知识】
接下来将具体讲解上面的五步操作。
项目结构:
易速鲜花内部员工知识库问答系统 │ DocQA.py │ ├─OneFlower │ 易速鲜花员工手册.pdf │ 易速鲜花运营指南.docx │ 花语大全.txt │ ├─static │ flower.png │ └─templates index.html
1. 加载文件
首先要准备文件,这里先给你提供样例文件:下载OneFlower中的三个文件。
我们首先用langchain_community中的document_loaders来加载各种格式的文本文件;在这一步中,我们从pdf、word和txt文件中加载文本并将这些文本存储在一个列表中。(可能还需要安装PyPDF、Docx2txt等库,根据报错安装即可)
代码如下:
# 1.loading: 文档加载器把Documents 加载为以LangChain能够读取的形式。 import os from langchain_community.document_loaders import PyPDFLoader,Docx2txtLoader,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())
2. 切分文件
我们已经得到了文本信息(存储在documents数组中),接下来我们就需要将文本切分成更小的块,以便于进行嵌入和向量存储。
相关工具:LangChain中的RecursiveCharacterTextSplitter
代码如下:
# 2.Splitting 文本分割器把Documents 切分为指定大小的分割,我把它们称为“文档块”或者“文档片”。 from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter(chunk_size=200,chunk_overlap=10) chunk_documents = text_splitter.split_documents(documents)
说明:chunk_size参数为切分的文档块chunk的最大长度,这里设定200。chunk_overlap参数为相邻两个chunk之间的重叠token数量,保证文本语义的连贯性。
3. 嵌入及存储
数据源的最后一步,我们将这些分割后的文本转换成嵌入的形式,并将其存储在一个向量数据库中。
相关工具:
嵌入工具:OpenAIEmbeddings(OpenAI的Embedding Model) 向量数据库:Qdrant(需安装库qdrant-client)注意:
处理OpenAI的模型,还可以使用其他的嵌入模型,如Doubao-embedding-large等 LangChain中支持很多向量数据库,比如Pinecone、Chroma和Qdrant,有些是收费的,有些则是开源的。Qdrant是开源向量数据库。 Qdrant.from_documents返回的是一个Qdrant类的实例,这个实例代表了一个向量数据库。这个函数是创建一个向量数据库并且根据参数存储在指定位置(location=":memory:"表示存储在内存中)。可以根据这个实例访问这个向量数据库。代码如下:
使用OpenAI模型:# 3.Store 将分割嵌入并存储在矢量数据库Qdrant中 from langchain_community.vectorstores import Qdrant from langchain.embeddings import OpenAIEmbeddings vecto_rstore = Qdrant.from_documents( documents=chunked_documents, # 以分块的文档 embedding=OpenAIEmbeddings(), # 用OpenAI的Embedding Model做嵌入 location=":memory:", # 存储在内存中 collection_name="my_documents", # 指定collection_name )
2. 使用豆包模型:
使用豆包模型就有些复杂,首先你需要在[火山方舟管理控制台 (volcengine.com)](https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint?config=%7B%7D)新创建一个Doubao-embedding-large或Doubao-embedding的接入点,然后在API调用中获取其base_url和model,接着创建对应的环境变量(当然可以直接定义); 安装包`volcengine-python-sdk`:pip install volcengine-python-sdk; 定义一个DoubaoEmbeddings类(目前还没弄明白实现原理),然后做嵌入使用这个类
# 3.Storage:将上一步中分割好的“文档块”以“嵌入”(Embedding)的形式存储到向量数据库(Vector DB)中,形成一个个的“嵌入片”。 from typing import Dict, List, Any from langchain.embeddings.base import Embeddings from langchain.pydantic_v1 import BaseModel from volcenginesdkarkruntime import Ark class DoubaoEmbeddings(BaseModel, Embeddings): client: Ark = None api_key: str = "" model: str def __init__(self, **data: Any): super().__init__(**data) if self.api_key == "": self.api_key = os.environ["ARK_API_KEY"] self.client = Ark( base_url=os.environ.get("EMBEDDING_BASE_URL"), api_key=self.api_key ) def embed_query(self, text: str) -> List[float]: """ 生成输入文本的 embedding. Args: texts (str): 要生成 embedding 的文本. Return: embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表. """ embeddings = self.client.embeddings.create(model=self.model, input=text) return embeddings.data[0].embedding def embed_documents(self, texts: List[str]) -> List[List[float]]: return [self.embed_query(text) for text in texts] class Config: arbitrary_types_allowed = True from langchain_community.vectorstores import Qdrant vector_store = Qdrant.from_documents( documents=chunk_documents, # 以分块的文档 embedding=DoubaoEmbeddings( model=os.environ.get("EMBEDDING_MODELEND"), ), # 用豆包的Embedding Model做嵌入 location=":memory:", # 存储在内存中 collection_name="my_document", # 指定collection_name )
4. 检索
检索器
相关工具:MultiQueryRetriever工具、RetrievalQA链
此工具用于从向量存储(vector_store)中检索与查询相关的多个文档。
检索器即retriever(vector_store.as_retriever()),将向量存储转化为检索器,然后使用MultiQueryRetriever利用大型语言模型的能力来创建一个多查询检索器,这个检索器可以处理和优化多个查询,从而提高检索的效果和效率。
最后使用LangChain下的RetrievalQA建立“一条链QA”,它的作用将用户与查询并返回形成“一条链”。之后通过RetrievalQA.from_chain_type函数返回的对象传入字典{“query”:问题}就可以使用了。
# 4.Retrieval:应用程序从存储中检索分割后的文档(例如通过比较余弦相似度,找到与输入问题类似的嵌入片). import logging from langchain_openai import ChatOpenAI from langchain.retrievers.multi_query import MultiQueryRetriever from langchain.chains import RetrievalQA # 设置Logging logging.basicConfig() logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO) # 实例化一个大模型工具 豆包 chat = ChatOpenAI( api_key = os.environ.get("ARK_API_KEY"), base_url = os.environ.get("BASE_URL"), model = os.environ.get("LLM_MODELEND"), temperature = 0.8, max_tokens = 600, ) # 实例化一个MultiQueryRetriever retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vector_store.as_retriever(), llm=chat) # 实例化一个RetrievalQA链 qa_chain= RetrievalQA.from_chain_type(chat,retriever=retriever_from_llm)
注意:logging的作用是对查询建立日志;可以去掉【但还是不要去掉】
5. 返回结果并可视化
可视化用一个flask建立一个网页,然后通过输入问题向服务端请求,之后返回结果到页面即可。
# 5.Output:把问题和相似的嵌入片传递给语言模型(LLM),使用包含问题和检索到的分割的提示生成答案。 from flask import Flask,request,render_template app = Flask(__name__) # Flask APP @app.route('/',methods=['GET','POST']) def home(): if request.method == 'POST': # 接收输入作为问题 question = request.form.get('question') # RetrievalQA链 - 读入问题,生成答案 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放入template文件夹中):
<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>
最终代码
# 1.loading 文档加载器把Documents 加载为以LangChain能够读取的形式。 import os from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader, 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()) # 2.Splitting 文本分割器把Documents 切分为指定大小的分割,我把它们称为“文档块”或者“文档片”。 from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10) chunk_documents = text_splitter.split_documents(documents) # 3.Storage:将上一步中分割好的“文档块”以“嵌入”(Embedding)的形式存储到向量数据库(Vector DB)中,形成一个个的“嵌入片”。 from typing import Dict, List, Any from langchain.embeddings.base import Embeddings from pydantic.v1 import BaseModel from volcenginesdkarkruntime import Ark class DoubaoEmbeddings(BaseModel, Embeddings): client: Ark = None api_key: str = "" model: str def __init__(self, **data: Any): super().__init__(**data) if self.api_key == "": self.api_key = os.environ["ARK_API_KEY"] self.client = Ark( base_url=os.environ.get("EMBEDDING_BASE_URL"), api_key=self.api_key ) def embed_query(self, text: str) -> List[float]: """ 生成输入文本的 embedding. Args: texts (str): 要生成 embedding 的文本. Return: embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表. """ embeddings = self.client.embeddings.create(model=self.model, input=text) return embeddings.data[0].embedding def embed_documents(self, texts: List[str]) -> List[List[float]]: return [self.embed_query(text) for text in texts] class Config: arbitrary_types_allowed = True from langchain_community.vectorstores import Qdrant vector_store = Qdrant.from_documents( documents=chunk_documents, # 以分块的文档 embedding=DoubaoEmbeddings( model=os.environ.get("EMBEDDING_MODELEND"), ), # 用豆包的Embedding Model做嵌入 location=":memory:", # 存储在指定路径 collection_name="my_document", # 指定collection_name ) # 4.Retrieval:应用程序从存储中检索分割后的文档(例如通过比较余弦相似度,找到与输入问题类似的嵌入片). import logging from langchain_openai import ChatOpenAI from langchain.retrievers.multi_query import MultiQueryRetriever from langchain.chains import RetrievalQA # 设置Logging logging.basicConfig() logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO) # 实例化一个大模型工具 豆包 chat = ChatOpenAI( api_key = os.environ.get("ARK_API_KEY"), base_url = os.environ.get("BASE_URL"), model = os.environ.get("LLM_MODELEND"), temperature = 0.8, max_tokens = 600, ) # 实例化一个MultiQueryRetriever retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vector_store.as_retriever(), llm=chat) # 实例化一个RetrievalQA链 qa_chain= RetrievalQA.from_chain_type(chat,retriever=retriever_from_llm) # 5.Output:把问题和相似的嵌入片传递给语言模型(LLM),使用包含问题和检索到的分割的提示生成答案。 from flask import Flask,request,render_template app = Flask(__name__) # Flask APP @app.route('/',methods=['GET','POST']) def home(): if request.method == 'POST': # 接收输入作为问题 question = request.form.get('question') # RetrievalQA链 - 读入问题,生成答案 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)
相关知识
18|CAMEL:通过角色扮演脑暴一个鲜花营销方案
LangChain实战课笔记05 思维链与思维树
梅花易数3D实战案例
本地SEO优化实战案例分析
06|链(上):写一篇完美鲜花推文?用SequencialChain链接不同的组件
搜索引擎营销实战教程(SEO/SEM)(附微课视频)
【30岁程序员失业】转行大模型还来得及吗?全面解析与职业转型建议
搜索引擎营销实战教程(SEO/SEM)(微课版 第2版)
DeepSeek超全使用攻略(实战版)
Thinkphp5.0实战教程
网址: LangChain实战课—“易速鲜花”本地部署 https://www.huajiangbk.com/newsview2289813.html
上一篇: 花语造句 |
下一篇: 【C语言 |
推荐分享

- 1君子兰什么品种最名贵 十大名 4012
- 2世界上最名贵的10种兰花图片 3364
- 3花圈挽联怎么写? 3286
- 4迷信说家里不能放假花 家里摆 1878
- 5香山红叶什么时候红 1493
- 6花的意思,花的解释,花的拼音 1210
- 7教师节送什么花最合适 1167
- 8勿忘我花图片 1103
- 9橄榄枝的象征意义 1093
- 10洛阳的市花 1039