Elasticsearch:BM25 及 使用 Elasticsearch 和 LangChain 的自查询检索器

news2024/11/16 7:50:40

本工作簿演示了 Elasticsearch 的自查询检索器将非结构化查询转换为结构化查询的示例,我们将其用于 BM25 示例。

在这个例子中:

  • 我们将摄取 LangChain 之外的电影样本数据集
  • 自定义 ElasticsearchStore 中的检索策略以仅使用 BM25
  • 使用自查询检索将问题转换为结构化查询
  • 使用文档和 RAG 策略来回答问题

安装

如果你还没有安装好自己的 Elasticsearch 及 Kibana,请参考文章:

安装 Elasticsearch 及 Kibana

如果你还没有安装好自己的 Elasticsearch 及 Kibana,那么请参考一下的文章来进行安装:

  • 如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch

  • Kibana:如何在 Linux,MacOS 及 Windows 上安装 Elastic 栈中的 Kibana

在安装的时候,请选择 Elastic Stack 8.x 进行安装。在安装的时候,我们可以看到如下的安装信息:

Python 安装包

我们需要安装 Python 版本 3.6 及以上版本。我们还需要安装如下的 Python 安装包:

pip3 install lark elasticsearch langchain openai load_dotenv
$ pip3 list | grep elasticsearch
elasticsearch                            8.12.0
rag-elasticsearch                        0.0.1        /Users/liuxg/python/rag-elasticsearch/my-app/packages/rag-elasticsearch

环境变量

在启动 Jupyter 之前,我们设置如下的环境变量:

export ES_USER="elastic"
export ES_PASSWORD="xnLj56lTrH98Lf_6n76y"
export ES_ENDPOINT="localhost"
export OPENAI_API_KEY="YOUR_OPEN_AI_KEY"

请在上面修改相应的变量的值。特别是你需要输入自己的 OPENAI_API_KEY。

拷贝 Elasticsearch 证书

我们把 Elasticsearch 的证书拷贝到当前的目录下:

$ pwd
/Users/liuxg/python/elser
$ cp ~/elastic/elasticsearch-8.12.0/config/certs/http_ca.crt .
$ ls http_ca.crt 
http_ca.crt

创建应用

我们在当前的目录下运行 jupyter notebook:

jupyter notebook

连接到 Elasticsearch

from elasticsearch import Elasticsearch
from dotenv import load_dotenv
import os
from elasticsearch import Elasticsearch

load_dotenv()
 
openai_api_key=os.getenv('OPENAI_API_KEY')
elastic_user=os.getenv('ES_USER')
elastic_password=os.getenv('ES_PASSWORD')
elastic_endpoint=os.getenv("ES_ENDPOINT")


url = f"https://{elastic_user}:{elastic_password}@{elastic_endpoint}:9200"
client = Elasticsearch(url, ca_certs = "./http_ca.crt", verify_certs = True)
 
print(client.info())

准备示例数据集

docs = [
    {
        "text": "A bunch of scientists bring back dinosaurs and mayhem breaks loose",
        "metadata": {"year": 1993, "rating": 7.7, "genre": "science fiction", "director": "Steven Spielberg", "title": "Jurassic Park"},
    },
    {
        "text": "Leo DiCaprio gets lost in a dream within a dream within a dream within a ...",
        "metadata": {"year": 2010, "director": "Christopher Nolan", "rating": 8.2, "title": "Inception"},
    },
    {
        "text": "A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea",
        "metadata": {"year": 2006, "director": "Satoshi Kon", "rating": 8.6, "title": "Paprika"},
    },
    {
        "text":"A bunch of normal-sized women are supremely wholesome and some men pine after them",
        "metadata":{"year": 2019, "director": "Greta Gerwig", "rating": 8.3, "title": "Little Women"},
    },
    {
        "text":"Toys come alive and have a blast doing so",
        "metadata":{"year": 1995, "genre": "animated", "director": "John Lasseter", "rating": 8.3, "title": "Toy Story"},
    },
    {
        "text":"Three men walk into the Zone, three men walk out of the Zone",
        "metadata":{
            "year": 1979,
            "rating": 9.9,
            "director": "Andrei Tarkovsky",
            "genre": "science fiction",
            "rating": 9.9,
            "title": "Stalker",
        }
    }
]

索引数据到 Elasticsearch

我们选择对 Langchain 外部的数据进行索引,以演示如何将 Langchain 用于 RAG 并在任何 Elasticsearch 索引上使用自查询检索。

from elasticsearch import helpers

# create the index
client.indices.create(index="movies_self_query")

operations = [
    {
      "_index": "movies_self_query",
      "_id": i,
      "text": doc["text"],
      "metadata": doc["metadata"]
    } for i, doc in enumerate(docs)
]

# Add the documents to the index directly
response = helpers.bulk(
  client,
  operations,
)

经过上面的操作后,我们可以在 Kibana 中进行查看:

设置查询检索器

接下来,我们将通过提供有关文档属性的一些信息和有关文档的简短描述来实例化自查询检索器。

然后我们将使用 SelfQueryRetriever.from_llm 实例化检索器

from langchain.vectorstores.elasticsearch import ApproxRetrievalStrategy
from typing import List, Union
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.llms import OpenAI
from langchain.vectorstores.elasticsearch import ElasticsearchStore

# Add details about metadata fields
metadata_field_info = [
    AttributeInfo(
        name="genre",
        description="The genre of the movie. Can be either 'science fiction' or 'animated'.",
        type="string or list[string]",
    ),
    AttributeInfo(
        name="year",
        description="The year the movie was released",
        type="integer",
    ),
    AttributeInfo(
        name="director",
        description="The name of the movie director",
        type="string",
    ),
    AttributeInfo(
        name="rating", description="A 1-10 rating for the movie", type="float"
    ),
]

document_content_description = "Brief summary of a movie"

# Set up openAI llm with sampling temperature 0
llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

class BM25RetrievalStrategy(ApproxRetrievalStrategy):

    def __init__(
        self
    ):
        pass

    def query(
        self,
        query: Union[str, None],
        filter: List[dict],
        **kwargs,
    ):
                
        if query:
            query_clause = [{
                "multi_match": {
                    "query": query,
                    "fields": ["text"],
                    "fuzziness": "AUTO",
                }
            }]
        else:
            query_clause = []


        bm25_query = {
            "query": {
                "bool": {
                    "filter": filter,
                    "must": query_clause
                }
            },
        }

        print("query", bm25_query)

        return bm25_query


vectorstore = ElasticsearchStore(
  index_name="movies_self_query",
  es_connection=client,
  strategy=BM25RetrievalStrategy()
)

仅使用 BM25 的检索器 

一种选择是自定义查询以仅使用 BM25 检索方法。 我们可以通过重写 custom_query 函数,指定查询仅使用 multi_match 来做到这一点。

在下面的示例中,自查询检索器使用 LLM 将问题转换为关键字和过滤器查询(query: dreams, filter: year range)。 然后使用自定义查询对关键字查询和过滤器查询执行基于 BM25 的查询。

这意味着如果你想在现有 Elasticsearch 索引上执行问题/答案用例,则不必对所有文档进行向量化。

from langchain.schema.runnable import RunnableParallel, RunnablePassthrough
from langchain.prompts import ChatPromptTemplate, PromptTemplate
from langchain.schema import format_document

retriever = SelfQueryRetriever.from_llm(
    llm, 
    vectorstore, 
    document_content_description, 
    metadata_field_info, 
    verbose=True
)

LLM_CONTEXT_PROMPT = ChatPromptTemplate.from_template("""
Use the following context movies that matched the user question. Use the movies below only to answer the user's question.

If you don't know the answer, just say that you don't know, don't try to make up an answer.

----
{context}
----
Question: {question}
Answer:
""")

DOCUMENT_PROMPT = PromptTemplate.from_template("""
---
title: {title}                                                                                   
year: {year}  
director: {director}     
---
""")

def _combine_documents(
    docs, document_prompt=DOCUMENT_PROMPT, document_separator="\n\n"
):
    print("docs:", docs)
    doc_strings = [format_document(doc, document_prompt) for doc in docs]
    return document_separator.join(doc_strings)


_context = RunnableParallel(
    context=retriever | _combine_documents,
    question=RunnablePassthrough(),
)

chain = (_context | LLM_CONTEXT_PROMPT | llm)

chain.invoke("Which director directed movies about dinosaurs that was released after the year 1992 but before 2007?")

整个 notebook 的源码可以在地址下载:https://github.com/liu-xiao-guo/semantic_search_es/blob/main/chatbot-with-bm25-only-example.ipynb

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fate-Serving推理服务源码解读

https://fate-serving.readthedocs.io/en/develop/?queryguest 什么是Fate-Serving fate-serving是FATE的在线部分,在使用FATE进行联邦建模完成之后,可以使用fate-serving进行包括单笔预测、多笔预测以及多host预测在内的在线联合预测。 模型的初始化流…

用友U8 Cloud ReportDetailDataQuery SQL注入漏洞复现(QVD-2023-47860)

0x01 产品简介 用友U8 Cloud 提供企业级云ERP整体解决方案,全面支持多组织业务协同,实现企业互联网资源连接。 U8 Cloud 亦是亚太地区成长型企业最广泛采用的云解决方案。 0x02 漏洞概述 用友U8 cloud ReportDetailDataQuery 接口处存在SQL注入漏洞,攻击者未经授权可以访…

adb push 将电脑中的文件传输到安卓开发板

1. adb remount 重新挂载设备的文件系统,以便可以对设备进行读写操作,通常情况下,安卓开发板在连接到计算机后,设备的文件系统会被挂载为只读文件系统,重新挂载后变成可读可写权限 C:\Users\Administrator>adb re…

C++ 动态规划 区间DP 石子合并

设有 N 堆石子排成一排,其编号为 1,2,3,…,N 。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻…

第三讲 多重背包问题①——转化

【题目来源】AcWing 4. 多重背包问题 I 【题意分析】和完全背包问题类似,但是区别在于每一种物品的数量是有限的。 【解决方法】 1.转化为 0 / 1 0/1 0/1 背包问题 因为每一种物品数量有限,所以将每个物品看作单独的种类,可转化为 0 / 1 0/…

Spring Data Envers 数据审计实战2 - 自定义监听程序扩展审计字段及字段值

上篇讲述了如何在Spring项目中集成Spring Data Envers做数据审计和历史版本查看功能。 之前演示的是业务表中已有的字段进行审计,那么如果我们想扩展审计字段呢? 比如目前对员工表加入了Audited审计,员工表有个字段为dept_id,为…

JVM 性能调优 - 参数基础(2)

查看 JDK 版本 $ java -version java version "1.8.0_151" Java(TM) SE Runtime Environment (build 1.8.0_151-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode) 查看 Java 帮助文档 $ java -help 用法: java [-options] class [args...] …

C语言实现memcpy、memmove库函数

目录 引言一、库函数介绍二、库函数详解三、源码实现1.memcpy源码实现2.memmove源码实现 四、测试1.memcpy函数2.memmove函数 五、源码1.memcpy源码2.memmove源码 六、参考文献 引言 关于memcpy和memmove这两个函数,不论是算法竞赛还是找工作面试笔试,对…

如何正确理解和获取S参数

S参数是网络参数,定义了反射波和入射波之间的关系,给定频率的S参数矩阵指定端口反射波b的矢量相对于端口入射波a的矢量,如下所示: bS∙a 在此基础上,如下图所示,为一个常见的双端口网络拓扑图:…

一文简介Maven初级使用

一.概述 Maven是专门用于管理和构建Java项目的工具,它的主要功能有: 提供了一套标准化的项目结构提供了一套标准化的项目构建流程(编译,测试,打包,发布)提供了一套依赖管理机制 一方面&…

跟着pink老师前端入门教程-day19

一、移动WEB开发之流式布局 1、 移动端基础 1.1 浏览器现状 PC端常见浏览器:360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 移动端常见浏览器:UC浏览器,QQ浏览器,欧朋浏览器&#xff0…

Intellij Idea的数据库工具 DataGrip

DataGrip DataGrip: IDEA自带,非常好用。智能提示很强大,快捷键跟IDEA自身一致。 如果下载不了 DataGrip,也可以直接用 IDEA 自带的。 常用的快捷键 alt8: 打开数据库Service ctrlshiftF10:打开常用的数…

使用arduino驱动直流减速电机(蓝牙控制)

此篇博客用于记录使用arduino驱动直流减速电机的过程,仅实现简单的功能:PID调速、蓝牙控制 1、直流减速电机简介2、DRV8833电机驱动模块简介3、HC-05蓝牙模块简介电机转动测试4、PID控制5、蓝牙控制电机 1、直流减速电机简介 我在淘宝购买的电机&#x…

Go 中如何检查文件是否存在?可能产生竞态条件?

嗨,大家好!本文是系列文章 Go 技巧第十三篇,系列文章查看:Go 语言技巧。 Go 中如何检查文件是否存在呢? 如果你用的是 Python,可通过标准库中 os.path.exists 函数实现。遗憾的是,Go 标准库没有…

pytorch 利用Tensorboar记录训练过程loss变化

文章目录 1. LossHistory日志类定义2. LossHistory类的使用2.1 实例化LossHistory2.2 记录每个epoch的loss2.3 训练结束close掉SummaryWriter 3. 利用Tensorboard 可视化3.1 显示可视化效果 参考 利用Tensorboard记录训练过程中每个epoch的训练loss以及验证loss,便于…

【Java数据结构】单向 不带头 非循环 链表实现

模拟实现LinkedList:下一篇文章 LinkedList底层是双向、不带头结点、非循环的链表 /*** LinkedList的模拟实现*单向 不带头 非循环链表实现*/ class SingleLinkedList {class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val …

【多模态大模型】视觉大模型SAM:如何使模型能够处理任意图像的分割任务?

SAM:如何使模型能够处理任意图像的分割任务? 核心思想起始问题: 如何使模型能够处理任意图像的分割任务?5why分析5so分析 总结子问题1: 如何编码输入图像以适应分割任务?子问题2: 如何处理各种形式的分割提示?子问题3:…

c++之说_10|自定义类型 union 联合体

之前我们说了一些 struct 结构体 现在来了解新的自定义类型 union 联合体 语法 union ptr {void* fptr;CLassFunPtr p;FunPtr p2;ptr& operator(CLassFunPtr ptr){p ptr;return *this;}ptr& operator(FunPtr Fptr){p2 Fptr;return *this;} } FunPtr_; 我们看到了…

第 383 场 LeetCode 周赛题解

A 边界上的蚂蚁 模拟 class Solution { public:int returnToBoundaryCount(vector<int> &nums) {int s 0;int res 0;for (auto x: nums) {s x;if (s 0)res;}return res;} };B 将单词恢复初始状态所需的最短时间 I 枚举&#xff1a;若经过 i i i 秒后 w o r d w…

Leetcode刷题笔记题解(C++):257. 二叉树的所有路径

思路&#xff1a;深度优先搜索 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right…