<?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[Luca Wintergerst - 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[Luca Wintergerst - 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/luca-wintergerst</link>
    </image>
    <link>https://www.elastic.co/search-labs/author/luca-wintergerst</link>
    <atom:link href="https://www.elastic.co/search-labs/rss/author/luca-wintergerst.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Fri, 18 Sep 2026 18:49:32 GMT</lastBuildDate>
  <item>
    <title><![CDATA[ChatGPT and Elasticsearch: enhance user experience with faceting, filtering, and more context]]></title>
    <description><![CDATA[By providing ChatGPT more context and using Elasticsearch's facets &amp; filters, you can refine the search and lower ChatGPT costs. Here's how.]]></description>
    <content:encoded><![CDATA[<p>In a recent blog post, we discussed how ChatGPT and Elasticsearch can <a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data">work together</a> to help manage proprietary data more effectively. By utilizing Elasticsearch's search capabilities and ChatGPT's contextual understanding, we demonstrated how the resulting outcomes can be improved.</p><p>In this post, we discuss how users’ experience can be further enhanced with the addition of facets, filtering, and additional context. By providing tools like ChatGPT additional context, you can increase the likelihood of obtaining more accurate results. See how Elasticsearch's faceting and filtering framework can allow users to refine their search and reduce the cost of engaging with ChatGPT.</p><h2>Comparing ChatGPT and Elasticsearch results</h2><p>To improve the user experience of our sample application, we've added a feature that displays the raw results alongside the ChatGPT-created response. This will help users better understand how ChatGPT works.</p><p>Since our source data set is only crawled, the structure in the documents makes it difficult to read for a human. To show this difference and therefore the value that ChatGPT can bring, we added the raw result next to the GPT created response.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c9d37f2456eefe3/6a17119ba929cf5105ae0ad9/0db9068dcd432aae0871c68bcdbf0227b7580e91-1440x939.png" alt="" /><p>Currently, this example application only returns a single result. And even though we have hybrid scoring with vector search and BM25, this result may not be perfect. If we take this not perfect result and pass it over to ChatGPT, there’s a good chance that the response we get won’t be great either, as the context was missing important information.</p><p>Ideally, we’d just pass more context into ChatGPT, but the current 3.5-turbo models are limited to 4,096 tokens (that’s including the response you expect to get, so the actual limit is much lower). Future models will likely have a much larger limit, but this also comes with a cost.</p><p>As of today, GPT-3.5-turbo costs $0.002 per 1K <a href="https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them">tokens</a>, while the up-and-coming GPT-4 with 32K context costs $0.06 per 1K tokens — that’s a factor of 30 more. Even with more powerful models coming in the next few years, there’s a chance that it’s not economically viable to do so for all user cases.</p><p>We will therefore not use GPT-4 right now and instead work around the max token limitation of GPT-3.5 by sending multiple concurrent requests and giving the user more flexibility in filtering the results.</p><h2>Leveraging aggregations, facets, and filtering in Elasticsearch to enhance ChatGPT</h2><p>To address this limitation, one of the biggest advantages of Elasticsearch is its robust faceting and filtering framework. When a user is searching for something, they may have additional preferences or context they can provide to dramatically increase the likelihood of obtaining the correct result. By leveraging Elasticsearch's faceting and filtering framework, we can allow users to refine their search based on various parameters such as date, location, or other relevant criteria.</p><p>It’s also important to note that many users have gotten used to having facet filtering options available when searching for something. Let us look at an example.</p><p>Searching for “How can I parse a message with Grok?” results in a document for ingest pipelines to be returned as the top result. This is not wrong, as ingest pipelines also support Grok expressions, but what if the user was interested in parsing his data using Logstash?</p><p>Using a simple terms aggregation as part of the request to fetch the hits, we can get a list of the top 10 product categories and offer these as a filtering option for a user.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt19e8ac27b6e24ca1/6a17119ccf4f25a6bcb2d267/39187b4da43ec7d9c75a4e7ff4ec6666b9f410b8-1440x816.png" alt="chatgpt options" /><p>If the user now selects “Logstash” on the left side, all results will be filtered for Logstash. It’s important to note that this all works while still using the same hybrid query model that we’ve talked about in the previous blog. We’re still using a combination of BM25 and kNN search to match our documents.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt150df5ca25922fc4/6a17119e8b73cb1bbe18a131/475f39166fd84e9728cb49d142b5f83e8af7b127-1440x814.png" alt="chatgpt grok filter plugin" /><h2>Loading multiple results in parallel</h2><p>We briefly mentioned the max token limit earlier. In short, the prompt that you send to the API and its response can’t be longer than 4,096 tokens. When searching your proprietary data, you would like to provide as much specific context as possible so the model can give you the best answer. However, the 4,096 tokens aren’t that much, especially when you include things like code snippets.</p><p>A very simple first step toward mitigating the limit is to just ask multiple times in parallel, giving a different context each time. Using our approach with Elasticsearch, instead of only fetching the top 1 result and sending that to OpenAI, we can change the application to load the top 10 hits instead and then ask the question with the respective context.</p><p>This gives us 10 unique answers to our question and greatly increases our chances of presenting a relevant answer to the end user. While we are increasing the burden of the user to look at the results, it still gives them more flexibility.</p><p>Think of it like this: if you try to debug a problem and search for an exception on Google, you quickly scan the list of the top four or five results that Google displays and click on the one that seems most fitting to your question. Showing the user multiple answers to their question is similar to this.</p><p>While having a single correct answer would be ideal, having more than one to choose from initially is a great starting point. And as mentioned before, it can be cheaper compared to using a more expensive model (such as GPT-4).</p><p>We can also get more creative with our prompt and ask ChatGPT to send us a specific response if it can’t answer the question using the provided context. This will allow us to remove the results from the UI later.</p><p>One prompt that worked well in our use case is:</p>prompt = f"Answer this question: {query}\n. Don’t give information not mentioned in the CONTEXT INFORMATION. If the CONTEXT INFORMATION contains code or API requests, your response should include code snippets. If the context does not contain relevant information, answer 'The provided page does not answer the question': \n {body}"
<h2>Working around the max token limit of ChatGPT: Answering a question from a set of answers</h2><p>Since we have more than a single answer to our question now, we can attempt to summarize them into a single response. For this, we will mostly follow the same approach as before, but instead of searching Elasticsearch for the context, we will just concatenate the individual answers we’ve received so far, excluding any where the model responded that it can’t answer it based on the provided context.</p><p>Note that the prompt for this run is a little different from the earlier prompt, so the model treats our context slightly differently. The provided prompt here is by no means perfect, and depending on the data, it should be adjusted and optimized further.</p>concatResult = ""
        for resultObject in results:
            if resultObject['choices'][0]["message"]["content"] != "The provided page does not answer the question.":
                concatResult += resultObject['choices'][0]["message"]["content"]
        if st.session_state['summarizeResults']['state']:
            results = [None] * 1
            tasks = []
            prompt = f"I will give you {numberOfResults} answers to this question.: \"{query}\"\n. They are ordered by their likelyhood to be correct. Come up with the best answer to the original question, using only the context I will provide you here. If the provided context contains code snippets or API requests, half of your response must be code snippets or API requests. \n {concatResult}"
            element = None
            with st.session_state['topResult']:
                with st.container():
                    st.markdown(f"**Summary of all results:**")
                    element = st.empty()

            with elasticapm.capture_span("top-result", "openai"):
                task = loop.create_task(achat_gpt(prompt, results, counter, element))
                tasks.append(task)
                loop.set_exception_handler(handle_exception)
                loop.run_until_complete(asyncio.wait(tasks))
	      loop.close()
<p>With this additional “reduce phase” in place, our app will now:</p><ul><li><p>Search Elasticsearch for the top 10 hits</p></li><li><p>10x in parallel ask OpenAI to answer the question, providing a different context each time</p></li><li><p>Concatenate responses from OpenAI and ask OpenAI once again to answer the question</p></li></ul><p>With this setup, we can use close to 40,000 tokens of context, while only paying for the considerably cheaper GPT-3.5 model. In another blog post, we will explore the cost in more detail and use Elastic APM for tracking our spend, alongside other metrics.</p><p>It should be noted that GPT-4 may still perform much better than the approach above, so use whatever works best for you and the amount of traffic you expect.</p><h2>Citations for your ChatGPT results</h2><p>One downside of large language models (LLMs) is their overconfidence and tendency to hallucinate. You ask a question, you get an answer. Whether the answer is actually correct is for you to decide. The model rarely admits that it does not know something. Providing the context and telling it to respond with a specific answer as we did above helps mitigates this to some extent.</p><p>But the provided context alongside getting the model to admit that it can’t answer a question also allows us to provide more accurate citations for the responses.</p><p>In the last section, we summarized our set of 10 answers into one global answer. In addition to just providing this global answer, we can also provide a list of all source documentation pages that we used to compile the result — basically any page where the model did not respond "The provided page does not answer the question."</p><p>In this screenshot, you can see the summary answer on a set of 10 results from Elasticsearch. Even though we inspected 10 results, we are only displaying the three links to the documentation that are actually relevant to answer the question. In this case, the other seven documents returned by Elasticsearch had something to do with documents or indices, but they didn’t specifically talk about how to index something.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4c4bc2ffece60c7c/6a17119fe8fbce22a839fd5d/ca302c403cfe19dc86a69d9f38d400510908bf81-1440x952.png" alt="chatgpt to index a document" /><h2>Searching proprietary data</h2><p>We’ve mentioned in an <a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data">earlier blog post</a> that it’s great to use Elasticsearch and OpenAI to search proprietary data. However, we did use a web-crawler to crawl public documentation. That may seem a bit counterintuitive, and you’re right to think about it! OpenAI trains GPT models on web data, so we will assume it knows our documentation already. So why do we need Elasticsearch in addition to that data? Does this setup actually work on data that’s not public? It does — let’s prove it.</p><p>Using the existing setup, we will push a single super secret document about an internal project into our index.</p>PUT search-elastic-docs/_doc/1?pipeline=search-elastic-docs@ml-inference
{
  "title": "Project LfQg832p6Jx040809WZc",
  "product_name": "SuperSecret",
  "url": "https://www.example.com",
  "body_content": """What is Project LfQg832p6Jx040809WZc? Project LfQg832p6Jx040809WZc is an internal project that's not public information. This is the plan for the project: Step 1 is writing a blog post about OpenAi and Elasticsearch for private data. Step 2 is noticing that we didn't actually include any private data. Step 3 is including an example about private data

  We also have some super secret API requests as part of this project:
  PUT project/_doc/hello-world
  {
    "secret": "don't share this with anyone!"
  }

  """
}
<p>Next we’ll then head over to our app and search for “What are the steps for the internal project?”</p><p>In summary, we used faceting and filtering to, for certain use cases, reduce the number tokens of context required to engage with ChatGPT. By providing additional context at query time, we showed it is also possible to improve the accuracy of search results.</p><p><a href="https://www.elastic.co/blog/may-2023-launch-announcement"><strong>Learn more about the possibilities with Elasticsearch and AI</strong></a> <strong>.</strong></p><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>Costs referred to herein are based on the current OpenAI API pricing and how often we call it when loading our sample app.</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-faceting-filtering-more-context</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-faceting-filtering-more-context</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Luca Wintergerst]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9fd006de03fddc5d/6a1711a1dc55de3cf7e00ef7/981b7b0cb9b9ca0561e9c1784f5ce51240199385-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ChatGPT and Elasticsearch: APM instrumentation, performance, and cost analysis]]></title>
    <description><![CDATA[Learn how to instrument a Python application that uses OpenAI, analyze its performance &amp; cost and integrate large language models (LLMs).]]></description>
    <content:encoded><![CDATA[<p>In a <a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data">previous blog post</a>, we built a small Python application that queries Elasticsearch using a mix of vector search and BM25 to help find the most relevant results in a proprietary data set. The top hit is then passed to OpenAI, which answers the question for us.</p><p>In this blog, we will instrument a Python application that uses OpenAI and analyze its performance, as well as the cost to run the application. Using the data gathered from the application, we will also show how to integrate large language models (LLMs) into your application. As a bonus, we will try to answer the question: why does ChatGPT print its output word by word?</p><h2>Instrumenting the application with Elastic APM</h2><p>If you’ve had a chance to give our <a href="https://github.com/jeffvestal/ElasticDocs_GPT/blob/main/elasticdocs_gpt.py">sample application</a> a try, you might have noticed that the result does not load as quickly as you’d expect it to, from a search interface.</p><p>The now is if this is from our two-phased approach of running a query in Elasticsearch first or if the slow behavior is emerging from OpenAI, or if it’s a combination of the two.</p><p>Using Elastic APM, we can easily instrument this application to get a better look. All we need to do for the instrumentation is the following (we will show the full example at the end of the blog post and also in a GitHub repository):</p>import elasticapm
# the APM Agent is initialized
apmClient = elasticapm.Client(service_name="elasticdocs-gpt-v2-streaming")

# the default instrumentation is applied
# this will instrument the most common libraries, as well as outgoing http requests
elasticapm.instrument()
<p>Since our sample application is using Streamlit, we will also need to start at least one transaction and eventually end it again. In addition, we can also provide information about the outcome of the transaction to APM, so we can track failures properly.</p># start the APM transaction
apmClient.begin_transaction("user-query")

(...)



elasticapm.set_transaction_outcome("success")

# or "failure" for unsuccessful transactions
# elasticapm.set_transaction_outcome("success")

# end the APM transaction
apmClient.end_transaction("user-query")
<p>And that’s it — this would be enough to have full APM instrumentation for our application. That being said, we will be doing a little extra work here in order to get some more interesting data.</p><p>As a first step, we will add the user’s query to the APM metadata. This way we can inspect what the user was trying to search and can analyze some popular queries or reproduce errors.</p>elasticapm.label(query=query)
<p>In our async method, which talks to OpenAI, we will also add some more instrumentation so we can better visualize the tokens we receive, as well as to collect additional statistics.</p>async with elasticapm.async_capture_span('openaiChatCompletion', span_type='openai'):
        async for chunk in await openai.ChatCompletion.acreate(engine=engine, messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": truncated_prompt}],stream=True,):
            content = chunk["choices"][0].get("delta", {}).get("content")
            # since we have the stream=True option, we can get the output as it comes in
            # one iteration is one token
	  # we start a new span here for each token. These spans will be aggregated
            # into a compressed span automatically
            with elasticapm.capture_span("token", leaf=True, span_type="http"):
                if content is not None:
                    # concatenate the output to the previous one, so have the full response at the end
                    output += content
                    # with every token we get, we update the element
                    element.markdown(output)
<p>And finally, toward the very end of our application, we will also add the number of tokens and approximate cost to our APM transaction. This will enable us to visualize these metrics later and correlate them to the application performance.</p><p>If you do not use streaming, then the OpenAI response will contain a “total_tokens” field, which is the sum of the context you sent and the response returned. If you are using the “stream=True” option, then it’s your responsibility to calculate the number of tokens or approximate them. A common recommendation is to use “(len(prompt) + len(response)) / 4” for english text, but especially code snippets can throw off this approximation. If you need more exact numbers, you can use libraries like <a href="https://github.com/openai/tiktoken">tiktoken</a> to calculate the number of tokens.</p># add the number of tokens as a metadata label
elasticapm.label(openai_tokens = st.session_state['openai_current_tokens'])
# add the approximate cost as a metadata label
# currently the cost is $0.002 / 1000 tokens
elasticapm.label(openai_cost = st.session_state['openai_current_tokens'] / 1000 * 0.002)

<h2>Analyzing the APM data — Elasticsearch vs. OpenAI performance</h2><p>After instrumenting the application, a quick look at the “Dependencies” gives us a better understanding of what’s going on. It looks like our requests to Elasticsearch return within 125ms on average, while OpenAI takes 8,500ms to complete a request. (This screenshot was taken on a version of the application that does not use streaming. If you use streaming, the default instrumentation only considers the initial POST request in the dependency response time and not the time it takes to stream the full response.)</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa68cd926558eb2/6a17117f47d49c04ba2d8b02/78cb423a576c698d97b3d47a13ec896e371008a5-1440x494.png" alt="chatgpt dependencies" /><p>If you’ve used ChatGPT yourself already, you might have been wondering why the UI is printing every word individually, instead of just returning the full response immediately.</p><p>As it turns out, this is not actually to entice you to pay money if you’re using the free version! It’s more of a limitation of the inference model. In simple terms, in order to compute the next token, <a href="https://lilianweng.github.io/posts/2023-01-10-inference-optimization/">the model</a> will need to take into consideration the last token as well. So there’s not much room for parallelization. And since every token is processed individually, this token can also be sent to the client, while the computation for the next token is running.</p><p>In order to improve the UX, it can be helpful to therefore use a streaming approach when using the ChatCompletion functionality. This way a user can start to consume the first results while the full response is being generated. You can see this behavior in the GIF below. Even though all three responses are still loading, the user can scroll down and inspect what’s there already.</p><p>As mentioned previously, we added a bit more custom instrumentation than just the bare minimum. This allows us to get detailed information on where our time is spent. Let’s take a look at a full trace and see this streaming in action.</p><p>Our application is configured to fetch the top three hits from Elasticsearch, and then run one ChatCompletion request against OpenAI in parallel.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6234efc85bc5c69b/6a171180ab7f080136db9f9f/c930cf97ddc51049f839bba4aa41b3d1901f4c67-1440x679.png" alt="elastic openai in parallel" /><p>As we can see in the screenshot, loading the individual results takes about 15s. We can also see that requests to OpenAI that return a larger response take longer to return. But this is only a single request. Does this behavior happen for all requests? Is there a clear correlation between response time and number of tokens to back up our claims from earlier?</p><h2>Analyzing cost and response time</h2><p>Instead of visualizing the data using Elastic APM, we can also use custom dashboards and create visualizations from our APM data. Two interesting charts that we can build show the relationship between the number of tokens in a response and the duration of the request.</p><p>We can see that the more tokens get returned (x-axis in the first chart), the higher the duration (y-axis in the first chart). In the chart to the right, we can also see that the duration per 100 tokens returned stays almost flat at around 4s, no matter the number of tokens returned in total (x-axis).</p><p>If you want to improve the responsiveness of your application that uses OpenAI models, it might be a good idea to tell the model to keep the response short.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ccfdcdb869cd30e/6a1711820c4857151a01ab8c/6bbadaa8995e8c947b593451732dad3135806871-1440x549.png" alt="chatgpt response time vs tokens" /><p>In addition to this, we can also track our total spend and the average cost per page load, as well as other statistics.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf567d2300b1599d2/6a171183dc55dee5e2e00ee9/d1f6a54c30109a7aea12b7cbcbd183f516f96e51-1440x820.png" alt="chatgpt total cost" /><p>With our sample application, the cost for a single search is around 1.1¢. This number does not sound high, but it’s likely not something that you will have on your public website as a search alternative anytime soon. For company internal data and a search interface that’s only used occasionally, this cost is negligible.</p><p>In our testing, we’ve also hit frequent errors when using the OpenAI API in Azure, which eventually made us add a retry loop to the sample app with an exponential backoff. We can also capture these errors using Elastic APM.</p>while tries &lt; 5:
    try:
        print("request to openai for task number: " + str(index) + " attempt: " + str(tries))
        async with elasticapm.async_capture_span('openaiChatCompletion', span_type='openai'):
            async for chunk in await openai.ChatCompletion.acreate(engine=engine, messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": truncated_prompt}],stream=True,):
                content = chunk["choices"][0].get("delta", {}).get("content")
                counter += 1
                with elasticapm.capture_span("token", leaf=True, span_type="http"):
                    if content is not None:
                        output += content
                        element.markdown(output)
        break
    except Exception as e:
        client = elasticapm.get_client()
        # capture the exception using Elastic APM and send it to the apm server
        client.capture_exception()
        tries += 1
        time.sleep(tries * tries / 2)
        if tries == 5:
            element.error("Error: " + str(e))
        else:
            print("retrying...")
<p>Any captured errors are then visible in the waterfall charts as part of the span where the failure happened.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf84f271da871a62e/6a1711854a531b456736aa6f/49939b8e2b24eabb366799917681ebd66a8e53fa-1440x871.png" alt="timeline user query" /><p>In addition, Elastic APM also provides an overview of all the errors. In the screenshot below, you can see the occasional RateLimitError and APIConnectionError that we’ve encountered. Using our crude exponential retry mechanism, we can mitigate most of these problems.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1d7f046dcd014818/6a171186a6c2b9203be797ec/203e60003a3f1da6dff6d5d21870bbe32da8c847-1440x764.png" alt="elasticdocs gpt v2 streaming" /><h2>Latency and failed transaction correlation</h2><p>With all the built-in metadata that the Elastic APM agent capture, as well as the custom labels we added, we can easily analyze if there’s any correlation between the performance and any of the metadata (like services version, user query, etc.)</p><p>As we can see below, there’s a small correlation between the query “How can I mount and index on a frozen node?” and a slower response time.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba078944b177b48f/6a171188d7c0228ae7de65a6/3839ef67669dcee18515618997d9e591d9445f63-1440x592.png" alt="latency distribution correlations" /><p>Similar analysis can be done on any transaction that resulted in an error. In this example, the two queries “How do I create an ingest pipeline” and “How can I create an ingest pipeline” fail more often than other queries, causing them to bubble up in this correlation analysis.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d4c16b1beb5ebfa/6a17118947d49cf9452d8b06/a4094ed5ba442a74e87b7fa73c18c110939452ff-1440x639.png" alt="failed transactions latency distribution" />import elasticapm
# the APM Agent is initialized
apmClient = elasticapm.Client(service_name="elasticdocs-gpt-v2-streaming")

# the default instrumentation is applied
# this will instrument the most common libraries, as well as outgoing http requests
elasticapm.instrument()

# if a user clicks the "Search" button in the UI
if submit_button:
	# start the APM transaction
apmClient.begin_transaction("user-query")
# add custom labels to the transaction, so we can see the users question in the API UI
elasticapm.label(query=query)



    async with elasticapm.async_capture_span('openaiChatCompletion', span_type='openai'):
        async for chunk in await openai.ChatCompletion.acreate(engine=engine, messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": truncated_prompt}],stream=True,):
            content = chunk["choices"][0].get("delta", {}).get("content")
            # since we have the stream=True option, we can get the output as it comes in
            # one iteration is one token
            with elasticapm.capture_span("token", leaf=True, span_type="http"):
                if content is not None:
                    # concatenate the output to the previous one, so have the full response at the end
                    output += content
                    # with every token we get, we update the element
                    element.markdown(output)
async def achat_gpt(prompt, result, index, element, model="gpt-3.5-turbo", max_tokens=1024, max_context_tokens=4000, safety_margin=1000):
    output = ""
    # we create on overall Span here to track the total process of doing the completion
    async with elasticapm.async_capture_span('openaiChatCompletion', span_type='openai'):
        async for chunk in await openai.ChatCompletion.acreate(engine=engine, messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": truncated_prompt}],stream=True,):
            content = chunk["choices"][0].get("delta", {}).get("content")
            # since we have the stream=True option, we can get the output as it comes in
            # one iteration is one token, so we create one small span for each
            with elasticapm.capture_span("token", leaf=True, span_type="http"):
                if content is not None:
                    # concatenate the output to the previous one, so have the full response at the end
                    output += content
                    # with every token we get, we update the element
                    element.markdown(output)
<p>In this blog, we instrumented an app written in Python to use OpenAI and analyze its performance. We looked at response latency and failed transactions, and we assessed the costs of running the application. We hope this how-to was useful for you!</p><p><a href="https://www.elastic.co/what-is/elasticsearch-machine-learning"><strong>Learn more about the possibilities with Elasticsearch and AI</strong></a> <strong>.</strong></p><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>Costs referred to herein are based on the current OpenAI API pricing and how often we call it when loading our sample app.</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-apm-instrumentation-performance-cost-analysis</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-elasticsearch-apm-instrumentation-performance-cost-analysis</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Luca Wintergerst]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt762b38f91bd71c8e/6a170db8b339d547bb76a048/368db71c500e72d20fe225fe44c2c40231e29765-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>