本篇是吴恩达 LangChain构建大模型系列教程的一部分,聚焦基于文档的问答系统开发,利用 LangChain 框架结合大语言模型,实现对文档内容的高效问答。
具体需求场景分析:一个外贸出口用户手里有一个商品清单列表(excel),清单里有商品名称,价格,款式,商品的功能特性,品牌特点等信息。当有进口商人过来寻找货品时他们的具体需求是这样的,"夏天快到了,请推荐一些有防晒功能的衬衫,我们要把它卖到夏威夷,人们在海边儿旅游的时候穿"。进口商人的需求里可能不会具体到品牌,价格,款式,他只会给你一些应用场景和功能特点。这时候如果使用传统的编程方式来检索清单很难解决问题,如果你使用数据库或者excel,你首先要检索关键词,根据不同的需求提取不同的关键词。这一步传统的计算机基本是做不到的,或者说很难做好,比如就是防晒这个功能,你的产品特性里可能会写着"防紫外线",而不是"防晒",总之可能产品描述里可能没有你想要的关键词。但是现在有了大模型和问答系统我们就可以准确检索到我们需要的产品,避免人工筛选关键词这个步骤,替代人类客服,实现AI配货客服的功能。
1.开发背景与技术价值:基于文档的问答系统能让语言模型处理其训练数据之外的内容,通过结合嵌入模型和向量存储技术,提升模型灵活性,满足用户对文档信息获取的需求。
先解释两个概念
1)什么是嵌入模型
嵌入模型(Embedding Model) 是一种将各种类型的数据,如文本、图像、音频等,转换为低维向量空间表示的数学模型。它的核心目的是将原本复杂、高维的数据以一种更紧凑、更易于计算机处理和分析的方式进行表示。
嵌入模型通常基于深度学习或机器学习算法,通过对大量数据的学习,自动发现数据中的内在结构和模式,从而将每个数据点映射为一个低维向量。以词嵌入为例,它会根据单词在文本中的上下文信息,将每个单词映射到一个固定长度的向量空间中,使得语义相似的单词在向量空间中的距离较近
在自然语言处理领域用于词、句子、文档的表示,进而应用于文本分类、情感分析、机器翻译、信息检索等任务。如 Word2Vec、GloVe 等词嵌入模型,以及 BERT 等基于 Transformer 的预训练语言模型,能将文本转化为有语义信息的向量表示。
数据比较和相似度计算:在低维向量空间中,可以方便地计算数据点之间的相似度,如余弦相似度、欧式距离等。这对于文本分类、图像检索、推荐系统等任务非常有用,例如可以根据用户的历史行为数据和物品的嵌入向量,计算用户与物品之间的相似度,为用户推荐相似的物品。
2)什么是向量存储技术
向量存储技术是指将向量数据(通常是高维向量)有效地存储在计算机系统中,并提供快速的检索、查询和更新等操作的技术。它旨在解决如何高效地处理大量向量数据,以便在各种应用中能够快速获取和利用这些数据。
常用类型
- 基于内存的向量存储:将向量数据存储在内存中,具有极高的读写速度,适用于对实时性要求极高的场景。但受内存容量限制,存储规模相对较小,例如 Faiss 库,它提供了多种基于内存的向量索引结构,能快速进行向量相似性搜索。
- 基于磁盘的向量存储:把向量数据存储在磁盘上,可存储大规模的数据,但访问速度相对较慢。为了提高检索效率,通常会结合索引技术和缓存机制,如 Annoy(Approximate Nearest Neighbors Oh Yeah),它基于磁盘存储构建索引,支持快速的近似最近邻搜索。
- 分布式向量存储:将向量数据分布存储在多个节点上,以实现大规模数据的存储和处理,并提供高可用性和容错机制。适用于处理海量向量数据的场景,例如 Cassandra 等分布式数据库可以用于向量数据的分布式存储和管理。
原文中的词嵌入模型是用的openAI的,国内不好用改成了阿里巴巴的 DashScopeEmbeddings
使用DashScope Embedding实现文本嵌入参考链接
python
from langchain_community.embeddings import DashScopeEmbeddings
# 使用稳定可靠的API服务
embeddings = DashScopeEmbeddings(
model="text-embedding-v1",
dashscope_api_key="your-dashscope-api-key"
)
text = "This is a test document."
# 获取文本的嵌入向量
query_result = embeddings.embed_query(text)
print("Query Embedding:", query_result)
# 获取文档的嵌入向量
doc_results = embeddings.embed_documents(["foo"])
print("Document Embedding:", doc_results)
2.开发步骤与核心代码实现
- 导入必要库:包括环境变量、检索 QA 链、Chat OpenAI 语言模型、CSV 文档加载器、向量存储(如 Docker Ray 内存搜索向量存储)等,为开发做准备。
- 加载数据:使用 CSV 加载器读取指定文件,将数据读入内存,为后续处理做准备。
- 创建向量存储索引:借助向量存储索引创建器,指定向量存储类,基于加载的数据创建向量存储索引,实现数据的有效组织。
- 查询与获取响应:输入查询语句,利用索引查询获得响应,可得到相关信息的 Markdown 表格和语言模型生成的摘要。
python
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import CSVLoader
from langchain.vectorstores import DocArrayInMemorySearch
from IPython.display import display, Markdown
from langchain.indexes import VectorstoreIndexCreator
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
# 使用稳定可靠的API服务
embeddings = DashScopeEmbeddings(
model="text-embedding-v1",
dashscope_api_key="your-dashscope-api-key"
)
#测试词嵌入
text = "This is a test document."
query_result = embeddings.embed_query(text)
print(query_result)
file = 'OutdoorClothingCatalog_1000.csv'
loader = CSVLoader(file_path=file)
#创建一个VectorstoreIndexCreator实例,
#通过vectorstore_cls参数指定使用DocArrayInMemorySearch作为向量数据库,
#该数据库将向量存储在内存中。
index = VectorstoreIndexCreator(
vectorstore_cls=DocArrayInMemorySearch,
embedding=embeddings
).from_loaders([loader])
query ="Please list all your shirts with sun protection \
in a table in markdown and summarize each one."
llm = ChatOpenAI(
openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
model_name="qwen-plus", # 模型名称
temperature=0.9
)
response = index.query(query, llm=llm)
display(Markdown(response))
3.关键技术原理详解
- 嵌入技术:将文本片段转化为数值表示,捕捉语义意义,相似语义的文本片段向量相似,便于在向量空间中比较,筛选出与问题相关的文本片段。
- 向量数据库:存储文本片段的向量表示。处理大型文档时,先将文档拆分为小片段,创建嵌入后存入向量数据库。查询时,为查询语句创建嵌入,与数据库中的向量比较,选取最相似的片段用于生成答案。
python
loader = CSVLoader(file_path=file)
docs = loader.load()
docs[0]
#测试词嵌入
text = "Hi my name is Harrison"
embed = embeddings.embed_query(text)
print(len(embed))
print(embed[:5])
#
db = DocArrayInMemorySearch.from_documents(
docs,
embeddings
)
query = "Please suggest a shirt with sunblocking"
docs = db.similarity_search(query)
len(docs)
docs[0]
retriever = db.as_retriever()#检索器
llm = ChatOpenAI(
openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
model_name="qwen-plus", # 模型名称
temperature=0.9
)
qdocs = "".join([docs[i].page_content for i in range(len(docs))])
response = llm.invoke(f"{qdocs} Question: Please list all your \
shirts with sun protection in a table in markdown and summarize each one.")
display(Markdown(response))
4.自定义与优化策略
- 自定义索引:创建索引时可指定嵌入方式,选择不同类型的向量存储,提高开发灵活性。
- 不同链类型对比:介绍了 stuff、mapreduce、优化、maprerank 等链类型。stuff 方法简单,将所有文档放入提示中进行一次语言模型调用;mapreduce 方法可处理大量文档,但调用次数多;优化方法基于前一个文档的答案迭代生成最终答案;maprerank 方法为每个文档调用语言模型并返回分数,根据分数选择答案,各有优劣和适用场景。
python
qa_stuff = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
verbose=True
)
query = "Please list all your shirts with sun protection in a table \
in markdown and summarize each one."
response = qa_stuff.invoke(query)
# response = qa_stuff.run(query)
print(response)
display(Markdown(response["query"]))
display(Markdown(response["result"]))
chain_type 类型有Stuff、Map Reduce、Refine ,Map Rerank
- Stuff:将所有文档合并到一个提示符中(在上面的示例中使用)。
- Map-reduce:独立处理文档块,然后进行汇总。
- Refine:以迭代方式构建以前的答案。
- Map-rerank:对每个文档进行评分,选择最高分。
对于不同的类型输出的信息的组织方式略有不同, - Stuff 是简单堆砌信息
- Stuff 是简单堆砌信息
- Refine 是对初始结果进行筛选优化
- Map Rerank 则是在 Map 的基础上通过复杂排序来整合输出信息。
它们根据不同的应用场景和需求,采用了不同的方式来处理和呈现最终的信息。