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

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

在这个例子中:

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

安装

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

安装 Elasticsearch 及 Kibana

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

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

Python 安装包

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

pip3 install lark elasticsearch langchain openai load_dotenv
markdown 复制代码
1.  $ pip3 list | grep elasticsearch
2.  elasticsearch                            8.12.0
3.  rag-elasticsearch                        0.0.1        /Users/liuxg/python/rag-elasticsearch/my-app/packages/rag-elasticsearch

环境变量

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

ini 复制代码
1.  export ES_USER="elastic"
2.  export ES_PASSWORD="xnLj56lTrH98Lf_6n76y"
3.  export ES_ENDPOINT="localhost"
4.  export OPENAI_API_KEY="YOUR_OPEN_AI_KEY"

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

拷贝 Elasticsearch 证书

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

bash 复制代码
1.  $ pwd
2.  /Users/liuxg/python/elser
3.  $ cp ~/elastic/elasticsearch-8.12.0/config/certs/http_ca.crt .
4.  $ ls http_ca.crt 
5.  http_ca.crt

创建应用

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

jupyter notebook

连接到 Elasticsearch

ini 复制代码
1.  from elasticsearch import Elasticsearch
2.  from dotenv import load_dotenv
3.  import os
4.  from elasticsearch import Elasticsearch

6.  load_dotenv()

8.  openai_api_key=os.getenv('OPENAI_API_KEY')
9.  elastic_user=os.getenv('ES_USER')
10.  elastic_password=os.getenv('ES_PASSWORD')
11.  elastic_endpoint=os.getenv("ES_ENDPOINT")

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

17.  print(client.info())

准备示例数据集

ini 复制代码
1.  docs = [
2.      {
3.          "text": "A bunch of scientists bring back dinosaurs and mayhem breaks loose",
4.          "metadata": {"year": 1993, "rating": 7.7, "genre": "science fiction", "director": "Steven Spielberg", "title": "Jurassic Park"},
5.      },
6.      {
7.          "text": "Leo DiCaprio gets lost in a dream within a dream within a dream within a ...",
8.          "metadata": {"year": 2010, "director": "Christopher Nolan", "rating": 8.2, "title": "Inception"},
9.      },
10.      {
11.          "text": "A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea",
12.          "metadata": {"year": 2006, "director": "Satoshi Kon", "rating": 8.6, "title": "Paprika"},
13.      },
14.      {
15.          "text":"A bunch of normal-sized women are supremely wholesome and some men pine after them",
16.          "metadata":{"year": 2019, "director": "Greta Gerwig", "rating": 8.3, "title": "Little Women"},
17.      },
18.      {
19.          "text":"Toys come alive and have a blast doing so",
20.          "metadata":{"year": 1995, "genre": "animated", "director": "John Lasseter", "rating": 8.3, "title": "Toy Story"},
21.      },
22.      {
23.          "text":"Three men walk into the Zone, three men walk out of the Zone",
24.          "metadata":{
25.              "year": 1979,
26.              "rating": 9.9,
27.              "director": "Andrei Tarkovsky",
28.              "genre": "science fiction",
29.              "rating": 9.9,
30.              "title": "Stalker",
31.          }
32.      }
33.  ]

索引数据到 Elasticsearch

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

ini 复制代码
1.  from elasticsearch import helpers

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

6.  operations = [
7.      {
8.        "_index": "movies_self_query",
9.        "_id": i,
10.        "text": doc["text"],
11.        "metadata": doc["metadata"]
12.      } for i, doc in enumerate(docs)
13.  ]

15.  # Add the documents to the index directly
16.  response = helpers.bulk(
17.    client,
18.    operations,
19.  )

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

设置查询检索器

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

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

python 复制代码
1.  from langchain.vectorstores.elasticsearch import ApproxRetrievalStrategy
2.  from typing import List, Union
3.  from langchain.retrievers.self_query.base import SelfQueryRetriever
4.  from langchain.chains.query_constructor.base import AttributeInfo
5.  from langchain.llms import OpenAI
6.  from langchain.vectorstores.elasticsearch import ElasticsearchStore

8.  # Add details about metadata fields
9.  metadata_field_info = [
10.      AttributeInfo(
11.          ,
12.          description="The genre of the movie. Can be either 'science fiction' or 'animated'.",
13.          type="string or list[string]",
14.      ),
15.      AttributeInfo(
16.          ,
17.          description="The year the movie was released",
18.          type="integer",
19.      ),
20.      AttributeInfo(
21.          ,
22.          description="The name of the movie director",
23.          type="string",
24.      ),
25.      AttributeInfo(

27.      ),
28.  ]

30.  document_content_description = "Brief summary of a movie"

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

35.  class BM25RetrievalStrategy(ApproxRetrievalStrategy):

37.      def __init__(
38.          self
39.      ):
40.          pass

42.      def query(
43.          self,
44.          query: Union[str, None],
45.          filter: List[dict],
46.          **kwargs,
47.      ):

49.          if query:
50.              query_clause = [{
51.                  "multi_match": {
52.                      "query": query,
53.                      "fields": ["text"],
54.                      "fuzziness": "AUTO",
55.                  }
56.              }]
57.          else:
58.              query_clause = []

61.          bm25_query = {
62.              "query": {
63.                  "bool": {
64.                      "filter": filter,
65.                      "must": query_clause
66.                  }
67.              },
68.          }

70.          print("query", bm25_query)

72.          return bm25_query

75.  vectorstore = ElasticsearchStore(
76.    index_,
77.    es_connection=client,
78.    strategy=BM25RetrievalStrategy()
79.  )

仅使用 BM25 的检索器

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

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

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

ini 复制代码
1.  from langchain.schema.runnable import RunnableParallel, RunnablePassthrough
2.  from langchain.prompts import ChatPromptTemplate, PromptTemplate
3.  from langchain.schema import format_document

5.  retriever = SelfQueryRetriever.from_llm(
6.      llm, 
7.      vectorstore, 
8.      document_content_description, 
9.      metadata_field_info, 
10.      verbose=True
11.  )

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

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

18.  ----
19.  {context}
20.  ----
21.  Question: {question}
22.  Answer:
23.  """)

25.  DOCUMENT_PROMPT = PromptTemplate.from_template("""
26.  ---
27.  title: {title}                                                                                   
28.  year: {year}  
29.  director: {director}     
30.  ---
31.  """)

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

41.  _context = RunnableParallel(
42.      context=retriever | _combine_documents,
43.      question=RunnablePassthrough(),
44.  )

46.  chain = (_context | LLM_CONTEXT_PROMPT | llm)

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

整个 notebook 的源码可以在地址下载:github.com/liu-xiao-gu...

相关推荐
SafePloy安策36 分钟前
ES信息防泄漏:策略与实践
大数据·elasticsearch·开源
涔溪42 分钟前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
csdn5659738504 小时前
Elasticsearch 重建索引 数据迁移
elasticsearch·数据迁移·重建索引
天幕繁星4 小时前
docker desktop es windows解决vm.max_map_count [65530] is too low 问题
windows·elasticsearch·docker·docker desktop
Elastic 中国社区官方博客4 小时前
Elasticsearch 8.16:适用于生产的混合对话搜索和创新的向量数据量化,其性能优于乘积量化 (PQ)
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
m1chiru4 小时前
Elasticsearch 实战应用:高效搜索与数据分析
elasticsearch
飞翔的佩奇4 小时前
ElasticSearch:使用dsl语句同时查询出最近2小时、最近1天、最近7天、最近30天的数量
大数据·elasticsearch·搜索引擎·dsl
Elastic 中国社区官方博客11 小时前
Elasticsearch 和 Kibana 8.16:Kibana 获得上下文和 BBQ 速度并节省开支!
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
一个处女座的程序猿11 小时前
LLMs之VDB:Elasticsearch的简介、安装和使用方法、案例应用之详细攻略
大数据·elasticsearch·搜索引擎
未 顾20 小时前
day12:版本控制器
大数据·elasticsearch·搜索引擎