Autor:LUCA WINTERGERST
In diesem Blog testen wir eine Python-Anwendung mit OpenAI und analysieren ihre Leistung und die Kosten für die Ausführung der Anwendung. Anhand der aus der Anwendung gesammelten Daten zeigen wir außerdem, wie Sie LLMs in Ihre Anwendung integrieren.
In einem früheren Blogbeitrag haben wir eine kleine Python-Anwendung erstellt, die Elasticsearch mithilfe einer Kombination aus Vektorsuche und BM25 abfragt, um dabei zu helfen, die relevantesten Ergebnisse in einem proprietären Datensatz zu finden. Die Top-Ergebnisse werden dann an OpenAI weitergeleitet, das die Frage für uns beantwortet.
In diesem Blog testen wir eine Python-Anwendung mit OpenAI und analysieren ihre Leistung und die Kosten für die Ausführung der Anwendung. Anhand der aus Ihrer Anwendung gesammelten Daten zeigen wir außerdem, wie Sie große Sprachmodelle (LLM) in Ihre Anwendung integrieren. Als zusätzlichen Bonus versuchen wir, diese Frage zu beantworten: Warum druckt chatgpt seine Ausgabe wörtlich?
Wenn Sie die Möglichkeit haben, probieren Sie unsere ausBeispielanwendungMöglicherweise stellen Sie fest, dass die Ergebnisse der Suchoberfläche nicht so schnell geladen werden, wie Sie es erwarten würden.
Die Frage ist nun, ob dies auf unseren zweiphasigen Ansatz zurückzuführen ist, bei dem die Abfrage zuerst in Elasticsearch ausgeführt wird, oder ob das langsame Verhalten von OpenAI herrührt oder ob es sich um eine Kombination aus beidem handelt.
Mit Elastic APM können wir die Anwendung einfach für ein besseres Erscheinungsbild instrumentieren. Zur Erkennung müssen wir lediglich Folgendes tun (das vollständige Beispiel zeigen wir am Ende des Blogbeitrags und im GitHub-Repository):
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()
Da unsere Beispielanwendung Streamlit verwendet, müssen wir außerdem mindestens eine Transaktion starten und diese schließlich wieder beenden. Darüber hinaus können wir APM Informationen über die Transaktionsergebnisse zur Verfügung stellen, damit wir Fehler ordnungsgemäß verfolgen können.
# 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")
Das ist alles – das reicht aus, um ein vollständiges APM-Tool für unsere Anwendung bereitzustellen. Abgesehen davon werden wir hier noch ein wenig zusätzliche Arbeit leisten, um weitere interessante Daten zu erhalten.
Als ersten Schritt fügen wir die Anfrage des Benutzers zu den APM-Metadaten hinzu. Auf diese Weise können wir überprüfen, wonach Benutzer suchen möchten, und einige häufig gestellte Suchanfragen analysieren oder Fehler reproduzieren.
elasticapm.label(query=query)
In unserer asynchronen Methode der Kommunikation mit OpenAI werden wir auch einige weitere Erkennungen hinzufügen, damit wir die empfangenen Token besser visualisieren und zusätzliche Statistiken sammeln können.
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)
Schließlich werden wir in der letzten Phase des Antrags auch den Token-Betrag und die ungefähren Kosten zur APM-Transaktion hinzufügen. Dadurch können wir diese Metriken später visualisieren und mit der Anwendungsleistung korrelieren.
Wenn Sie kein Streaming verwenden, enthält die OpenAI-Antwort ein Feld „total_tokens“, das die Summe des von Ihnen gesendeten Kontexts und der zurückgegebenen Antwort darstellt. Wenn Sie die Option stream=True verwenden, liegt es in Ihrer Verantwortung, die Anzahl der Token oder eine ungefähre Zahl zu berechnen. Ein häufiger Vorschlag ist die Verwendung von „(len(prompt) + len(response)) / 4“ für englischen Text, aber insbesondere Codeausschnitte können von dieser Näherung abweichen.Wenn Sie genauere Zahlen benötigen, können Sie diese verwenden Tick Tack Warten Sie, bis die Bibliothek die Anzahl der Token berechnet hat.
# 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)
Nach der Instrumentierung der Anwendung kann uns ein kurzer Blick auf die „Abhängigkeiten“ eine bessere Vorstellung davon geben, was vor sich geht. Es sieht so aus, als ob unsere Anfragen an Elasticsearch im Durchschnitt innerhalb von 125 Millisekunden zurückgegeben wurden, während OpenAI 8.500 Millisekunden brauchte, um die Anfrage abzuschließen. (Dieser Screenshot wurde mit einer Version der Anwendung aufgenommen, die kein Streaming verwendet. Wenn Sie Streaming verwenden, berücksichtigt die Standarderkennung nur die erste POST-Anfrage in der Abhängigkeitsantwortzeit, nicht die Zeit, die zum Streamen der vollständigen Antwort erforderlich ist. Erforderliche Zeit. )
Wenn Sie ChatGPT selbst verwendet haben, fragen Sie sich möglicherweise, warum die Benutzeroberfläche jedes Wort einzeln ausgibt, anstatt sofort die vollständige Antwort zurückzugeben.
Es stellt sich heraus, dass die Nutzung der kostenlosen Version eigentlich nicht dazu gedacht ist, Sie zum Bezahlen zu verleiten! Dies ist eher eine Einschränkung des Inferenzmodells. Kurz gesagt, um den nächsten Token zu berechnen,ModellEs gibt noch ein letztes Zeichen, das berücksichtigt werden muss. Es gibt also nicht viel Raum für Parallelisierung. Da jeder Token einzeln verarbeitet wird, kann dieser Token auch an den Client gesendet werden, während die Berechnung für den nächsten Token läuft.
Um das Benutzererlebnis zu verbessern, kann es hilfreich sein, bei Verwendung der ChatCompletion-Funktion Streaming-Methoden zu verwenden. Auf diese Weise kann der Benutzer mit der Verwendung der ersten Ergebnisse beginnen, während die vollständige Antwort generiert wird. Sie können dieses Verhalten im GIF unten sehen. Auch wenn alle drei Antworten noch geladen werden, kann der Benutzer nach unten scrollen und prüfen, was bereits vorhanden ist.
Wie bereits erwähnt, haben wir mehr benutzerdefinierte Erkennung hinzugefügt als das Nötigste. Dadurch erhalten wir detaillierte Informationen darüber, wo wir unsere Zeit verbringen. Werfen wir einen Blick auf die vollständige Ablaufverfolgung, um zu sehen, wie dieser Stream tatsächlich aussieht.
Unsere Anwendung ist so konfiguriert, dass sie die drei besten Treffer von Elasticsearch erhält und dann parallel eine ChatCompletion-Anfrage gegen OpenAI ausführt.
Wie wir im Screenshot sehen können, dauert das Laden eines einzelnen Ergebnisses etwa 15 Sekunden. Wir können auch sehen, dass die Rückgabe von OpenAI-Anfragen, die größere Antworten zurückgeben, länger dauert. Aber das ist nur eine Bitte. Tritt dieses Verhalten bei allen Anfragen auf? Gibt es einen klaren Zusammenhang zwischen der Reaktionszeit und der Anzahl der Token, die unsere vorherige Behauptung stützen?
Anstatt Elastic APM zur Visualisierung der Daten zu verwenden, können wir auch benutzerdefinierte Dashboards verwenden und Visualisierungen basierend auf APM-Daten erstellen. Wir können zwei interessante Diagramme erstellen, die die Beziehung zwischen der Anzahl der Token in der Antwort und der Dauer der Anfrage zeigen.
Wir können sehen, dass die Dauer umso länger ist (Y-Achse im ersten Diagramm), je mehr Token zurückgegeben werden (X-Achse im ersten Diagramm). Im Bild rechts können wir auch sehen, dass unabhängig von der Gesamtzahl der zurückgegebenen Token (x-Achse) die Dauer pro 100 zurückgegebenen Token nahezu konstant bei etwa 4 Sekunden bleibt.
Wenn Sie die Reaktionsfähigkeit einer Anwendung verbessern möchten, die ein OpenAI-Modell verwendet, ist es eine gute Idee, das Modell anzuweisen, die Antworten kurz zu halten.
Darüber hinaus können wir unter anderem auch unsere Gesamtausgaben und die durchschnittlichen Kosten pro Seitenaufruf verfolgen.
Für unsere Beispielanwendung kostet eine einzelne Suche ca. 1,1 Cent. Diese Zahl hört sich nicht hoch an, wird aber wahrscheinlich in absehbarer Zeit nicht als Suchoption auf Ihrer öffentlichen Website angezeigt. Bei unternehmensinternen Daten und gelegentlich genutzten Suchoberflächen ist dieser Aufwand vernachlässigbar.
Bei unseren Tests stießen wir außerdem häufig auf Fehler bei der Verwendung der OpenAI-API in Azure, was uns letztendlich dazu veranlasste, der Beispielanwendung eine Wiederholungsschleife mit exponentiellem Backoff hinzuzufügen. Wir können Elastic APM auch verwenden, um diese Fehler abzufangen.
while tries
Alle erfassten Fehler werden dann im Wasserfalldiagramm als Teil der Zeitspanne angezeigt, in der der Fehler aufgetreten ist.
Darüber hinaus bietet Elastic APM eine Übersicht aller Fehler. Im Screenshot unten sehen Sie die RateLimitError- und APIConnectionError-Fehler, auf die wir gelegentlich gestoßen sind. Mithilfe unseres groben exponentiellen Wiederholungsmechanismus können wir die meisten dieser Probleme abmildern.
Mit allen integrierten Metadaten, die vom Elastic APM-Agenten erfasst werden, und den von uns hinzugefügten benutzerdefinierten Tags können wir problemlos analysieren, ob es Korrelationen zwischen der Leistung und Metadaten wie Dienstversion, Benutzerabfragen usw. gibt.
Wie unten gezeigt, besteht eine kleine Korrelation zwischen der Abfrage „Wie kann ich einen eingefrorenen Knoten mounten und indizieren?“ und langsamere Reaktionszeiten.
Eine ähnliche Analyse kann für jede Transaktion durchgeführt werden, die den Fehler verursacht hat. In diesem Beispiel schlagen die beiden Abfragen „Wie erstelle ich eine Aufnahmepipeline“ häufiger fehl als die anderen Abfragen, weshalb sie in dieser Korrelationsanalyse hervorstechen.
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)
In diesem Blog testen wir eine in Python geschriebene Anwendung zur Verwendung von OpenAI und analysieren ihre Leistung. Wir untersuchten Antwortverzögerungen und fehlgeschlagene Transaktionen und bewerteten die Kosten für die Ausführung der Anwendung. Wir hoffen, dass Sie diesen Leitfaden nützlich finden!
Erfahren Sie mehr über die Möglichkeiten von Elasticsearch und KI。
In diesem Blogbeitrag haben wir möglicherweise generative KI-Tools von Drittanbietern verwendet, die Eigentum ihrer jeweiligen Eigentümer sind und von diesen betrieben werden. Elastic hat keine Kontrolle über Tools von Drittanbietern und wir übernehmen keine Verantwortung für deren Inhalt, Betrieb oder Nutzung oder für Verluste oder Schäden, die aus Ihrer Nutzung solcher Tools entstehen können. Seien Sie vorsichtig, wenn Sie Tools der künstlichen Intelligenz zum Umgang mit persönlichen, sensiblen oder vertraulichen Informationen verwenden. Alle von Ihnen übermittelten Daten können für Schulungen zur künstlichen Intelligenz oder für andere Zwecke verwendet werden. Es gibt keine Garantie dafür, dass die von Ihnen bereitgestellten Informationen sicher oder vertraulich behandelt werden. Sie sollten sich mit den Datenschutzpraktiken und Nutzungsbedingungen jedes generativen KI-Tools vertraut machen, bevor Sie es verwenden.
Die in diesem Artikel genannten Kosten basieren auf den aktuellen OpenAI-API-Preisen und darauf, wie oft wir sie beim Laden der Beispielanwendung aufrufen.
Elastic, Elasticsearch und verwandte Marken sind Marken, Logos oder eingetragene Marken von Elasticsearch NV. in den Vereinigten Staaten und anderen Ländern. Alle anderen Firmen- und Produktnamen sind Marken, Logos oder eingetragene Marken ihrer jeweiligen Eigentümer.
Original:ChatGPT und Elasticsearch: APM-Instrumentierung, Leistung und Kostenanalyse – Elastic Search Labs