はじめに
内容は執筆時点の情報です。
記事はClaudeと一緒に作りましたが、最終調整・確認は人間がしてます。
生成AIアプリケーションを運用していくうえで、
「誰が・何に・どれだけ利用しているのか」を把握することは、コスト管理の第一歩です。
Amazon BedrockではIAM Principal単位で利用主体を追跡できます。
ただし、複数ユーザーが同じLambdaなどを経由してBedrockを利用する構成では、
Bedrockから見える実行主体は同じIAMロールになります。
つまり問題は「IAM Principalが取れない」ことではなく、
IAM Principal
≠
実際のApplication User
であることです。
社内で複数人・複数用途(要約、FAQ、分類…)が同じAPIを共有していると、
IAM Principal単位の情報だけでは内訳が見えなくなります。
2026年9月、AWSは「AWS Tokenomics Strategy」というブログで、
生成AI利用のコスト管理を次の4ステップに整理しました。
See → Visibility & Attribution(可視化・帰属)
Save → Optimization(最適化)
Run → Governance(ガバナンス)
Plan → Value / ROI(価値・投資対効果)
このうち最初のステップであるSeeでは、
- IAM Principal Allocation(CUR 2.0でBedrockのモデル推論コストをIAM Principal単位に帰属)
- Application Inference Profiles / Bedrock Projects / Bedrock Workspacesでのタグ付け
- Model Invocation Logging(プロンプト、レスポンス、Token数、モデル、アカウント、IAMロールを記録)
が紹介されています。
IAM Principal AllocationやApplication Inference Profilesなどを使えば、
IAM Principal・チーム・アプリケーション・プロジェクト単位でコストを帰属できます。
また、Model Invocation Loggingでは推論リクエスト単位でToken数やモデル、
IAM Principalなどを自動的に記録できます。
一方、複数のエンドユーザーや複数用途が同じLambda / IAMロールを共有している場合、
そのリクエストが「どのエンドユーザーによるものか」「どの業務用途なのか」といった
アプリケーション固有の情報までは自動的には付与されません。
本記事では、このギャップをrequestMetadataで補えるかを検証しました。
Amazon Bedrock公式ドキュメントによれば、
requestMetadataはTokenomicsの記事自体では明示的に取り上げられていないものの、
team / application / environment / experimentのようにリクエストごとに変化する
属性をModel Invocation Logsへ付与するための仕組みとして、
Converse / ConverseStream /InvokeModel / InvokeModelWithResponseStreamの
4操作で利用できます(1リクエストあたり最大16個、キー・値それぞれ最大256文字)
つまり、
誰が・どの用途で・何Token使ったか
を本当に追跡できるのか、実際に実装して検証しました。
検証したいこと
誰が?
↓
どの用途で?
↓
どのモデルを使い?
↓
Input Token / Output Tokenを何Token消費した?
最終的に、以下のような粒度で利用状況を見られることをゴールにしました。
| user_id | use_case | model | input_tokens | output_tokens |
|---|---|---|---|---|
| user-001 | summary | Claude Haiku 4.5 | 172 | 103 |
| user-002 | faq | Claude Haiku 4.5 | 69 | 374 |
| user-001 | classification | Claude Haiku 4.5 | 94 | 8 |
アーキテクチャ
簡易な構成図としては以下のようになり、
- API Gateway + Lambda: サーバー管理不要な最小構成
-
Bedrock Converse API: 今回はモデル差異を吸収しやすく、レスポンスからToken Usageも取得しやすいためConverse APIを採用(
requestMetadata自体はInvokeModel系APIでもヘッダ経由で利用可能) - Model Invocation Logging: Bedrock呼び出し単位の詳細ログをCloudWatch Logsに出力する機能
IaCはAWS CDKで管理しています。
計測する3つの情報とその取得元
| 項目 | 取得元 |
|---|---|
| 誰が? | IAM Principal(Lambda実行ロール、固定) + requestMetadata.user_id(アプリケーション側のエンドユーザー識別子) |
| どの用途で? |
requestMetadata.use_case(summary / faq / classification) |
| 何Token? | Bedrock Converse APIレスポンスのusage、およびModel Invocation Loggingに記録されるToken数 |
鍵になるのがrequestMetadataです。LambdaからConverseを呼ぶときに、以下のように
アプリケーション側のコンテキストを埋め込みます。
bedrock_response = bedrock_runtime.converse(
modelId=BEDROCK_MODEL_ID,
system=[{"text": system_prompt}],
messages=[{"role": "user", "content": [{"text": text}]}],
requestMetadata={
"user_id": user_id,
"use_case": use_case,
"application": APPLICATION_NAME,
"environment": ENVIRONMENT_NAME,
},
)
これだけで、Model Invocation Loggingの出力にrequestMetadataがそのまま載ってきます。
IAM PrincipalとApplication Userの違い
AWS IAMの観点では、Bedrockを呼び出しているPrincipal(実行主体)は常に1つです。
user-001 がAPIを呼んでも
user-002 がAPIを呼んでも
IAM上は同じ "InvokeFunctionRole"(Lambda実行ロール)
API GatewayとLambdaを共有する構成では、CloudTrailやIAMのアクセス履歴を見ても
「user-001が呼んだ」という情報は一切出てきません。IAMが記録するのは
「InvokeFunctionRoleというロールがBedrockを呼んだ」という事実だけです。
実際にModel Invocation Loggingのログを見ると、これがそのまま確認できます
(アカウントIDとロール名の一部は伏字にしています)。
{
"requestMetadata": {
"use_case": "summary",
"user_id": "user-001",
"application": "tokenomics-poc",
"environment": "poc"
},
"identity": {
"arn": "arn:aws:sts::123456789012:assumed-role/TokenomicsPocStack-***-InvokeFunctionServiceRole***/TokenomicsPocStack-***-InvokeFunctionC***"
},
"input": { "inputTokenCount": 172 },
"output": { "outputTokenCount": 103 },
"inferenceRegion": "us-east-2"
}
user_idがuser-001 user-002どちらでも、
identity.arn(IAM Principal)は完全に同一でした。
つまり、
Infrastructure Principal(IAM Principal)
→ Lambda実行ロール。AWSリソースへのアクセス制御・認可の単位
Application User
→ requestMetadata.user_id。アプリケーションが「誰のリクエストか」を表現するための識別子
という2つの「誰」は、仕組みとしてはっきり分かれています。
requestMetadata.user_idはLambdaコード内で自由に設定できる値で、
IAMによる認証・検証を経たものではありません。
本番環境では、Cognito・OIDC・社内IdP・JWTなど認証済みのIdentityから
取得したuser_idのみをrequestMetadataに渡すべきです。
なお、requestMetadata はModel Invocation Logsに保存されるため、
本番では氏名やメールアドレスなどの個人情報を直接格納せず、
分析用の内部IDなどを利用するのが安全です。
https://docs.aws.amazon.com/en_en/bedrock/latest/userguide/cost-mgmt-request-metadata.html
今回場合はAPI Gatewayに認証を設けておらず、user_idもリクエストボディから
直接受け取る検証用の簡易実装です。
検証結果
実際にログから、
user_id / use_caseごとのToken使用量を抜き出しました。
LambdaExecutionRole(IAM上は常に同一)
│
├ user-001
│ ├ summary → input: 172 / output: 103
│ ├ faq → input: 66 / output: 360
│ └ classification → input: 94 / output: 8
│
└ user-002
├ summary → input: 80 / output: 85
└ faq → input: 69 / output: 374
IAM上はidentity.arnが常に同一のLambda実行ロールであるのに対し、
requestMetadata経由ではユーザー・用途単位の内訳がきれいに取れています。
Tokenomicsの「See」が上手く実現できていますね。
CloudWatch Logs Insightsで集計する
1件ずつログを目視確認するだけでなく、CloudWatch Logs Insightsを使えば、
user_id × use_case単位でToken使用量を集計できます。
本検証で実際に出力されたログのフィールド構造
(requestMetadata.user_id、input.inputTokenCount等)に
合わせたクエリは以下の通りです。
fields requestMetadata.user_id, requestMetadata.use_case,
input.inputTokenCount, output.outputTokenCount
| stats count(*) as requests,
sum(input.inputTokenCount) as inputTokens,
sum(output.outputTokenCount) as outputTokens
by requestMetadata.user_id, requestMetadata.use_case
| sort inputTokens desc
※対象ロググループは/aws/bedrock/tokenomics-poc(Model Invocation Loggingの送信先)
実際にaws logs start-query / get-query-resultsで実行したところ、
以下のようにuser_id × use_case単位で集計された結果が返ってきました。
| user_id | use_case | requests | inputTokens | outputTokens |
|---|---|---|---|---|
| user-001 | summary | 2 | 172 | 103 |
| user-001 | classification | 2 | 94 | 8 |
| user-001 | faq | 1 | 66 | 360 |
| user-002 | summary | 1 | 80 | 85 |
| user-002 | faq | 2 | 69 | 374 |
| (なし) | (なし) | 3 | 17 | 59 |
最後の行(user_id/use_caseが空)は、
動作確認の過程でBedrockコンソールのPlaygroundから直接呼び出した分です。
Playground経由のリクエストだけrequestMetadataが空になったことで、
メタデータを付与することの意味も実データ上で確認できました。
公式ドキュメントにも「requestMetadataはBedrockによって強制されるものではなく、
付与しなくてもリクエストは成功する」と明記されており、今回の実測結果と一致します。
組織全体でカバレッジを保証したい場合は、共有クライアントやLLMゲートウェイ側で
付与を強制する設計が公式にも推奨されています。
1件ずつのログ確認だけでなく、ユーザー・用途単位での集計まで同じログソースから行えることが確認できます。
まとめ
Amazon BedrockのrequestMetadataとModel Invocation Loggingを組み合わせれば、
「誰が・どの用途で・どれだけ使ったか」をアプリ側で簡単に可視化できました。
今回はコスト計算そのものまでは踏み込みませんでした。
requestMetadataは、あくまでModel Invocation Loggingによるリクエスト単位の
分析のための仕組みです。これに対して、AWS Tokenomics Strategyが紹介する、
IAM Principal Allocation(CUR 2.0)や
Application InferenceProfiles / Bedrock Projects / Bedrock Workspaces
でのタグ付けは、請求・コスト帰属のレイヤーで役割が異なります。
今回取得したuser_id / use_caseを分析軸として使い、これらのコスト帰属機能と
組み合わせることで、「IAM Principal・タグ単位」では届かなかった、
エンドユーザー単位まで含めた用途別コスト管理へ発展させられるはずです。
見えた先に何をするか
Tokenomics StrategyはSee(可視化・帰属)の先に、
Save(最適化)・Run(ガバナンス)・Plan(価値・ROI)と続きます。
今回Seeで取れたデータは、実はそのまま次のステップの入力になります。
See → FAQ用途では他用途と比べてOutput Tokenが多い傾向が見える
Save → FAQの回答長を制御する / プロンプトを改善する /
より安価なモデルを検討する、といった最適化対象が見えてくる
Run → AWS BudgetsやSCPなどで過剰利用を防ぐ
Plan → FAQにかかるコストに対して、問い合わせ対応時間削減などの
業務価値を評価する
つまり、Seeで「誰が・どの用途で・何Token使ったか」を集計し、
Save/Run/Planを行っていく、SeeはTokenomicsのスタート地点になります。
