
Python
Pythonは明確で読みやすい構文を持っているため、プログラミング初心者にもおすすめの言語です。また多くのコミュニティがあり、それぞれがライブラリ開発やフレームワーク開発に貢献しています。
イベント
マガジン
技術ブログ
こんにちは、クロスイノベーション本部リーディングエッジテクノロジーセンターの山下です。 最近は、gpt-ossやQwen3.5といったローカルLLM(Local Large Language Model)も注目されており、これらを活用したプロジェクトも増えてきています。 今回の記事では、ローカルLLMのベンチマークソフトウェアである GuideLLM について紹介します。LLMの性能には様々な観点がありますが、GuideLLMはLLMサーバ自体の応答速度などを測るためのベンチマークソフトウェアです。 GuideLLMでは、以下のような観点でLLMサーバの性能を評価します。 レイテンシー(応答時間) スループット(処理能力) 同時リクエスト数に対する性能の変化 エラー率や安定性 GuideLLMは、LLMサーバに対して様々な負荷をかけることで、これらの観点で性能を評価します。例えば、同時に複数のリクエストを送ることで、スループットやレイテンシーの変化を測定します。また、長時間の負荷テストを行うことで、安定性やエラー率も評価します。 LLMサーバの評価項目 LLMサーバの評価には、TTFT(Time To First Token)、ITL(Inter Token Latency)やThroughputなどの指標が用いられます。 TTFT(Time To First Token) : LLMが最初のトークンを生成するまでの時間を測定します。これは、ユーザーが応答を受け取るまでの待ち時間を示す重要な指標です。 ITL(Inter Token Latency) : トークン間の生成時間を測定します。これにより、LLMの応答速度や処理能力を評価できます。 Throughput : 一定時間内に処理できるリクエストの数を測定します。これは、LLMサーバの処理能力を示す指標です。 Latency(レイテンシー) : LLMサーバがリクエストに対して応答するまでの時間を測定します。これはトークンごとではなくすべての生成が完了するまでの時間になります。 同時リクエスト数に対する性能の変化 : 同時に複数のリクエストを送ることで、LLMサーバの性能がどのように変化するかを評価します。これにより、サーバのスケーラビリティや負荷耐性を測定できます。 エラー率や安定性 : 長時間の負荷テストを行うことで、LLMサーバの安定性やエラー率を評価します。 通常のWebサーバのベンチマークソフトウェアは、HTTPリクエストの処理能力や応答時間を測定することが一般的です。しかし、LLMサーバは応答を作成するための時間が長く、しかも生成された結果をリアルタイムに返す仕組みになっています。このため、通常のWebサーバの指標に加えてTTFTやITLといったLLM特有の指標が重要になります。特にTTFTはユーザーが最初の応答を受け取るまでの待ち時間を示し、ITLは1つのトークンが生成されてから次のトークンが生成されるまでの時間になります。これらがユーザーエクスペリエンスに直結する重要な指標となります。 ChatGPTなどのサービスを利用している際に、最初の一文字が出るまでの時間が長いと感じることがあるかもしれませんが、これはTTFTが長いことを意味しています。TTFTが短いほど、ユーザーは迅速に応答を受け取ることができ、より快適な体験を提供できます。 GuideLLMは実際にLLMサーバに対して負荷をかけることで、これらの指標を測定し、LLMサーバの性能を評価します。これにより、LLMサーバの性能を定量的に評価し、改善点を特定することができます。 GuideLLMの使用方法 GuideLLMは、Pythonで実装されたベンチマークソフトウェアです。以下のコマンドでインストールできます。 他のインストール方法については、公式の GitHubリポジトリ を参照してください。 pip install guidellm[recommended] インストール後、例えば以下のコマンドでベンチマークを実行できます。 guidellm benchmark run\ --target "http://<LLMサーバのアドレス:ポート>" \ --profile sweep \ --max-seconds 30 \ --data "prompt_tokens=256,output_tokens=128" ここでは --target オプションでLLMサーバのアドレスとポートを指定します。 --profile オプションでは、ベンチマークのプロファイルを指定します。 sweep プロファイルは、様々な負荷条件でベンチマークを実行するためのプロファイルです。 --max-seconds オプションでは、ベンチマークの最大実行時間を指定します。 --data オプションでは、ベンチマークに使用するデータを指定します。ここでは、プロンプトトークン数と出力トークン数を指定しています。 --profile として今回は sweep を指定しています。これの動作は以下のようなものになっています。 まずは1並列でのベンチマークを行い、ベースラインのレイテンシーを測定します。これにより、LLMサーバが単一リクエストに対してどれくらいの応答時間があるかを把握します。 次に並列アクセスを行い(デフォルトでは512並列)LLMサーバの最大スループットを測定します。これにより、LLMサーバがどれくらいのリクエストを同時に処理できるかを把握します。 2つのベンチマークでベースとなる性能と最大スループットがわかりました。次に、ベースラインのレイテンシーと最大スループットの間の設定でベンチマークを試します。これにより、LLMサーバが異なる負荷条件でどのように性能が変化するかを把握します。 --profile には他にも色々なプロファイルが用意されているので、目的に応じて選択することができます。 公式のページ を参考にしてください。 実行例 ここでは、実際にGuideLLMを使用してベンチマークを実行した例を紹介します。今回は、ローカルで動作しているLLMサーバに対してベンチマークを実行しました。 通信するLLMサーバはDGX Sparkで動作しているvLLMサーバでgpt-oss-120b を使用しています。 以下のコマンドでベンチマークを実行しました。 GUIDELLM__MAX_CONCURRENCY は、guidLLMがベンチマークを実行する際の最大並列数です。 今回の接続先がDGX Sparkなのでそこまで多くの接続はしない想定なので128並列を指定してみました。 また、 --processor オプションで、ベンチマークで使用するデータを生成するために使用するプロセッサを指定しています。ここでは、gpt-oss-120bが接続先になるので、 openai/gpt-oss-120b を指定しています。 export GUIDELLM__MAX_CONCURRENCY=128 guidellm benchmark --target "LLMサーバのアドレス:ポート" \ --output-dir ./output-dir/ \ --profile sweep \ --max-seconds 300 \ --data "prompt_tokens=256,output_tokens=128" \ --processor "openai/gpt-oss-120b" ベンチマークの結果は以下のようになりました。 すべての結果を載せると長すぎるので、ベンチマーク結果のサマリの部分を抜粋しています。 サマリを見てみると全体の数字の傾向として、同時リクエスト数が増えるにつれて、レイテンシー(Lat)が増加し、スループット(Tok gen/s)も増加していることがわかります。 また、TTFTやITLも同様に増加しています。つまりリクエスト数が増えると応答速度が遅くなっていることがわかります。 一方でTTFTは最悪でも500ms程度で、ITLも131ms程度なので、同時リクエスト数が増えても応答速度はまだ許容可能な範囲に収まっていることがわかります。 特に throughput@128 の行を見ると、スループットが約5.1 req/sに達していることがわかります。さらに、123.2並列まで問題なく処理できていることがわかります。この場合でも、TTFTは1506.7ms、ITLは177.3msなので、同時リクエスト数が増えても応答速度はまだ許容可能な範囲と言えるのではないでしょうか。 複数ある constant@数字 の結果は、ベースラインのレイテンシーと最大スループットの間の設定でベンチマークを試した結果になります。 @ の右側の数字はリクエストの頻度になります。 これらの結果から、TTFTがどの程度までなら許容できるのかを基準にして許容可能なリクエスト頻度を決めることもできます。例えば、TTFTで900ms以下に応答してほしいという場合であるなら constant@1.49 、 constant@2.09 の結果が参考になるかもしれません。 constant@1.49 ではTTFTが786.1ms、 constant@2.09 ではTTFTが943.2msとなっています。つまり、1.49リクエスト/秒程度であれば、TTFTが900ms以下に収まる可能性があることがわかります。 利用してみて気が付いた点 ベンチマークの出力形式としては、JSONやCSVに加えてHTMLも選択できます。しかし、今回HTML形式で出力したものを確認したところ、うまく表示されませんでした。生成されているHTMLに問題がありそうですが原因は不明です。JSONやCSV形式で出力したものは問題なく利用できたので今回はそちらを使用しました。 まとめ 今回は、ローカルLLMのベンチマークソフトウェアであるGuideLLMについて紹介しました。GuideLLMは、LLMサーバの性能を様々な観点で評価するためのベンチマークソフトウェアです。GuideLLMを使用することで、LLMサーバの性能を定量的に評価し、改善点を特定することができます。 ローカルLLMを活用したプロジェクトを進める際には、LLMサーバの性能を評価して最適な設定を見つけることが重要です。ぜひ、GuideLLMを活用して、ローカルLLMの性能を評価して最適な設定を見つけてください。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @yamashita.tsuyoshi レビュー: @sato.taichi ( Shodo で執筆されました )
はじめに 今回は AgentCore CLI を使ったエージェント開発を本番運用できるかを検討した際に、複数環境のデプロイについて詰まったポイントがあったので、ご紹介させていただきます。 AgentCore CLIは2026年4月17日現在では、GA前段階のため、本記事で紹介する内容が今後変更される可能性があります。 検証に使用したエージェント構成 今回検証のために使用したエージェントの構成を簡単に紹介します。 今回はAgentCore CLIの使い方の説明が主題ではないため、使い方についての詳細は省かせていただきます。 AgentCore CLIの agentcore create コマンドで以下のようなエージェントを作成したという前提で話を進めさせていただきます。 - Project name : MyProject - Agent name : analysis - Type : Create new agent - Language : Python - Build : Direct Code Deploy - Protocol : HTTP - Framework : OpenAI Agents - Advanced : defaults コマンドを実行すると以下のような構成でファイルが生成されます。 主要なものに絞って記載していますが、実際には CDK の設定ファイルや LLM コンテキストファイルなども生成されます。 MyProject/ # プロジェクトルート ├── AGENTS.md # エージェントの概要・設計ドキュメント ├── README.md # プロジェクトのREADME ├── agentcore/ │ ├── agentcore.json # エージェント定義(ランタイム、Gateway、Credential等) │ ├── aws-targets.json # デプロイ先のAWSアカウント・リージョン │ ├── tool-schema.json # Gatewayターゲットのツール定義 │ ├── .env.local # APIキー等のシークレット │ ├── .cli/ │ │ └── deployed-state.json # デプロイ済みリソースの状態 │ └── cdk/ │ ├── bin/cdk.ts # CDKエントリポイント │ └── lib/cdk-stack.ts # CDKスタック定義 └── app/ # エージェントのアプリケーションコード └── analysis/ ├── main.py └── pyproject.toml また、上記構成に含まれていませんが、今回の構成では、AgentCore CLIで作成したエージェントが Gateway 経由で、 lambroll でデプロイした Lambda 関数をツールとして呼び出します。 デプロイの仕組み agentcore deploy を実行すると、内部では以下が行われます: デプロイターゲット( aws-targets.json )の読み込み agentcore.json のバリデーション CDKプロジェクトのビルド Credential(APIキー等)のセットアップ CloudFormationテンプレートの合成(synth) CloudFormationスタックのデプロイ CloudFormationスタックには、ランタイム、Gateway、IAMロール等のリソースがまとめて含まれます。 詰まったポイント 1. --target オプションでデプロイ先が絞り込めない 問題 AgentCore CLIでは、デプロイ先のAWSアカウント・リージョンを aws-targets.json に定義します。 dev/prodを分離するために、以下のように2つのターゲットを定義しました。 // aws-targets.json [ { " name ": " dev ", " account ": " 111111111111 ", " region ": " ap-northeast-1 " } , { " name ": " prod ", " account ": " 999999999999 ", " region ": " ap-northeast-1 " } ] agentcore deploy コマンドには --target オプションがあり、デプロイ先を指定できます。 --target dev を指定すればdev環境のみにデプロイされると期待しましたが、実際には以下のようにprodのCloudFormationスタックもdevアカウントに作成されてしまいました。 # targetをdevに指定してデプロイ AWS_PROFILE=dev-profile agentcore deploy --target dev 実際にはdevアカウントに以下の2つのスタックが作成される - AgentCore-MyProject-dev (意図通り) - AgentCore-MyProject-prod (意図しない) 原因 この問題は、CLIとCDKの間でターゲット情報が連携されていないことが原因のようです。 CLIの --target オプションは、 aws-targets.json からターゲット情報(account, region)を取得してIdentityのセットアップやデプロイ後の状態記録に使われますが、CDKのsynth(CloudFormationテンプレートの合成)やdeploy処理にはターゲット名が伝わりません。 agentcore create で生成される cdk.ts のデフォルトコードでは、 aws-targets.json に定義された 全ターゲット に対してスタックを生成するforループになっています。 // cdk.ts(デフォルト生成コード) for ( const target of targets) { // --target の指定に関係なく、全ターゲット分のスタックが生成される new AgentCoreStack(app, stackName, { ... } ); } CDKのsynthはローカルで実行されるため、アカウントIDが異なっていてもテンプレート生成自体は成功します。その結果、devアカウントのクレデンシャルで実行しているにもかかわらず、prod用のスタック定義もdevアカウントにデプロイされてしまいます。 対策 対策1: cdk.ts を修正して環境変数でフィルタ cdk.ts に環境変数 AGENTCORE_TARGET でターゲットをフィルタするコードを追加しました。 // cdk.ts const app = new App (); // 環境変数でターゲットをフィルタ const targetFilter = process .env.AGENTCORE_TARGET; const filteredTargets = targetFilter ? targets. filter ( t => t. name === targetFilter) : targets; for ( const target of filteredTargets) { // フィルタされたターゲットのみスタックを生成 new AgentCoreStack(app, stackName, { ... } ); } デプロイ時に環境変数を指定して実行します: # dev環境のみデプロイ AGENTCORE_TARGET=dev AWS_PROFILE=dev-profile agentcore deploy --target dev # prod環境のみデプロイ AGENTCORE_TARGET=prod AWS_PROFILE=prod-profile agentcore deploy --target prod 注意点 : agentcore create で新規プロジェクトを作成するたびに cdk.ts が初期状態で生成されるため、毎回この修正を適用する必要があります。 CLIの --target オプションの値はCDKプロセスに自動的に引き渡されないため、環境変数 AGENTCORE_TARGET として別途指定する必要があります。 --target はCLI内部でのターゲット情報取得に、 AGENTCORE_TARGET はCDKのsynthでのスタック絞り込みに使われるため、両方に同じ値を指定する必要があり、冗長になってしまいます。将来のCLIバージョンで改善される可能性はありますが、現時点(v0.8.0)ではこの対応が必要です。 対策2: aws-targets.json を毎回リセットしてプロファイルから自動検出 aws-targets.json を空( [] )にしてからデプロイすると、CLIが AWS_PROFILE からアカウントIDとリージョンを自動検出し、 "default" という名前のターゲットを自動生成します。 # dev環境(aws-targets.json が空の状態で実行) AWS_PROFILE=dev-profile agentcore deploy # prod環境(aws-targets.json をリセットしてから実行) echo '[]' > agentcore/aws-targets.json AWS_PROFILE=prod-profile agentcore deploy 一見シンプルですが、実用上は問題があります。devデプロイ後に aws-targets.json にはdevターゲットが追加された状態になっています。この状態でリセットせずにprodをデプロイすると、 aws-targets.json に2つのターゲットが登録され、対策1で述べたのと同じ問題(全ターゲット分のスタックがsynthされる)が発生してしまいます。 そのため、デプロイのたびに aws-targets.json をリセットする運用が必要になりますが、CI/CDを使い、 echo '[]' > agentcore/aws-targets.json を実行してからデプロイする形にすれば、毎回クリーンなワークスペースから始まるためリセット忘れは防げると思います。 対策1は agentcore create で自動生成される cdk.ts を書き換える必要があり、CLIのバージョンアップで生成内容が変わった際に手動マージが必要になったり、修正漏れで予期せぬ挙動を起こすリスクがあります。そのため、基本的には自動生成ファイルには手を入れず、対策2をCI/CDで運用するのが望ましいと考えています。 2. Lambda ARNのハードコーディング 問題 GatewayにLambda関数をターゲットとして追加するには、以下のようにコマンドを実行します。 agentcore add gateway-target \ --gateway Gateway \ --name DataFetcher \ --type lambda-function-arn \ --lambda-arn arn:aws:lambda:ap-northeast-1:111111111111:function:get-data \ --tool-schema-file ./agentcore/tool-schema.json ./agentcore/tool-schema.json にはLambda関数が提供するツールの定義を記述したJSONファイルを指定します。 Lambda ARNターゲットの場合、Gatewayがどのツールを公開しているか知る手段がないため、このファイルを自分で用意する必要があります。 // tool-schema.json の例 { " tools ": { " get-data ": { " name ": " get-data ", " description ": " 分析用のデータを取得する ", " inputSchema ": { " type ": " object ", " properties ": { " date ": { " type ": " string ", " description ": " 取得対象の日付 " } } , " required ": [ " date " ] } } } } このコマンドを実行すると、 agentcore.json に以下のようなGatewayターゲットが追加されます。 // agentcore.json " agentCoreGateways ": [ { " name ": " Gateway ", " targets ": [ { " name ": " DataFetcher ", " targetType ": " lambdaFunctionArn ", " lambdaFunctionArn ": { " lambdaArn ": " arn:aws:lambda:ap-northeast-1:111111111111:function:get-data ", " toolSchemaFile ": " ./agentcore/tool-schema.json " } } ] } ] ここで問題になるのが lambdaArn の値です。Lambda ARNにはAWSアカウントIDが含まれるため、dev/prodでアカウントが異なる場合、デプロイ前に毎回この値を対象環境のARNに書き換える必要があります。 devにデプロイする場合: arn:aws:lambda:ap-northeast-1:111111111111:function:get-data prodにデプロイする場合: arn:aws:lambda:ap-northeast-1:999999999999:function:get-data agentcore.json はgit管理されるファイルのため、デプロイのたびにARNを書き換えてコミットするのは手間がかかりますし、書き換え忘れにより誤った環境のARNでデプロイしてしまうリスクもあります。 対策 agentcore.json にはdev用のARNを登録しておき、 cdk.ts 側で関数名だけを取り出して、ターゲットのアカウント・リージョンからARNを動的に再構築するようにしました。 // agentcore.json(dev用のARNで登録しておく) { " lambdaArn ": " arn:aws:lambda:ap-northeast-1:111111111111:function:get-data ", " toolSchemaFile ": " ./agentcore/tool-schema.json " } // cdk.ts const resolvedMcpSpec = mcpSpec ? JSON . parse ( JSON . stringify (mcpSpec)) : undefined ; if (resolvedMcpSpec?.agentCoreGateways) { for ( const gw of resolvedMcpSpec.agentCoreGateways) { for ( const t of gw.targets ?? [] ) { if (t.lambdaFunctionArn?.lambdaArn) { // 元のARNから関数名を抽出し、ターゲットのアカウント・リージョンで再構築 const functionName = t.lambdaFunctionArn.lambdaArn. split ( ':' ). pop (); t.lambdaFunctionArn.lambdaArn = `arn:aws:lambda: ${ target. region} : ${ target.account } :function: ${ functionName } ` ; } } } } 注意点 : cdk.ts は agentcore create で新規プロジェクトを作成するたびに初期状態で生成されるため、毎回この修正を適用する必要があります。 また、前述の通り自動生成ファイルを書き換えるのはCLIのバージョンアップ等でバグを生みやすいので、CI/CDのデプロイジョブ内で agentcore.json のARNを対象環境のアカウントIDに置換してからデプロイする方が安全かなと思います。 3. APIキーをdev/prodで分けたい 問題 エージェントが外部API(OpenAI等)を利用する場合、APIキーをCredentialとして登録します。登録されたAPIキーはAgentCore Identityサービスの アウトバウンド認証 (エージェントから外部サービスへの認証情報)として管理されます。 agentcore add credential --name OpenAIApiKey --api-key sk-xxxxx このコマンドを実行すると、以下の2箇所に情報が書き込まれます。 agentcore/agentcore.json — Credentialのメタ情報(名前・タイプ) agentcore/.env.local — APIキーの実際の値 // agentcore.json " credentials ": [ { " authorizerType ": " ApiKeyCredentialProvider ", " name ": " OpenAIApiKey " } ] agentcore/.env.local AGENTCORE_CREDENTIAL_OPENAIAPIKEY=sk-xxxxx 環境変数名は Credential名から AGENTCORE_CREDENTIAL_{NAME} の形式で自動生成されます。デプロイ時にこの値が読み取られ、AWS側の Token Vault (AgentCore Identityサービスのシークレットストア)に登録されます。 dev/prodで同じAPIキーを使う場合は、 .env.local の値をそのまま使えるので問題ありません。しかし、セキュリティや課金管理の観点からdev/prodでAPIキーを分けたい場合、 .env.local は1ファイルしかないため、デプロイのたびに値を書き換える必要があります。 対策 デプロイ時に環境変数でAPIキーを上書きします。環境変数が設定されていれば .env.local の値より優先されます。 # dev環境 AGENTCORE_CREDENTIAL_OPENAIAPIKEY=sk-dev-xxxxx \ AGENTCORE_TARGET=dev \ AWS_PROFILE=dev-profile \ agentcore deploy --target dev # prod環境 AGENTCORE_CREDENTIAL_OPENAIAPIKEY=sk-prod-xxxxx \ AGENTCORE_TARGET=prod \ AWS_PROFILE=prod-profile \ agentcore deploy --target prod ただし、毎回デプロイコマンドにAPIキーを環境変数として渡すのであれば、 .env.local を直接書き換える運用と手間は変わりません。今回はCI/CDを使わずローカルからデプロイする運用のため、デプロイ前に .env.local の値を対象環境のAPIキーに書き換える方法を採用しました。 別のアプローチ:dev/prodでプロジェクトを分ける ここまで紹介した課題は、いずれも 1つのプロジェクトでdev/prodを共有する ことに起因しています。 これらをすべて解消するシンプルなアプローチとして、dev用とprod用でそれぞれ別のAgentCoreプロジェクトを作成する方法があります。 MyAgent-dev/ ├── agentcore/ │ ├── agentcore.json ← dev用のLambda ARN、dev用のAPIキー │ ├── aws-targets.json ← devアカウントのみ │ └── cdk/ └── app/ └── analysis/ MyAgent-prod/ ├── agentcore/ │ ├── agentcore.json ← prod用のLambda ARN、prod用のAPIキー │ ├── aws-targets.json ← prodアカウントのみ │ └── cdk/ └── app/ └── analysis/ この方法なら cdk.ts のカスタマイズは不要で、 aws-targets.json にはターゲットが1つだけなのでsynthの問題も発生せず、 .env.local も環境ごとに独立しています。 ただし、 app/ 配下のエージェントコードが2つのプロジェクトで重複するため、ロジックを変更するたびに両方を更新する必要があります。コードの同期忘れによる環境差異が生まれるリスクもあるため、この方法を積極的に採用することはできないなと思いました。 まとめ AgentCore CLIを使用してみて、実際に本番運用できるのかを検討しました。 CLIで必要なリソースを簡単に素早く作成できるというメリットはありますが、環境を分離するには課題が多いという検証結果になりました。 最後まで読んでいただきありがとうございました!
この記事は、さくらインターネットが実施した高火力 DOKハンズオンの参加者に提供していた資料を、一般公開するために記事化したものです。 はじめに OpenVoiceとは? OpenVoiceは、ボイスクローンができるAI […]
























