<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Jeff Vestal - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Jeff Vestal - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/search-labs/author/jeff-vestal</link>
    </image>
    <link>https://www.elastic.co/search-labs/author/jeff-vestal</link>
    <atom:link href="https://www.elastic.co/search-labs/rss/author/jeff-vestal.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Wed, 23 Sep 2026 09:50:58 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Fast vs. accurate: Measuring the recall of quantized vector search]]></title>
    <description><![CDATA[Explaining how to measure recall for vector search in Elasticsearch with minimal setup.]]></description>
    <content:encoded><![CDATA[<p>Everyone wants vector search to be instant. But high-dimensional vectors are heavy. A single 1,024-dimension float-32 vector takes up significant memory, and comparing it against millions of others is computationally expensive.</p><p>To solve this, search engines like Elasticsearch use two main optimization strategies:</p><ol><li><p><strong>Approximate search (hierarchical navigable small world [HNSW]):</strong> Instead of scanning every document, we build a navigation graph to jump quickly to the likely neighborhood of the answer.</p></li><li><p><strong>Quantization:</strong> We compress the vectors (for example, from 32-bit floats to 8-bit integers or even 1-bit binary values) to reduce memory usage and speed up calculations.</p></li></ol><p>But optimization often comes with a tax: <strong>accuracy</strong>.</p><p>The fear is valid: "If I compress my data and take shortcuts during the search, will I miss the best results?" "Does this optimization degrade the relevance of my search engine?"</p><p>To prove that Elastic’s quantization doesn’t degrade results, we built a repeatable test harness using the <a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"><strong>DBPedia-14</strong></a><a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"> dataset</a> to calculate exactly how much accuracy (specifically, <strong>recall)</strong> we trade for speed when using default optimizations in Elasticsearch.</p><p>tldr: It’s likely much less than you think. Check out the <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">notebook here</a>, and try it yourself</p><h2><strong>The definitions (for the non-experts)</strong></h2><p>Before we look at the code, let’s level-set on some terms.</p><ul><li><p><strong>Relevance versus recall:</strong> <strong>Relevance</strong> is subjective (did I find good stuff?). <strong>Recall</strong> is mathematical. If there are 10 documents in the database that are the <em>perfect</em> mathematical matches for your query, and the search engine finds nine of them, your recall is 90% (or 0.9).</p></li><li><p><strong>Exact search (flat):</strong> Sometimes called the "brute force" method. The search engine scans every single document in an index and calculates the distance.</p><ul><li><p><em>Pros:</em> 100% perfect recall.</p></li><li><p><em>Cons:</em> Computationally expensive and slow at scale.</p></li></ul></li><li><p><strong>Approximate search (HNSW):</strong> The "shortcut" method. The search engine builds an <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">HNSW</a> graph. It traverses the graph to find the nearest neighbors.</p><ul><li><p><em>Pros:</em> Extremely fast and scalable.</p></li><li><p><em>Cons:</em> You might miss a neighbor if the graph traversal stops too early.</p></li></ul></li></ul><h2><strong>The experiment: Exact versus approximate</strong></h2><p>To test recall, we used the <strong>DBPedia-14</strong> dataset, a large dataset of titles and abstracts across 14 ontology classes, commonly used for training and evaluating text categorization models. Specifically, we’ll focus on the "Film" category. We wanted to compare the optimized production settings against a mathematically perfect ground truth.</p><p>For this experiment, we are using the <a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">jina-embeddings-v5-text-small</a> model, a state-of-the-art multilingual model that leads industry benchmarks for text representation. We chose this model because it defines the current standard for high-performance embeddings. By combining Jina v5’s elite accuracy with Elasticsearch’s native quantization, we can demonstrate a search architecture that is both computationally efficient and uncompromising on retrieval quality.</p><p>We set up an index with dual mapping. We ingested the same text into two different fields simultaneously:</p><ol><li><p><strong><code>content.raw</code></strong>with type: <code>flat</code>. This forces Elasticsearch to perform a brute-force scan of the full Float32 vectors. This returns exact match results and will be used for our baseline.</p></li><li><p><strong><code>content</code></strong>with type <code>semantic_text</code>. With defaults using HNSW + Better Binary Quantization (BBQ). This is the standard, optimized production setting for approximate match.</p></li></ol><h3><strong>The Recall@10 test</strong></h3><p>For our metric, we used Recall@10.</p><p>We picked 50 random movies and ran the same query against both fields.</p><ul><li><p>If the <strong>exact (flat)</strong> search says the top 10 neighbors are IDs [1, 2, 3... 10].</p></li><li><p>And the <strong>approximate (HNSW)</strong> search returns IDs [1, 2, 3... 9, 99].</p></li><li><p>We found nine out of the top 10 correctly. The score is <strong>0.9</strong>.</p></li></ul><p>Here’s the mapping we used:</p># The "Control Group": Forces exact brute-force scan
"raw": {
    "type": "semantic_text",
    "inference_id": ".jina-embeddings-v5-text-small",
    "index_options": {
        "dense_vector": {
            "type": "flat"
        }
    }
}<p><strong>The results: The "flat line" of success</strong></p><p>We ran a scale test, reloading the full dataset and testing against index sizes of 1,000 to 40,000 documents.</p><p>Here’s what happened to the recall score:</p><p>Documents</p><p>Recall@10 score</p><p>1,000</p><p>1.000 (100%)</p><p>5,000</p><p>0.998 (100%)</p><p>10,000</p><p>0.992 (99.4%)</p><p>20,000</p><p>0.999 (99.0%)</p><p>40,000</p><p>0.992 (98.8%)</p><p>The results were incredibly stable. Even as we scaled up, the approximate search matched the brute-force exact search <strong>&gt;99% of the time</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8168a0a4946bade7/6a170e154a531b61b536a9eb/a4bfacb1d0cce6fdf6df0e1a9d4fc5d4007a66da-1999x1209.png" alt="vector search stability: Recall vs  Index Size" /><h2><strong>Why did it work so well?</strong></h2><p>You might expect that compressing vectors to binary values would hurt accuracy more than this. The reason it doesn't lies in how Elasticsearch handles the retrieval.</p><p>Most embedding models today output Float32 vectors, which are large. To make search efficient, Elasticsearch uses quantization for high-dimensional vectors. Specifically, since 9.2, it uses <a href="https://www.elastic.co/search-labs/blog/elasticsearch-9-1-bbq-acorn-vector-search">BBQ</a> by default.</p><p>BBQ uses a <strong>rescoring</strong> mechanism:</p><ol><li><p><strong>Traversal:</strong> The search engine uses the compressed (quantized) vectors to traverse the HNSW graph quickly. Because the vectors are small, it can efficiently over-sample, gathering a larger list of candidates (for example, the top 100 roughly similar docs) without a performance penalty.</p></li><li><p><strong>Rescore:</strong> Once it has those candidates, it retrieves the full-precision values for just those few documents to calculate the final, precise ranking.</p></li></ol><p>This gives you the best of both worlds, the speed of quantization for the heavy lifting, and the precision of floats for the final sort.</p><h2><strong>Can we do better?</strong></h2><p>It’s worth noting that the results we’re seeing here are using default settings and a random sampling of data. Think of this as a high-performance starting point. While Jina v5 is a beast, these recall scores aren't a "one size fits all" guarantee for every dataset. Every data collection has its own quirks, and while you can definitely tune things further to squeeze out even more performance, you should always benchmark against your own specific data to see where your ceiling is.</p><h2><strong>Conclusion</strong></h2><p>This is a very small-scale test. But the point of the exercise is not to measure the embedding model or BBQ specifically, it’s to demonstrate how you can easily measure the recall of your dataset with minimal setup.</p><p>If you want to run this test on your own data, you can check out the <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">notebook here</a> and try it yourself.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/recall-vector-search-quantization</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/recall-vector-search-quantization</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt198c7085db96aa04/6a170e17cdacbfe88c7d2a86/09f03b9239d66c36763cdab3fafcdac207ff6d83-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ChatGPT and Elasticsearch revisited: Building a chatbot using RAG]]></title>
    <description><![CDATA[Learn how to create a chatbot using ChatGPT and Elasticsearch, utilizing all of the newest RAG features.]]></description>
    <content:encoded><![CDATA[<p>Follow up to the blog <a href="https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-openai-meets-private-data">ChatGPT and Elasticsearch: OpenAI meets private data</a>.</p><p>In this blog, you will learn how to:</p><ul><li><p>Create an Elasticsearch Serverless project</p></li><li><p>Create an Inference Endpoint to generate embeddings with ELSER</p></li><li><p>Use a Semantic Text field for auto-chunking and calling the Inference Endpoint</p></li><li><p>Use the Open Crawler to crawl blogs</p></li><li><p>Connect to an LLM using Elastic’s Playground to test prompts and context settings for a RAG chat application.</p></li></ul><p>If you want to jump right into the code, you can view the <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/rag-ties-the-app-together/ChatGPT_and_Elasticsearch__The_RAG_Really_Tied_the_App_Together.ipynb">accompanying Jupyter Notebook here</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2779150d12271ef1/6a1711e460084bdf023c4688/e0e3d205d5b4cb58c4b4b5f22aab57c8ef659ed6-1440x807.png" alt="The Dude Abides" /><h2>ChatGPT and Elasticsearch (April 2023)</h2><p>A lot has changed since I wrote the initial <a href="https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-openai-meets-private-data">ChatGPT and Elasticsearch: OpenAI meets private data</a>. Most people were just playing around with ChatGPT, if they had tried it at all. And every booth at every tech conference didn’t feature the letters “AI” (whether it is a useful fit or not).</p><h2>Updates in Elasticsearch (August 2024)</h2><p>Since then, Elastic has embraced being a full featured vector database and is putting a lot of engineering effort into making it the best vector database option for anyone building a search application. So as not to spend several pages talking about all the enhancements to Elasticsearch, here is a non-exhaustive list in no particular order:</p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/introducing-elser-v2-part-1">ELSER - The Elastic Learned Sparse Encoder</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/building-elastic-cloud-serverless">Elastic Serverless Service</a> was built and is in public beta</p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-azure-openai-completion-support">Elasticsearch open Inference API</a> </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-amazon-bedrock-support">Embeddings</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-openai-completion-support">Chat completion</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-cohere-rerank">Semantic rerankers</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Semantic_text type</a> - Simplify semantic search</p><ul><li><p>Automatic chunking</p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/rag-playground-introduction">Playground</a> - Visually experiment with RAG application building in Elasticsearch</p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-retrievers">Retrievers</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-open-crawler-release">Open web crawler</a></p></li></ul><p>With all that change and more, the original blog needs a rewrite. So let’s get started.</p><h2>Updated flow: ChatGPT, Elasticsearch &amp; RAG</h2><p>The plan for this updated flow will be:</p><ol><li><p>Setup  </p><ol><li><p>Create a new Elasticsearch serverless search project</p></li><li><p>Create an embedding inference API using ELSER</p></li><li><p>Configure an index template with a <code>semantic_text</code> field</p></li><li><p>Create a new LLM connector</p></li><li><p>Configure a chat completion inference service using our LLM connector</p></li></ol></li><li><p>Ingest and Test</p><ol><li><p>Crawl the Elastic Labs sites (Search, Observability, Security) with the Elastic Open Web Crawler.</p></li><li><p>Use Playground to test prompts using our indexed Labs content</p></li></ol></li><li><p>Configure and deploy our App </p><ol><li><p>Export the generated code from Playground to an application using FastAPI as the backend and React as the front end.</p></li><li><p>Run it locally</p></li><li><p>Optionally deploy our chatbot to Google Cloud Run</p></li></ol></li></ol><h2>Setup</h2><h3>Elasticsearch Serverless Project</h3><p>We will be using an Elastic serverless project for our chatbot. Serverless removes much of the complexity of running an Elasticsearch cluster and lets you focus on actually using and gaining value from your data. Read more about the <a href="https://www.elastic.co/search-labs/blog/building-elastic-cloud-serverless">architecture of Serverless here</a>.</p><p>If you don’t have an Elastic Cloud account, you can create a free two-week trial at <a href="https://cloud.elastic.co/registration">elastic.co</a> (Serverless pricing <a href="https://www.elastic.co/pricing/serverless-search">available here</a>). If you already have one, you can simply log in.</p><p>Once logged in, you will need to <a href="https://cloud.elastic.co/account/keys">create a cloud API key</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt03e19072d48a28bb/6a1711e5dc55def695e00f03/d8121ed3d0fb4bbd5927a78aee20619589106df8-1300x1920.png" alt="alt_text" /><p><strong>NOTE: In the steps below, I will show the relevant parts of Python code. For the sake of brevity, I’m not going to show complete code that will import required libraries, wait for steps to complete, catch errors, etc.</strong></p><p><strong>For more robust code you can run, please see the </strong><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/rag-ties-the-app-together/ChatGPT_and_Elasticsearch__The_RAG_Really_Tied_the_App_Together.ipynb"><strong>accompanying Jypyter notebook</strong></a><strong>!</strong></p><h3>Create Serverless Project</h3><p>We will use our newly created API key to perform the next setup steps.</p><p>First off, create a new Elasticsearch project.</p>url = "https://api.elastic-cloud.com/api/v1/serverless/projects/elasticsearch" 

project_data = {
    "name": "The RAG Really Tied the App Together",
    "region_id": "aws-us-east-1",
    "optimized_for": "vector"
}

auth_header = f"ApiKey {api_key}"  # seeing what a comment lokos like with pound
headers = {
    "Content-Type": "application/json",
    "Authorization": auth_header
}

es_project = requests.post(url, json=project_data, headers=headers)  :four:
<ul><li><p><code>url</code> - This is the standard Serverless endpoint for Elastic Cloud</p></li><li><p><code>project_data</code> - Your Elasticsearch Serverless project settings </p><ul><li><p><code>name</code> - Name we want for the project</p></li><li><p><code>region_id</code> - Region to deploy</p></li><li><p><code>optimized_for</code> - Configuration type - We are using <code>vector</code> which isn’t strictly required for the ELSER model but can be suitable if you select a dense vector model such as e5.</p></li></ul></li></ul><h3>Create Elasticsearch Python client</h3><p>One nice thing about creating a programmatic project is that you will get back the connection information and credentials you need to interact with it!</p>es = Elasticsearch(es_project_keys['endpoints']['elasticsearch'],
                   basic_auth=(es_project_keys['credentials']['username'],
                              es_project_keys['credentials']['password']
                              )
                   )
<h3>ELSER Embedding API</h3><p>Once the project is created, which usually takes less than a few minutes, we can prepare it to handle our labs’ data.</p><p>The first step is to configure the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html#inference-example-elser">inference API for embedding</a>. We will be using the <a href="https://www.elastic.co/search-labs/blog/introducing-elser-v2-part-2">Elastic Learned Sparse Encoder</a> (ELSER).</p><ul><li><p>Command to create the inference endpoint</p></li><li><p>Specify this endpoint will be for generating sparse embeddings</p></li></ul>model_config = {
    "service": "elser",
    "service_settings": {
        "num_allocations": 8,
        "num_threads": 1
    }
}

inference_id = "my-elser-model"

create_endpoint = es.inference.put_model(
    inference_id=inference_id,
    task_type="sparse_embedding",
    body=model_config
)
<ul><li><p><code>model_config</code> - Settings we want to use for deploying our semantic reranking model </p><ul><li><p><code>service</code> - Use the pre-defined <code>elser</code> inference service</p></li><li><p><code>service_settings.num_allocations</code> - <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-deploy-model.html">Deploy the model</a> with 8 allocations</p></li><li><p><code>service_settings.num_threads</code> - Deploy with one thread per allocation</p></li></ul></li><li><p><code>inference_id</code> - The name you want to give to you inference endpoint</p></li><li><p><code>task_type</code>- Specifies this endpoint will be for generating sparse embeddings</p></li></ul><p>This single command will trigger Elasticsearch to perform a couple of tasks:</p><ol><li><p>It will download the ELSER model.</p></li><li><p>It will deploy (start) the ELSER model with eight allocations and one thread per allocation.</p></li><li><p>It will create an inference API we use in our field mapping in the next step.</p></li></ol><h3>Index Mapping</h3><p>With our ELSER API created, we will create our index template.</p>template_body = {
    "index_patterns": ["elastic-labs*"],
    "template": {
        "mappings": {
            "properties": {
                "body": {
                    "type": "text",
                    "copy_to": "semantic_body"
                },
                "semantic_body": {
                    "type": "semantic_text",
                    "inference_id": "my-elser-model"
                },
                "headings": {
                    "type": "text"
                },
                "id": {
                    "type": "keyword"
                },
                "meta_description": {
                    "type": "text"
                },
                "title": {
                    "type": "text"
                }
            }
        }
    }
}

template_resp = es.indices.put_index_template(  :eight:
    name="labs_template",
    body=template_body
)
<ul><li><p><code>index_patterns</code> - The pattern of indices we want this template to apply to.</p></li><li><p><code>body</code> - The main content of a web page the crawler collects will be written to</p><ul><li><p><code>type</code> - It is a text field</p></li><li><p><code>copy_to</code> - We need to copy that text to our semantic text field for semantic processing</p></li></ul></li><li><p><code>semantic_body</code> is our <a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">semantic text field</a> </p><ul><li><p>This field will automatically handle chunking of long text and generating embeddings which we will later use for semantic search</p></li><li><p><code>inference_id</code> specifies the name of the inference endpoint we created above, allowing us to generate embeddings from our ELSER model</p></li></ul></li><li><p><code>headings</code> - Heading tags from the html</p></li><li><p><code>id</code> - crawl id for this document</p></li><li><p><code>meta_description</code> - value of the description meta tag from the html</p></li><li><p><code>title</code> is the title of the web page the content is from</p></li></ul><p>Other fields will be indexed but auto-mapped. The ones we are focused on pre-defining in the template will not need to be both keyword and text type, which is defined automatically otherwise.</p><p>Most importantly, for this guide, we must define our <code>semantic_text</code> field and set a source field to copy from with <code>copy_to</code>. In this case, we are interested in performing semantic search on the body of the text, which the crawler indexes into the <code>body</code>.</p><h2>Crawl All the Labs!</h2><p>We can now install and configure the crawler to crawl the Elastic * Labs. We will loosely follow the excellent guide from the <a href="https://www.elastic.co/search-labs/blog/elastic-open-crawler-release#how-do-i-use-it">Open Crawler released for tech-preview</a> Search Labs blog.</p><p>The steps below will use docker and run on a MacBook Pro. To run this with a different setup, consult the <a href="https://github.com/elastic/crawler?tab=readme-ov-file#elastic-open-web-crawler">Open Crawler Github readme</a>.</p><h3>Clone the repo</h3><p>
Open the command line tool of your choice. I’ll be using Iterm2. Clone the <a href="https://github.com/elastic/crawler">crawler repo</a> to your machine.</p>~/repos
❯ git clone git@github.com:elastic/crawler.git
Cloning into 'crawler'...
remote: Enumerating objects: 1944, done.
remote: Counting objects: 100% (418/418), done.
remote: Compressing objects: 100% (243/243), done.
remote: Total 1944 (delta 237), reused 238 (delta 170), pack-reused 1526
Receiving objects: 100% (1944/1944), 84.85 MiB | 31.32 MiB/s, done.
Resolving deltas: 100% (727/727), done.
<h3>Build the crawler container</h3><p>Run the following command to build and run the crawler.</p>docker build -t crawler-image . &amp;&amp; docker run -i -d --name crawler crawler-image
~/repos
 ❯ cd crawler
~/repos/crawler main
 ❯ docker build -t crawler-image . &amp;&amp; docker run -i -d --name crawler crawler-image

[+] Building 66.9s (6/10)                                                                                                                                                                docker:desktop-linux
 =&gt; [internal] load build definition from Dockerfile					0.0s
 =&gt; =&gt; transferring dockerfile: 333B							0.0s
 =&gt; [internal] load .dockerignore							0.0s
 =&gt; =&gt; transferring context: 2B								0.0s
 =&gt; [internal] load metadata for docker.io/library/jruby:9.4.7.0-jdk21		1.7s
 =&gt; [auth] library/jruby:pull token for registry-1.docker.io			0.0s
...
...
 =&gt; [5/5] RUN make clean install								50.7s
 =&gt; exporting to image									0.9s
 =&gt; =&gt; exporting layers									0.9s
 =&gt; =&gt; writing image sha256:6b3f4000a121e76aba76fdbbf11b53f53a3fabba61c0b7cf3fdcdb21e244f1d8	0.0s
 =&gt; =&gt; naming to docker.io/library/crawler-image					0.0s
cc6c16941de04355c050ef5f5fd0041ee7f3505b8cf8448c7223f0d2e80b5498
<h3>Configure the crawler</h3><p>Create a new YAML in your favorite editor (vim):</p>~/repos/crawler main
 ❯ vim config/elastic-labs.yml
<p>We want to crawl all the documents on the three labs’ sites, but since blogs and tutorials on those sites tend to link out to other parts of elastic.co, we need to set a couple of runs to restrict the scope. We will allow crawling the three paths for our site and then deny anything else.</p><p>Paste the following in the file and save</p>domains:
  - url: https://www.elastic.co
    seed_urls:
      - https://www.elastic.co/search-labs
      - https://www.elastic.co/observability-labs
      - https://www.elastic.co/security-labs
    crawl_rules:
      - policy: allow
        type: begins
        pattern: /search-labs
      - policy: allow
        type: begins
        pattern: /observability-labs
      - policy: allow
        type: begins
        pattern: /security-labs
      - policy:deny
        type: regex
        pattern: .*/author/.*
      - policy: deny
        type: regex
        pattern: .*

output_sink: elasticsearch
output_index: elastic-labs
max_crawl_depth: 2

elasticsearch:
  host: "https://&lt;your_serverless_project&gt;.es.&lt;region&gt;.aws.elastic.cloud"
  port: "443"
  api_key: "&lt;API Key generated above&gt;"
<p>Copy the configuration into the Docker container:</p>~/repos/crawler main ⇣
 ❯ docker cp config/elastic-labs.yml crawler:/app/config/elastic-labs.yml

Successfully copied 2.05kB to crawler:/app/config/elastic-labs.yml
<h3>Validate the domain</h3><p>Ensure the config file has no issues by running:</p> ❯ docker exec -it crawler bin/crawler validate config/elastic-labs.yml
Domain https://www.elastic.co is valid
<h3>Start the crawler</h3><p>When you first run the crawler, processing all the articles on the three lab sites may take several minutes.</p>docker exec -it crawler bin/crawler crawl config/elastic-labs.yml
~/repos/crawler/config main ⇣
 ❯ docker exec -it crawler bin/crawler crawl config/elastic-labs.yml
[crawl:6692c3b584f98612e3a465ce] [primary] Initialized an in-memory URL queue for up to 10000 URLs
[crawl:6692c3b584f98612e3a465ce] [primary] ES connections will be authorized with configured API key
[crawl:6692c3b584f98612e3a465ce] [primary] ES connections will use SSL without ca_fingerprint
[crawl:6692c3b584f98612e3a465ce] [primary] Elasticsearch sink initialized for index [elastic-labs] with pipeline [ent-search-generic-ingestion]
[crawl:6692c3b584f98612e3a465ce] [primary] Starting the crawl with up to 10 parallel thread(s)...
[crawl:6692c3b584f98612e3a465ce] [primary] Crawl status: queue_size=11, pages_visited=1, urls_allowed=12, urls_denied={}, crawl_duration_msec=847, crawling_time_msec=635.0, avg_response_time_msec=635.0, active_threads=1, http_client={:max_connections=&gt;100, :used_connections=&gt;1}, status_codes={"200"=&gt;1}
<h3>Confirm articles have been indexed</h3><p>We will confirm two ways.</p><p>First, we will look at a sample document to ensure that ELSER embeddings have been generated. We just want to look at any doc so we can search without any arguments:</p>GET elastic-labs/_search
<p>Ensure you get results and then check that the field <code>body</code> contains text and <code>semantic_body.inference.chunks.0.embeddings</code> contains tokens.</p>    "hits": [
      {
        "_index": "elastic-labs",
...
        "_source": {
          "body": "Tutorials Integrations Blog Start Free Trial Contact Sales Open navigation menu Overview ...
          "semantic_body": {
            "inference": {
              "inference_id": "my-elser-model",
              "model_settings": {
                "task_type": "sparse_embedding"
              },
              "chunks": [
                {
                  "text": "Tutorials Integrations Blog Start Free Trial Contact Sales Open navigation menu Overview ...
                  "embeddings": {
                    "##her": 2.1016746,
                    "elastic": 2.084594,
                    "##ai": 1.6336359,
                    "dock": 1.5765089,
                    ...
<p>We can check we are gathering data from each of the three sites with a <code>terms</code> aggregation:</p>GET elastic-labs/_search
{
  "size": 0,
  "aggs": {
    "url_path_dir1": {
      "terms": {
        "field": "url_path_dir1.keyword"
      }
    }
  }
}
<p>You should see results that start with one of our three site paths.</p>      "buckets": [
        {
          "key": "security-labs",
          "doc_count": 37
        },
        {
          "key": "observability-labs",
          "doc_count": 30
        },
        {
          "key": "search-labs",
          "doc_count": 6
        }
      ]
<h2>To the Playground!</h2><p>With our data ingested, chunked, and inference, we can start working on the backend application code that will interact with the LLM for our RAG app.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1ceed13b358b1bf/6a1711e767045b096445c2fd/abd9cb1460436f0e658f654f76ab90828892a671-494x144.png" alt="alt_text" /><h3>LLM Connection</h3><p>We need to configure a connection for Playground to make API calls to an LLM. As of this writing, Playground supports chat completion connections to OpenAI, AWS Bedrock, and Google Gemini. More connections are planned, so check the docs for the latest list.</p><p>When you first enter the Playground UI, click on “Connect to an LLM”</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8338faedbc15c2a5/6a1711e9961e69ce8ac4d021/ff5dbf52272a53ffe1c97136cf6bc02e0b05ff45-1146x872.png" alt="alt_text" /><p>Since I used OpenAI for the original blog, we’ll stick with that. The great thing about the Playground is that you can switch connections to a different service, and the Playground code will generate code specifically to that service’s API specification. You only need to select which one you want to use today.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt472f08d397464176/6a1711eb4a531b73db36aa9b/cf27f69cd578b936d78476bdf8ee5c387e725061-1440x480.png" alt="alt_text" /><p>In this step, you must fill out the fields depending on which LLM you wish to use. As mentioned above, since Playground will abstract away the API differences, you can use whichever supported LLM service works for you, and the rest of the steps in this guide will work the same.</p><p>If you don’t have an Azure OpenAI account or OpenAI API account, you can get one <a href="https://platform.openai.com/signup/">here</a> (OpenAI now requires a $5 minimum to fund the API account).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd63e7d871549ab9/6a1711ed1949f72e3fe7ab52/1ac508303f4cc9d9427ae039f354b8ae0ac4473d-1370x1642.png" alt="alt_text" /><p>Once you have completed that, hit “Save,” and you will get confirmation that the connector has been added. After that, you just need to select the indices we will use in our app. You can select multiple, but since all our crawler data is going into <code>elastic-labs,</code> you can choose that one.</p><p>Click “Add data sources” and you can start using Playground!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a1b97d9677c2dd4/6a1711ee0e2e496c2a41a266/365855fbca95613171777e9171d2c3dd65b11694-1128x840.png" alt="alt_text" /><p>Select the “restaurant_reviews” index created earlier.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4cdedc79bcf7e160/6a1711f01949f787f3e7ab56/4355652e648e3e69915fad0afcade2a1a55ab1f7-740x524.png" alt="alt_text" /><h2>Playing in the Playground</h2><p>After adding your data source you will be in the Playground UI.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8d1b52a4bffced1/6a1711f12b835f39adf4b329/6ad316fb6dfcd815d1f66844a2b23e02f8cf0826-1440x874.png" alt="alt_text" /><p>To keep getting started as simple as possible, we will stick with all the default settings other than the prompt. However, for more details on Playground components and how to use them, check out the <a href="https://www.elastic.co/search-labs/blog/rag-playground-introduction">Playground: Experiment with RAG applications with Elasticsearch in minutes</a> blog and the <a href="https://www.elastic.co/guide/en/kibana/current/playground.html">Playground documentation</a>.</p><p>Experimenting with different settings to fit your particular data and application needs is an important part of setting up a RAG-backed application.</p><p>The defaults we will be using are:</p><ul><li><p>Querying the <code>semantic_body</code> chunks</p></li><li><p>Using the three nearest semantic chunks as context to pass to the LLM</p></li></ul><h3>Creating a more detailed prompt</h3><p>The default prompt in Playground is simply a placeholder. Prompt engineering continues to develop as LLMs become more capable. Exploring the ever-changing world of prompt engineering is a blog, but there are a few basic concepts to remember when creating a system prompt:</p><ul><li><p>Be detailed when describing the app or service the LLM response is part of. This includes what data will be provided and who will consume the responses.</p></li><li><p>Provide example questions and responses. This technique, called <em>few-shot-prompting</em>, helps the LLM structure its responses.</p></li><li><p>Clearly state how the LLM should behave.</p></li><li><p>Specify the Desired Output Format.</p></li><li><p>Test and Iterate on Prompts.</p></li></ul><p>With this in mind, we can create a more detailed system prompt:</p>You are a helpful and knowledgeable assistant designed to assist users in querying information related to Search, Observability, and Security. Your primary goal is to provide clear, concise, and accurate responses based on semantically relevant documents retrieved using Elasticsearch.

Guidelines:

Audience:
Assume the user could be of any experience level but lean towards a technical slant in your explanations.
Avoid overly complex jargon unless it is common in the context of Elasticsearch, Search, Observability, or Security.

Response Structure:
Clarity: Responses should be clear and concise, avoiding unnecessary verbosity.
Conciseness: Provide information in the most direct way possible, using bullet points when appropriate.

Formatting: Use Markdown formatting for:
Bullet points to organize information
Code blocks for any code snippets, configurations, or commands
Relevance: Ensure the information provided is directly relevant to the user's query, prioritizing accuracy.

Content:
Technical Depth: Offer sufficient technical depth while remaining accessible. Tailor the complexity based on the user's apparent knowledge level inferred from their query.

Examples: Where appropriate, provide examples or scenarios to clarify concepts or illustrate use cases.
Documentation Links: When applicable, suggest additional resources or documentation from Elastic.co that can further assist the user.

Tone and Style:
Maintain a professional yet approachable tone.
Encourage curiosity by being supportive and patient with all user queries, regardless of complexity.

Example Queries:
"How can I optimize my Elasticsearch cluster for large-scale data?"
"What are the best practices for implementing observability in a microservices architecture?"
"How can I secure sensitive data in Elasticsearch?"
<p>Feel free to to test out different prompts and context settings to see what results you feel are best for your particular data. For more examples on advanced techiques, check out the <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#prompts">Prompt section on the two part blog Advanced RAG Techniques</a>. Again, see the <a href="https://www.elastic.co/search-labs/blog/rag-playground-introduction">Playground blog post</a> for more details on the various settings you can tweak.</p><h2>Export the Code</h2><p>Behind the scenes, Playground generates all the backend chat code we need to perform semantic search, parse the relevant contextual fields, and make a chat completion call to the LLM. No coding work from us required!</p><p>In the upper right corner click on the “View Code” button to expand the code flyout</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf523ed7f26673f46/6a1711f35091687af1e1bbee/aca43cd5554f5a35cc4a336557b0497675c044a2-962x406.png" alt="alt_text" /><p>You will see the generated python code with all the settings your configured as well as the the functions to make a semantic call to Elasticsearch, parse the results, built the complete prompt, make the call to the LLM, and parse those results.</p><p>Click the copy icon to copy the code.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt841e18f13ddd4145/6a1711f514b270564ce3c6f9/ff6a3a012c9a2f5f760efe78f7b663ae6261ec52-1440x1449.png" alt="alt_text" /><p>You can now incorporate the code into your own chat application!</p><h2>Wrapup</h2><p>A lot has changed since the first iteration of this blog over a year ago, and we covered a lot in this blog. You started from a cloud API key, created an Elasticsearch Serverless project, generated a cloud API key, configured the Open Web Crawler, crawled three Elastic Lab sites, chunked the long text, generated embeddings, tested out the optimal chat settings for a RAG application, and exported the code!</p><p><em>Where’s the UI, Vestal?</em></p><p>Be on the lookout for part two where we will integrate the playground code into a python backend with a React frontend. We will also look at deploying the full chat application.</p><p>For a complete set of code for everything above, see the <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/rag-ties-the-app-together/ChatGPT_and_Elasticsearch__The_RAG_Really_Tied_the_App_Together.ipynb">accompanying Jypyter notebook</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-rag-enhancements</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-rag-enhancements</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2779150d12271ef1/6a1711e460084bdf023c4688/e0e3d205d5b4cb58c4b4b5f22aab57c8ef659ed6-1440x807.png" length="0" type="image/png"/>
    <pubDate>Mon, 19 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Cloud adds Elasticsearch Vector Database optimized instance to Google Cloud]]></title>
    <description><![CDATA[Elasticsearch's vector search optimized profile for GCP is available. Learn more about it and how to use it in this blog.]]></description>
    <content:encoded><![CDATA[<p>Elastic Cloud Vector Search optimized hardware profile is available for Google Elastic Cloud users. This hardware profile is optimized for applications that require the storage of dense or sparse embeddings for search and Generative AI use cases powered by RAG (retrieval augmented generation). This release follows the previous release of a Vector Search optimized hardware profile for AWS Elastic Cloud users in Nov 2023.</p><h2>GCP Vector Search optimized instances: what you need to know</h2><p>Elastic Cloud users benefit from having Elastic managed infrastructure across all major cloud providers (GCP, AWS and Azure) along with <a href="https://www.elastic.co/guide/en/cloud/current/ec-regions-templates-instances.html">wide region support</a> for GCP users. For more specific details on the instance configuration for this hardware profile, refer to our documentation for instance type: <a href="https://www.elastic.co/guide/en/cloud/current/ec-default-gcp-configurations.html">gcp.es.datahot.n2d.64x8x11</a></p><h2>Vector Search, HNSW, and memory</h2><p>Elasticsearch uses the <a href="https://www.elastic.co/search-labs/blog/vector-search-elasticsearch-rationale">Hierarchical Navigable Small World</a> graph (HNSW) data structure to implement its Approximate Nearest Neighbor search (ANN). Because of its layered approach, HNSW's hierarchical aspect offers excellent query latency. To be most performant, HNSW requires the vectors to be cached in the node's memory. This caching is done automatically and uses the available RAM not taken up by the Elasticsearch JVM. Because of this, memory optimizations are important steps for scalability.</p><p>Consult our vector search <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-knn-search.html#_ensure_data_nodes_have_enough_memory">tuning guide</a> to determine the right setup for your vector search embeddings and whether you have adequate memory for your deployment.</p><p>With this in mind, the Vector Search optimized hardware profile is configured with a smaller than standard Elasticsearch JVM heap setting. This provides more RAM for caching vectors on a node, allowing users to provision fewer nodes for their vector search use cases.</p><p>If you’re using compression techniques like <a href="https://www.elastic.co/search-labs/blog/scalar-quantization-in-lucene">scalar quantization</a>, the memory requirement is lowered by a factor of 4. To store quantized embeddings (available in versions Elasticsearch 8.12 and later) simply ensure that you’re storing in the correct <code>element_type: byte</code>. To utilize our <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-quantization">automatic quantization</a> of <code>float</code> vectors update your embeddings to use index type: <code>int8_hnsw</code> like in the following mapping example.</p>PUT my-byte-quantized-index
{
  "mappings": {
    "properties": {
      "my_vector": {
        "type": "dense_vector",
        "dims": 512,
        "index_options": {
          "type": "int8_hnsw"
        }
      }
    }
  }
}
<p>In upcoming versions, Elasticsearch will provide this as the default mapping, removing the need for users to adjust their mapping.</p><p>Combining this optimized hardware profile with Elasticsearch’s <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-quantization">automatic quantization</a> are two examples where Elastic is focused on vector search to be cost-effective while still being extremely performant.</p><h2>Getting Started with Elastic Cloud vector search optimized profile for GCP</h2><p>Start a <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">free trial</a> on Elastic Cloud and simply select the new Vector Search optimized profile to get started.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4a33219437748674/6a17d7823e9e458302ba12de/c0434f399ee75c99b290060d7b0e613cbcd0829b-1440x1390.png" alt="cloud UI view for new deployments" /><h2>Migrating existing Elastic Cloud deployments</h2><p>Migrating to this new Vector Search optimized hardware profile is a few clicks away. Simply navigate to your Elastic Cloud management UI, click to manage the specific deployment, and edit the hardware profile. In this example, we are migrating from a ‘Storage optimized’ profile to the new ‘Vector Search’ optimized profile. When choosing to do so, while there is a reduction to available storage and vCPU, what is gained is the ability to store more vectors per memory with vector search.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18139bf4f13d62da/6a17d7843e9e4537e0ba12e2/f13962f914d5d9a3be765bde2ac95a9e2d797d3f-1440x561.png" alt="cloud UI view for migrating deployments" /><p>Migrating to a new hardware profile uses the grow and shrink approach for deployment changes. This approach adds new instances, migrates data from old instances to the new ones, and then shrinks the deployment by removing the old instances. This approach allows for high availability during configuration changes even for single availability zones.</p><p>The following image shows a typical architecture for a deployment running in Elastic Cloud, where vector search will be the primary use case.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbaac029d716db095/6a17d785e9ea874ffca9c421/58e00f32bef1411dbc11849a78b5ecd3c334528a-1440x570.png" alt="deployment view" /><p>This example deployment uses our new Vector Search optimized hardware profile, now available in GCP. This setup includes:</p><ul><li><p>Two data nodes in our hot tier with our vector search profile</p></li><li><p>One Kibana node</p></li><li><p>One Machine Learning node</p></li><li><p>One integration server</p></li><li><p>One master tiebreaker</p></li></ul><p>By deploying these two “full-sized” data nodes with the Vector Search optimized hardware profile and while taking advantage of Elastic’s automatic dense vector <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-quantization">scalar quantization</a>, you can index roughly 60 million vectors, including one replica (with 768 dimensions).</p><h2>Conclusion</h2><p>Vector search is a powerful tool when building modern search applications, be it for semantic document retrieval on its own or integrating with an LLM service provider in a <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">RAG setup</a>. Elasticsearch provides a full-featured vector database natively integrated with a full-featured search platform. Along with improving vector search feature set and usability, Elastic continues to improve scalability. The vector search node type is the latest example, allowing users to scale their search application.</p><p>Elastic is committed to providing scalable, price effective infrastructure to support enterprise grade search experiences. Customers can depend on us for reliable and easy to maintain infrastructure and cost levers like vector compression, so you benefit from the lowest possible total cost of ownership for building search experiences powered by AI.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-vector-profile-gcp</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-vector-profile-gcp</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Elastic Cloud Hosted]]></category>
    <dc:creator><![CDATA[Serena Chou,Jeff Vestal,Yuvraj Gupta]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbaac029d716db095/6a17d785e9ea874ffca9c421/58e00f32bef1411dbc11849a78b5ecd3c334528a-1440x570.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ChatGPT and Elasticsearch: OpenAI meets private data]]></title>
    <description><![CDATA[Integrate Elasticsearch's search relevance with ChatGPT's question-answering capability to enhance your domain-specific knowledge base.]]></description>
    <content:encoded><![CDATA[<p><strong>NOTE: This blog has been revisited with an update incorporating new features Elastic has released since this was first published. </strong><a href="https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-rag-enhancements"><strong>Please check out the new blog here!</strong></a></p><p>Combine Elasticsearch's search relevance with OpenAI's ChatGPT's question-answering capabilities to query your data. In this blog, you'll learn how to connect ChatGPT to proprietary data stores using Elasticsearch and build question/answer capabilities for your data.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt01bcddf80e3722d2/6a1711a3cdacbf135a7d2afc/ffbdc3b88620a1f53af18480929c6978d2fcaa44-1440x1187.png" alt="elasticdocs gpt list the steps free trial" /><h2>What is ChatGPT?</h2><p>In recent months, there has been a surge of excitement around ChatGPT, a groundbreaking AI model created by OpenAI. But what exactly is ChatGPT?</p><p>Based on the powerful GPT architecture, ChatGPT is designed to understand and generate human-like responses to text inputs. GPT stands for "Generative Pre-trained Transformer.” The Transformer is a cutting-edge model architecture that has revolutionized the field of natural language processing (NLP). These models are pre-trained on vast amounts of data and are capable of understanding context, generating relevant responses, and even carrying on a conversation. To learn more about the history of transformer models and some NLP basics in the Elastic Stack, be sure to check out the great <a href="https://www.youtube.com/watch?v=SvvbMCwyOnU">talk by Elastic ML Engineer Josh Devins</a>.</p><p>The primary goal of ChatGPT is to facilitate meaningful and engaging interactions between humans and machines. By leveraging the recent advancements in NLP, ChatGPT models can provide a wide range of applications, from chatbots and virtual assistants to content generation, code completion, and much more. These AI-powered tools have rapidly become an invaluable resource in countless industries, helping businesses streamline their processes and enhance their services.</p><h2>Limitations of ChatGPT &amp; how to minimize them</h2><p>Despite the incredible potential of ChatGPT, there are certain limitations that users should be aware of. One notable constraint is the knowledge cutoff date. Currently, ChatGPT is trained on data up to September 2021, meaning it is unaware of events, developments, or changes that have occurred since then. Consequently, users should keep this limitation in mind while relying on ChatGPT for up-to-date information. This can lead to outdated or incorrect responses when discussing rapidly changing areas of knowledge such as software enhancements and capabilities or even world events.</p><p>ChatGPT, while an impressive AI language model, can occasionally hallucinate in its responses, often exacerbated when it lacks access to relevant information. This overconfidence can result in incorrect answers or misleading information being provided to users. It is important to be aware of this limitation and approach the responses generated by ChatGPT with a degree of skepticism, cross-checking and verifying the information when necessary to ensure accuracy and reliability.</p><p>Another limitation of ChatGPT is its lack of knowledge about domain-specific content. While it can generate coherent and contextually relevant responses based on the information it has been trained on, it is unable to access domain-specific data or provide personalized answers that depend on a user's unique knowledge base. For instance, it may not be able to provide insights into an organization’s proprietary software or internal documentation. Users should, therefore, exercise caution when seeking advice or answers on such topics from ChatGPT directly.</p><p>One way to minimize these limitations is by providing ChatGPT access to specific documents relevant to your domain and questions, and enabling ChatGPT’s language understanding capabilities to generate tailored responses.</p><p>This can be accomplished by connecting ChatGPT to a search engine like Elasticsearch.</p><h2>Elasticsearch — you know, for search!</h2><p>Elasticsearch is a scalable data store and vector database designed to deliver relevant document retrieval, ensuring that users can access the information they need quickly and accurately. Elasticsearch’s primary focus is on delivering the most relevant results to users, streamlining the search process, and enhancing user experience.</p><p>Elasticsearch boasts a myriad of features to ensure top-notch search performance, including support for traditional keyword and text-based search (<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-similarity.html">BM25</a>) and an AI-ready vector search with exact match and approximate kNN (<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">k-Nearest Neighbor</a>) search capabilities. These advanced features allow Elasticsearch to retrieve results that are not only relevant but also for queries that have been expressed using natural language. By leveraging traditional, vector, or hybrid search (BM25 + kNN), Elasticsearch can deliver results with unparalleled precision, helping users find the information they need with ease.</p><p>One of the key strengths of Elasticsearch is its robust API, which enables seamless integration with other services to extend and enhance its capabilities. By integrating Elasticsearch with various third-party tools and platforms, users can create powerful and customized search solutions tailored to their specific requirements. This flexibility and extensibility makes Elasticsearch an ideal choice for businesses looking to improve their search capabilities and stay ahead in the competitive digital landscape.</p><p>By working in tandem with advanced AI models like ChatGPT, Elasticsearch can provide the most relevant documents for ChatGPT to use in its response. This synergy between Elasticsearch and ChatGPT ensures that users receive factual, contextually relevant, and up-to-date answers to their queries. In essence, the combination of Elasticsearch's retrieval prowess and ChatGPT's natural language understanding capabilities offers an unparalleled user experience, setting a new standard for information retrieval and AI-powered assistance.</p><h2>How to use ChatGPT with Elasticsearch</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d63e45e8e71c526/6a1711a5961e696f34c4d013/4c3858ece4620036b838131efd4548b844a1c8ae-1440x951.png" alt="use chatgpt with elasticsearch" /><ol><li><p>Python interface accepts user questions.</p></li></ol><p>Generate a hybrid search request for Elasticsearch</p><ul><li><p>BM25 match on the title field</p></li><li><p>kNN search on the title-vector field</p></li><li><p>Boost kNN search results to align scores</p></li><li><p>Set size=1 to return only the top scored document</p></li></ul><ol><li><p>Search request is sent to Elasticsearch.</p></li><li><p>Documentation body and original url are returned to python.</p></li><li><p>API call is made to OpenAI ChatCompletion.</p></li></ol><ul><li><p>Prompt: "answer this question &lt;question&gt; using only this document &lt;body_content from top search result&gt;"</p></li></ul><ol><li><p>Generated response is returned to python.</p></li><li><p>Python adds on original documentation source url to generated response and prints it to the screen for the user.</p></li></ol><p>The ElasticDoc ChatGPT process utilizes a Python interface to accept user questions and generate a hybrid search request for Elasticsearch, combining BM25 and kNN search approaches to find the most relevant document from the Elasticsearch Docs site, now indexed in Elasticsearch. However, you do not have to use hybrid search or even vector search. Elasticsearch provides the flexibility to use whichever search pattern best fits your needs and provides the most relevant results for your specific data sets.</p><p>After retrieving the top result, the program crafts a prompt for OpenAI's ChatCompletion API, instructing it to answer the user's question using only the information from the selected document. This prompt is key to ensuring the ChatGPT model only uses information from the official documentation, lessening the chance of hallucinations.</p><p>Finally, the program presents the API-generated response and a link to the source documentation to the user, offering a seamless and user-friendly experience that integrates front-end interaction, Elasticsearch querying, and OpenAI API usage for efficient question-answering.</p><p>Note that while we are only returning the top-scored document for simplicity, the best practice would be to return multiple documents to provide more context to ChatGPT. The correct answer could be found in more than one documentation page, or if we were generating vectors for the full body text, those larger bodies of text may need to be chunked up and stored across multiple Elasticsearch documents. By leveraging Elasticsearch's ability to search across numerous vector fields in tandem with traditional search methods, you can significantly enhance your top document recall.</p><h2>Technical setup</h2><p>The technical requirements are fairly minimal, but it takes some steps to put all the pieces together. For this example, we will configure the <a href="https://www.elastic.co/web-crawler">Elasticsearch web crawler</a> to ingest the Elastic documentation and generate vectors for the title on ingest. You can follow along to replicate this setup or use your own data. To follow along we will need:</p><ul><li><p>Elasticsearch cluster</p></li><li><p>Eland Python library</p></li><li><p>OpenAI API account</p></li><li><p>Somewhere to run our python frontend and api backend</p></li></ul><h3>Elastic Cloud setup</h3><p>The steps in this section assume you don’t currently have an Elasticsearch cluster running in Elastic Cloud. If you do you, can skip to the next section.</p><p><strong>Sign up</strong> If you don’t already have an Elasticsearch cluster, you can sign up for a free trial with <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">Elastic Cloud</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3af069b6efc24a7b/6a1711a647d49c1eb52d8b0a/1e9fcc7281b87db1024bdd52d97050b680cf654d-920x1086.png" alt="start free trial" /><p><strong>Create deployment</strong> After you sign up, you will be prompted to create your first deployment.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b04d11d688b0e04/6a1711a8a292996a4cd01124/9bab0a32b62863103ac57843078b35bae6b3d939-1440x823.png" alt="create first deployment" /><ul><li><p>Create a name for your deployment.</p></li><li><p>You can accept the default cloud provider and region or click Edit Settings and choose another location.</p></li><li><p>Click Create deployment. Shortly a new deployment will be provisioned for you and you will be logged in to Kibana. <strong>Back to the Cloud</strong> We need to do a couple of things back in the Cloud Console before we move on: Click on the Navigation Icon in the upper left and select Manage this deployment.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte4d09a0d03af921d/6a1711a947d49c15562d8b0e/c4064762d0fe4858de9f92018084dd3d654f8a68-277x449.png" alt="manage this deployment" /><p>Add a machine learning node.</p><ul><li><p>Back in the Cloud Console, click on Edit under your Deployment’s name in the left navigation bar.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt570ab096155f4cd7/6a1711aa28671432b593e42e/cdbf2abe7b7f2efe95c083a5800ce5c8edbac5e0-330x252.png" alt="deployments edit monitoring" /><ul><li><p>Scroll down to the Machine Learning instances box and click +Add Capacity.</p></li><li><p>Under Size per zone, click and select 2 GB RAM.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt79a4423f5ab22324/6a1703aeb339d5901a769e85/e30e63a849b2ba1fdcc58c946a5a482db8ac88d0-1432x292.png" alt="machine learning instances" /><ul><li><p>Scroll down and click on Save.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75bcca2c5fb12ec1/6a1711ac28671421f193e432/1d3efb1b02888e310c47948966aef3a8fd8879a2-556x176.png" alt="save equivalent api request" /><ul><li><p>In the pop-up summarizing the architecture changes, click Confirm.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ed7ef67cc298e2a/6a1711ae14b270b607e3c6eb/04e0da7b1378609ae810fabe2ac84f9917b5a69c-384x152.png" alt="cancel confirm" /><ul><li><p>In a few moments, your deployment will now have the ability to run machine learning models!</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt438d9d1a5474c693/6a1711afb0367dae8e72be2a/c5dcac69868ce404bf986ccf7a6e6429b4ad807c-1440x156.png" alt="change summary" /><p>Reset Elasticsearch Deployment User and password:</p><ul><li><p>Click on Security on the left navigation under your deployment’s name.</p></li><li><p>Click on Reset Password and confirm with Reset. (Note: as this is a new cluster nothing should be using this Elastic password.)</p></li><li><p>Download the newly created password for the “elastic” user. (We will use this to load our model from Hugging Face and in our python program.)</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt76c75e99c823c09f/6a1711b17d8d67766970e85c/da1dd29d4b3d61ce79921b0bb1a15629b553e689-912x638.png" alt="save deployment credentials" /><p>Copy the Elasticsearch Deployment Cloud ID.</p><ul><li><p>Click on your Deployment name to go to the overview page.</p></li><li><p>On the right-hand side click the copy icon to copy your Cloud ID. (Save this for use later to connect to the Deployment.)</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt352796cac3fbe52b/6a1711b37d8d6725a570e860/f5dcfe766eea84e4a116caf33d8e068119863bba-1440x159.png" alt="applications hardware profile" /><h3>Eland</h3><p>We next need to load an embedding model into Elasticsearch to generate vectors for our blog titles and later for our user’s search questions. We will be using the <a href="https://huggingface.co/sentence-transformers/all-distilroberta-v1">all-distilroberta-v1</a> model trained by SentenceTransformers and hosted on the Hugging Face model hub. This particular model isn’t required for this setup to work. It is good for general use as it was trained on very large data sets covering a wide range of topics. However, with vector search use cases, using a model fine-tuned to your particular data set will usually provide the best relevancy.</p><p>To do this, we will use the <a href="https://github.com/elastic/eland#readme">Eland python library</a> created by Elastic. The library provides a wide range of data science functions, but we will be using it as a bridge to load the model into Elasticsearch from the Hugging Face model hub so it can be deployed on machine learning nodes for inference use.</p><p>Eland can either be run as part of a python script or on the command line. The repo also provides a Docker container for users looking to go that route. Today we will run Eland in a <a href="https://github.com/jeffvestal/ElasticDocs_GPT/blob/main/load_embedding_model.ipynb">small python notebook</a>, which can run in Google’s Colab in the web browser for free.</p><p>Open the <a href="https://github.com/jeffvestal/ElasticDocs_GPT/blob/main/load_embedding_model.ipynb">program link</a> and click the “Open in Colab” button at the top to launch the notebook in colab.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt277c97e73b673f82/6a1711b460084b6b6a3c4680/5a1b9de1ba50f8e48dab6341070194c70b715b61-236x40.png" alt="open in colab" /><p>Set the variable hf_model_id to the model name. This model is set already in the example code but if you want to use a different model or just for future information:</p><ul><li><p>hf_model_id='sentence-transformers/all-distilroberta-v1'</p></li><li><p>Copy model name from Hugging Face. The easiest way to do this is to click the copy icon to the right of the model name.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcbf5b198c21da493/6a1711b514b27074d6e3c6ef/d883b5342323b8bfcbf5a2a29014f48750717b25-1212x270.png" alt="hugging face" /><p>Run the cloud auth section, and you will be prompted to enter:</p><ul><li><p>Cloud ID (you can find this in the Elastic Cloud Console)</p></li><li><p>Elasticsearch Username (easiest will be to use the “Elastic” user created when the deployment was created)</p></li><li><p>Elasticsearch User Password</p></li></ul><p>Run the remaining steps.</p><ul><li><p>This will download the model from Hugging face, chunk it up, and load it into Elasticsearch.</p></li><li><p>Deploy (start) the model onto the machine learning node.</p></li></ul><h3>Elasticsearch index and web crawler</h3><p>Next up we will create a new Elasticsearch index to store our Elastic Documentation, configure the web crawler to automatically crawl and index those docs, as well as use an ingest pipeline to generate vectors for the doc titles.</p><strong>Note that you can use your proprietary data for this step, to create a question/answer experience tailored to your domain.</strong><ul><li><p>Open Kibana from the Cloud Console if you don’t already have it open.</p></li><li><p>In Kibana, Navigate to Enterprise Search -&gt; Overview. Click Create an Elasticsearch Index.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdc51e9f198d426e7/6a1711b74a531b8d0936aa8d/57ae4a7024863162265b67da3f0419bcd7bd6f62-752x180.png" alt="create an elasticsearch index" /><ul><li><p>Using the Web Crawler as the ingestion method, enter elastic-docs as the index name. Then, click Create Index.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65d0058455a44207/6a1711b8b0367da8aa72be2e/51bd68374c03625dfa1e06cc3170e4173b5fb7b6-1440x474.png" alt="select an ingestion method" /><ul><li><p>Click on the “Pipelines” tab.</p></li><li><p>Click Copy and customize in the Ingest Pipeline Box.</p></li><li><p>Click Add Inference Pipeline in the Machine Learning Inference Pipelines box.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltabf90aa59e3e850f/6a1711ba0c4857d1a001ab9c/e3d85ab9e4b6688dc5ea8614f6b2248394177485-1186x436.png" alt="machine learning inference pipelines" /><ul><li><p>Enter the name elastic-docs_title-vector for the New pipeline.</p></li><li><p>Select the trained ML model you loaded in the Eland step above.</p></li><li><p>Select title as the Source field.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc443155a95f5187b/6a1711bb964cea4f6108bcd5/555170b190ea940418f2bf8ba7d45c41d2589a75-1440x818.png" alt="configure add a new pipeline" /><ul><li><p>Click Continue, then click Continue again at the Test stage.</p></li><li><p>Click Create Pipeline at the Review stage.</p></li></ul><p>Update mapping for dense_vector field. (Note: with Elasticsearch version 8.8+, this step should be automatic.)</p><ul><li><p>In the navigation menu, click on Dev Tools. You may have to click Dismiss on the flyout with documentation if this is your first time opening Dev Tools.</p></li><li><p>In Dev Tools in the Console tab, update the mapping for our dense vector target field with the following code. You simply paste it in the code box and click the little arrow to the right of line 1.</p></li></ul>POST search-elastic-docs/_mapping
{
  "properties": {
    "title-vector": {
      "type": "dense_vector",
      "dims": 768,
      "index": true,
      "similarity": "dot_product"
    }
  }
}
<ul><li><p>You should see the following response on the right half of the screen:</p></li></ul>{
  "acknowledged": true
}
<ul><li><p>This will allow us to run kNN search on the title field vectors later on.</p></li></ul><p>Configure web crawler to crawl Elastic Docs site:</p><ul><li><p>Click on the navigation menu one more time and click on Enterprise Search -&gt; Overview.</p></li><li><p>Under Content, click on Indices.</p></li><li><p>Click on search-elastic-docs under Available indices.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39f78c6403e11420/6a1711bd47d49c67992d8b1a/7b05934f8775f4f602eb258c7bb8c3c1b82bd285-1440x177.png" alt="available indices" /><ul><li><p>Click on the Manage Domains tab.</p></li><li><p>Click “Add domain.”</p></li><li><p>Enter <a href="https://www.elastic.co/guide/en">https://www.elastic.co/guide/en</a>, then click Validate Domain.</p></li><li><p>After the checks run, click Add domain. Then click Crawl rules.</p></li><li><p>Add the following crawl rules one at a time. Start with the bottom and work up. Rules are evaluated according to first match.</p></li></ul><p></p><p></p><p></p><p>Disallow</p><p>Contains</p><p>release-notes</p><p>Allow</p><p>Regex</p><p>/guide/en/.*/current/.*</p><p>Disallow</p><p>Regex</p><p>.*</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72127923b67d3b5a/6a1711bed7c022c73ade65c4/efd9052ba0038084855988b4fa6888a2a114a974-1440x410.png" alt="crawl rules" /><ul><li><p>With all the rules in place, click Crawl at the top of the page. Then, click Crawl all domains on this index.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd9093ca85f8da56/6a1711c0a929cf9114ae0ae1/a50db802ba5cadb3863ffa314a8deee9b64c8dd7-638x380.png" alt="search engines crawl" /><p>Elasticsearch’s web crawler will now start crawling the documentation site, generating vectors for the title field, and indexing the documents and vectors.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteedc9efa600488e7/6a1711c2c1e8a5aea3f883d9/41eac123618338f127abffe9c957300f4e61fd0a-338x128.png" alt="crawling" /><p>The first crawl will take some time to complete. In the meantime, we can set up the OpenAI API credentials and the Python backend.</p><h2>Connecting with OpenAI API</h2><p>To send documents and questions to ChatGPT, we need an OpenAI API account and key. If you don’t already have an account, you can create a free account and you will be given an initial amount of free credits.</p><ul><li><p>Go to <a href="https://platform.openai.com">https://platform.openai.com</a> and click on Signup. You can go through the process to use an email address and password or login with Google or Microsoft.</p></li></ul><p>Once your account is created, you will need to create an API key:</p><ul><li><p>Click on <a href="https://platform.openai.com/account/api-keys">API Keys</a>.</p></li><li><p>Click Create new secret key.</p></li><li><p>Copy the new key and save it someplace safe as you won’t be able to view the key again.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3c4b4def8f822024/6a1711c3a292995b95d0112e/b94891d6199c0c0c9f451eec9c8ac8d250882991-1114x586.png" alt="api key generated" /><h2>Python backend setup</h2><h3>Clone or download the python program</h3><p><a href="https://github.com/jeffvestal/ElasticDocs_GPT/blob/main/elasticdocs_gpt.py">Github Link to code</a></p><ol><li><p>Install required python libraries. We are running the example program in Replit, which has isolated environments. If you are running this on a laptop or VM, best practice is to <a href="https://docs.python.org/3/library/venv.html">set up a virtual ENV for python</a>.</p></li></ol><ul><li><p>Run pip install -r requirements.txt</p></li></ul><ol><li><p>Set authentication and connection environment variables (e.g., if running on the command line: export openai_api=”123456abcdefg789”)</p></li></ol><ul><li><p>openai_api - OpenAI API Key</p></li><li><p>cloud_id - Elastic Cloud Deployment ID</p></li><li><p>cloud_user - Elasticsearch Cluster User</p></li><li><p>cloud_pass - Elasticsearch User Password</p></li></ul><ol><li><p>Run the streamlit program. More info about <a href="https://docs.streamlit.io/library/get-started/installation">streamlit can be found in its docs</a>.</p></li></ol><ul><li><p>Streamlit has its own command to start: streamlit run elasticdocs_gpt.py</p></li></ul><ol><li><p>This will start a web browser and the url will be printed to the command line.</p></li></ol><h2>Sample chat responses</h2><p>With everything ingested and the front end up and running, you can start asking questions about the Elastic Documentations.</p><p>Asking “Show me the API call for an inference processor” now returns an example API call and some information about the configuration settings.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltffaa60616a42dc47/6a1711c50e2e49673b41a258/d767639258b64417da444346e191a158620cf134-1440x1448.png" alt="show api call" /><p>Asking for steps to add a new integration to Elastic Agent will return:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf0a7fe68f5fe11c5/6a1711c6a929cf650bae0ae5/5065af9bf9ae5ee5fd2a1c7636d2293746fb241d-1440x1272.png" alt="how add new integration" /><p>As mentioned earlier, one of the risks of allowing ChatGPT to answer questions based purely on data it has been trained on is its tendency to hallucinate incorrect answers. One of the goals of this project is to provide ChatGPT with the data containing the correct information and let it craft an answer.</p><p>So what happens when we give ChatGPT a document that does not contain the correct information? Say, asking it to tell you how to build a boat (which isn’t currently covered by Elastic’s documentation):</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba5ff5dddb7b8982/6a1711c8b339d58b8d76a0e8/43f80bfefab55261493751672669616cc6d0f54b-1440x548.png" alt="show build boat" /><p>When ChatGPT is unable to find an answer to the question in the document we provided, it falls back on our prompt instruction simply telling the user it is unable to answer the question.</p><h2>Elasticsearch’s robust retrieval + the power of ChatGPT</h2><p>In this example, we've demonstrated how integrating Elasticsearch's robust search retrieval capabilities with cutting-edge advancements in AI-generated responses from GPT models can elevate the user experience to a whole new level.</p><p>The individual components can be tailored to suit your specific requirements and adjusted to provide the best results. While we used the Elastic web crawler to ingest public data, you're not limited to this approach. Feel free to experiment with alternative embedding models, especially those fine-tuned for your domain-specific data.</p><p>You can try all of the capabilities discussed in this blog today! To build your own ElasticDocs GPT experience, sign up for an <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">Elastic trial account</a>, and then look at this <a href="https://github.com/jeffvestal/ElasticDocs_GPT">sample code repo</a> to get started.</p><p>If you would like ideas to experiment with search relevance, here are two to try out:</p><ul><li><p><a href="https://www.elastic.co/blog/how-to-deploy-nlp-text-embeddings-and-vector-search">[BLOG] Deploy NLP text embeddings and vector search using Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/blog/implement-image-similarity-search-elastic">[BLOG] Implement image similarity search with Elastic</a></p></li></ul><p><em>In this blog post, we may have used third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p><p><em>Elastic, Elasticsearch and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-openai-meets-private-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-openai-meets-private-data</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4478d2508f563479/6a1711c9a929cf0b9fae0ae9/1d616d244f05328ed677b008941db001d79c86b7-1440x840.png" length="0" type="image/png"/>
    <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>