使用 Elasticsearch、OpenAI 和 LangChain 进行语义搜索

在本教程中,我将引导您使用 Elasticsearch、OpenAI、LangChain 和 FastAPI 构建语义搜索服务。

LangChain 是这个领域的新酷孩子。 它是一个旨在帮助你与大型语言模型 (LLM) 交互的库。 LangChain 简化了与 LLMs 相关的许多日常任务,例如从文档中提取文本或在向量数据库中对它们建立索引。 如果你现在正在与 LLMs 一起工作,LangChain 可以节省你的工作时间。

然而,它的一个缺点是,尽管它的文档很广泛,但可能比较分散,对于新手来说很难理解。 此外,大多数在线内容都集中在最新一代的向量数据库上。 由于许多组织仍在使用 Elasticsearch 这样经过实战考验的技术,我决定使用它编写一个教程。

我将 LangChain 和 Elasticsearch 结合到了最常见的 LLM 应用之一:语义搜索。 在本教程中,我将引导你使用 Elasticsearch、OpenAI、LangChain 和 FastAPI 构建语义搜索服务。 你将创建一个应用程序,让用户可以提出有关马可·奥勒留《沉思录》的问题,并通过从书中提取最相关的内容为他们提供简洁的答案。

让我们深入了解吧!

前提条件

你应该熟悉这些主题才能充分利用本教程:

  • Elasticsearch:语义搜索、知识图和向量数据库概述

  • Elasticsearch:关于在 Python 中使用 Elasticsearch 你需要知道的一切 - 8.x

此外,你必须安装 Docker 并在 OpenAI 上创建一个帐户。

设计语义搜索服务

你将构建一个包含三个组件的服务:

  • 索引器:这将创建索引,生成嵌入和元数据(在本例中为书籍的来源和标题),并将它们添加到向量数据库中。
  • 矢量数据库:这是一个用于存储和检索生成的嵌入的数据库。
  • 搜索应用程序:这是一个后端服务,它使用用户的搜索词,从中生成嵌入,然后在矢量数据库中查找最相似的嵌入。

这是该架构的示意图:

接下来,你将设置本地环境。

设置你的本地环境

请按照以下步骤设置您的本地环境:

1)安装 Python 3.10。
2)安装 Poetry。 它是可选的,但强烈推荐。

sudo pip install poetry


3)  克隆项目的存储库:

git clone https://github.com/liu-xiao-guo/semantic-search-elasticsearch-openai-langchain

4)从项目的根文件夹中,安装依赖项:

  • 使用 Poetry:在项目同目录下创建虚拟环境并安装依赖:
poetry config virtualenvs.in-project true
poetry install
  • 使用 venv 和 pip:创建虚拟环境并安装 requirements.txt 中列出的依赖项:
python3.10 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

5)打开 src/.env-example,添加你的 OpenAI 密钥,并将文件另存为 .env。

(.venv) $ pwd
/Users/liuxg/python/semantic-search-elasticsearch-openai-langchain/src
(.venv) $ ls -al
total 32
drwxr-xr-x   7 liuxg  staff  224 Sep 17 17:27 .
drwxr-xr-x  13 liuxg  staff  416 Sep 17 21:23 ..
-rw-r--r--   1 liuxg  staff   41 Sep 17 17:27 .env-example
-rw-r--r--   1 liuxg  staff  870 Sep 17 17:27 app.py
-rw-r--r--   1 liuxg  staff  384 Sep 17 17:27 config.py
drwxr-xr-x   3 liuxg  staff   96 Sep 17 17:27 data
-rw-r--r--   1 liuxg  staff  840 Sep 17 17:27 indexer.py
(.venv) $ mv .env-example .env
(.venv) $ vi .env

到目前为止,你将设置一个包含所需库和存储库的本地副本的虚拟环境。 你的项目结构应该如下所示:

.
├── LICENSE
├── README.md
├── docker-compose.yml
├── .env
├── poetry.lock
├── pyproject.toml
├── requirements.txt
└── src├── app.py├── config.py├── .env├── .env-example     ├── data│   └── Marcus_Aurelius_Antoninus_-_His_Meditations_concerning_himselfe└── indexer.py

请注意:在上面的文件结构中,有两个 .env 文件。根目录下的 .env 文件是为 docker-compose.yml 文件所使用,而 src 目录里的文件是为应用所示使用。我们可以在根目录里的 .env 文件中定义想要的 Elastic Stack 版本号。

这些是项目中最相关的文件和目录:

  • poetry.lock 和 pyproject.toml:这些文件包含项目的规范和依赖项,被 Poetry 用来创建虚拟环境。
  • requirements.txt:该文件包含项目所需的 Python 包列表。
  • docker-compose.yml:此文件包含用于在本地运行 Elasticsearch 集群及 Kibana。
  • src/app.py:该文件包含搜索应用程序的代码。
  • src/config.py:此文件包含项目配置规范,例如 OpenAI 的 API 密钥(从 .env 文件读取)、数据路径和索引名称。
  • src/data/:该目录包含最初从维基文库下载的 Meditations 。 你将使用它作为本教程的文本语料库。
  • src/indexer.py:此文件包含用于创建索引并将文档插入 Elasticsearch 的代码。
  • .env-example:此文件通常用于环境变量。 在本例中,你可以使用它将 OpenAI 的 API 密钥传递给您的应用程序。
  • .venv/:该目录包含项目的虚拟环境。

全做完了! 我们继续向下进行吧。

启动本地 Elasticsearch 集群

在我们进入代码之前,你应该启动一个本地 Elasticsearch 集群。 打开一个新终端,导航到项目的根文件夹,然后运行:

docker-compose up

在上面的部署中,出于方便,我们使用了没有带安全的 Elastic Stack 的安装以方便进行开发。具体的安装步骤,请参阅另外一篇文章 “Elasticsearch:如何在 Docker 上运行 Elasticsearch 8.x 进行本地开发”。如果一切顺利,我们可以使用如下的命令来进行查看:

docker ps
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                              NAMES
a2866c0356a2   kibana:8.9.2          "/bin/tini -- /usr/l…"   4 minutes ago   Up 4 minutes   0.0.0.0:5601->5601/tcp             kibana
b504079c59ea   elasticsearch:8.9.2   "/bin/tini -- /usr/l…"   4 minutes ago   Up 4 minutes   0.0.0.0:9200->9200/tcp, 9300/tcp   elasticsearch

我们可以在浏览器中针对 Elasticsearch 进行访问:

我们还可以在 localhost:5601 上访问 Kibana:

拆分书籍并为其建立索引

在此步骤中,你将执行两件事:

  • 通过将书中的文本拆分为 1,000 个 token 的块来处理该文本。
  • 对你在 Elasticsearch 集群中生成的文本块(从现在开始称为文档)建立索引。

看一下 src/indexer.py

from langchain.document_loaders import BSHTMLLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import ElasticVectorSearchfrom config import Paths, openai_api_keydef main():loader = BSHTMLLoader(str(Paths.book))data = loader.load()text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(chunk_size=1000, chunk_overlap=0)documents = text_splitter.split_documents(data)embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)db = ElasticVectorSearch.from_documents(documents,embeddings,elasticsearch_url="http://localhost:9200",index_name="elastic-index",)print(db.client.info())if __name__ == "__main__":main()

此代码采用 Meditations(书),将其拆分为 1,000 个 token 的文本块,然后在 Elasticsearch 集群中为这些块建立索引。 以下是详细的细分:

  • 第 1 行到第 4 行从 langchain 导入所需的组件:
    • BSHTMLLoader:此 Loader 使用 BeautifulSoup4 来解析文档。
    • OpenAIembeddings:该组件是 OpenAI 嵌入的包装器。 它可以帮助你生成文档和查询的嵌入。
    • RecursiveCharacterTextSplitter:此实用程序函数通过尝试按旨在保持语义相似内容邻近的顺序尝试各种字符来分割输入文本。 用于分割的字符按以下顺序排列为:“\n\n”、“\n”、“ ”、“”。
    • ElasticSearchVector:这是 Elasticsearch 客户端的包装器,可简化与集群的交互。
  • 第 6 行从 config.py 导入相关配置
  • 第 11 行和第 12 行使用 BSHTMLLoader 提取书籍的文本。
  • 第 13 至 16 行初始化文本拆分器,并将文本拆分为不超过 1,000 个标记的块。 在这种情况下,你可以使用 tiktoken 来计算 token,但你也可以使用不同长度的函数,例如计算字符数而不是 token 或不同的 token 化函数。
  • 第 18 至 25 行初始化嵌入函数,创建新索引,并对文本拆分器生成的文档建立索引。 在 elasticsearch_url 中,你指定应用程序在本地运行的端口,在index_name 中指定你将使用的索引的名称。 最后,打印 Elasticsearch 客户端信息。

要运行此脚本,请打开终端,激活虚拟环境,然后从项目的 src 文件夹中运行以下命令:

# ../src/
export export OPENAI_API_KEY=your_open_ai_token
python indexer.py

注意:你如果使用 OpenAI 来进行矢量化,那么你需要在你的账号中有充分的钱来支付这种费用,否则你可能得到如下的错误信息:

Retrying langchain.embeddings.openai.embed_with_retry.<locals>._embed_with_retry in 4.0 seconds as it raised RateLimitError: You exceeded your current quota, please check your plan and billing details..

如果一切顺利,你应该得到与此类似的输出:

{'name': '0e1113eb2915', 'cluster_name': 'docker-cluster', 'cluster_uuid': 'og6mFMqwQtaJiv_3E_q2YQ', 'version': {'number': '8.9.2', 'build_flavor': 'default', 'build_type': 'docker', 'build_hash': '09520b59b6bc1057340b55750186466ea715e30e', 'build_date': '2023-03-27T16:31:09.816451435Z', 'build_snapshot': False, 'lucene_version': '9.5.0', 'minimum_wire_compatibility_version': '7.17.0', 'minimum_index_compatibility_version': '7.0.0'}, 'tagline': 'You Know, for Search'}

接下来,让我们创建一个简单的 FastAPI 应用程序,以与你的集群进行交互。

创建搜索应用程序

在此步骤中,你将创建一个简单的应用程序来与 Meditations 交互。 你将连接到 Elasticsearch 集群,始化检索提问/应答 Chain,并创建一个 /ask 端点以允许用户与应用程序交互。

看一下 src/app.py 的代码:

from fastapi import FastAPI
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import ElasticVectorSearchfrom config import openai_api_keyembedding = OpenAIEmbeddings(openai_api_key=openai_api_key)db = ElasticVectorSearch(elasticsearch_url="http://localhost:9200",index_name="elastic-index",embedding=embedding,
)
qa = RetrievalQA.from_chain_type(llm=ChatOpenAI(temperature=0),chain_type="stuff",retriever=db.as_retriever(),
)app = FastAPI()@app.get("/")
def index():return {"message": "Make a post request to /ask to ask questions about Meditations by Marcus Aurelius"}@app.post("/ask")
def ask(query: str):response = qa.run(query)return {"response": response,}

此代码允许用户提出有关马库斯·奥勒留《沉思录》的问题,并向用户提供答案。 让我向你展示它是如何工作的:

  • 第 1 至 5 行导入所需的库:
    • FastAPI:此类初始化应用程序。
    • RetrievalQA:这是一个允许你询问有关向量数据库中文档的问题的 Chain。 它根据你的问题找到最相关的文档并从中生成答案。
    • ChatOpenAI:这是 OpenAI 聊天模型的包装。
    • OpenAIembeddings 和 ElasticVectorSearch:这些是上一节中讨论的相同包装器。
  • 第 7 行导入 OpenAI 密钥。
  • 第 9 至 15 行使用 OpenAI 嵌入初始化 Elasticsearch 集群。
  • 第 16 至 20 行使用以下参数初始化 RetrievalQA Chain:
    • llm:指定用于运行链中定义的提示的 LLM。
    • chain_type:定义如何从向量数据库检索和处理文档。 通过指定内容,将检索文档并将其传递到链以按原样回答问题。 或者,你可以在回答问题之前使用 map_reduce 或 map_rerank 进行额外处理,但这些方法使用更多的 API 调用。 有关更多信息,请参阅 langchain 文档。
    • retrieve:指定链用于检索文档的向量数据库。
  • 第 22 至 36 行初始化 FastAPI 应用程序并定义两个端点。 / 端点为用户提供有关如何使用应用程序的信息。 /ask 端点接受用户的问题(查询参数)并使用先前初始化的链返回答案。

最后,你可以从终端运行该应用程序(使用你的虚拟环境):

uvicorn app:app --reload

然后,访问 http://127.0.0.1:8000/docs,并通过询问有关这本书的问题来测试 /ask:

如果一切顺利,你应该得到这样的结果:

就是这样! 您现在已经启动并运行了自己的基于 Elasticsearch、OpenAI、Langchain 和 FastAPI 的语义搜索服务。

结论

干得好! 在本教程中,你学习了如何使用 Elasticsearch、OpenAI 和 Langchain 构建语义搜索引擎。

特别是,你已经了解到:

  • 如何构建语义搜索服务。
  • 如何使用 LangChain 对文档进行拆分和索引。
  • 如何使用 Elasticsearch 作为向量数据库与 LangChain 一起使用。
  • 如何使用检索问答链通过向量数据库回答问题。
  • 产品化此类应用程序时应考虑什么。

希望您觉得本教程有用。 如果你有任何疑问,请参入讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140088.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3 - Element Plus暗黑模式适配、切换及自定义颜色

GitHub Demo 地址 在线预览 Element Plus 2.2.0 版本开始支持暗黑模式&#xff0c;启用方式参考 Element Plus 官方文档 - 暗黑模式 demo通过Element Plus和VueUse 的 useDark 方法实现具有自动数据持久性的响应式暗黑模式。 安装 npm install element-plus --save npm in…

一个关于 i++ 和 ++i 的面试题打趴了所有人

前言 都说大城市现在不好找工作&#xff0c;可小城市却也不好招人。 我们公司招了挺久都没招到&#xff0c;主管感到有些心累。 我提了点建议&#xff0c;是不是面试问的太深了&#xff0c;在这种小城市&#xff0c;能干活就行。 他说自己问的面试题都很浅显&#xff0c;如果答…

HUAWEI华为荣耀猎人游戏本V700 i7独显2060(FRD-WFD9)原装出厂Windows10系统工厂模式(含F10还原)

华为HONOR荣耀笔记本原厂系统镜像包&#xff0c;安装恢复时自动创建F10一键智能还原功能 链接&#xff1a;https://pan.baidu.com/s/1_px_3Fr9qEE6jExz1eKKKg?pwdk6uc 提取码&#xff1a;k6uc 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、华为电脑管家等预装程序…

ChatGLM GPT原理介绍

图解GPT 除了BERT以外,另一个预训练模型GPT也给NLP领域带来了不少轰动,本节也对GPT做一个详细的讲解。 OpenAI提出的GPT-2模型(https://openai.com/blog/better-language-models/) 能够写出连贯并且高质量的文章,比之前语言模型效果好很多。GPT-2是基于Transformer搭建的,相…

SQL Server数据库中了360后缀勒索病毒怎么办,勒索病毒解密数据恢复

随着互联网的发展&#xff0c;网络安全问题日益凸显&#xff0c;勒索病毒已经成为当今数字威胁中的一大主要犯罪行为之一。其中&#xff0c;360后缀勒索病毒作为一种常见的数据库攻击形式&#xff0c;对数据库的安全性提出了极大挑战。近期我们收到很多企业的求助&#xff0c;企…

QT:使用行编辑器、文本编辑器、单选按钮、水平布局、垂直布局做一个小项目

widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QLineEdit> //行编辑器 #include <QTextEdit> //文本编辑器 #include <QRadioButton> //单选按钮class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *pare…

项目进度网络图

概念 项目网络图是项目所有活动及其之间逻辑关系&#xff08;依赖关系&#xff09;的一个图解表示&#xff0c;并从左到右来表示项目的时间顺序。 可手工编制也可用计算机实现。可包括整个项目的全部细节&#xff0c;也可包含一个或多个概括性活动&#xff0c;还相应伴有一个…

uniapp中vue3使用uni.createSelectorQuery().in(this)报错

因为VUE3中使用setup没有this作用域&#xff0c;所以报错 解决办法&#xff1a;使用getCurrentInstance()方法获取组件实例 import { getCurrentInstance } from vue;const instance getCurrentInstance(); // 获取组件实例 const DOMArr uni.createSelectorQuery().in(ins…

智荟康午休课桌椅成为第十届中国慈善展览会公益亮点产品

第十届中国慈善展览会&#xff08;以下简称“慈展会”&#xff09;于9月15日至17日在深圳会展中心隆重举办&#xff0c;此次展会为期3天&#xff0c;主要围绕“共建现代化慈善&#xff0c;聚力高质量发展”的主题&#xff0c;重点聚焦聚力民生福祉&#xff0c;将打造“一展多元…

asrpro 天问BLOCK 总结

ASRPRO芯片信息 主频240MHz 640KByte SRAM 2-4M FLASH (https://haohaodada.com/jpeguploadfile/twen/ASRPRO/asr_pro_core.pdf) 下载 &#xff08;注意最好用好点的USB转TTL或是网方的下载器&#xff0c;否则会怀疑人生&#xff09; 下载程序步骤 安装VSCODE 在字符模式下&a…

CSDN热榜分析3:实现领域热榜的爬取

文章目录 领域热榜爬取函数领域热榜按钮功能实现 热榜分析系列&#xff1a; CSDN热榜分析&#x1f525; UI界面&#x1f525; 领域热榜 领域热榜爬取函数 CSDN热榜共有21个子领域&#xff0c;包括C、云原生、人工智能、前沿技术、软件工程、后端、Java、JavaScript、PHP、P…

程序员的快乐如此简单

最近在GitHub上发起了一个关于Beego框架的小插件的开源仓库&#xff0c;这一举动虽然看似微小&#xff0c;但其中的快乐和意义却是无法用言语表达的。 Beego是一个开源的Go语言Web框架&#xff0c;它采用了MVC架构模式&#xff0c;并集成了很多常用的功能和中间件。小插件是指…

手机也可以将声音转为字幕!支持中英日韩4种语言

快去看看你的华为手机有没有这个功能——AI字幕&#xff0c;可以将手机里的音频转换为文字&#xff08;以字幕形式展现&#xff0c;可保存在手机备忘录&#xff09; AI字幕有什么用途&#xff1f; 1. 在听觉不太好使的环境下&#xff0c;将音频信息转化到视觉&#xff08;文本…

C++中的Template

模板的概念 建立通用的模具&#xff0c;大大提高复用性 模板不可直接使用 函数模板 函数模板语法 函数模板作用&#xff1a; 建立一个通用函数&#xff0c;其函数返回值类型和形参类型可以不具体制定&#xff0c;用一个虚拟的类型来代表。 语法&#xff1a; template&l…

LeetCode_二叉树_中等_1448.统计二叉树中好节点的数目

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。「好节点」X 定义为&#xff1a;从根到该节点 X 所经过的节点中&#xff0c;没有任何节点的值大于 X 的值。 示例 1&#xff1a; 输…

有名管道及其应用

创建FIFO文件 1.通过命令&#xff1a; mkfifo 文件名 2.通过函数: mkfifo #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 参数&#xff1a; -pathname&#xff1a;管道名称的路径 -mode&#xff1a;文件的权限&a…

linux、windows的pip一键永久换源[清华源、中科大、豆瓣、阿里云]

前言 本文概述&#xff1a;linux、windows操作系统一键将pip下载源永久设置为国内下载源&#xff0c;避免了使用临时源需要到处找镜像地址的麻烦。 作者介绍&#xff1a;作者本人是一名人工智能炼丹师&#xff0c;目前在实验室主要研究的方向为生成式模型&#xff0c;对其它方向…

SpringBoot中xml映射文件

1.规范 说明&#xff1a;XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;。 xML映射文件的namespace属性为Mapper接口全类名一致。 XML映射文件中sql语句的id与Mapper接口中的方法名一致&…

谷歌浏览器jsonView插件安装与使用

1、打开 https://github.com &#xff1b; 2、搜索 jsonView 链接&#xff1a;https://gitee.com/wangl2020/chrome_JSONVue 3、选择需要的插件我是选这个&#xff1b; 4、点击【Download Zip】&#xff0c;插件下载完成&#xff0c;解压缩到相应目录&#xff08;D:\Downloa…

pycharm 中package, directory, sources root, resources root的区别

【遇到的问题】 导入yolov5中有utils文件&#xff0c;自己的代码中也有utils文件&#xff0c;使得yolov5中的这部分引用出错了。 【解决方案】 单独建立detection文件夹&#xff0c;把检测相关的都放在这里&#xff0c;yolov5是github上拉取的源码&#xff0c;发现yolov5中fr…