《易车实战学习Langchain开发》-01-使用Langchain构建“易车”销售平台智能问答系统

#生成式 AI 的发展方向,是 Chat 还是 Agent?#

大家好,我是静愚,朋友们都喜欢叫我“鲸鱼”。我将带领大家开启一段Langchain技术的实战之旅。
以《易车》实际应用场景为背景的系列学习文章,旨在帮助你们更直观、更深入地理解和掌握Langchain技术。
掌握一项技术,最重要的是能够解决实际问题。脱离了业务场景的技术,就像是无根之木、无源之水。
在这里,我们一起探索如何将Langchain技术应用于实际业务,让技术落地生根,助力业务发展。

上篇讲解了整个实战学习Langchain项目的环境准备。

在深入剖析LangChain的各个核心组件之前,我将从零开始,逐步构建一个实用的实践项目。通过这一过程体验LangChain作为基于大型语言模型的应用开发框架,其功能的强大究竟体现在何处。

这种直观的感受将有助于您更深刻地理解LangChain的潜力和应用范围。

项目介绍

我先大概介绍一下这个项目,以及此项目的开发框架。

项目名称:“易车”——汽车销售智问智答。

项目介绍:“易车”作为一个大型的在线汽车销售问答系统,有业务流程和规范,同时也有员工的销售指导手册。新员工入职销售培训时,会分享相关的信息。但是,这些信息分散于内部网和其他部门目录各处,有时不便查询;有时因为文档过于冗长,员工无法第一时间找到想要的内容;有时公司销售政策已更新,但是员工手头的文档还是旧版内容。

基于上述需求,我们将开发一套基于各种内部汽车销售知识的 “LLM-QA” 系统。这个系统将充分利用 LangChain 框架,处理从销售手册中提到的各种问题。这个问答系统能够理解销售人员的问题,并基于最新的销售指导手册,给出精准的答案。

项目框架:下面这张图片描述了通过 LangChain 框架实现一个知识库文档系统的整体框架。

项目框架
其中:

数据源(Data Sources):数据可以有很多种,包括 PDF 在内的非结构化的数据(Unstructured Data)、SQL 在内的结构化的数据(Structured Data)。在这个示例中,我们聚焦于对非结构化数据的处理。

大模型应用(Application,即 LLM App):以大模型为逻辑引擎,生成我们所需要的回答。

用例(Use-Cases):大模型生成的回答可以构建出 QA/ 聊天机器人等系统。

核心机制:项目的核心实现原理就是下图的数据处理及检索流程。

项目流程图
在构建基于文档的问答系统的每一步中,LangChain 精心准备了一系列高效工具,旨在让这一过程变得前所未有的流畅与便捷。整个流程精妙地划分为五个关键阶段,让我们逐一深入探索:

  1. Loading(加载):LangChain 的文档加载器如同一位灵巧的搬运工,将各类文档转化为LangChain易于解读的格式。
  2. Splitting(分割):接着,文本分割器如同一位细心的厨师,将庞大的文档精心切割成更易于管理的“文档块”。这一步骤的关键在于平衡信息的完整性与处理的效率,确保每一块内容都能独立成章,同时又便于后续的分析与检索。
  3. Storage(存储):随后,这些精心分割的文档块被赋予了新的生命——它们被转化为“嵌入”(Embedding),一种能够捕捉文本深层含义的数值表示,并被妥善存储于向量数据库(Vector DB)之中。
  4. Retrieval(检索):当应用程序接收到用户的查询时,检索机制便如同一位敏锐的侦探,开始在向量数据库中穿梭,利用余弦相似度等高级算法,寻找与用户问题最为契合的嵌入片。
  5. Output (输出):最终,经过层层筛选的相似嵌入片与用户的原始问题一同被传递给语言模型(LLM)。在这里,它们将共同激发创意的火花,生成既准确又富有洞察力的答案。LangChain的这一环节,就像是连接用户与知识海洋的桥梁,让每一次问答都充满惊喜与价值。

值得注意的是,上述流程中涉及的“嵌入”、“向量存储”等概念,虽然初听起来可能略显抽象,但它们正是现代自然语言处理技术的核心所在。无需担忧,随着我们后续的详细讲解,这些概念将逐渐变得清晰易懂,助您更好地掌握LangChain这一强大工具。

文件准备及Load

此次材料准备了三种文件类型的源文档,包括PDF、doc、txt格式的文件。可以在这里下载
在这里插入图片描述
首先用 LangChain 中的 document_loaders 来加载各种格式的文本文件。在项目目录下,我创建了一个files的目录用于存放文件附件。

在这一步中,我们从 pdf、word、txt和md 文件中加载文本,然后将这些文本存储在一个列表中。(注意:可能需要安装 PyPDF、Docx2txt 等库)

# 1. 导入Document Loaders
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.document_loaders import Docx2txtLoader
from langchain_community.document_loaders import TextLoader

# 加载文件
base_file_path = './files'  # 文档的存放目录
documents = []
for file in os.listdir(base_file_path):
    # 构建完整的文件路径
    file_path = os.path.join(base_file_path, 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())
    elif file.endswith('.md'):
        loader = TextLoader(file_path)
        documents.extend(loader.load())

文本内容Splitting

上面使用不同的Loader加载了文件,接着需要使用Split将加载的文件切分成不同的小块,便便进行Embedding和向量化存储。

因为我们的文件篇幅不大,所以将Chunk大小设置为200个字符,同时为了保证上下文的连贯性,我们保留20字符的冗余。

# 2. 将加加载的文件进行切分
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)
chunked_documents = text_splitter.split_documents(documents)

我们这次使用的是 RecursiveCharacterTextSplitter 对文本内容记性切分,实际上langchain支持了很多种切分策略在后续的章节中,会详细讲解。

文本Chunk进行Embedding和Storage

上一步,我们将文件使用Splitter对文件进行了Chunk切分,切分之后的文件块还不能直接使用。

我们知道,采用语义相似度匹配,需要将文本内容转为向量存储,这个由文本转为向量的过程我们称之为Embedding(嵌入)。

本案例中我们使用百川的Embedding-02模型来实现嵌入,然后使用Qdrant这个向量数据库来存储。

首先我们需要安装以下Qdrant。

pip install qdrant-client

Langchain & Qdrant

具体逻辑如下:

# 3. 持久化向量数据
from langchain_community.vectorstores import Qdrant
from langchain_community.embeddings import BaichuanTextEmbeddings

embeddings = BaichuanTextEmbeddings(baichuan_api_key=os.getenv("BAICHUAN_API_KEY"))
vectorstore = Qdrant.from_documents(
    documents=chunked_documents,  # 以分块的文档
    embedding=embeddings,  # 用百川Embedding Model做嵌入
    location=":memory:",  # in-memory 存储
    collection_name="jingyu_yi_che", )  # 指定collection_name

百川Embedding & Langchain

基于问题Retrieval并通过LLM生成答案

上面咱们顺利将文件进行了切片、Embedding、和向量化存储,那么下面就是要考虑怎么去使用这些数据了。

此时,信息提取的基本方式就是把问题也转换为向量,然后去和向量数据库中的各个向量进行比较,提取最接近的信息。

向量之间的比较通常基于向量的距离或者相似度。在高维空间中,常用的向量距离或相似度计算方法有欧氏距离和余弦相似度。

接下来就需要构建一个Chat场景,然后使用Retrieval Query Ansewer链。其实这也就是大家熟知的RAG实现流程,具体如下:

  1. 将用户问题进行Embedding;
  2. 使用问题向量数据在向量数据库Qdrant中进行相似度检索,检索出TopK的文件Chunk;
  3. 将检索的Chunks以及用户问题交给LLM进行推理
  4. 最后得到用户的的答案。

具体代码如下:

# 4. Retrieval and chat
from langchain_community.chat_models import ChatBaichuan  # ChatBaichuan 模型
from langchain.retrievers.multi_query import MultiQueryRetriever  # MultiQueryRetriever工具
from langchain.chains import RetrievalQA  # RetrievalQA链
from langchain.prompts import PromptTemplate

# 实例化一个大模型工具 - OpenAI的GPT-3.5
llm = ChatBaichuan(
    baichuan_api_key=os.getenv('BAICHUAN_API_KEY'),
    model="Baichuan3-Turbo",
)

# 实例化一个MultiQueryRetriever
retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vectorstore.as_retriever(), llm=llm)

prompt_template = """Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.
{context}
Question: {question}
Answer in Chinese:"""
PROMPT = PromptTemplate(
    template=prompt_template, input_variables=["context", "question"]
)
# 自定义 chain prompt
chain_type_kwargs = {"prompt": PROMPT}
# 实例化一个RetrievalQA链
chat_chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever_from_llm,
                                         chain_type_kwargs=chain_type_kwargs, return_source_documents=True)

# 查询问题答案
chat_chain.invoke(input="如何介绍汽车特点和优势?")

输出结果如下:

{'query': '如何介绍汽车特点和优势?',
 'result': '在介绍汽车特点和优势时,首先需要了解客户的需求和购车目的,然后针对客户的需求来突出汽车的特定特点和优势。例如,如果客户注重安全性,那么可以重点介绍汽车的安全性能,如稳定性和防侧倾能力;如果客户关心耐用性,那么可以强调汽车的耐用程度和质保时间;如果客户关注油耗,那么可以说明汽车的节能性能和燃油经济性。同时,还可以提供试驾机会,让客户亲身体验汽车的驾驶感受和性能表现,从而更好地做出购车决策。',
 'source_documents': [Document(metadata={'source': 'files\\汽车销售话术.pdf', 'page': 1, '_id': '76a168f185ba4b7d9569d9ecbba7135f', '_collection_name': 'yi-che'}, page_content='但是开起来会很过瘾呀。而且它的耐用性普遍 要比日系车好很多,特别是跑高\n速和转弯的时候会非常平稳,不容易侧倾,安全性也更好,喜欢它的人,大多是\n冲着这些优点来的呢。\n6.提出异议 :你别管我看没看过,你就报个底价得了,比别人家便宜我就马上\n买。'),
  Document(metadata={'source': 'files\\汽车销售话术.pdf', 'page': 1, '_id': 'd50f481caf164614b2bd3e57fbee7f03', '_collection_name': 'yi-che'}, page_content='养维护都有着很大的关系。 这是一款成熟的车型, 已经销售了 3年多了, 而且 还\n是乘用车市场上质保时间最长的车型,它的质保期达到了 5年或 10万公里呢,\n这本身就体现出我们车辆在耐用性上的优势, 同时这款车也是今年市场上的销量\n冠军车型。这些足以证明我们的产品是市场上同类级车型中最好的选择。\n4.提出异议 :你们的车子空间确实非常大,钢板也很厚实,那一定很费油吧?'),
  Document(metadata={'source': 'files\\汽车销售话术.pdf', 'page': 3, '_id': '05e04867defb483dba87719ac8f6a836', '_collection_name': 'yi-che'}, page_content='司的服务标兵, 还奖励了我一个名牌电饭煲, 我今天就把这个奖品作为购车礼物\n送给您吧! 除此之外我就没有别的东西可以给您了, 都这个份上了, 您看还需要\n我怎么做呢?\n11.提出异议 :国产车质量恐怕不过关吧?\n神奇回答 :天呀,您现在还有这样的担心呀。这点您就不用质疑了,从整体上来\n说,现在的国产车大部分已经提高到合 资车的质量水平了,有些已经做到了超'),
  Document(metadata={'source': 'files\\汽车销售技巧和话术.docx', '_id': '4bbd678103fc444283096beb48d0087c', '_collection_name': 'yi-che'}, page_content='3.介绍汽车特点和优势\n\n当客户对某款汽车感兴趣时,你需要介绍汽车的特点和优势,包括车型、配置、性能、安全性等。你需要让客户了解汽车的优势,并帮助客户做出决策。\n\n\n\n4.回答客户问题\n\n当客户有问题时,你需要及时回答客户的问题,并提供专业的建议和意见。你需要了解客户的疑虑和困惑,并给予客户满意的答复。\n\n5.给客户提供试驾机会'),

最后

本章节仅仅是为了带大家入个门,从全局看一下Langchain的应用及其能力。

后续的章节中,我将对Langchain的Model I/O、Chains、Memory、Agent等组件进行详细拆解。

到那时你将可以通过Langchain实现更多有意义的场景。

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值