ChatGPT 和 Elasticsearch:APM 工具、性能和成本分析

news2024/12/29 8:47:59

作者:LUCA WINTERGERST

在本博客中,我们将测试一个使用 OpenAI 的 Python 应用程序并分析其性能以及运行该应用程序的成本。 使用从应用程序收集的数据,我们还将展示如何将 LLMs 成到你的应用程序中。

在之前的博客文章中,我们构建了一个小型 Python 应用程序,该应用程序使用向量搜索和 BM25 的组合来查询 Elasticsearch,以帮助在专有数据集中找到最相关的结果。 然后,最热门的结果会传递给 OpenAI,它会为我们解答问题。

在本博客中,我们将测试使用 OpenAI 的 Python 应用程序并分析其性能以及运行该应用程序的成本。 使用从应用程序收集的数据,我们还将展示如何将大型语言模型 (LLM) 集成到你的应用程序中。 作为额外的奖励,我们将尝试回答这个问题:为什么 ChatGPT 逐字打印其输出?

使用 Elastic APM 检测应用程序

如果你有机会尝试我们的示例应用程序,你可能会注意到,从搜索界面加载结果的速度没有你期望的那么快。

现在的问题是,这是否来自我们首先在 Elasticsearch 中运行查询的两阶段方法,或者缓慢的行为是否来自 OpenAI,或者是否是两者的组合。

使用 Elastic APM,我们可以轻松地检测该应用程序以获得更好的外观。 我们需要为检测做的所有事情如下(我们将在博客文章末尾以及 GitHub 存储库中展示完整的示例):

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()

由于我们的示例应用程序使用 Streamlit,因此我们还需要启动至少一项 transaction 并最终再次结束它。 此外,我们还可以向 APM 提供有关 transaction 结果的信息,以便我们可以正确跟踪故障。

# 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")

就是这样 ---- 这足以为我们的应用程序提供完整的 APM 工具。 话虽这么说,我们将在这里做一些额外的工作,以获得一些更有趣的数据。

第一步,我们将用户的查询添加到 APM 元数据中。 通过这种方式,我们可以检查用户尝试搜索的内容,并可以分析一些流行的查询或重现错误。

elasticapm.label(query=query)

在我们与 OpenAI 对话的异步方法中,我们还将添加一些更多的检测,以便我们可以更好地可视化我们收到的 tokens,并收集额外的统计数据。

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)

最后,在应用程序的最后阶段,我们还将向 APM 交易添加 token 数量和大致成本。 这将使我们能够稍后可视化这些指标并将它们与应用程序性能相关联。

如果你不使用流式传输,则 OpenAI 响应将包含一个 total_tokens 字段,它是你发送的上下文和返回的响应的总和。 如果你使用 stream=True 选项,那么你有责任计算 token 数量或近似数量。 一个常见的建议是对英文文本使用 “(len(prompt) + len(response)) / 4”,但特别是代码片段可能会偏离这种近似值。 如果你需要更准确的数字,你可以使用 tiktoken 等库来计算 token 数量。

# 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)

检查 APM 数据 — Elasticsearch 或 OpenAI 哪个更慢?

对应用程序进行检测后,快速查看 “Dependencies” 可以让我们更好地了解正在发生的情况。 看起来我们对 Elasticsearch 的请求平均在 125 毫秒内返回,而 OpenAI 需要 8,500 毫秒才能完成请求。 (此屏幕截图是在不使用流式传输的应用程序版本上拍摄的。如果你使用流式传输,则默认检测仅考虑依赖项响应时间中的初始 POST 请求,而不考虑流式传输完整响应所需的时间。)

如果你自己已经使用过 ChatGPT,你可能想知道为什么 UI 单独打印每个单词,而不是立即返回完整的响应。

事实证明,如果你使用免费版本,这实际上并不是为了诱使你付费! 这更多的是推理模型的限制。 简而言之,为了计算下一个 token,模型还需要考虑最后一个 token。 所以并行化的空间不大。 由于每个 token 都是单独处理的,因此在运行下一个 token 的计算时,该 token 也可以发送到客户端。

为了改善用户体验,在使用 ChatCompletion 功能时使用流式方法会很有帮助。 这样,用户可以在生成完整响应的同时开始使用第一个结果。 你可以在下面的 GIF 中看到这种行为。 即使所有三个响应仍在加载,用户也可以向下滚动并检查已有的内容。

如前所述,我们添加了比最低限度更多的自定义检测。 这使我们能够获得有关我们的时间花在哪里的详细信息。 让我们看一下完整的跟踪,看看这个流的实际情况。

我们的应用程序配置为从 Elasticsearch 获取前三名点击,然后针对 OpenAI 并行运行一个 ChatCompletion 请求。

正如我们在屏幕截图中看到的,加载单个结果大约需要 15 秒。 我们还可以看到,返回较大响应的 OpenAI 请求需要更长的时间才能返回。 但这只是一个请求。 所有请求都会发生这种行为吗? 响应时间和支持我们之前主张的 token 数量之间是否存在明显的相关性?

分析成本和响应时间

我们还可以使用自定义仪表板并根据 APM 数据创建可视化效果,而不是使用 Elastic APM 来可视化数据。 我们可以构建两个有趣的图表,显示响应中的 token 数量与请求持续时间之间的关系。

我们可以看到返回的 token 越多(第一个图表中的 x 轴),持续时间就越长(第一个图表中的 y 轴)。 在右图中,我们还可以看到,无论返回的 token 总数(x 轴)有多少,每返回 100 个 token 的持续时间几乎保持在 4 秒左右。

如果你想提高使用 OpenAI 模型的应用程序的响应能力,最好告诉模型保持简短的响应。

除此之外,我们还可以跟踪我们的总支出和每个页面加载的平均成本,以及其他统计数据。

对于我们的示例应用程序,单次搜索的成本约为 1.1 美分。 这个数字听起来并不高,但它可能不会很快出现在你的公共网站上作为搜索选项。 对于公司内部数据和偶尔使用的搜索界面来说,这个成本可以忽略不计。

在我们的测试中,我们在 Azure 中使用 OpenAI API 时也经常遇到错误,这最终导致我们向示例应用程序添加了一个具有指数退避的重试循环。 我们还可以使用 Elastic APM 捕获这些错误。

while tries < 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...")

然后,任何捕获的错误都会在瀑布图中可见,作为发生故障的跨度的一部分。

此外,Elastic APM 还提供所有错误的概述。 在下面的屏幕截图中,你可以看到我们偶尔遇到的 RateLimitError 和 APIConnectionError。 使用我们粗略的指数重试机制,我们可以缓解大多数此类问题。

延迟和失败的 transaction 关联

借助 Elastic APM 代理捕获的所有内置元数据以及我们添加的自定义标签,我们可以轻松分析性能与任何元数据(如服务版本、用户查询等)之间是否存在任何相关性。

如下所示,查询 “How can I mount and index on a frozen node?” 之间存在很小的相关性。 和较慢的响应时间。

可以对任何导致错误的事务进行类似的分析。 在此示例中,“How do I create an ingest pipeline” 这两个查询比其他查询更频繁地失败,导致它们在此相关性分析中脱颖而出。

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)

在本博客中,我们测试了一个用 Python 编写的应用程序,以使用 OpenAI 并分析其性能。 我们研究了响应延迟和失败的事务,并评估了运行应用程序的成本。 我们希望本指南对你有用!

详细了解 Elasticsearch 和 AI 的可能性。

在这篇博文中,我们可能使用了第三方生成式人工智能工具,这些工具由其各自所有者拥有和运营。 Elastic 对第三方工具没有任何控制权,我们对其内容、操作或使用不承担任何责任,也不对您使用此类工具可能产生的任何损失或损害负责。 使用人工智能工具处理个人、敏感或机密信息时请务必谨慎。 你提交的任何数据都可能用于人工智能培训或其他目的。 无法保证你提供的信息将得到安全或保密。 在使用之前,你应该熟悉任何生成式人工智能工具的隐私惯例和使用条款。

本文提到的成本基于当前 OpenAI API 定价以及我们在加载示例应用程序时调用它的频率。

Elastic、Elasticsearch 和相关标志是 Elasticsearch N.V. 的商标、徽标或注册商标。 在美国和其他国家。 所有其他公司和产品名称均为其各自所有者的商标、徽标或注册商标。

原文:ChatGPT and Elasticsearch: APM instrumentation, performance, and cost analysis — Elastic Search Labs

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

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

相关文章

SpringBoot+Vue 整合websocket实现简单聊天窗口

效果图 1 输入临时名字充当账号使用 2 进入聊天窗口 3 发送消息 &#xff08;复制一个页面&#xff0c;输入其他名字&#xff0c;方便展示效果&#xff09; 4 其他窗口效果 代码实现 后端SpringBoot项目&#xff0c;自行创建 pom依赖 <dependency><groupId…

docker安装xxl-job连接数据库时显示无法连接问题

背景&#xff1a; 在项目中需要定时任务调度&#xff0c;需要在docker容器中安装xxl-job 遇到的问题 部署成功后&#xff0c;可以访问xxl-job登录界面&#xff0c;点登录没反应&#xff0c;但过一段时间就弹出数据库拒绝连接&#xff0c;说MyBatis连接用户失败 原因&#xf…

华为云API图像识别Image的趣味性—AI识别迈克尔·杰克逊

云服务、API、SDK&#xff0c;调试&#xff0c;查看&#xff0c;我都行 阅读短文您可以学习到&#xff1a;人工智能AI图像识别的图像识别、名人识别 1 IntelliJ IDEA 之API插件介绍 API插件支持 VS Code IDE、IntelliJ IDEA等平台、以及华为云自研 CodeArts IDE&#xff0c;基…

深度学习算法

深度学习算法 1. 各种网络框架及其联系1.1 两阶段与一阶段区别1.1.1 detectron算法框架套路&#xff1a;1.1.2 multi-stage1.1.3 two-stage 算法1.1.4 one-stage 算法 2. 常用算法2.1 SS(选择性搜索算法&#xff0c;Selective Search) 3. 神经元模型4. 神经网络分类4.1 前馈神经…

Linux内核分析与应用5-中断

本系列是对 陈莉君 老师 Linux 内核分析与应用[1] 的学习与记录。讲的非常之好&#xff0c;推荐观看 留此记录&#xff0c;蜻蜓点水,可作抛砖引玉 中断机制概述 中断是CPU对系统发生的某个事件作出的一种反应, 当中断发生时,CPU暂停正在执行的程序,保留现场后,自动转去执行相应…

一本快速入门Java的书

关于这本书 很高兴&#xff0c;我又一本书籍《Java编程动手学》上市了。记得早在2017年&#xff0c;在我跟人邮出版社的傅道坤编辑合作完《Tomcat内核设计剖析》这本书后&#xff0c;傅编就问我考不考虑写一本面向Java初学者的图书&#xff0c;当时我谢绝了傅编的邀请。一来是我…

总结986

时间记录&#xff1a; 7:10起床 8:00~下午2:00课程设计&#xff0c;偷学了3小时 2:17~3:55午觉 4:10~5:30计网 5:35~6:41数据结构 7:00~7:22继续数据结构课后习题重做 7:23~8:07考研政治&#xff0c;做题20道纠错 8:15~8:39每日长难句 8:39~10:21 14年tex2纠错标记 1…

Unity下如何实现RTMP或RTSP播放端录像?

好多开发者问我们&#xff0c;Unity环境下&#xff0c;除了RTSP或RTMP的播放&#xff0c;如果有录像诉求&#xff0c;怎么实现&#xff1f;实际上录像相对播放来说&#xff0c;更简单一些&#xff0c;因为不涉及到绘制&#xff0c;只要拉流下来数据&#xff0c;直接写mp4文件就…

pytorch代码实现之SAConv卷积

SAConv卷积 SAConv卷积模块是一种精度更高、速度更快的“即插即用”卷积&#xff0c;目前很多方法被提出用于降低模型冗余、加速模型推理速度&#xff0c;然而这些方法往往关注于消除不重要的滤波器或构建高效计算单元&#xff0c;反而忽略了特征内部的模式冗余。 原文地址&am…

BUUCTF Reverse/[羊城杯 2020]login(python程序)

查看信息,python文件 动调了一下&#xff0c;该程序创建了一个线程来读入数据&#xff0c;而这个线程的代码应该是放在内存中直接执行的&#xff0c;本地看不到代码&#xff0c;很蛋疼 查了下可以用PyInstaller Extractor工具来解包&#xff0c;可以参考这个Python解包及反编译…

华为云云服务器云耀L实例评测 | 在华为云耀L实例上搭建电商店铺管理系统:一次场景体验

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

sqli第一关

1.在下使用火狐访问sqlilabs靶场并使用burpsuite代理火狐。左为sqlilabs第一关&#xff0c;右为burpsuite。 2.输入?id1 and 11 与?id1 and 12试试 可以看出没有变化哈&#xff0c;明显我们输入的语句被过滤了。在?id1后面尝试各种字符&#xff0c;发现单引号 包…

Linux内核分析与应用4-内存管理

本系列是对 陈莉君 老师 Linux 内核分析与应用[1] 的学习与记录。讲的非常之好&#xff0c;推荐观看 留此记录&#xff0c;蜻蜓点水,可作抛砖引玉 4.1 Linux内存管理机制 lscpu[2] 命令, 类似是优化后的 cat /proc/cpuinfo 实现虚拟内存的几种机制: 当 程序一旦跑起来,那就变成…

IDEA在创建包时如何把包分开实现自动分层

IDEA在创建包时如何把包分开实现自动分层 文章目录 IDEA在创建包时如何把包分开实现自动分层一、为什么要把包分开二、建包时如何把包自动分开三、如何编写配置文件路径&#xff1f; 一、为什么要把包分开 一开始的时候&#xff0c;我也一直以为包连在一起和分开没什么区别&am…

二叉搜索树/二叉排序树/二叉查找树

文章目录 1.概念2.操作3.实现3.1框架3.2BSTree.h3.3test.cpp 1.概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;…

python 学习笔记(5)——SMTP 使用QQ邮箱发送邮件

目录 发送邮件 1、准备工作&#xff1a; 2、发送纯文本信息内容&#xff1a; 3、发送 HTML 格式的内容&#xff1a; 4、发送带附件的邮件&#xff1a; 5、群发&#xff08;一个邮件&#xff0c;发给多个人&#xff09;&#xff1a; 发送邮件 以下都 以 QQ邮箱 为发送方举…

敏捷开发方法管理项目,适应变化,引领未来

​敏捷开发方法是一种灵活且高效的项目管理方法&#xff0c;旨在应对不断变化的需求和快速发展的项目环境。使用敏捷开发方法可以帮助团队更好地应对不确定性&#xff0c;提高项目的质量和效率。以下是使用敏捷开发方法管理项目的具体步骤&#xff1a; 明确项目目标和范围 在…

算法通过村第六关-树白银笔记|层次遍历

文章目录 前言1. 层次遍历介绍2. 基本的层次遍历与变换2.1 二叉树的层次遍历2.2 层次遍历-自底向上2.3 二叉树的锯齿形层次遍历2.4 N叉树的层次遍历 3. 几个处理每层元素的题目3.1 在每棵树行中找出最大值3.2 在每棵树行中找出平均值3.3 二叉树的右视图3.4 最底层最左边 总结 前…

C高级day4(shell脚本)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.创建一个文件&#xff0c;给组用户可读权限&#xff0c;所属用户可写权限&#xff0c;其他用户可执行权限&#xff0c;使用if判断文件有哪些权限 #!/bin/bash touch 1 chmod 241 1 if [ -r 1 ] thenecho "文件…

为 DevOps 战士准备的 Linux 命令

点击链接了解详情 这篇文章将帮助理解DevOps工程师所需的大部分重要且经常使用的Linux命令。 要执行这些命令&#xff0c;你可以使用任何Linux机器、虚拟机或在线Linux终端来迅速开始使用这些命令。 系统信息命令&#xff1a; hostname - 显示系统主机的名称。 hostid - 显示…