カミナシ エンジニアブログ

株式会社カミナシのエンジニアが色々書くブログです

Amazon BedrockのLLM呼び出しコストをApplication inference profileでAI機能別に可視化する

はじめに

カミナシでエンジニアリングマネージャーをしている、すずけん(@szk3)です。自チームのプロダクト「カミナシ 設備保全」には 2 つの AI 機能があり(プレスリリース)、どちらも Amazon Bedrock AgentCore 上のエージェントから同じ LLM モデルを呼び出しています。

リリースからしばらく経つと、「で、それぞれのAI機能でLLMの呼び出しいくらかかってるの?」という当然の疑問が出てきました。ところがこの2つのAI機能は同じLLMモデルを呼び出しており、請求上は合算されるため、機能ごとのコストを切り分けられていませんでした。

この記事では、Amazon Bedrock の Application inference profile を使って、同じモデルを呼び出す複数の AI 機能のコストを機能別に可視化した話を共有します。

同じモデルを呼ぶと、コストが混ざる

AWS Cost Explorer で Amazon Bedrock のコストを見ると、使用タイプ(モデル × 入出力トークン種別)の単位までは分解できます。請求レコードはこの使用タイプ単位で積み上がるので、同じモデルを使う複数の機能は同じレコードに合算され、そこから先は分解できません。自チームが提供する 2 つの AI 機能に関しても、Bedrock が提供する同一の LLM モデルを利用していました。

この状態では、次の点が困ります。

  • 同じモデルを使うため、機能ごとの AI 呼び出しコストが区別できない
  • 機能単位での投資対効果を評価できない
  • 今後 AI 機能が増えたときのコスト管理の見通しが立たない

AI 機能は作って終わりではなく、どの機能に投資を続けるかを判断しながら育てていくものです。コストの解像度が機能単位まで上がっていないと、その判断の材料が取れません。

Application inference profile とは

2024 年 11 月に Amazon Bedrock に追加された機能です。モデルへの参照となるリソースを自分のアカウント内に作成し、そこにコスト配分タグ(cost allocation tags)を付けられます。

モデル呼び出し時に、モデル ID の代わりにこのプロファイルの ARN を modelId に指定すると、その呼び出しの請求レコードにプロファイルのタグが付きます。これにより AWS Cost Explorer や AWS Cost and Usage Reports(CUR 2.0)でタグ別に集計できるようになります。

Inference Profile には 2 種類あります。

種類 作成者 主な用途
System-defined inference profile AWS Cross-Region inference(複数リージョンへのリクエストルーティング)
Application inference profile 利用者 コストと使用状況のトラッキング

ポイントは、Application inference profile の作成時に、コピー元として System-defined inference profile を指定できることです。これにより Cross-Region inference によるルーティングを維持したまま、コスト追跡だけを足せます。

実装

やることは 4 つです。本記事中のコードは、実際の実装をもとにしたサンプルです。

  1. 機能ごとに Application inference profile を作成する(Terraform)
  2. IAM 権限を付与する
  3. 呼び出し側で、モデル ID の代わりにプロファイルの ARN を指定する
  4. コスト配分タグを有効化する

1. 機能ごとに Application inference profile を作る

Terraform の AWS Provider には aws_bedrock_inference_profile リソースがあるので、これで作成します。

resource "aws_bedrock_inference_profile" "feature_a_sonnet_4_5" {
  name        = "myapp-feature-a-${var.env}-sonnet-4-5"
  description = "Feature A Agent Claude Sonnet 4.5"

  model_source {
    copy_from = "arn:aws:bedrock:ap-northeast-1::inference-profile/jp.anthropic.claude-sonnet-4-5-20250929-v1:0"
  }

  tags = {
    "myorg:myapp:application" = "myapp"
    "myorg:myapp:feature"     = "feature-a"
    "myorg:myapp:environment" = var.env
  }
}

copy_from には Foundation Model の ARN(単一リージョン)か、System-defined inference profile の ARN(Cross-Region inference)を指定します。自チームは日本国内リージョン(東京と大阪)でルーティングする jp. プレフィックスの Cross-Region inference を使っているので、後者を指定しています。

別の AI 機能に対して新しく Profile を追加する際も、タグの featurefeature-b に変えただけの同じ構成で作ります。

リソース名にモデル名(サンプルでは sonnet_4_5)を含めているのは、プロファイルがモデルと 1:1 で紐づくためです。モデルを更新するときは、新しいプロファイルを追加して参照を切り替え、古いプロファイルを後から削除します(この運用は後述します)。

タグキーの付け方では、当初 Application / Feature / Environment のような汎用的なキー名を考えていましたが、最終的には myorg:myapp:feature のように組織やチームの名前空間を付けたキーにしました(キー名はサンプルです)。理由としては、コスト配分タグの有効化はアカウントの請求設定として行うため、将来コスト管理用のタグが全社で一元的に統制されると、汎用キーは他チームのタグとキー名が衝突し、自チームのコスト管理が壊れかねないと考えたからです。名前空間は最初に切っておくのが安全だと思います。

2. IAM 権限を付与する

プロファイル経由で呼び出すには、呼び出し元のロール(自チームでは AgentCore Runtime の実行ロール)に、プロファイル自身への許可を追加します。

statement {
  sid    = "BedrockInferenceProfileAccess"
  effect = "Allow"
  actions = [
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithResponseStream",
    "bedrock:GetInferenceProfile"
  ]
  resources = [
    aws_bedrock_inference_profile.feature_a_sonnet_4_5.arn
  ]
}

ここに注意点がひとつあります。プロファイルの ARN への許可だけでは動きません。AWS のドキュメントに明記されているとおり、Resource に Inference profile を指定する場合は、そのプロファイルに紐づく各リージョンの Foundation Model への bedrock:InvokeModel 系の許可も併せて必要です。自チームでは Foundation Model 側の許可を別のステートメントで付与済みだったため、追加したのは上記のステートメントだけで済みました。

3. 呼び出し側で ARN を渡す

InvokeModel / Converse の modelId は、モデル ID のほかに Inference profile の ARN も受け付けます。そのため呼び出し側の変更は、モデル ID の代わりにプロファイルの ARN を渡すだけです。

自チームのエージェントは Strands Agents SDK を使っています。Terraform 側で AgentCore Runtime の環境変数にプロファイルの ARN を渡し、それを BedrockModelmodel_id に指定しています。環境変数が未設定の場合は、モデル ID の直接指定にフォールバックします。

import os

from strands.models import BedrockModel

DEFAULT_MODEL_ID = "jp.anthropic.claude-sonnet-4-5-20250929-v1:0"

def create_bedrock_model() -> BedrockModel:
    # model_id にはモデルIDの他に Application inference profile の ARN も指定できる
    model_id = os.environ.get("INFERENCE_PROFILE_ARN", DEFAULT_MODEL_ID)
    return BedrockModel(
        model_id=model_id,
        region_name="ap-northeast-1",
    )

4. コスト配分タグを有効化する

タグを付けただけでは Cost Explorer には出てきません。AWS Billing and Cost Management コンソールの「コスト配分タグ」で、付けたタグキーを有効化する必要があります。注意点が 2 つあります。

  • 有効化してから Cost Explorer / CUR に反映されるまで、最大 24 時間かかる
  • タグは遡及しない。有効化した以降に発生したコストにしか、タグは付かない

どちらも、タグの有効化を実装より先に済ませておく理由になります。タグの付いたリソースがまだ存在しなくても有効化はできるので、自チームでは実装 PR を作る前に有効化を済ませました。

Cost Explorer での見え方

このように、Cost Explorer で「サービス」の絞り込みで LLM モデルを指定した上で、「グループ化の条件」に feature を表すタグキー(サンプルでは myorg:myapp:feature)を指定すると、feature-afeature-b のコストが分かれて表示されます。同様に environment のタグキーで dev と prod を切り分けられます。

これで「feature-a 機能に月いくら、feature-b 機能に月いくら」という問いに答えられるようになりました。

とはいえ、完璧ではない

リクエスト単位のコストは分からない

Application inference profile が Cost Explorer / CUR に届けるのは集計済みの金額で、粒度は「使用タイプ × 日」が最小です。「このプロンプト 1 回にいくらかかったか」を知りたい場合は、リクエスト単位のメタデータタグ付け(per-request metadata tagging)とモデル呼び出しログ(model invocation logging)を組み合わせる別のアプローチが必要です。機能単位の月次コストが見たいだけなら、Application inference profile で足ります。

プロファイルはモデルと 1:1 で増えていく

プロファイルは特定のモデルに紐づくので、「モデル × 機能 × 環境」の組み合わせの数だけ増えます。自チームは 2 機能 × 2 環境 = 4 個で済んでいますが、機能やモデルのバリエーションが多い組織では、モデル更新のたびにプロファイルの作り直しが発生し、管理対象が膨らみます。AWS のドキュメントでも、スケールするコスト追跡では、タグの粒度をチームやコストセンター単位に留めることなどが推奨されています。

タグは遡及しない

前述のサンプル画像にあるとおり、導入前に発生したコストは合算されたままで、後から分けられません。

ほかの按分手段

Bedrock のコスト按分は Application inference profile だけではありません。OpenAI 互換の Responses / Chat Completions API(bedrock-mantle エンドポイント)を使う場合は、モデルに紐づかない Projects が使え、本記事で触れた「プロファイルがモデルごとに増える」問題自体が起きません。bedrock-runtime のままでも、呼び出し元の IAM ロール / ユーザー単位で按分する IAM principal attribution が追加コードなしで使え、Application inference profile と併用できます(こちらは「機能単位」ではなく「誰が呼んだか」の軸です)。用途に応じて、機能別は Application inference profile、ID 別は IAM principal attribution、リクエスト別は per-request metadata tagging、と使い分けるとよいです。

振り返り

導入のコア部分は、機能ごとに数十行の Terraform と、modelId の差し替えだけでした。アプリケーションコードのロジックには手を入れずに「AI 機能ごとに月いくらかかっているか」へ答えられるようになったので、投資対効果は良い部類だと思います。

AI 機能の開発がこれからも増えていくことは、容易に想像がつきます。1 つ目、2 つ目の時点ではコストの内訳は気にならないかもしれませんが、「どの機能に投資を続けるか」という判断はいずれ必ず来ます。そのときコスト配分タグは遡及できないので、過去分のデータは後から取れません。Bedrock で複数の AI 機能を作るなら、最初の機能をリリースするタイミングで Application inference profile をセットで導入しておくのがおすすめです。

参考リンク