7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS】AgentCoreの内部実装から読み解く認証フローと実行環境差

7
Posted at

はじめに

本記事は AWS の公式ドキュメントに載っていない部分を個人的に検証して確かめたことをまとめた記事です。そのため、正確な内部実装とは異なる可能性があることをご留意ください。

Amazon Bedrock AgentCore を Mac で開発・動作確認したあと、会社の Windows PC でも同じ手順を試したところ、Macでは何も指定しなくても動くのに Windowsではエラーになる という現象に遭遇しました。

ERROR: Workload access token has not been set.
If invoking agent runtime via SIGV4 inbound auth,
please specify the X-Amzn-Bedrock-AgentCore-Runtime-User-Id header and retry.

エラーメッセージだけ見ると「--user-id を付ければいい」で終わりそうですが、なぜ Mac では不要で Windows では必要なのか が気になり、公開されているソースコードを追って原因を突き止めました。

試した構成

構成図

構成は以下のようになります。
Runtime,Gateway,Identityを利用したシンプルな構成です。

image.png

これを会社PCでも再現してハンズオン教材にしようかと思ったのですが、今回の件にぶち当たったというわけです。
ちなみに、Runtime単体で起動するようなケースではエラーは発生しません。
ツール利用など、Identityへの認証情報のやりとりが挟まると発生します。

利用環境

項目 Mac Windows
OS アーキテクチャ linux/arm64(ネイティブ) linux/amd64
Docker Desktop あり なし
agentcore invoke--user-id なし) ✅ 動く ❌ エラー

使用したコマンドはどちらも同じです。

uv run agentcore invoke '{"prompt": "利用できるツールを教えて"}'

発生したエラー

Windows で invoke すると以下のクライアントエラーが返ります。

Invocation failed: An error occurred (RuntimeClientError) when calling
the InvokeAgentRuntime operation: Received error (500) from runtime.
Please check your CloudWatch logs for more information.

CloudWatch ログを確認すると、実際のエラーはこちらです。

{
  "level": "ERROR",
  "message": "Invocation failed (0.001s)",
  "errorType": "ValueError",
  "errorMessage": "Workload access token has not been set. If invoking agent runtime via SIGV4 inbound auth, please specify the X-Amzn-Bedrock-AgentCore-Runtime-User-Id header and retry.",
  "stackTrace": [
    "File \"/app/agent-test.py\", line 36, in invoke",
    "File \".../bedrock_agentcore/identity/auth.py\", line 90, in sync_wrapper",
    "File \".../bedrock_agentcore/identity/auth.py\", line 285, in _get_workload_access_token",
    "ValueError: Workload access token has not been set..."
  ]
}

スタックトレースから、エラーはローカル PC ではなく AWS 上で動いているエージェントコードの中で発生していることが分かります。

また、ファイルパスが /app/agent-test.py となっており、Dockerコンテナ内で実行されていることも確認できます。

環境構成まとめ

今回の検証は Mac と Windows でそれぞれ別の AWS アカウントを使用しています。そのため CloudWatch ログの比較はあくまで参考情報です。実行環境の違いの根本原因は、後述する deploy 時のビルドパスの分岐によるものです。

Windows のエラーログから、実行環境の手がかりが得られます。

項目 Mac Windows
AWS アカウント 個人アカウント(異なるアカウント) 会社アカウント(異なるアカウント)
DOCKER_CONTAINER 環境変数 未設定 =1

Windows のスタックトレースには /app/agent-test.py というパスが出ており、Docker コンテナ内で実行されていることが確認できます。Mac は成功しているためスタックトレースがなく、同じ条件での比較はできません。

この DOCKER_CONTAINER の有無がどこで決まるのか、deploy ステップで詳しく見ていきます。

なぜ Windows(Docker Desktop なし)はエラーになる?

原因を理解するには、configure → deploy → invoke の 3 ステップで何が起きているかを追う必要があります。

① agentcore configure

.bedrock_agentcore.yaml を生成し、platform: linux/arm64 を設定します。

Windows では実行時にこの警告が表示されます。

Platform mismatch: Current system is 'linux/amd64'
but Bedrock AgentCore requires 'linux/arm64',
so local builds won't work.
Please use default launch command which will do
a remote cross-platform build using CodeBuild.

② agentcore deploy

deploy 時に .venv 内の container.py が Docker の有無を検知します。

# container.py:36, 40-45
self.available_runtimes = ["finch", "docker", "podman"]

for runtime in self.available_runtimes:
    if self._is_runtime_installed(runtime):
        self.runtime = runtime
        self.has_local_runtime = True
        break

検知の実装はシンプルで、docker version コマンドを実行して成功するかどうか を見るだけです。

# container.py:87-93
def _is_runtime_installed(self, runtime: str) -> bool:
    result = subprocess.run([runtime, "version"], capture_output=True, check=False)
    return result.returncode == 0

この結果によってビルドパスが分岐します。

環境 docker version の結果 ビルド方式 実行環境
Mac(Docker Desktop あり) 成功 ローカルビルド Lambda互換環境(/var/task/
Windows(Docker Desktop なし) 失敗 CodeBuild(リモートビルド) Docker コンテナ/app/

CodeBuild でビルドされるコンテナは Dockerfile.j2 テンプレートから生成されます。このテンプレートに DOCKER_CONTAINER=1 が含まれています。

# Dockerfile.j2:9, 32
ENV DOCKER_CONTAINER=1

これが Windows の Docker コンテナ内に DOCKER_CONTAINER=1 がセットされる理由です。

③ agentcore invoke

invoke 時、エージェントコードは @requires_access_token デコレーターを通じて workload access token を取得しようとします。

# auth.py:278-291
async def _get_workload_access_token(client):
    token = BedrockAgentCoreContext.get_workload_access_token()
    if token is not None: #トークンがNoneでないなら_set_up_local_authによって自動セットアップする
        return token
    else: #DOCKER_CONTAINER"が1のため、エラーとする
        if os.getenv("DOCKER_CONTAINER") == "1":
            raise ValueError(
                "Workload access token has not been set. "
                "If invoking agent runtime via SIGV4 inbound auth, "
                "please specify the X-Amzn-Bedrock-AgentCore-Runtime-User-Id header..."
            )
        return await _set_up_local_auth(client)  # 自動セットアップ

workload access token は、invoke 時に --user-idruntimeUserId)を指定した場合に AWS Runtime インフラが自動でセットしてくれます。指定しない場合は自動でNoneとなります。
今回の場合、何も指定せずに起動しているため、workload access token はNoneとなります。
そして、上記で説明したように、DOCKER_CONTAINER == 1となっているため、500エラーとなるわけです。

Windows(Docker コンテナ環境)の場合:
ここまでの流れをまとめると以下のようになります。


workload token = None(invoke時にオプション指定しないため自動的にNoneになる)
DOCKER_CONTAINER == "1"(deploy時にdocker versionを判定し)
→ ValueError を raise
→ 500 エラー ❌

エージェントサンドボックスでも同様にエラーになる

CLI からの invoke だけでなく、AgentCore のマネージメントコンソール上に用意されているサンドボックス機能でも同じエラーが発生します。

image.png

内部的には --user-idなしのSIGV4 呼び出しと同等の扱いになるため、CodeBuild でデプロイされた環境では同様に失敗します。

macOS(Docker Desktop あり)が成功する理由

では、macOS(Docker Desktop あり)はなぜ成功するのか、そちらも整理してみます。invoke 時、Runtime インフラは --user-idruntimeUserId)が指定されている場合のみ workload access token を生成し、リクエストヘッダーにセットします。指定がない場合はヘッダー自体が存在しないため、エージェントコード側で token を取得しようとしても None のままになります。

# app.py:343
# ヘッダーに token があればセット、なければ何もしない
agent_identity_token = headers.get(ACCESS_TOKEN_HEADER)
if agent_identity_token:
    BedrockAgentCoreContext.set_workload_access_token(agent_identity_token)

token が None のとき、次の処理は DOCKER_CONTAINER 環境変数の有無で分岐します。

# auth.py:278-291
if os.getenv("DOCKER_CONTAINER") == "1":
    raise ValueError(...)          # Docker コンテナ環境 → エラー
return await _set_up_local_auth(client)  # それ以外 → 自動セットアップ

Mac は deploy 時にローカルビルドを行うため、Dockerfile.j2 テンプレートが適用されない実行環境にデプロイされます。その結果 DOCKER_CONTAINER が設定されておらず、_set_up_local_auth() が自動で呼ばれます。

# auth.py:291
return await _set_up_local_auth(client)

_set_up_local_auth() の処理内容

_set_up_local_auth()auth.py:294-330 で定義されており、以下を順に行います。

.agentcore.json の確認(auth.py:299-309)

Runtime コンテナ内のカレントディレクトリに .agentcore.json があれば、前回作成した workload_identity_nameuser_id を再利用します。

config_path = Path(".agentcore.json")  # コンテナ内のカレントディレクトリ
if config_path.exists():
    config = json.load(file)
workload_identity_name = config.get("workload_identity_name")

② Workload Identity の作成(auth.py:313)

.agentcore.json がない場合(初回)、AgentCore の コントロールプレーン API を呼び出して Workload Identity を新規作成します。

# identity.py:115-124
# boto3 クライアント: "bedrock-agentcore-control"
self.cp_client.create_workload_identity(
    name="workload-xxxxxxxx",  # ランダムな名前が自動生成される
    allowedResourceOauth2ReturnUrls=[]
)

③ user_id の生成(auth.py:320)

user_id もない場合、8文字のランダム文字列を生成します。

user_id = uuid.uuid4().hex[:8]  # 例: "a3f9c2b1"

.agentcore.json に保存(auth.py:323-328)

次回の invoke 時に再利用できるよう、コンテナ内に保存します。ファイルへの書き込みに失敗しても処理は続行されます。

config = {"workload_identity_name": "workload-xxxxxxxx", "user_id": "a3f9c2b1"}
# 書き込み失敗しても Warning を出して続行

⑤ workload access token の取得(auth.py:330)

最後に AgentCore の データプレーン API を呼び出してトークンを取得し、返します。
実際のトークン取得処理はidentity.pyに記載されています。

# identity.py:96-113
# boto3 クライアント: "bedrock-agentcore"
self.dp_client.get_workload_access_token_for_user_id(
    workloadName="workload-xxxxxxxx",
    userId="a3f9c2b1"
)
# → workloadAccessToken が返る

CloudWatch ログで確認

実際のCloudWatch ログでも認証情報を作成している処理が確認できます。

image.png

これにより --user-id を指定しなくても自動でトークンが取得され、成功します。

検知フローまとめ

ここで改めて、両方の処理フローをまとめてみます。
図示すると以下のような形になります。

image.png

macOS(Docker Desktop あり)

① agentcore configure
   platform: linux/arm64 を設定(ネイティブ一致)

② agentcore deploy
   "docker version" → 成功
   → ローカルビルド
   → DOCKER_CONTAINER は設定されない

③ agentcore invoke
   workload token = None
   DOCKER_CONTAINER が "1" でない
   → _set_up_local_auth() が自動実行
   → Workload Identity を作成・トークン取得
   → 成功 ✅

Windows(Docker Desktop なし)

① agentcore configure
   platform: linux/arm64 を設定
   "Platform mismatch" 警告が出る(amd64 環境)

② agentcore deploy
   "docker version" → 失敗
   → CodeBuild でリモートビルド
   → Dockerfile.j2 により DOCKER_CONTAINER=1 がセット

③ agentcore invoke
   workload token = None
   DOCKER_CONTAINER == "1"
   → ValueError を raise
   → 500 エラー ❌

解決策

では一応になりますが、今回の事象を回避するための手法も紹介します。

A 案:--user-id を付けて invoke する

uv run agentcore invoke --user-id any-user-id '{"prompt": "利用できるツールを教えて"}'

--user-id の値は任意の文字列で構いません。これが設計通りの正しい使い方です。

B 案:Windows に Docker Desktop をインストールして再デプロイ

元も子もないですが、Docker Desktopをインストールすれば解決します。
Mac と同じビルドパス(ローカルビルド)になり、--user-id が不要になります。ただし、会社 PC へのインストール制約がある場合は現実的ではないかもしれません。

さいごに

今回の挙動差の検証は公式ドキュメントに詳細な記載がないため、かなり苦労しました。エラーメッセージの「--user-id を指定してください」だけを見ると原因が分からず、見落としがちな落とし穴です。

根本的な流れは以下の通りです。

  1. docker version コマンドの成否(Docker Desktop の有無)
  2. ビルドパスの分岐(ローカルビルド / CodeBuild)
  3. 実行環境の違い(Lambda互換環境 / Docker コンテナ)
  4. DOCKER_CONTAINER=1 の有無
  5. workload access token の取得可否

この調査を通じて、AWS マネージドサービスのエラーはエラーメッセージだけを見るのではなく、公開されているライブラリのソースコードを追うことで根本原因まで辿り着けることを改めて実感しました。

同じ問題でハマっている方の参考になれば幸いです。

参考

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?