ガトリングでLLM APIをロードテストする方法

大規模言語モデル](https://zilliz.com/glossary/large-language-models-(llms))(LLM)を使ってアプリケーションを構築する場合、様々な需要レベルに対応できることを確認することが不可欠だ。そこで、負荷テストの出番となる。負荷テストは実際のトラフィックをシミュレートし、異なる条件下でのAPIのパフォーマンスを評価する。このアプローチにより、潜在的なボトルネックや改善すべき領域を特定し、アプリケーションの信頼性と応答性を確保することができます。
最近のベルリンの Unstructured Data Meetup で、 Gatling の開発者支援者である Samir Akarioh は、Gatlingを使用したLLM APIの負荷テスト方法について話しました。Gatlingは、オープンソースのパフォーマンステストフレームワークであり、JavaScriptウェブアプリケーションの負荷テストに使用される。彼の洞察は、LLM APIを負荷テストすることの重要性と、使用されるさまざまな方法に光を当てました。このブログでは、彼の重要なポイントを要約し、大規模な言語モデルアプリケーション、特に Milvus のような ベクターデータベース を搭載した RAG アプリ(検索拡張世代)を負荷テストして、パフォーマンス、負荷、および応答時間を改善する方法について説明します。
負荷テストとは?
負荷テストとは、特定の負荷条件下でシステムがどのように振る舞うかを評価するパフォーマンステストの一種であり、通常、膨大な量のデータでシステムに負荷をかけるテストを試みます。主な目的は、システムが通常時やピーク時に予想されるユーザー・トラフィックやデータ量を処理できるかどうかを評価することです。負荷テストでは、システムの挙動を監視し、ユーザー・エクスペリエンスに影響を与える可能性のあるボトルネック、性能低下、障害を特定する。
LLM APIについては、これらのシステムの複雑な性質と、自然言語を処理するための高い計算要求のために、負荷テストは特に重要です。適切な負荷テストを実施しないと、サービス停止、遅い応答時間、または不正確な結果につながり、ユーザーの信頼とAI搭載アプリケーションの全体的な信頼性を損なう可能性があります。
この非構造化データ・ミートアップで、サミールは3種類の負荷テストについて説明した:**キャパシティ・テスト、ストレス・テスト、そしてソーク・テストだ。少しペースを落として、それぞれを詳しく見てみよう。
キャパシティ・テスト
キャパシティテストは、パフォーマンス要件を満たしながらAPIが処理できる最大負荷を決定する。 彼のゴールは、レスポンスタイムやスループットを低下させることなく、システムがピーク負荷で動作する "スイートスポット "を特定することである。LLM API の場合、キャパシティ・テストは、正確でタイムリーなレスポンスを提供しながら、1秒間に処理できる最大リクエスト数を見つける。
例えば、キャパシティ・テストでは、APIにプロンプトを送信する同時ユーザーの数を、応答時間が長くなり始めるか、精度が低下し始めるまで、徐々に増やしていく。この情報はキャパシティプランニングにとって貴重であり、いつインフラをスケールアップするか、あるいはAPIを最適化するかという決定に役立つ。
キャパシティ・テストは、予想されるトラフィックの計画を立て、パフォーマンスの低下が始まる前に限界を理解するのに役立つ。ユーザー体験を損なうことなく、システムが予想される成長やピーク時の使用量に対応できるようにするために不可欠です。
ストレステスト
**キャパシティ・テストが最適な負荷を特定するのに対して、ストレス・テストは限界点を発見するためにシステムの限界を超えてシステムをプッシュする。
ストレステストは、AIを搭載したアプリケーションに突然大量のトラフィックをもたらすバイラルなソーシャルメディア投稿のような現実世界の状況をシミュレートすることができる。これらのテストでは、応答時間やスループットだけでなく、エラー率、リソースの使用率(CPU、メモリ、ネットワーク)、APIの応答品質も監視することが重要です。
ストレステストは、LLM APIにどのような障害が発生しうるか、また障害が発生したときに何が起こるか-クラッシュするのか、遅くなるのか、回復するのか-を理解するために不可欠である。この情報は、システムの回復力を向上させ、予期せぬ使用量の急増に優雅に対処できるようにするために不可欠である。また、より良いフェイルオーバーやロードバランシング戦略を設計するのにも役立つ。
ソークテスト
ソークテスト(耐久テスト)は、APIが長期間にわたってどのように動作するかを評価する。 メモリリーク、パフォーマンス低下、データベース接続の飽和など、短時間のテストでは明らかにならない問題を特定する。LLM API の場合、ソークテストは数時間から数日にわたって安定したリクエストストリームを実行し、システ ムがどのようにパフォーマンスを維持するかを観察する。
ソークテストの理想的な期間は、システムと予想される使用パターンによって異なる。あるLLM APIでは、24時間のテストで十分かもしれないが、他のAPIでは、長期間に渡ってのみ顕在化する微妙な問題を発見するために、1週間にわたるテストが有益かもしれない。
ソークテストは特に、徐々に進行する性能劣化を明らかにするのに適している。例えば、レスポンスタイムが時間の経過とともに徐々に長くなることや、大量のリクエストを処理した後に生成されるテキストの品質が微妙に低下することを示すかもしれない。このような洞察は、事前予防的なメンテナンス対策を実施し、長期的なパフォーマンスを最適化する上で極めて重要です。
このテストは、APIが劣化することなく持続的な需要を処理できることを保証する。これは、継続的に実行されることが期待されるアプリケーションや、長期にわたる一貫したトラフィックを処理するアプリケーションにとって極めて重要である。
LLM APIの負荷テストのベストプラクティス
LLM APIの負荷テストを実施する場合、以下のベストプラクティスを考慮してください:
1.実際の使用パターンを模倣した現実的なデータとシナリオを使用すること。
2.パフォーマンスのしきい値を正確に特定するために、徐々に負荷を上げる。
3.応答時間、エラー率、リソース利用率など、幅広いメトリクスを監視する。
4.ネットワーク遅延を考慮した、異なる地理的位置からのテスト。
5.APIが通常処理する様々なタイプのリクエストを混ぜてください。
負荷テストのためのツール LLM API の負荷テストには、以下のようないくつかのツールを使用できる:
Apache JMeter:様々なタイプの負荷テストに使用できるオープンソースのツール。
Locust: Pythonベースのツール:Pythonベースのツールで、特に分散負荷テストに適している。
Gatling:Scalaベースのツールで、大量の負荷テストに適している。次のセクションでは、Gatlingを使った負荷テストを紹介する。
Gatlingを使ったLLM APIの負荷テスト
Gatlingは、DevOpsと継続的インテグレーションのために設計されたウェブアプリケーションの負荷テストツールです。負荷テストの理論は理解できたと思うので、Gatlingを使ってOpenAI チャット完了APIの負荷テストを実際にどのように実施できるか見てみよう。このガイド](https://docs.gatling.io/tutorials/)に従って、Gatlingプロジェクトをセットアップしてください。
1.シミュレーションクラスのセットアップ
まず、必要なライブラリをインポートし、シミュレーション・クラスをセットアップします:
インポート static io.gatling.javaapi.core.CoreDsl.*;
インポート static io.gatling.javaapi.http.HttpDsl.*;
インポート io.gatling.javaapi.core.*;
インポート io.gatling.javaapi.http.*;
これらのインポートにより、Gatlingでシナリオを作成し、HTTPリクエストを処理するためのコアDSL(ドメイン固有言語)とHTTP DSLが導入されます。ライブラリをインポートした後、シミュレーションクラスを定義します。
public class SSELLM extends Simulation { シミュレーションを拡張したクラスです。
String api_key = System.getenv("api_key");
上記のコードでは、SSELLM
クラスは Simulation
を継承しています。api_key` 変数は環境変数からAPIキーを取得し、機密情報がハードコードされないようにします。
2.HTTPプロトコルの設定
APIキーを渡したら、次はLLMのサービスベースURLを指定します。
HttpProtocolBuilder httpProtocol =
http.baseUrl("https://api.openai.com/v1/chat")
.sseUnmatchedInboundMessageBufferSize(100);
baseUrlは API のベースエンドポイントを指定し、
sseUnmatchedInboundMessageBufferSize(100)` は期待されるレスポンスにマッチしないサーバー送信イベント(SSE)メッセージを処理するためのバッファサイズを設定する。
3.シナリオの定義
ガトリングテストの中心は、ユーザーの行動をシミュレートするシナリオです。テストのシナリオを定義してみましょう。
シナリオビルダー prompt = scenario("Scenario").exec()
sse("LLMに接続して回答を得る")
.post("/completions")
.header("Authorization", "Bearer " + api_key)
.body(StringBody("{"model":"gpt-3.5-turbo", "stream":true, "messages":[{"role": "user", "content": "What is a vector database "}]}"))
.asJson()、
asLongAs("#{stop.isUndefined()}").on()
sse.processUnmatchedMessages((メッセージ, セッション) -> {)
return messages.stream()
.anyMatch(message -> message.message().contains("{"data":"[DONE]"}")) ? session.set("stop", true) : session;
})
),
sse("close").close()
);
上記のシナリオでは、ユーザが OpenAI Chat API にリクエストを送信し、応答を待つ様子をシミュレートします。テストは API への SSE (Server-Sent Events) 接続を確立します。そして、POST リクエストが /completions
エンドポイントに送られ、モデルにベクターデータベースを定義するよう促すメッセー ジが表示されます。このテストでは、asLongAs("#{stop.isUndefined()}")
を使って SSE メッセージの受信処理を継続し、特定の条件が満たされるまで接続をオープンにしておく。
メッセージを受信すると、sse.processUnmatchedMessages(...)
はレスポンスに "[DONE]"
シグナルが含まれているかどうかをチェックします。このシグナルが検出されると、セッションは停止し、接続は sse("close").close()
で閉じられる。
4.仮想ユーザーの注入
シナリオを作成したら、最後にシミュレーションするユーザー数を指定し、プロトコルを設定します。
{
setUp(
prompt.injectOpen(atOnceUsers(3))
).protocols(httpProtocol);
}
}
上記のコードは、3人のバーチャルユーザーを同時にシナリオに注入する。この単純な負荷テストは、3つの同時リクエストを処理するときのAPIのパフォーマンスをチェックします。
コードを実行するには以下のコマンドを使用する:
.mvnw.cmd gatling:test
コードが実行されると、ガトリングはターミナル上にファイルパスを提供する。これがテストレポートへのパスです。以下はレポートのサンプルです。
図1: OpenAIチャット完了APIをテストしたGatling応答時間範囲レポート](https://assets.zilliz.com/Figure_Gatling_Response_Time_Range_Report_on_testing_Open_AI_chat_completion_API_544200d7e4.jpg)
OpenAIのチャット補完APIの期待通り、テストはOpenAIのAPIが同時リクエストを効率的に処理し、すべてのレスポンスが適切な時間枠で受信されたことを示しています。しかし、私たちは3つの同時リクエストを送信し、非常に短いプロンプトを使用したことを覚えておいてください。
実際のシナリオでは、LLMは何千ものリクエストを同時に受け取り、より長いプロンプトを受け取るかもしれません。長いプロンプトは、たいていの場合、ユーザーがLLMにより多くのコンテキストを提供するときです。実際のケースで、仮想ユーザーの数とプロンプトの長さを追加することを検討してください。
ガトリングはLLM APIに限定されません。また、RAG(Retrieval Augmented Generation)アプリケーションを動かすAPIのロードテストにも使うことができる。このアプローチによって、アプリケーションのパフォーマンスを全体的に評価することができます。RAG を動力とするアプリケーションをロードテストするシナリオを作ってみましょう。
しかし、その前に、RAG とは何か?まず RAG の概念を理解しましょう。
#検索拡張世代(RAG)を理解する
RAG(検索拡張生成)とは、大規模言語モデル(LLM)の生成能力と、MilvusやZilliz Cloudのようなベクトルデータベース(管理型Milvus)から関連情報を取得する検索メカニズムを組み合わせた手法である。外部データをコンテキストとして活用することで、LLMは幻覚を起こしにくくなり、より正確でコンテキストに関連した応答を生成します。さらに、RAGは、データセキュリティの問題を心配することなく、よりパーソナライズされた応答のためのコンテキストとして、LLMのプライベートデータや専有データを取得することも可能です。
典型的なRAGのセットアップ](https://zilliz.com/blog/a-beginners-guide-to-using-llama-3-with-ollama-milvus-langchain)では、ユーザからのクエリを受信すると、RAGシステムはベクトルデータベースによって提供される知識ベースから関連するドキュメントやスニペットを検索します。これらの文書はLLMにコンテキストを提供するために使用され、LLMはより情報に基づいた包括的な応答を生成する。
図2:RAGの仕組み](https://assets.zilliz.com/Figure_1_How_RAG_works_24108cefb1.png)
RAG アプリケーションをテストするシナリオを作成する
RAGを理解したところで、Gatling.を使ってRAGアプリケーションをロードテストする方法を学びましょう。
顧客サポートアプリケーションがLLMで動いている状況を想像してください。ユーザは、詳細で文脈を考慮した応答を必要とする質問をシステムに問い合わせるかもしれません。アプリケーションは、RAGを活用して、Milvusのようなベクトルデータベースから関連ドキュメントを取り出し、これらの回答の質を高める。これらの文書はLLMに必要な文脈を提供し、より正確で情報に基づいた回答を可能にする。
我々の負荷テストでは、以下のようなプロセスでシナリオを設計する。
1.**仮想ユーザーは、追加のコンテキストを必要とする複雑なクエリを送信します。例えば、ユーザーは "How do I troubleshoot a connection issue with my device?" と尋ねるかもしれません。この質問だけでは、LLM から高品質の回答を得るには十分な情報が得られないため、システムは Milvus ベクターデータベースから関連するトラブルシューティング文書を取得する必要があります。
2.コンテキスト検索:システムは、ユーザークエリに基づいてMilvusから最も関連性の高い文書を取得します。このステップは、LLMに提供されるコンテキストの質を決定し、生成される応答の精度に直接影響するため、非常に重要である。RAGパイプラインでは、Milvusは膨大な量の文書を索引付けし、最も関連性の高い情報を素早く見つけるためにベクトル類似性検索を行います。
3.LLMのレスポンス生成:一旦関連文書が検索されると、それらはLLMにコンテキストとして提供される。LLMはこの情報を使ってユーザーのクエリに応答する。このプロセスは回答を生成し、検索された文書から特定の情報源を引用する可能性もある。
4.**次に、より多くの仮想ユーザーを投入し、ピーク時の使用状況をシミュレートします。
5.モニタリングと分析: Gatlingがレポートを生成する際、アプリケーションを動かすAPIが遭遇する可能性のあるボトルネックをモニタリングします。テスト結果は、大規模な言語モデルのパフォーマンスと、負荷がかかった状態での検索メカニズムの効率性の両方に影響されることに注意することが重要です。これにより、システム全体のパフォーマンスを総合的に評価することができます。
上記のシナリオを使用してRAG搭載アプリの負荷テストを行うことで、スケーラビリティの問題を確実に特定することができます。
結論
Samir氏は、Gatlingを使用したLLM APIの負荷テストに関する貴重な洞察を提供してくれました。彼は、さまざまな種類の負荷テストと、LLM APIの負荷テスト方法について説明しました。また、RAGベースのカスタマーサポートアプリケーションのような、他のLLMを利用したアプリケーションで負荷テストを実行する方法を探るため、記事を拡張しました。この知識があれば、API の API テストを開発し、製品が問題なくスケールできることを保証できる。LLM API の負荷テストを実施する際には、以下のベストプラクティスを考慮してください:
LLM API の負荷テストのベストプラクティス
1.実際の使用パターンを模倣した現実的なデータとテストケースを使用する。
2.パフォーマンスのしきい値を正確に特定するために、徐々に負荷を上げる。
3.応答時間、エラー率、リソース利用率など、幅広いメトリクスを監視する。
4.ネットワーク遅延を考慮した、異なる地理的位置からのテスト。
5.APIが通常処理する様々なタイプのリクエストを混ぜてください。
RAG、GenAI、ベクトル検索に関するその他のリソース
RAGとは](https://zilliz.com/learn/Retrieval-Augmented-Generation)
RAGアプリケーションの最適化:信頼性向上のための方法論、メトリクス、評価ツールガイド](https://zilliz.com/blog/how-to-evaluate-retrieval-augmented-generation-rag-applications)
GenAIエコシステムの展望:LLMとベクトルデータベースを超えて](https://zilliz.com/blog/landscape-of-gen-ai-ecosystem-beyond-llms-and-vector-databases)
Llama 3、Ollama、Milvus、LangChainによるローカルRAGセットアップ](https://zilliz.com/blog/a-beginners-guide-to-using-llama-3-with-ollama-milvus-langchain)
Gemini、BGE-M3、Milvus、LangChainによるマルチモーダルRAGの構築](https://zilliz.com/learn/build-multimodal-rag-gemini-bge-m3-milvus-langchain)
Milvus、vLLM、MetaのLlama 3.1でRAGを構築する](https://zilliz.com/blog/building-rag-milvus-vllm-llama-3-1)
あなたのGenAIアプリのためのトップパフォーマンスAIモデル|Zilliz](https://zilliz.com/ai-models)
データに適した埋め込みモデルの選び方](https://zilliz.com/blog/choosing-the-right-embedding-model-for-your-data)
ジェネレーティブAIリソースハブ|Zilliz](https://zilliz.com/learn/generative-ai)
読み続けて

Why Deepseek is Waking up AI Giants Like OpenAI And Why You Should Care
Discover how DeepSeek R1's open-source AI model with superior reasoning capabilities and lower costs is disrupting the AI landscape and challenging tech giants like OpenAI.

DeepSeek vs. OpenAI: A Battle of Innovation in Modern AI
Compare OpenAI's o1 and o3-mini with DeepSeek R1's open-source alternative. Discover which AI model offers the best balance of reasoning capabilities and cost efficiency.

How to Select the Most Appropriate CU Type and Size for Your Business?
Explore Zilliz Cloud’s three CU options and learn how to choose the most suitable one for your business