colab地址(包含本文所有案例)、gitbook(方便阅读)、github地址
如果想把 OPENAI API 的请求根路由修改成自己的代理地址,可以通过设置环境变量 “” 来进行修改,参考代码。或在初始化OpenAI相关模型对象时,传入“openai_api_base” 变量,参考代码。
LangChain官方文档
1.1 背景
众所周知 OpenAI 的 API 无法联网的,所以如果只使用自己的功能实现联网搜索并给出回答、总结 PDF 文档、基于某个 Youtube 视频进行问答等等的功能肯定是无法实现的。所以,我们来介绍一个非常强大的第三方开源库: 。
LangChain 是一个用于开发由语言模型驱动的应用程序的框架。他主要拥有 2 个能力:
- 可以将 LLM 模型(大规模语言模型)与外部数据源进行连接
- 允许与 LLM 模型进行交互
1.2 基础功能
- 支持多种模型接口,比如 OpenAI、Hugging Face、AzureOpenAI …
- Fake LLM,用于测试
- 缓存的支持,比如 in-mem(内存)、SQLite、Redis、SQL
- 用量记录
- 支持流模式(就是一个字一个字的返回,类似打字效果)
Prompt管理,支持各种自定义模板
拥有大量的文档加载器,比如 Email、Markdown、PDF、Youtube …
对索引的支持
- 文档分割器
- 向量化
- 对接向量存储与搜索,比如 Chroma、Pinecone、Qdrand
Chains
- LLMChain
- 各种工具Chain
- LangChainHub
相信大家看完上面的介绍多半会一脸懵逼。不要担心,上面的概念其实在刚开始学的时候不是很重要,当我们讲完后面的例子之后,在回来看上面的内容会一下明白很多。但是,下面几个概念是必须知道的。
2.1 Loader 加载器
Loader文档
顾名思义,这个就是从指定源进行加载数据的。比如:
- 文件夹 、
- Azure 存储
- CSV文件
- 印象笔记
- Google网盘
- 任意的网页
- S3 /、
- Youtube
上面只是简单的进行列举了几个,官方提供了超级的多的加载器供你使用。
2.2 Document 文档
当使用加载器读取到数据源后,数据源需要转换成 对象后,后续才能进行使用。
2.3 Text Spltters 文本分割
顾名思义,文本分割就是用来分割文本的。为什么需要分割文本?因为我们每次不管是做把文本当作 发给 ,还是还是使用 功能都是有字符限制的。
比如我们将一份300页的 pdf 发给 ,让他进行总结,他肯定会报超过最大 Token 错。所以这里就需要使用文本分割器去分割我们 进来的 。
2.4 Vectorstores 向量数据库
Vectorstores文档
因为数据相关性搜索其实是向量运算。所以,不管我们是使用 功能还是直接通过向量数据库直接查询,都需要将我们的加载进来的数据 进行向量化,才能进行向量运算搜索。转换成向量也很简单,只需要我们把数据存储到对应的向量数据库中即可完成向量的转换。官方也提供了很多的向量数据库供我们使用。
2.5 Chain(链)
Chains文档
我们可以把 Chain 理解为任务。一个 Chain 就是一个任务,当然也可以像链条一样,一个一个的执行多个链。
2.6 Agent 代理
我们可以简单的理解为他可以动态的帮我们选择和调用chain或者已有的工具,执行过程可以参考下面这张图:
2.7 Embedding
Embedding用于衡量文本的相关性,这个也是 OpenAI API 能实现构建自己知识库的关键所在。相比 最大的优势就是,不用进行训练,并且可以实时添加新的内容,而不用加一次新的内容就训练一次,并且各方面成本要比 fine-tuning 低很多。具体比较和选择可以参考此视频。
下面使用Open AI 范例进行说明,大家也可以根据自己任务的需要换成自己需要的 LLM 模型。
3.1 单次问答
在开始之前,我们需要先设置我们的 openai 的 key,这个 key 可以在用户管理里面创建,这里就不细说了。
然后,我们进行导入和执行
这时,我们就可以看到他给我们的返回结果了,怎么样,是不是很简单。
3.2 通过 Google 搜索并返回答案
接下来,我们就来搞点有意思的。我们来让我们的 OpenAI api 联网搜索,并返回答案给我们。
这里我们需要借助 来进行实现, 提供了 google 搜索的 api 接口。首先需要我们到 Serpapi官网上注册一个用户,并复制他给我们生成 。然后我们需要像上面的 一样设置到环境变量里面去。
貌似对中文不是很友好,所以提问的 prompt 建议使用英文。
然后,开始编写我的代码
我们可以看到,他正确的返回了日期(有时差),并且返回了历史上的今天。
在 chain 和 agent 对象上都会有 这个参数,这个是个非常有用的参数,开启他后我们可以看到完整的 chain 执行过程。可以在上面返回的结果看到,他将我们的问题拆分成了几个步骤,然后一步一步得到最终的答案。
关于 几个选项的含义(理解不了也不会影响下面的学习,用多了自然理解了):
- ::根据工具的描述和请求内容的来决定使用哪个工具(最常用)
- ::使用 ReAct 框架和 docstore 交互, 使用 和 工具, 前者用来搜, 后者寻找term, 举例: 工具
- :此代理只使用一个工具: Intermediate Answer, 它会为问题寻找事实答案(指的非 gpt 生成的答案, 而是在网络中,文本中已存在的), 如 工具
- ::为会话设置而设计的代理, 它的prompt会被设计的具有会话性, 且还是会使用 ReAct 框架来决定使用来个工具, 并且将过往的会话交互存入内存
LLM 的 ReAct 模式的 Python 实现: https://til.simonwillison.net/llms/python-react-pattern
当然,官方已经写好了 的 agent,未来 chatgpt 能用啥插件,我们在 api 里面也能用插件,想想都美滋滋。不过目前只能使用不用授权的插件,期待未来官方解决这个,感兴趣的可以看此文档。
Chatgpt 只能给官方赚钱,而 Openai API 能给我赚钱
3.3 对超长文本进行总结
假如我们想要用 对一段文本进行总结,我们通常的做法就是直接发给 api 让他总结。但是如果文本超过了 api 最大的 token 限制就会报错。
这时,我们一般会进行对文章进行分段,比如通过 计算并分割,然后将各段发送给 api 进行总结,最后将各段的总结再进行一个全部的总结。
可以很好的帮我们处理这个过程,代码也很简单。
首先我们对切割前和切割后的 document 个数进行了打印,我们可以看到,切割前就是只有整篇的一个 document,切割完成后,会把上面一个 document 切成 317 个 document。
最终输出了对前 5 个 document 的总结。
这里有几个参数需要注意:
1. 文本分割器的 参数
这个是指切割后的每个 document 里包含几个上一个 document 结尾的内容,主要作用是为了增加每个 document 的上下文关联。比如,时, 第一个 document 为 aaaaaa,第二个为 bbbbbb;当 时,第一个 document 为 aaaaaa,第二个为 aabbbbbb。不过,这个也不是绝对的,要看所使用的那个文本分割模型内部的具体算法。
文本分割器参考此文档
2. chain 的 参数
这个参数主要控制了将 document 传递给 llm 模型的方式,一共有 4 种方式:
-
::这种最简单粗暴,会把所有的 document 一次全部传给 llm 模型进行总结。如果document很多的话,势必会报超出最大 token 限制的错,所以总结文本的时候一般不会选中这个。
-
::这个方式会先将每个 document 进行总结,最后将所有 document 总结出的结果再进行一次总结。
- ::这种方式会先总结第一个 document,然后在将第一个 document 总结出的内容和第二个 document 一起发给 llm 模型在进行总结,以此类推。这种方式的好处就是在总结后一个 document 的时候,会带着前一个的 document 进行总结,给需要总结的 document 添加了上下文,增加了总结内容的连贯性。
- ::这种一般不会用在总结的 chain 上,而是会用在问答的 chain 上,他其实是一种搜索答案的匹配方式。首先你要给出一个问题,他会根据问题给每个 document 计算一个这个 document 能回答这个问题的概率分数,然后找到分数最高的那个 document ,在通过把这个 document 转化为问题的 prompt 的一部分(问题+document)发送给 llm 模型,最后 llm 模型返回具体答案。
3.4 构建本地知识库问答机器人
Openai embeddings文档
在这个例子中,我们会介绍如何从我们本地读取多个文档构建知识库,并且使用 Openai API 在知识库中进行搜索并给出答案。这个是个很有用的教程,比如可以很方便的做一个可以介绍公司业务的机器人,或是介绍一个产品的机器人。
我们可以通过结果看到,他成功的从我们的给到的数据中获取了正确的答案。
3.5 构建向量索引数据库
我们上个案例里面有一步是将 document 信息转换成向量信息,和embeddings的信息并临时存入 数据库。因为是临时存入,所以当我们上面的代码执行完成后,上面的向量化后的数据将会丢失。如果想下次使用,那么就还需要再计算一次embeddings,这肯定不是我们想要的。
本案例,我们通过 和 这两个数据库,来讲一下如何做向量数据持久化。更多 LangChain 数据库,参考此文档。
1. Chroma
是个本地的向量数据库,他提供的一个 来设置持久化目录进行持久化。读取时,只需要调取 方法加载即可。
2. Pinecone
Pinecone是一个在线的向量数据库。所以,我可以第一步依旧是注册,然后拿到对应的 api key。(免费版如果索引14天不使用会被自动清除。)
一个简单从数据库获取 embeddings并回答的代码如下
3.6 使用GPT3.5模型构建油管频道问答机器人
在 chatgpt api(也就是 GPT-3.5-Turbo)模型出来后,因钱少活好深受大家喜爱,所以 LangChain 也加入了专属的链和模型,我们来跟着这个例子看下如何使用他。
我们可以看到他能很准确的围绕这个油管视频进行问答
使用流式回答也很方便
3.7 用 OpenAI 连接万种工具
我们主要是结合使用 zapier来实现将万种工具连接起来。所以我们第一步依旧是需要申请账号和他的自然语言 api key。zapier api key 填入基础信息后,基本可以秒在邮箱里看到审核通过的邮件。
然后,我们通过右键里面的连接打开我们的api 配置页面。我们点击右侧的 来配置我们要使用哪些应用。我在这里配置了 Gmail 读取和发邮件的 action,并且所有字段都选的是通过 AI 猜。
配置好后,我们开始写代码
我们可以看到他成功读取了给他发送的最后一封邮件,并将总结的内容又发送给了。这是我发送给 Gmail 的邮件:
这只是个小例子,因为 有数以千计的应用,所以我们可以轻松结合 搭建自己的工作流。
主要知识点已经讲完了,下面用一些比较有趣的小例子,当作拓展延伸。
4.1 执行多个chain
是链式的,所以他也可以按顺序依次去执行多个 chain:
4.2 结构化输出
有时候我们希望输出的内容不是文本,而是像 json 那样结构化的数据。
4.3 爬取网页并输出JSON数据
有些时候我们需要爬取一些结构性比较强的网页,并且需要将网页中的信息以JSON的方式返回回来。我们就可以使用 类去实现,具体可以参考下面代码
为了方便理解,我在例子中直接使用了Prompt的方法去格式化输出结果,而没用使用上个案例中用到的 去格式化,也算是提供了另外一种格式化的思路
我们可以看到,他很好的将格式化后的结果输出了出来
4.4 自定义agent中所使用的工具
自定义工具里面有个比较有意思的地方,使用哪个工具的权重是靠 来实现的,和我们之前编程靠数值来控制权重完全不同。
比如 Calculator 在描述里面写到,如果你问关于数学的问题就用他这个工具。我们就可以在上面的执行过程中看到,他在我们请求的 prompt 中数学的部分,就选用了Calculator 这个工具进行计算。
4.5 使用Memory实现一个带记忆的对话机器人
上一个例子我们使用的是通过自定义一个列表来存储对话的方式来保存历史的。当然,你也可以使用自带的 memory 对象来实现这一点。
4.6 使用 Hugging Face 模型
使用 Hugging Face 模型之前,需要先设置环境变量
使用在线的 Hugging Face 模型
将 Hugging Face 模型直接拉到本地使用
将模型拉到本地使用的好处:
- 训练模型
- 可以使用本地的 GPU
- 有些模型无法在 Hugging Face 运行
4.7 通过自然语言执行SQL命令
参考教程《Spark SQL Agent》、《SQL Database Agent》
我们通过 或者 都可以实现执行SQL命令的操作