LiteLLM の s3_use_team_prefix と s3_use_key_prefix の挙動を検証して Team と Key 命名の方針を考えてみた

LiteLLM の s3_use_team_prefix と s3_use_key_prefix の挙動を検証して Team と Key 命名の方針を考えてみた

2026.05.19

こんにちは。クラウド事業本部コンサルティング部の桑野です。
前回はLiteLLM ProxyをAmazon ECS on FargateにTerraform + ecspressoでデプロイする構成を共有しました。

https://dev.classmethod.jp/articles/litellm-proxy-on-fargate/

その構成ではs3_v2コールバックでリクエストログをS3バケットへ出力していますが、運用を考えていくとひとつ気になる点があります。1つのバケットに全Teamと全Keyのログが混ざるという点です。

LiteLLMには s3_use_team_prefixs3_use_key_prefix というフラグがあり、有効化するとオブジェクトキーの先頭にTeam aliasやKey aliasがprefixとして付くようになります。とはいえ「実際にどんなパスになるのか」「両方ONにしたときの結合順は」「aliasが空のときは」「日本語やスラッシュを混ぜたら」といった具体的な挙動はドキュメントだけでは見えにくいです。

今回はこれらのフラグを実際に検証し、Team / Key命名における運用上の方針を考えてみたので共有します。

s3_use_team_prefix / s3_use_key_prefix とは?

LiteLLMのs3_v2コールバックのオプションで、S3に書き出すオブジェクトキーにTeam alias / Key aliasをprefixとして付けるかどうかを制御します。

https://docs.litellm.ai/docs/proxy/logging#s3-buckets

config.yamlではlitellm_settings配下にフラグとして指定します。

litellm_settings:
  success_callback: ["s3_v2"]
  s3_callback_params:
    s3_bucket_name: litellm-log-archive
    s3_region_name: ap-northeast-1
    s3_use_team_prefix: true
    s3_use_key_prefix: true

s3_use_key_prefixPR #16237で2025-11-05にmainにマージされた比較的新しめのフラグです。本記事執筆時点での最新版で利用できます。

ソースコードから見る挙動

検証前に実装箇所を確認しました。
以下リンクのL478-L496あたりです。

https://github.com/BerriAI/litellm/blob/main/litellm/integrations/s3_v2.py

        # Base prefix (default empty)
        prefix_components = []
        if self.s3_use_team_prefix:
            team_alias = standard_logging_payload.get("metadata", {}).get(
                "user_api_key_team_alias", None
            )
            if team_alias:
                prefix_components.append(team_alias)
        if self.s3_use_key_prefix:
            user_api_key_alias = standard_logging_payload.get("metadata", {}).get(
                "user_api_key_alias", None
            )
            if user_api_key_alias:
                prefix_components.append(user_api_key_alias)

        # Construct full prefix path
        prefix_path = "/".join(prefix_components)
        if prefix_path:
            prefix_path += "/"

ここから読み取れる仕様は以下です。

  • コード上の追加順から、結合順は team_aliaskey_alias になる
  • aliasがNoneや空文字ならifで弾かれてprefixに含まれない
  • 値はそのまま使われ、サニタイズ処理は無い

つまりaliasの値次第でオブジェクトキーがそのまま変わってしまうわけです。実際にどうなるかを動かして確認していきます。

検証環境と手順

前回の記事で構築したリポジトリのローカル開発構成(LiteLLM + MinIO + PostgreSQL のdocker compose)を流用しています。

https://github.com/k-kuwan0/litellm-proxy-on-fargate

  • LiteLLMバージョン:本記事執筆時点の最新版(s3_use_key_prefix が含まれていれば再現可能)
  • モデル:bedrock/amazon.nova-lite-v1:0
  • リクエスト経路:Admin UI の Test Key ボタン
  • ログ確認:MinIOコンソール(http://localhost:9001)でバケット litellm-log-archiverequest_logs/ 配下を確認

検証では以下の組み合わせを順番に試しました。

  • 基本動作マトリクス:両フラグの4通り × Team所属あり / なし / master key
  • 特殊文字耐性:Key aliasに / や日本語を混ぜた場合の挙動

検証用のTeam / Keyを作成する

LiteLLMをローカル起動して http://localhost:4000/ui にmaster keyでログインし、検証用のTeamとKeyを事前にまとめて作成しておきます。

Team

alias 用途
team-alpha 通常ケース
team/sub 特殊文字(スラッシュ)
営業チーム 特殊文字(マルチバイト)
team alpha 特殊文字(スペース)
teamA/ 特殊文字(末尾スラッシュ)

スクリーンショット 2026-05-18 22.36.25

スクリーンショット 2026-05-18 22.37.29

スクリーンショット 2026-05-18 22.38.13

スクリーンショット 2026-05-18 22.39.07

スクリーンショット 2026-05-18 22.39.52

5つ登録しました。

スクリーンショット 2026-05-18 22.40.34

Key(Virtual Keys)

alias 所属Team 用途
key-prod team-alpha 通常ケース(Team所属)
key-noteam (なし) Team未所属
key/sub team-alpha 特殊文字(スラッシュ)
キー日本語 team-alpha 特殊文字(マルチバイト)

スクリーンショット 2026-05-18 22.42.29

スクリーンショット 2026-05-19 0.08.32

スクリーンショット 2026-05-18 22.48.32

スクリーンショット 2026-05-18 22.49.25

4つ登録しました。

スクリーンショット 2026-05-18 22.50.05

なお、Keyのalias空欄での作成も試みましたが、Admin UI側のバリデーションで弾かれ、作成自体ができませんでした。

スクリーンショット 2026-05-18 22.47.07

master key を使うケースは、Admin UI で master key の値を直接入力してTest Keyボタンから送信します。

フラグ切り替えとリクエスト送信

ラウンドごとに config.yaml の以下2行を書き換えて docker compose restart litellm で反映します。

s3_use_team_prefix: true   # ラウンドごとに true / false を切り替え
s3_use_key_prefix: true    # ラウンドごとに true / false を切り替え

各キーからのリクエスト送信はAdmin UIの「Virtual Keys → Test Key」ボタンから行います。master keyの場合もTest Key画面でmaster keyの値を直接入力して送信します。

ラウンドの境界がわかりやすいよう、各ラウンド前にMinIOコンソール上でバケット litellm-log-archive 配下のオブジェクトをまとめて削除しています。

検証結果

A. 基本動作マトリクス

ラウンド1: s3_use_team_prefix: true / s3_use_key_prefix: true

使ったKey team_alias key_alias 実際のオブジェクトキー
key-prod team-alpha key-prod request_logs/team-alpha/key-prod/2026-05-18/time-...json
key-noteam (なし) key-noteam request_logs/key-noteam/2026-05-18/time-...json
master key (なし) (なし) request_logs/2026-05-18/time-...json

MinIO上での実際の格納先は以下です。

key-prod(team所属)

スクリーンショット 2026-05-18 23.00.41

key-noteam(team未所属)

スクリーンショット 2026-05-18 23.01.24

master key

スクリーンショット 2026-05-18 23.02.34

期待していた通り、request_logs/<team>/<key>/<date>/time-... の階層になりました。
aliasがNoneのときは想定通りprefixからスキップされています。

ラウンド2: s3_use_team_prefix: true / s3_use_key_prefix: false

使ったKey team_alias key_alias 実際のオブジェクトキー
key-prod team-alpha key-prod request_logs/team-alpha/2026-05-18/time-...json
key-noteam (なし) key-noteam request_logs/2026-05-18/time-...json
master key (なし) (なし) request_logs/2026-05-18/time-...json

key-prod(team所属)

スクリーンショット 2026-05-18 23.16.01

key-noteam / master key(同じ階層に並列で格納される。タイムスタンプが小さい方がkey-noteam、大きい方がmaster key)

スクリーンショット 2026-05-18 23.17.26

Team prefixのみ有効です。Team未所属のKeyとmaster keyは同じrequest_logs/<date>/ 直下に並列で格納されます。

ラウンド3: s3_use_team_prefix: false / s3_use_key_prefix: true

使ったKey team_alias key_alias 実際のオブジェクトキー
key-prod team-alpha key-prod request_logs/key-prod/2026-05-18/time-...json
key-noteam (なし) key-noteam request_logs/key-noteam/2026-05-18/time-...json
master key (なし) (なし) request_logs/2026-05-18/time-...json

key-prod

スクリーンショット 2026-05-18 23.20.26

key-noteam

スクリーンショット 2026-05-18 23.21.06

master key

スクリーンショット 2026-05-18 23.21.44

Team所属のKeyでもTeam prefixは付かず、Key prefixのみが効きます。team-alpha所属 という情報はオブジェクトキーから読み取れなくなります。

ラウンド4: s3_use_team_prefix: false / s3_use_key_prefix: false

使ったKey team_alias key_alias 実際のオブジェクトキー
key-prod team-alpha key-prod request_logs/2026-05-18/time-...json
master key (なし) (なし) request_logs/2026-05-18/time-...json

key-prod / master key(同じ階層に並列で格納される)

スクリーンショット 2026-05-18 23.24.46

全件 request_logs/<date>/ 直下に並列で格納されます。Team / Keyによる区別は完全にオブジェクトキー上消失します。

B. master keyの挙動

各ラウンドを通して、master keyからのリクエストは 両フラグの設定に関わらず一貫してprefix無しrequest_logs/<date>/ 直下に格納されました。

これはLiteLLM内部で user_api_key_team_alias / user_api_key_alias がいずれも None になるためで、ソースコードの if alias: で弾かれる挙動と一致しています。

C. 特殊文字を含むaliasの挙動

ラウンド1(両フラグtrue)状態でKey aliasに / や日本語を入れた場合の挙動を確認しました。

パターン 設定したalias Admin UIで作れたか 実際のオブジェクトキー
スラッシュ含む key/sub 作れた request_logs/team-alpha/key/sub/2026-05-18/time-...json
日本語 キー日本語 作れた MinIOに書き込みなし(オブジェクト未生成)

key/sub のオブジェクトキー

スクリーンショット 2026-05-18 23.03.21

team-alpha 配下の様子(意図しない key フォルダが出現)

スクリーンショット 2026-05-18 23.08.07

スラッシュ入りaliasは、/ がそのままディレクトリ階層として展開されました。team-alpha/key/sub/ の3階層になり、team-alpha 配下に意図しない key というフォルダが、本来の key-prod と並んで出現しています。Admin UI / LiteLLM本体ともにバリデーションがかかっていないため、aliasに / を含めた瞬間に階層構造が壊れます。

日本語aliasの方は、Admin UIへの登録自体は通るもののS3への書き込みが行われませんでした。リクエスト自体はLiteLLM Proxy側では成功していたため、s3_v2 callback内のどこかで失敗していると見られます。S3のオブジェクトキー仕様上、ASCIIの「Safe characters」(0-9 A-Z a-z! - _ . * ' ( ))以外は「Characters that might require special handling」(取り扱い注意)の扱いになっており、LiteLLM / boto3 / S3 のいずれかでエンコードが噛み合わず失敗していると考えられます。

https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html

運用方針として考えたこと

検証結果を踏まえて、本番運用時のTeam alias / Key aliasの命名 / フラグ設定としてどうするべきかを考えてみました。

基本は両フラグONで運用する

s3_use_team_prefix: true / s3_use_key_prefix: true の組み合わせが、

  • Team単位での絞り込みもKey単位での絞り込みもオブジェクトキー上で完結する
  • aliasが無いログ(master key等)は自然とrequest_logs/直下に集まり区別が付く

という観点で最も運用しやすいです。ロギング基盤連携(Athena / Glue等での集計)でも、prefixでパーティション分割しやすくなります。

master keyはアプリから使わせない

master keyから発行されたリクエストは、両フラグをONにしていてもprefixが付かずrequest_logs/<date>/直下に格納されます。これはLiteLLM内部で user_api_key_team_alias / user_api_key_aliasNone になるためで、ソースコードの仕様上避けられません。

結果として、

  • Team / Keyごとの集計から漏れる
  • Team未所属の virtual key と同じ階層に混在して識別が難しくなる

という二重の問題が起きます。本番ではアプリケーションから渡すのはvirtual key(sk-...)のみとし、master keyは管理操作のみに限定する方針が安全かと考えました。

Team / Key aliasの命名規約を決める

検証で見えた通り、aliasの値はそのままオブジェクトキーになります。命名規約を決めておかないとあとからS3階層が荒れる原因になります。最低限以下は揃えておきたいところです。

  • 半角英数 + ハイフン / アンダースコアのみ
  • スラッシュ・スペース・マルチバイト文字は禁止
  • 末尾スラッシュ禁止(// 二重スラッシュ生成の懸念)

LiteLLM側にサニタイズが無いので、運用側のレビューやAdmin UI操作の手順書でカバーする必要があります。

Team未所属のvirtual keyを作らない

ラウンド2の結果から分かるように、Team未所属のKeyとmaster keyは request_logs/<date>/ 直下に並んで区別が付きません。s3_use_team_prefix: true を活かすためにも、virtual keyは必ず何らかのTeamに紐付けて発行する運用にしておくと、集計や監査が実施しやすいのではないかと考えています。

まとめ

いかがだったでしょうか。LiteLLMのs3_use_team_prefix / s3_use_key_prefixの挙動を検証し、Team / Key命名の運用方針を考えてみました。

検証から見えたポイントを改めて整理すると以下です。

  • 両フラグONで request_logs/<team>/<key>/<date>/time-...json の階層になる
  • aliasがNoneの場合は該当prefixがスキップされる(master keyは常にprefix無し)
  • aliasにサニタイズが無いため、/ や日本語を混ぜるとオブジェクトキーが破綻する
  • 運用上は両フラグON / master keyはアプリから使わせない / Team / Key alias命名規約を整備 / Team未所属key禁止、の方針が扱いやすい

LiteLLMの運用上の細かい挙動はドキュメントだけでは見えにくいので、実際に動かして確かめる作業はやはり大切だなと感じます。今回検証したことは、実際にS3にフォールバックする際にも同じ挙動になるかと思いますので、この階層構造を前提にAthenaで分析してみるのも面白いかもなと思っています。
引き続き検証していきますので、新しい気づきがあれば別の記事で共有します。

最後までご覧いただきありがとうございました。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事