# 1. LangChain框架的介绍与使用
# 1.1 LangChain简介
项目简介:LangChain是一个开源的框架,它可以让开发人员把像GPT-4这样的大型语言模型(LLM)和外部数据结合起来,它提供了Python或JavaScript的包。ChatGPT模型是用到2021年的数据训练的,这可能会有很大的局限性。虽然这些模型的通用知识很棒,但是如果能让它们连接到自定义的数据和计算,就会有更多的可能性,这就是LangChain做的事情。简单来说,它可以让你的LLM在回答问题时参考整个数据库。所以你现在可以让你的GPT模型访问最新的数据,比如报告、文档和网站信息。
- 项目地址:https://github.com/langchain-ai/langchain (opens new window)
- 官方文档:https://langchain-langchain.vercel.app/docs/get_started/introduction.html (opens new window)
- 非官方教程:https://github.com/liaokongVFX/LangChain-Chinese-Getting-Started-Guide (opens new window)
# 1.2 LangChain基本概念
# 1.2.1 Loader 加载器
顾名思义,这个就是从指定源进行加载数据的。比如:文件夹 DirectoryLoader
、Azure 存储 AzureBlobStorageContainerLoader
、CSV文件 CSVLoader
、印象笔记 EverNoteLoader
、Google网盘 GoogleDriveLoader
、任意的网页 UnstructuredHTMLLoader
、PDF PyPDFLoader
、S3 S3DirectoryLoader
或 S3FileLoader
、Youtube YoutubeLoader
等。
# 1.2.2 Document 文档
当使用 Loader 加载器读取到数据源后,数据源需要转换成 Document 文档对象后,后续才能进行使用。
# 1.2.3 Text Spltters 文本分割
顾名思义,Text Spltters 就是用来分割文本的,因为 OpenAI API 是有字符限制的。比如我们将一份300页的 pdf 发给 OpenAI API,让它进行总结,肯定会报超过最大 Token 的错误,所以这里就需要使用文本分割器去分割我们 Loader 进来的 Document 文档对象。
# 1.2.4 Vector Stores 向量数据库
因为数据相关性搜索其实是向量运算。所以需要将我们的加载进来的数据 Document 文档对象进行向量化,才能进行向量运算搜索。转换成向量也很简单,只需要我们把数据存储到对应的向量数据库中即可完成向量的转换。
# 1.2.5 Chain 链
我们可以把 Chain 理解为任务。一个 Chain 就是一个任务,当然也可以像链条一样,一个一个的执行多个链。
# 1.2.6 Agent 代理
可以简单的理解为它可以动态的帮我们选择和调用Chain或者已有的工具,执行过程可以参考下面这张图:

# 1.2.7 Embedding 嵌入
用于衡量文本的相关性,这个也是 OpenAI API 能实现构建自己知识库的关键所在。它相比 fine-tuning 最大的优势就是,不用进行训练,并且可以实时添加新的内容,而不用加一次新的内容就训练一次,并且各方面成本要比 fine-tuning 低很多。
# 1.3 LangChain使用实例
以下使用的 LLM 都是以 Open AI 的 ChatGPT 为例,以 ipynb 的方式运行。账号与API-KEY自己注册或直接购买,详见我的另一篇博客:ChatGPT智能问答AI使用指南 (opens new window)。如果 LLM 不想使用 ChatGPT,可以根据自己任务的需要换成自己需要的 LLM 模型即可。
# 1.3.1 完成一次问答
第一个案例就来个最简单的,用 LangChain 加载 OpenAI 的模型,并且完成一次问答。在开始之前,我们需要先设置我们的 OpenAI 的 key。
import os
os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY'
2
然后,我们进行导入和执行:
from langchain.llms import OpenAI
llm = OpenAI(model_name="text-davinci-003",max_tokens=1024)
llm("怎么评价人工智能")
2
3
4
# 1.3.2 使用 Memory 使会话带有记忆
可以使用自带的 Memory 来实现会话记忆功能。
import os
os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY'
from langchain.memory import ChatMessageHistory
from langchain.chat_models import ChatOpenAI
chat = ChatOpenAI(temperature=0, openai_api_base="YOUR_OPENAI_API_KEY_BASE")
# 初始化 MessageHistory 对象
history = ChatMessageHistory()
# 给 MessageHistory 对象添加对话内容
history.add_ai_message("解释一下量子计算")
history.add_user_message("它与量子力学什么关系?")
# 执行对话
ai_response = chat(history.messages)
print(ai_response)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.3.3 通过 Google 搜索并返回答案
让我们的 OpenAI API 联网搜索,并返回答案给我们。这里我们需要借助 Serpapi 来进行实现,Serpapi 提供了 Google 搜索的 API。首先需要到 Serpapi官网 (opens new window)上注册一个用户,并复制他给我们生成 API-KEY,然后将其配置到环境变量里面去。
import os
os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY'
os.environ["SERPAPI_API_KEY"] = 'YOUR_SERPAPI_API_KEY'
2
3
然后,我们进行导入和执行:
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.llms import OpenAI
from langchain.agents import AgentType
# 加载 OpenAI 模型
llm = OpenAI(temperature=0,max_tokens=2048)
# 加载 serpapi 工具
tools = load_tools(["serpapi"])
# 工具加载后都需要初始化,verbose 参数为 True,会打印全部的执行详情
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
# 运行 agent
agent.run("What's the date today? What great events have taken place today in history?")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注:这个 Serpapi 对中文不是很友好,所以提问的 prompt 建议使用英文。
它将我们的问题拆分成了几个步骤,然后一步一步得到最终的答案,正确的返回了日期(有时差),并且返回了历史上的今天。
在 chain 和 agent 对象上都会有 verbose
这个参数,这个是个非常有用的参数,开启它之后可以看到完整的 chain 执行过程。
关于 agent type 几个选项的含义:
- zero-shot-react-description:根据工具的描述和请求内容的来决定使用哪个工具(最常用)。
- react-docstore:使用 ReAct 框架和 docstore 交互, 使用
Search
和Lookup
工具,前者用来搜,后者寻找term, 举例:Wipipedia
工具。 - self-ask-with-search 此代理只使用一个工具:Intermediate Answer,它会为问题寻找事实答案(指的非 GPT 生成的答案,而是在网络文本中已存在的), 如
Google search API
工具。 - conversational-react-description:为会话设置而设计的代理,它的prompt会被设计的具有会话性,且还是会使用 ReAct 框架来决定使用来个工具,并且将过往的会话交互存入内存。
# 1.3.4 对超长文本进行总结
假如想要用 OpenAI API 对一个段文本进行总结,我们通常的做法就是直接发给 API 让他总结,但是如果文本超过了 API 最大的 token 限制就会报错。这时,我们一般会进行对文章进行分段,比如通过 tiktoken 计算并分割,然后将各段发送给 API 进行总结,最后将各段的总结再进行一个全部的总结。LangChain很好的帮我们处理了这个过程,使得我们编写代码变的非常简单。
from langchain.document_loaders import UnstructuredFileLoader
from langchain.chains.summarize import load_summarize_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain import OpenAI
# 导入文本
loader = UnstructuredFileLoader("/your_path/long_text.txt")
# 将文本转成 Document 对象
document = loader.load()
print(f'documents:{len(document)}')
# 初始化文本分割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 500,
chunk_overlap = 0
)
# 切分文本
split_documents = text_splitter.split_documents(document)
print(f'documents:{len(split_documents)}')
# 加载 llm 模型
llm = OpenAI(max_tokens=1500)
# 创建总结链
chain = load_summarize_chain(llm, chain_type="refine", verbose=True)
# 执行总结链,(为了快速演示,只总结前5段)
chain.run(split_documents[:5])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
首先我们对切割前和切割后的 document 个数进行了打印,可以看到,切割前就是只有整篇的一个 document,切割完成后,会把上面一个 document 切成 59 个 document。为了快速演示,这里最终只输出了前 5 个 document 的总结。
这里有几个参数需要注意:
[1] 文本分割器的 chunk_overlap
参数
这个是指切割后的每个 document 里包含几个上一个 document 结尾的内容,主要作用是为了增加每个 document 的上下文关联。比如,chunk_overlap=0
时, 第一个 document 为 aaaaaa,第二个为 bbbbbb;当 chunk_overlap=2
时,第一个 document 为 aaaaaa,第二个为 aabbbbbb。不过,这个也不是绝对的,要看所使用的那个文本分割模型内部的具体算法。
[2] chain 的 chain_type
参数
这个参数主要控制了将 document 传递给 llm 模型的方式,一共有 4 种方式:
stuff
: 这种最简单粗暴,会把所有的 document 一次全部传给 llm 模型进行总结。如果document很多的话,势必会报超出最大 token 限制的错,所以总结文本的时候一般不会选中这个。map_reduce
: 这个方式会先将每个 document 进行总结,最后将所有 document 总结出的结果再进行一次总结。refine
: 这种方式会先总结第一个 document,然后在将第一个 document 总结出的内容和第二个 document 一起发给 llm 模型在进行总结,以此类推。这种方式的好处就是在总结后一个 document 的时候,会带着前一个的 document 进行总结,给需要总结的 document 添加了上下文,增加了总结内容的连贯性。map_rerank
: 这种一般不会用在总结的 chain 上,而是会用在问答的 chain 上,他其实是一种搜索答案的匹配方式。首先你要给出一个问题,他会根据问题给每个 document 计算一个这个 document 能回答这个问题的概率分数,然后找到分数最高的那个 document ,在通过把这个 document 转化为问题的 prompt 的一部分(问题+document)发送给 llm 模型,最后 llm 模型返回具体答案。
# 1.3.5 构建本地知识库问答机器人
在这个例子中,介绍如何从我们本地读取多个文档构建知识库,并且使用 OpenAI API 在知识库中进行搜索并给出答案。这个非常有用的,比如可以很方便的做一个可以介绍公司产品的机器人。
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain import OpenAI,VectorDBQA
from langchain.document_loaders import DirectoryLoader
# 加载文件夹中的所有txt类型的文件
loader = DirectoryLoader('/your_path/doc', glob='**/*.pdf')
# 将数据转成 document 对象,每个文件会作为一个 document
documents = loader.load()
# 初始化加载器
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
# 切割加载的 document
split_docs = text_splitter.split_documents(documents)
# 初始化 openai 的 embeddings 对象
embeddings = OpenAIEmbeddings()
# 将 document 通过 openai 的 embeddings 对象计算 embedding向量信息并临时存入 Chroma 向量数据库,用于后续匹配查询
docsearch = Chroma.from_documents(split_docs, embeddings)
# 创建问答对象
qa = VectorDBQA.from_chain_type(llm=OpenAI(), chain_type="stuff", vectorstore=docsearch,return_source_documents=True)
# 进行问答
result = qa({"query": "Kafka为什么基于磁盘还那么快"})
print(result)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
可以通过结果看到,它成功的从我们的给到的文档中获取了正确的答案。
# 1.3.6 爬取网页并输出JSON数据
有些时候我们需要爬取一些结构性比较强的网页,并且需要将网页中的信息以JSON的方式返回回来,可以使用 LLMRequestsChain
类去实现。
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
from langchain.chains import LLMRequestsChain, LLMChain
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0)
template = """在 >>> 和 <<< 之间是网页的返回的HTML内容。
网页是新浪财经A股上市公司的公司简介。
请抽取参数请求的信息。
>>> {requests_result} <<<
请使用如下的JSON格式返回数据
{{
"company_name":"a",
"company_english_name":"b",
"issue_price":"c",
"date_of_establishment":"d",
"registered_capital":"e",
"office_address":"f",
"Company_profile":"g"
}}
Extracted:"""
prompt = PromptTemplate(
input_variables=["requests_result"],
template=template
)
chain = LLMRequestsChain(llm_chain=LLMChain(llm=llm, prompt=prompt))
inputs = {
"url": "https://vip.stock.finance.sina.com.cn/corp/go.php/vCI_CorpInfo/stockid/600519.phtml"
}
response = chain(inputs)
print(response['output'])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
可以看到,它很好的将格式化后的爬取结果输出了出来:
# 2. 使用LangFlow实现原生LLM图形化开发
实验环境:Macbook Pro 2021,M1 pro芯片,16G内存,macOS Ventura13.3.1系统,Python3.9环境
本文使用的是当前最新的 Langflow 0.2.13 版本,由于 Langflow 目前更新迭代比较快,不保证能够向前、向后兼容。
# 2.1 LangFlow简介
Langflow 是一个LangChain UI,提供了一种交互界面来使用LangChain,通过拖拽即可搭建自己的实验、原型流。
# 2.2 LangFlow组件介绍
LangFlow的安装与启动非常简单,langflow启动的详细参数见官方文档:
$ pip3 install langflow
$ langflow
2
启动成功后,使用Chrome浏览器打开 http://127.0.0.1:7860 (opens new window) 地址,可以看到如下界面:
在左侧边栏中,可以看到所有你可以使用的工具:
- Agent——将大语言模型的决策与工具结合起来,使其能够实施和执行行动;
- Chains——允许组合人工智能应用程序的不同组件,如prompts,大语言模型和存储器;
- Loaders——允许与外部源集成以上传信息,如PDF、CSV等;
- Embeddings——将文本嵌入到向量潜在空间的模型;
- LLMs——用于理解和生成文本的模型,如OpenAI模型;
- Memories——LangChain组件,能够维护会话的记忆;
- Prompts——用于定义prompts框架的模板;
- Retrievers——用于信息检索的组件,通过倒排索引从大规模文本数据集中检索相关信息,以支持多轮对话系统的问答;
- Text Splitters——将文本分割成更小的块并避免tokens限制问题的实用程序;
- Toolkits——为特定用例与外部系统集成,例如,CSV文件或Python Agent;
- Tools——代理可以用来与外部工具交互的功能,如必应搜索或本地文件系统;
- Utilities——对Google API或Bing API等系统有用的包装器;
- Vector Stores——向量数据库,文本Embeddings可以保存,然后与用户的输入进行匹配;
- Wrappers——请求库的轻量级包装器。
注:如果需要对组件进行二次开发的话,在./langflow/src
目录下有前后端的代码。
- 后端安装好依赖,右键
./langflow/src/backend
目录,依次点击Mark Directory as、Sources Root,启动main.py
。(默认http://localhost:7860/
) - 前端使用
npm install
安装好依赖后使用npm start
启动。(默认http://localhost:3000/
)
# 2.3 LangFlow使用实例
LangFlow官方还有个项目是专门放置使用示例的,里面有很多现成的流程可以供我们入门时参考。
注:在 LangFlow 里使用 OpenAI 组件时,将 OpenAl API Base 参数项配置成国内代理地址即可,这样可以大大加快速度。
# 2.3.1 基本OpenAI查询
打开LangFlow界面,点击 + New Project
按钮新建一个项目,然后点击左上方的 Import 按钮,导入 Basic Chat.json
示例,之后填写 OpenAl APl Key,点击一下右下方的闪电按钮,构建成功后组建会变成绿色,右下角出现聊天按钮。
点击右下角的聊天按钮,输入问题,即可实现 OpenAI 问答了。
# 2.3.2 记录历史的OpenAI查询
导入 Basic Chat with Prompt and History.json
示例,之后填写 OpenAl APl Key,输入你要询问的问题。
使用 ConversationBufferMemory 组件可以记录查询历史。可以看到从历史查询记录中获取到了“它”指代的是量子计算。
# 2.3.3 使用Agent自动分解任务
导入 SerpAPI Tool.json
示例,之后填写 OpenAl APl Key 和 Serpapi API Key,输入你要询问的问题。
ZeroShotAgent组件可以自动将用户问题进行分解。可以看到,提出问题后它自动给分解成几个子问题,查询Google,然后总结结果。
# 2.4 在Python里调用导出的流程
在界面上点击Export按钮,会弹出导出弹框,记得勾选上“Save with my APl keys”,然后点击Download Flow按钮,会下载一个JSON格式的流程文件。
导出的流程在Python代码里调用非常简单,只需要以下三行代码,可以进一步将其封装成服务。
from langflow import load_flow_from_json
flow = load_flow_from_json("/your_path/your_flow.json")
flow("解释一下量子计算")
2
3
4
# 3. 参考资料
[1] LangChain Introduction from LangChain官方文档 (opens new window)
[2] LangChain 中文入门教程 from Gitbook (opens new window)
[3] LangChain:一个让你的LLM变得更强大的开源框架 from 知乎 (opens new window)
[4] 500页超详细中文文档教程,助力LLM/chatGPT应用开发 from LangChain中文网 (opens new window)
[6] langfow:0门槛0代码拖拽生成企业的专属Al大模型,专属llm会成为每个企业的标配 from Bilibili (opens new window)
[7] 本地部署开源大模型的完整教程:LangChain + Streamlit+ Llama from 知乎 (opens new window)
[8] LangFlow--可视化的LangChain from 土猛的员外 (opens new window)
[9] 500页超详细中文文档教程,助力LLM/chatGPT应用开发 from LangChain中文网 (opens new window)
[10] 用LangChain构建大语言模型应用 from CSDN (opens new window)
[11] Awesome LangChain from Github (opens new window)
[12] 一图带你了解 LangChain 的功能模块 from Bilibili (opens new window)
[13] LangChain结合了大型语言模型、知识库和计算逻辑,可以用于快速开发强大的AI应用 from Github (opens new window)