学习如何使用AI应用开发框架LangChain入门
首先
本文的目的
本文是为对LangChain技术感兴趣的读者提供信息而撰写的,旨在尽可能地提供入门级的知识,不需要任何预先的背景知识。
主要的目标读者
这篇稿件的主要目标读者是涉及商业应用程序开发和架构的人员。因此,重点更加注重实践方面(例如,从利用生成人工智能进行服务开发的角度)。
本文的结构
本文的结构包括以下三个步骤。
-
- 生成AI活用アプリケーション概要
-
- ベクトル検索
- LangChain入門
生成AI活用应用程序概述
首先,我们将看一下利用生成人工智能的应用程序是什么样的。
在生成AI应用程序的内部,用户输入的数据被处理为LLM的输入,即大规模语言模型。如果考虑最简单的构成,则可以简单地认为是调用外部的LLM API。

由于在现有的LLM上进行简单使用,无论是通过API调用还是使用开源模型,都无法为企业的服务或内部系统提供实质性的价值,这一点可以立即理解。因此,在这种情况下,利用企业拥有的数据变得至关重要。

在这个图表中,概念上我们将其表达为“企业持有数据+LLM=增强LLM”。然而,在增强LLM的方法方面,我们具体介绍了两种方式。
调整细节设置
第一种方法是进行微调。通过使用企业拥有的数据对现有的LLM进行重新训练,为该企业(如A公司)构建专属的LLM模型。

即时工程

接下来是Prompt Engineering。在这里,“Prompt”可以理解为对LLM的输入和理解。在LLM的提示中,不是直接使用用户的请求,而是通过使用与该请求相关的企业内部数据来处理,即进行工程处理,可以将系统对用户的响应转化为具有企业独特价值的回应。
术语“Prompt Engineering”给人一种工程师的视角,而术语“Context Injection”,即上下文插入,也被用来指代同一概念。
在这里,向LLM发出的请求,简单来说,就是回答用户的请求,如左下方的气泡中所示。在这种情况下,要利用以下提供的数据。
在这里,从企业拥有的大量数据中快速找到与用户问题相关的信息非常重要。在这里,数据的搜索需要能够处理非结构化数据,例如自然语言(在这里,“能够处理”这个表达上,不仅仅意味着可以操作,还暗示了对自然语言的理解和处理)。
矢量搜索
在这里,用于搜索非结构化数据的目的的向量搜索变得重要。

目前,有许多提供向量搜索功能的向量存储或向量数据库应运而生。同时,诸多向量搜索引擎和向量数据库也相继问世。
向量数据库存储了向量数据,并通过对这些向量数据进行索引化,实现了向量检索的功能。
向量搜索是什么?
接下来,我将解释一下什么是向量搜索。
在谷歌的一篇博客文章中(标题为“实现对所有数据的即时访问的谷歌向量搜索技术”),对于向量搜索的内容被描述为一项能够即刻访问任何数据的技术。

这个表达是基于所有数据都可以转换为“数字向量”的前提。
此外,无论是文本还是图像,只要将数据集转换为向量,就可以通过数值计算来比较数据之间的相似性。这些是向量搜索的两个关键 。
向量数据库所涵盖的是即后者,即用于检测向量之间相似性的算法。这样的算法有几种类型,而每种向量数据库所涵盖的范围也不同。
在另一方面,数据向量化也存在许多不同的方法。您可以参考以下文章来了解向量化(嵌入)的含义。
在向量数据库中,有一些是专门为向量搜索而开发的,例如Pinecone和Milvus,还有一些是在现有数据库中添加了向量搜索功能的,例如PostgreSQL和MongoDB。
通过在Apache Cassandra中添加向量搜索功能,DataStax Astra可以作为为生成AI应用程序而设计的向量数据库。

Cassandra的向量相似度搜索引擎:JVector

简要介绍AstraDB和即将在Cassandra的下一个版本中搭载的向量相似性搜索引擎。
在AstraDB的最初实现中,采用了业界标准的Lucene。然而,DataStax的工程师们发现了Lucene实现中存在的问题,于是他们开发了一款名为JVector的独特向量引擎,并为Cassandra社区做出了贡献。
此外,构成向量搜索引擎的索引功能使用了一种称为存储附加索引的技术,以克服传统的Cassandra索引所面临的问题。
通过这些开发,AstraDB的搜索引擎实现了比行业标准的Lucene高出12倍以上的吞吐量。
如果您对 Jvector 开发的经过感兴趣,可以阅读我们的博客《解决向量搜索中五个困难问题及 Cassandra 的方法》,详细介绍了其发展历程。请务必前往查看。
在这里,我们将从这篇文章中引用一张图。

我們進行了專門針對向量搜索的向量存儲庫Pinecone和AstraDB的吞吐量性能比較。紫色表示AstraDB,灰色表示Pinecone。
灰色的柱狀圖有兩個,而深灰色的柱狀圖表示的是在數據庫中進行數據索引的性能。
這樣,專門的向量存儲庫中,由於數據更改引起的索引更新對性能的影響非常顯著。
向量搜索功能包括兩個步驟:數據索引和利用索引進行搜索。
這種性能差異可能是由於對運營中的數據添加和更新需求的不同考慮所導致的。在某些用例中,這可能仍然足夠。例如,在研發領域等情況下,這種缺點可能並不成問題。然而,對於僅限於向量搜索的一般性數據庫來說,在數據庫進行數據添加和更新的同時,整個數據庫的搜索性能受到影響是難以接受的。
SQL和向量搜索:简单易操作
在AstraDB中,对Cassandra进行功能扩展并添加了向量搜索的好处不止一个。

Cassandra的CQL是基于标准SQL的查询语言,现在在引入向量搜索功能后,用户可以将向量搜索功能当作CQL的扩展语法来使用。
作为具体扩展,引入了新的数据类型VECTOR。通过定义索引,可以对该类型进行向量搜索。此外,作为CQL查询的扩展,引入了用于向量搜索的操作符ANN OF。ANN代表近似最近邻搜索,即使用近似方法进行最近邻搜索。
有一篇关于Apache Cassandra的向量搜索功能的文章如下解释。
对于相似度的理解
此外,还提供了用于以数值形式衡量向量相似度的函数。
SELECT
description,
similarity_cosine(item_vector, [0.1, 0.15, 0.3, 0.12, 0.05])
FROM vsearch.products
ORDER BY item_vector ANN OF [0.1, 0.15, 0.3, 0.12, 0.05] LIMIT 1;
向量之间的关系是通过“相似度”而不是“匹配度”来衡量的。换句话说,向量搜索查询的本质是排序功能,而不是通常在SQL的WHERE子句中假设的搜索条件。因此,如果存储在数据库中的数据很稀少,或者查询向量与数据库内容差异较大,从向量数据库中返回的相似数据可能与之似乎没有关系。通过以数值形式衡量向量相似度,并丢弃低于某个特定数值的数据,可以避免这种不匹配。
向量数据库面临的三个挑战
我将对向量数据库可能面临的三个挑战进行整理。
検索インデックス更新
データは更新される
専用ベクトルストアは運用中のインデックス更新に難あり
スケールアウト
データは増える
性能要件は様々
データベースとしての汎用性
ベクトル検索だけでは事足りない
二つのデータベース(汎用とベクトルストア)を運用するか?
ベクトル検索機能を持った一つのデータベースを運用するのか?
開発への影響
既存の知識・経験を活かせる利点
正如前面提到的,专用的向量存储在索引更新期间可能存在问题,不适合进行正式的服务运营。此外,无论忽略索引化对性能的影响,数据库都会受到处理数据增加、以及作为服务使用量增加等变化的影响。像Cassandra这样被称为NoSQL的数据库就是为了应对这种规模扩展的挑战而开发的。此外,数据库的通用性不仅直接相关运营成本,还会影响开发成本。
LangChain的介紹
那么,现在我们开始进入LangChain的入门教程。
这篇文章无法对LangChain的所有功能做全面讨论,但是考虑到它在实际服务开发中的使用,我会根据过去的进展来解释一些有用的信息。
LLM框架的背景和优势
产生AI利用的选择

关于AI生成,除了涉及处理自然语言的一面,还有图像生成的方面,但在接下来的讨论中,特别关注自然语言处理,基于其在商业上的重要性。
生成AI在自然语言处理领域的应用包括理解和回答自然语言查询,以及针对非结构化数据如PDF文件的操作、搜索和摘要生成,这些都可以通过理解文本的含义和上下文来实现。在LangChain中,专门提供了相应的组件来实现这些功能。然而,生成AI的潜力远不止于此,特别是在商业化应用方面。
在服务化领域,LLM不仅可以用于直接与用户进行聊天等交互式服务,还可以作为系统的一部分与各种其他功能结合,从而发挥更大的价值。提供LLM的各个供应商都提供了API,通过利用这些API,实现此类服务变得更加容易。
例如,我们可以考虑使用系统自动发送邮件的方式来处理用户通过电子邮件提出的查询,而无需经过人工确认的流程,从而提高效率。另外,我们还可以考虑通过生成基于触发器上下文的文章,在电子商务活动中进行促销活动,并在网站上进行推送通知。
LLM可以根据指令来指定响应的输出格式,如JSON或CSV等系统可处理的格式,从而方便系统集成。
实际上,使用LLM将新服务用于企业似乎成功实现了这样的系统集成。另一方面,根据我个人的观点,透过LangChain的文档本身,或者以此为基础的解释性文章等,似乎会产生对文档处理和仅适用于聊天界面等领域的限制性印象。因此,在这里,我想尽量消除这种限定印象,并进行解释。
应用/框架的核心:代理

使用这种框架时,典型的应用程序的构成包括一个被称为代理的组件,正如其名称所示,该组件充当用户的代理人,扮演中介各种功能的角色。其中之一的角色是除了提示工程之外,还有各种缓存的操作。例如,缓存LLM的响应,并在接收到相同请求时返回缓存的响应,就像过去已经发生过一样。
更重要的是,这些LLM的响应可以多次执行以回应系统的单个请求目的。在提示工程技术的发展中,有各种关于利用这些LLM的见解被分享。这些见解以框架的形式可供使用。
作为这种框架的代表,有LangChain。
使用LangChain的好处
首先,让我们整理一下使用LangChain的好处。
典型的的AI应用程序结构的表示方式
首先,关于生成人工智能应用程序的典型结构,有如下的表达方式。
-
- モデル
-
- データ
-
- データソース(典型的なデータソースをベクトルストアにロードする機能)
-
- ベクトルストア
- エージェント
实施Prompt Engineering的各种技术。
同时,实施了各种提取工程的成果的多种技巧。
-
- ReAct
-
- Chain Of Thought
- FLARE、等
单独实施生成AI应用程序构建要素。
除此之外,还提供了多种不同的单独实施,如各种LLM模型和矢量存储,用于构建生成型人工智能应用程序的要素。
-
- LLM:OpenAI等
-
- ベクトルストア:PineCone, Cassandra等
- データローダー、等
在向向量存储中注册数据时,还可以考虑包含数据加载器功能。
卡西奥
在使用LangChain的优点中,Cassandra社区推出了CassIO项目作为最后一个要素。
CassIO的目标是在与各种生成AI框架结合时,将框架对Cassandra数据库的访问细节进行抽象化,实现Cassandra与框架的无缝集成。

从开发的角度来看CassIO,Cassandra类包含在langchain.vectorstore包中(实现由import cassio提供)。

这个Cassandra类与langchain.vectorstora包中的其他向量数据库具有相同的设计,因此开发者不一定需要了解Cassandra的专有技术就可以将Cassandra用作向量存储。
LangChain入门:数据源和向量存储
接下来,我们将着眼于应用程序开发的更具体方面。
作为人工智能中典型的数据源,文档和文本被提供相应的组件。这些组件从数据源中读取数据,并进行必要的转换,然后加载到向量存储以构建向量存储,涵盖了一系列操作。
在加载数据到向量存储中的“文档加载器”中,存在着以下多种不同的选择。
这些是框架在效用方面的一些方面,在这里只介绍一部分。
向量存储:嵌入级别的向量存储
如果我们将注意力转向VectorStore本身,我们首先会在LangChain的文档中看到的是一个被称为Chroma的VectorStore。Chroma可以说是一个嵌入级别的数据库,通过使用Chroma,您可以在Vector数据库中安装数据库,使用连接点和用户进行注册,而无需使用API密钥。然而,正如稍加思考就会明白的那样,对于真正的应用程序来说,这还不够。然而,在框架中,对VectorStore的操作已经在稍后介绍的类Retriever级别上进行了抽象化。因此,在开始学习LangChain时,使用Chroma可以快速入门。
使用DataStax Astra来操作向量数据库。
在这里,我们介绍使用AstraDB的情况,预计在更为正式的应用中使用。
使用cassandra包,初始化与Cassandra数据库的会话,并使用该会话作为参数,通过初始化Cassandra类来将AstraDB用作LangChain框架中的向量存储。
通过初始化该类,将自动在AstraDB上进行表定义和索引定义。
通过这个类,自动生成的表会成为一个共同模型,除了名字以外都是相同的。

基本上,记录由向量数据和向量化之前的数据构成。但是,我们可以将与用例相适应的值存储在由项目名和值组成的映射格式的列中,称为元数据。
创建定制的Retriever

在LangChain中,除了支持Cassandra和其他向量搜索功能的数据库外,还可以通过扩展BaseRetriever并定义自己的Retriever类来实现另一种方法。
在这种情况下,可以充分利用该数据库最原始的潜力。
在嵌入式级别的数据库中,可以进行与使用案例和数据模型相适应的设计和操作,例如,分布式环境下的数据布局优化,这是无法想象的。
而且,如果使用LangChain的向量存储类,即使没有数据库特定的知识,也可以执行通过元数据进行过滤、获取相似度评分等常规处理,而如果实现Retriever,将需要直接编写针对数据库的查询语句。
如果你不希望过多地关注对数据层的操作,那么使用类是合理的选择。但对于那些具备数据库系统开发经验的开发者来说,通过开发自定义的检索器(Retriever)类可以最大程度地利用设计和开发自由度。
LangChain开始使用:代理商
接下来,我将解释生成AI应用程序的核心代理。
在进入代理商细节之前,我们先提一下在LangChain中有一个类似于代理商的组件链,其目的与代理商相似。
这是从LangChain的文档中引用的,但是在链条中,一系列的动作是硬编码在代码中的,而在代理中,语言模型被用作推理引擎,决定以什么顺序执行哪些动作。
在LangChain上使用LLM有三种方法。
-
- まずは、単純に、モデルを直接実行することができます。
-
- 次にチェーンを使った場合、ハードコードした一連のアクションを実行可能と説明しましたが、チェーンを使わずにハードコードで順次モデルを実行した場合と比べた利点として、それらのアクションを実行する際のバッチ処理のさまざまな手法を活用できることがあります。
- そしてエージェントを利用した場合には、言語モデルを推論エンジンとして利用することができるという、非常に多くの利点があります。
那么,从这里开始我们来看一下代理商。
代理商的建立和使用
在这里,我们将重点介绍Agent和Retriever之间的关系。
正如LangChain的文档中所单独提到的那样,构建Agent有几种不同的方法可以在LangChain中实现。
通过解开它们之间的关系,我们希望提供一些信息来应用于今后的学习。
首先,LangChain中的“Agent with retrieval tool”部分的解释从构造函数用于构建代理的子部分开始。
个人认为这个表达有些误导性,这里所说的构造函数不是指构造函数方法负责初始化类,而是指用于普通函数或功能的表达。

通过使用LLM和TOOLS作为参数,构建名为create_conversational_retrieval_agent的函数中的agent_executor。
另外,请记住这个函数所在的包是langchain.agents.agent_toolkits。
通过实用方法创建代理。
查看这个函数的内部定义后,我们可以看到在执行了多个相关的处理步骤之后,创建了一个名为OpenAIFunctionsAgent的类的实例。
换句话说,这个函数并不是作为通用函数设计的,而是假定使用特定的最常用的LLM提供商的函数。考虑到这个函数包含在一个名为”工具包”的软件包中,这也不奇怪。
因此,可以将这个函数视为一个实用程序,它展示了使用LangChain框架的样本,而不是实现某种特定的自定义处理。
使用Agent类创建代理
重要的是,在这种情况下,我们需要创建一个名为AgentExecutor的通用类,而不是直接执行为OpenAI实现的代理。
我认为你可能已经理解到了,在此之前,我们选择了适用于各种模式的代理类,并为初始化提供了必要的元素,以构建代理。此外,我们还从该代理构建了代理执行器。
在这些准备中,LLM和提示等组件之外,还有一个名为工具的要素。
作为代理商工具的检索器
Retriever(检索器)被描述为一种对向量存储的操作进行抽象化的工具之一。具体而言,我们将Retriever转换为RetrieverTool的形式,以便作为代理工具使用。这个转换包括了代理如何使用该工具的说明。
LangChain代理是一种可扩展的设计,并提供了多种扩展类的实现,基于各种提示工程的成果。
这个工具元素用于吸收抽象化的层次、在使用LLM时附带的各种处理等。
最后的结论
这篇稿件结束了。
作为未来进一步讨论的话题,我认为仔细研究Prompt Engineering在LangChain中的发展和实施是非常有趣的探索。
另外,虽然这次无法涉及到内存缓存的功能,但我认为它也是一个值得讨论的主题。
类似这样的在LangChain中无法处理的部分或者其他生成AI应用程序的构建,可以考虑在之后的机会再进行讨论,例如使用其他的框架等。