mcpField
A
agents-harden

agents-harden

github.com/awsセキュリティ3.3k2.0k 回インストール今日更新
リポジトリ

agents-harden について

agents-harden は、AgentCore エージェントを本番環境に移行する開発者向けのスキルです。プロジェクト設定を読み取り、セキュリティ、信頼性、性能に関する個別のチェックリストを作成します。

主な機能

  1. 01プロジェクト別の本番チェックリスト
  2. 02IAM 権限を絞るための指針
  3. 03インバウンド認証の確認
  4. 04エラー処理と入力検証の確認
  5. 05シークレットとレート制限の指針

活用シーン

  1. 01公開前の AgentCore エージェントの確認
  2. 02エージェントを呼び出せる主体の制限
  3. 03スロットリングやクォータエラーの調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aws/agent-toolkit-for-aws --skill agents-harden

インストール後、Claude Code で/agents-hardenを入力して読み込まれたか確認します。

同梱ファイルreferences/limits.md

詳細

harden

AgentCore エージェントを本番環境に備えます — セキュリティ、信頼性、パフォーマンス。

使用するタイミング

  • エージェントを本番環境に移行する直前
  • リリース前に確認すべき項目のチェックリストが必要な場合
  • エージェントを呼び出せるユーザーを制限したい場合
  • デフォルトの IAM 権限をさらに絞り込みたい場合
  • スロットリングまたはクォータのエラーが発生している場合(references/limits.mdを読み込みます)
  • ワークロードに合わせてセッションのライフサイクルを調整する必要がある場合
  • エージェントで長時間実行されるバックグラウンド処理を実行している場合

入力

引数は不要です。このスキルはプロジェクトの設定を読み込み、プロジェクト固有の所見を含むチェックリストを作成します。

プロセス

ステップ 0:CLI バージョンを確認する

agentcore --versionを実行します。このスキルには v0.9.0 以降が必要です。バージョンが古い場合は、続行する前にagentcore updateを実行するよう開発者に伝えてください。

ステップ 1:プロジェクトを読み込む

agentcore/agentcore.jsonを読み、以下を確認します。

  • 設定されているリソース(memory、gateway、credentials、evaluators)
  • 使用されているフレームワーク
  • 設定されているネットワークモード(PUBLIC または VPC)
ステップ 2:チェックリストを確認する

各カテゴリを順に確認し、プロジェクト固有の所見を報告します。


IAM:権限を絞り込む

自動作成される実行ロールには、広範な Bedrock アクセス(arn:aws:bedrock:*::foundation-model/*)が付与されています。本番環境では、エージェントが使用する特定のモデルに権限を絞り込んでください。

現在の実行ロールを確認します:

agentcore status --json | jq -r '.runtimes[0].executionRoleArn'

本番環境に推奨される Bedrock ポリシー:

{
  "Effect": "Allow",
  "Action": [
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithResponseStream"
  ],
  "Resource": [
    "arn:aws:bedrock:<REGION>::foundation-model/anthropic.claude-sonnet-4-5-20250929-v1:0"
  ]
}

リソース ARN は、エージェントが使用する特定のモデルに置き換えてください。

ECR アクセス: 特定のリポジトリに権限を絞り込みます。

{
  "Effect": "Allow",
  "Action": ["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"],
  "Resource": "arn:aws:ecr:<REGION>:<YOUR_ACCOUNT_ID>:repository/bedrock-agentcore-<AGENT_NAME>-*"
}

信頼ポリシー: 実行ロールの信頼ポリシーが自分のアカウントに限定されていることを確認します。

{
  "Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
  "Action": "sts:AssumeRole",
  "Condition": {
    "StringEquals": {"aws:SourceAccount": "<YOUR_ACCOUNT_ID>"},
    "ArnLike": {"aws:SourceArn": "arn:aws:bedrock-agentcore:<REGION>:<YOUR_ACCOUNT_ID>:*"}
  }
}

ランタイムのリソースベースポリシー(API のみ):IAM ロールや JWT 認証による制御を超えて、ランタイムを呼び出せるプリンシパルを細かく制御するには、boto3 経由でPutAgentRuntimeResourcePolicyを使用します。これは CLI またはagentcore.jsonでは公開されていません。利用可能な場合は、awsknowledge MCP サーバーを使用して現在の API 仕様を確認してください。


シェルアクセス:InvokeAgentRuntimeCommandの権限を別途絞り込む

プロジェクトでInvokeAgentRuntimeCommandを使用している場合(agents-build/references/integrate.mdを参照)、IAM 権限をInvokeAgentRuntimeとは別に監査してください。この 2 つのアクションでは影響範囲が異なります。InvokeAgentRuntimeCommandは、実行ロールのすべての権限を持つ稼働中のマイクロ VM 内で任意のシェルを実行します。呼び出し元はファイルシステムの読み取りと書き込み、エージェントがアクセスできるあらゆるネットワークリソースへの接続、実行ロールの認証情報へのアクセスが可能です。

この権限を持つプリンシパルを確認します:

# List customer-managed policies in your account, then inspect each for InvokeAgentRuntimeCommand
aws iam list-policies --scope Local \
  --query 'Policies[*].[PolicyName, Arn, DefaultVersionId]' \
  --output table
# Then for each policy of interest:
aws iam get-policy-version \
  --policy-arn <POLICY_ARN> \
  --version-id <VERSION_ID> \
  --query 'PolicyVersion.Document'

または、IAM コンソールで確認します。IAM → Policies → Filter by type: Customer managed → ポリシーの JSON エディターでInvokeAgentRuntimeCommandを検索します。

コマンド呼び出し元用の IAM ポリシー — InvokeAgentRuntimeを許可するポリシーとは分けて管理します。

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "bedrock-agentcore:InvokeAgentRuntimeCommand",
    "Resource": "arn:aws:bedrock-agentcore:<REGION>:<YOUR_ACCOUNT_ID>:runtime/<RUNTIME_NAME>-*"
  }]
}

CloudTrail アラートを有効にします。 InvokeAgentRuntimeCommandが呼び出されたときにセキュリティチームへ通知する EventBridge ルールを作成します。

aws events put-rule \
  --name AgentCoreCommandExecution \
  --event-pattern '{"source":["aws.bedrock-agentcore"],"detail-type":["AWS API Call via CloudTrail"],"detail":{"eventName":["InvokeAgentRuntimeCommand"]}}' \
  --state ENABLED

呼び出し側のコードで、ユーザー入力からコマンドを構築する場合: 渡す前に検証し、&&、;、$(...)、バッククォート、|などのシェルメタ文字を含む文字列を拒否してください。


インバウンド認証:エージェントを呼び出せるユーザーを制御する

デフォルトでは、エージェントはインバウンド認証に AWS IAM(SigV4)を使用します。本番環境では、正しく設定されていることを確認してください。

現在の認証設定を確認する:

agentcore status --runtime <AgentName> --json | jq '.runtimes[0].authorizerConfig'

オプション:

AWS_IAM(デフォルト)— 呼び出し元は SigV4 でリクエストに署名する必要があります。内部サービスや AWS ネイティブのクライアントに適しています。

CUSTOM_JWT — 呼び出し元は ID プロバイダーからの JWT を提示します。ウェブ/モバイルアプリや外部クライアントに適しています。

agentcore add agent \
  --name MyAgent \
  --authorizer-type CUSTOM_JWT \
  --discovery-url https://your-idp.example.com/.well-known/openid-configuration \
  --allowed-audience my-api \
  --allowed-clients my-client-id

警告

本番環境では、決して --authorizer-type NONE を使用しないでください。エージェントへの未認証アクセスが許可され、 エンドポイント URL を知っている人なら誰でも呼び出せるようになります。必ず AWS_IAM または CUSTOM_JWT を使用してください。本番環境で NONE が設定されている場合は、ただちに変更してください。

allowedClients と allowedAudience の選択

これは JWT の設定で最もよくある誤りです。どちらを選ぶべきかは、ID プロバイダーが発行するトークンに何が含まれているかによって決まります。

サンプルトークンをデコードし(ID プロバイダー、または jwt.io を使用)、ペイロードを確認します。

  • トークンに client_id クレームがあり、aud クレームがない → ランタイムで allowedClients を設定します
  • トークンに aud クレームがある → ランタイムで allowedAudience を設定します
  • 両方ある → allowedAudience を使用します。aud クレームは標準の OIDC audience フィールドなので、主なチェック項目として使用してください。

誤った方を選ぶと、有効なトークンでも呼び出しが 403 を返します。ランタイムが、トークンに存在しないクレームを検証するためです。

issuer と discovery URL のプレフィックス要件

AgentCore は OIDC discovery 仕様(RFC 8414 §3)を適用します。discovery ドキュメントの issuer 値は、discovery エンドポイントの URL のプレフィックスである必要があります。

つまり、discovery URL が https://qa.example.com/.well-known/openid-configuration の場合、そのドキュメントの issuer フィールドは https://qa.example.com で始まる必要があります。ドキュメントが https://example.com(サブドメインなし)のような issuer を示している場合、検証は失敗します。

一部のエンタープライズ向け ID プロバイダー(PingFederate、Paylocity、一部の Keycloak 構成)では、環境固有のサブドメインで discovery エンドポイントをホストしながら、本番環境の issuer を示しています。このパターンは RFC 8414 のプレフィックス要件と互換性がありません。

修正方法:

  1. ID プロバイダーの discovery エンドポイントを issuer に合わせる — issuer と同じオリジンから discovery を提供します。
  2. 実際の discovery URL のドメインをランタイムに指定する — トークンの issuer と一致するサブドメインを含めて、ランタイムの discovery URL を設定します。
JWT 認証エラーのデバッグ

呼び出しが 403 で失敗する場合は、どのチェックで失敗しているかを切り分けます。

Authorization method mismatch — ランタイムの認証タイプとリクエストの認証タイプが一致していません。次の 2 つのケースがあります。

  • ランタイムが AWS_IAM(またはオーソライザーなし)に設定されているのに、呼び出し元が Bearer トークンを送信している → ランタイムを CUSTOM_JWT に再設定するか、呼び出し元に SigV4 を使用させます。
  • ランタイムが CUSTOM_JWT に設定されているのに、呼び出し元のリクエストが SigV4 で署名されている → SDK または環境が Bearer トークンとともに SigV4 ヘッダーを挿入している可能性があります。送信リクエストに X-Amz-Date、X-Amz-Security-Token、または Authorization: AWS4-HMAC-SHA256 が含まれていないか確認します。SigV4 の経路を取り除き、Bearer トークンのみを送信してください。

Invalid inbound token(または類似のエラー)— JWT バリデーターがトークンを拒否しました。次の項目を順に確認してください。

  1. issuer と discovery URL のプレフィックス(上記)— トークンの iss クレームが discovery URL のオリジンと一致していることを確認します
  2. allowedClients と allowedAudience — ランタイムはトークン形式に合ったクレームで設定されていますか?
  3. JWKS への到達性 — AgentCore は discovery ドキュメントに記載された jwks_uri に到達できますか?この URI はパブリックにアクセス可能である必要があります。
  4. トークンの有効期限切れ — トークンをデコードし、exp を現在時刻と照合します
  5. 署名アルゴリズムのサポート — 一部の ID プロバイダーは、どこでもサポートされているとは限らないアルゴリズム(PS256、ES384 など)で署名します。ID プロバイダーがサポートするアルゴリズムを確認し、互換性が問題であれば RS256 に切り替えてください。

これらすべてを除外してから、サービス側の問題として扱ってください。


エラー処理:適切にエラーを処理する

エージェントのコードが、内部の詳細を公開せずにエラーを処理することを確認してください。

from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload, context):
    try:
        # your agent logic
        return {"response": result}
    except Exception as e:
        # Log the full error internally
        app.logger.error(f"Agent error: {e}", exc_info=True)
        # Return a safe message to the caller
        return {"error": "An error occurred. Please try again."}

if __name__ == "__main__":
    app.run()

確認項目: エラーを黙って握りつぶす裸の except ブロック、呼び出し元にスタックトレースや内部の詳細を公開するエラーメッセージ、ツール呼び出しコードのエラー処理の不足。


入力検証とレート制限

エージェントのエントリーポイントは、呼び出し元から任意のペイロードを受け取ります。処理する前に入力を検証してください。

@app.entrypoint
def invoke(payload, context):
    prompt = payload.get("prompt", "")

    # Validate input
    if not prompt or not isinstance(prompt, str):
        return {"error": "Missing or invalid 'prompt' field"}
    if len(prompt) > 10000:
        return {"error": "Prompt exceeds maximum length (10,000 characters)"}

    # Sanitize — strip control characters, excessive whitespace
    prompt = " ".join(prompt.split())

    # Proceed with validated input
    result = agent(prompt)
    return {"response": str(result)}

検証項目:

  • 必須フィールドが存在し、想定される型であること
  • 文字列入力が妥当な長さの上限を超えていないこと(モデルへのトークン爆弾を防止します)
  • 数値入力が想定される範囲内であること
  • ユーザー指定の ID(actor_id、session_id)が想定される形式に一致すること

レート制限: AgentCore Runtime には、組み込みの呼び出しレート制限があります(エージェントあたりのデフォルトは 25 TPS です。詳細は references/limits.md を参照してください)。アプリケーションレベルのレート制限(ユーザーごと、テナントごと)を適用する場合は、エージェントコード自体ではなく、呼び出し側のアプリケーションまたは API Gateway レイヤーで実装してください。リクエストがエージェントに到達するまでに、すでにレート制限されているものとしてエージェントを設計してください。


シークレット:コードに認証情報を記述せず、ランタイム環境変数にシークレットを設定しない

確認すべき失敗パターンは 2 つあります。

1. エージェントコード内のハードコードされたシークレット
# Search for common secret patterns in agent code
grep -r "sk-\|api_key\s*=\s*['\"]" app/ --include="*.py"
grep -r "password\s*=\s*['\"]" app/ --include="*.py"
2. ランタイム環境変数から取得するシークレット

AgentCore Runtime の環境変数は、Vault による保護の対象ではありません。開発者がランタイムの環境変数に設定する値(CDK、boto3 の UpdateAgentRuntime などを介して設定したもの)は、プレーンテキストの設定値であり、シークレットではありません。次のパターンを監査してください。

# Flag any os.getenv / os.environ call whose name implies a secret
grep -rE "os\.(getenv|environ).*(TOKEN|SECRET|KEY|PASSWORD|CREDENTIAL)" app/ --include="*.py"

プラットフォームから注入されるシークレットではない識別子は問題ありません。許可リストに一致させないでください(例:MEMORY_*_ID、AGENTCORE_GATEWAY_*_URL、AWS_REGION、下流エージェントの ARN)。検出箇所を確認し、シークレットが含まれていないことを確認してください。

正しいパターン: 各外部接続用の認証情報を agentcore add credential で登録し、統合された認証情報プロバイダーを介してコード内で取得してください。

from bedrock_agentcore.identity.auth import requires_api_key, requires_access_token

@requires_api_key(provider_name="MyAPI")
def call_api(payload: dict, *, api_key: str) -> dict:
    ...

@requires_access_token(provider_name="MyOAuthProvider", scopes=["read"], auth_flow="M2M")
async def call_downstream(data: dict, *, access_token: str) -> dict:
    ...

デコレーターは呼び出し時に Secrets Manager から取得し、キャッシュと更新を処理します。この方法で登録した認証情報は保存時に暗号化され、再デプロイなしでローテーションされます。

ローカル開発: agentcore/.env.local(gitignore 対象)は agentcore dev によって読み込まれるため、デコレーターはローカルで認証情報を解決できます。このファイルはデプロイ時にランタイムへアップロードされません。本番環境の認証情報は認証情報プロバイダーに保存してください。


ツールの範囲:エージェントコードからの直接 HTTP 呼び出しより Gateway ターゲットを優先する

関連する監査として、エージェントが呼び出すすべての外部サービスについて、エージェントコード内に埋め込まれた直接 HTTP 呼び出しではなく、Gateway ターゲットにすべきかを確認してください。Gateway の認証情報プロバイダーはエッジで認証情報を注入するため、エージェントプロセスがシークレットを目にすることはありません。ツールカタログにはポリシーを適用でき、エージェントコードからトレースバックやログ行が漏えいしても、エージェントに渡されていない認証情報が流出することはありません。

# Find direct outbound HTTP calls in agent code
grep -rEn 'httpx\.|requests\.|aiohttp\.' app/ --include="*.py"

該当するものごとに、次のように判断します。

該当するもの対応
エージェントがツールとして扱う外部 REST API を呼び出しているGateway ターゲットとして前面に配置します(agentcore add gateway-target --type open-api-schema または api-gateway)。agents-connect/SKILL.md のパス C を読み込みます。
MCP サーバーを直接呼び出しているGateway ターゲットとして前面に配置します(--type mcp-server)。agents-connect/SKILL.md のパス A を読み込みます。
AWS サービス(S3、DynamoDB など)を呼び出している。この行に該当させるのは適切ではなく、boto3 を使うべきですrequests/httpx から boto3 クライアントに移行し、IAM にはランタイムの実行ロールを使用します。認証情報は不要です。
ストリーミングサービス(ライブ出力を伴う SSE、WebSocket、WebRTC)を呼び出している直接呼び出しのままで構いません。Gateway はまだこれらを前面に配置できません。認証には os.getenv ではなく @requires_* が使われていることを確認します。
A2A 経由で別のエージェントを呼び出している直接呼び出しのままで構いません。A2A は設計上 HTTP を使用します。ベアラートークンには @requires_access_token が使われていることを確認します。
チームが選択した、測定済みのレイテンシー重視のホットパスを呼び出している構いませんが、測定が実施されており、認証に @requires_* が使われていることを確認します。

該当するものが「直接呼び出しのままで構わない」行に当てはまらない場合は、Gateway ターゲットに変換するチケットを起票してください。ほとんどのフレームワーク統合では、エージェントのコードを変更せずに Gateway ターゲットを追加できます(MCP ツールの検出によってバインドが処理されます)。


可観測性:トレースが有効になっていることを確認する

AgentCore は X-Ray トレースと CloudWatch ログを自動的に有効にします。次の項目を確認します。

agentcore status --runtime <AgentName> --json | jq '.runtimes[0].observabilityConfig'

CloudWatch ダッシュボード: AWS コンソール → CloudWatch → GenAI Observability → Bedrock AgentCore

ログの保持期間: デフォルトでは、ログは無期限に保持されます。コストを管理するため、保持ポリシーを設定します。

aws logs put-retention-policy \
  --log-group-name /aws/bedrock-agentcore/runtimes/<AGENT_ID>-DEFAULT \
  --retention-in-days 30

評価のベースライン:リリース前に品質を把握する

本番環境に移行する前に、デグレードを検出できるよう品質のベースラインを確立します。

# Run a baseline eval
agentcore run eval \
  --evaluator "Builtin.Helpfulness" \
  --evaluator "Builtin.GoalSuccessRate"

# Set up continuous monitoring
agentcore add online-eval \
  --name production_monitor \
  --runtime <AgentName> \
  --evaluator "Builtin.Helpfulness" \
  --sampling-rate 5
agentcore deploy -y

ベースラインのスコアを記録します。変更後にスコアが大幅に低下した場合は、作業を続ける前に原因を調査してください。


ネットワーク:プライベートリソースには VPC を使用する

エージェントがプライベートな AWS リソース(RDS、内部 API)にアクセスする場合は、VPC を設定します。

agentcore add agent \
  --name MyAgent \
  --network-mode VPC \
  --subnets subnet-abc,subnet-def \
  --security-groups sg-123

VPC の設定方法について詳しくは、完全なガイダンスが記載されている agents-build(references/vpc.md を読み込みます)を参照してください。


初期化時間:コールドスタートのパフォーマンスを最適化する

エージェントの初期化に時間がかかると、タイムアウトや 424 エラーが発生し、ユーザー体験が損なわれます。特に、しばらく非アクティブだった後の最初の呼び出しで顕著です。リクエストを処理できる状態になるまでにエージェントが行うすべての処理が、ユーザーの待ち時間に加算されます。

コールドスタート時間の内訳

新しい環境での一般的なコールドスタートには、約 20~30 秒かかります。おおよその内訳は次のとおりです。

  • コンテナイメージのプル — コンテナビルドでは、これが大部分を占めます。100 MB のイメージなら数秒ですが、500 MB のイメージでは 15 秒以上かかることがあります。
  • アプリケーションの起動 — コードのインポート時間、フレームワークの初期化、モジュールレベルのセットアップです。通常は 5~10 秒ですが、インポート時にモデルを読み込んだり接続を開いたりすると、さらに時間がかかることがあります。
  • プラットフォームのオーバーヘッド(microVM の起動、ネットワークの接続、コンテナの起動)— 1 秒未満から数秒程度です。

制御できるのは、イメージサイズとアプリケーションの起動時間の 2 つです。どちらかを最適化すれば、最初の応答までの時間を直接短縮できます。

セッションの再利用は、最も効果の大きい最適化です

同一セッション内のリクエストは、初期化済みの既存環境にルーティングされます。コールドスタートは発生しません。セッションごとの最初のリクエストにはコールドスタートのコストがかかりますが、同じセッションでのそれ以降のリクエストは高速です。

具体的なパターン:

  • 複数ターンの会話: ターン間で同じ session_id を再利用します。ターンごとに新しい UUID を生成しないでください。
  • バッチ処理: バッチ内の項目間で同じ session_id を再利用します。
  • ユーザー向けアプリ: セッションはユーザーとのやり取り単位(例:チャットの会話ごとに1つのセッション)に設定し、メッセージごとには設定しないでください。

SDK 間の注意点:MCP を使用する場合は、runtimeSessionId と mcpSessionId の両方を同時に渡すのではなく、セッション識別子を1つ渡してください。両方を送信すると、プラットフォームが同じ論理セッションに対して別々の環境を2つ割り当て、コールドスタートのコストが2倍になることがあります。

パッケージサイズの予算

デプロイパッケージが1 MB増えるごとに、コールドスタート時間も長くなります。

  • 目標: 200 MB未満を目指します。可能であれば100 MB未満を目指してください。
  • Container ビルドの場合: マルチステージ Dockerfile、軽量または distroless のベースイメージを使用し、ビルドツールやテストファイルを削除して、.dockerignore を追加します。
  • CodeZip ビルドの場合: pyproject.toml / requirements.txt から開発用依存関係を削除します。tests/、docs/、.git/、ローカルキャッシュは含めないでください。
  • 定期的に監査する: pip list(Python)または npm ls(Node)で、実際にインストールされているものを確認できます。使用していないものはすべて削除してください。
重い初期化は後回しにする

大規模なモデルの読み込み、データベースへの接続、MCP クライアントの初期化を、モジュールのインポート時に行わないでください。モジュールのインポートに費やす1秒ごとに、エージェントがリクエストに応答できるまでの時間も1秒長くなります。

# ❌ Slow — runs at import time, before the agent can handle requests
import heavy_library
client = heavy_library.Client(config)

# ✅ Fast — defers until first request
_client = None
def get_client():
    global _client
    if _client is None:
        import heavy_library
        _client = heavy_library.Client(config)
    return _client
デフォルトではなく、トラフィックパターンに応じてデプロイタイプを選ぶ

以前、このスキルでは可能な場合に Container より CodeZip を推奨していました。これは単純化しすぎた説明です。実際のトレードオフは次のとおりです。

  • CodeZip: 反復開発が容易で、対象範囲も小さくなります。コールドスタートにはコードのダウンロードと展開が含まれます。約95 MBのパッケージでは、アプリケーションの起動が始まる前に、プラットフォームによるダウンロードだけで約1.3秒かかります。
  • Container: イメージ全体を制御でき、カスタムのシステム依存関係が必要な場合に適しています。イメージが大きいほどコールドスタートごとのコストは増えますが、マルチステージビルドを使って積極的に最適化できます。

常に優れている方はありません。どちらも、セッションの再利用とパッケージの小型化によって同じようにメリットを得られます。トラフィックパターンに、コールドスタートが頻発する短時間のセッションが多い場合は、使用しているデプロイ成果物の縮小に取り組んでください。トラフィックパターンでセッションを再利用する場合、デプロイタイプの重要性は大幅に下がります。

Gateway の背後にある Lambda を対象とする場合

Lambda のコールドスタートをなくすには、Lambda 関数でプロビジョニングされた同時実行を使用してください。これは Runtime の初期化とは別のものです。コールド状態の Lambda への初回呼び出しでレイテンシーを追加するのは Lambda 自体です。


セッションのライフサイクル管理

セッション管理は、コスト、パフォーマンス、maxVms クォータと密接に関係しています。適切に管理できるかどうかが、本番環境へのスムーズなリリースと、クォータによるブロックの分かれ目になることがよくあります。

デフォルトのライフサイクル

新しいセッション ID を含むリクエストが届くと、ランタイムはそのセッション用に新しい環境を初期化します。その環境は、次のいずれかが発生するまで稼働し続けます。

  1. セッションが明示的に停止される(StopRuntimeSession を使用)。
  2. アイドルタイムアウトが経過する。 idleRuntimeSessionTimeout(デフォルトは900秒)の間リクエストを受け取っていない環境は、ランタイムによって回収されます。
  3. 最大有効期間に達する(maxLifetime、デフォルトは8時間)。

アイドル状態の環境はトラフィックを処理していなくても、回収されるまでは maxVms クォータを消費します。これが、予期しない maxVms エラーの最大の原因です。

ワークロードの形に合わせてタイムアウトを選ぶ

本番環境でデフォルト値のままにしないでください。実際のセッションの使い方に合った値を選びましょう。

ワークロードidleRuntimeSessionTimeoutmaxLifetime理由
対話型チャット/サポートエージェント600~900s(デフォルト)3600~7200sユーザーが読んだり考えたりするために一時停止します。離れた後はすぐに解放します。
フォローアップのないリクエスト/レスポンス API60~120s1800s各呼び出しは独立しています。VM をすぐに解放します。
バッチ処理、ジョブごとに 1 セッション120sジョブの長さ+バッファーに合わせるバッチ内の項目間のアイドル時間は短いため、ジョブ間で積極的に解放します。
バックグラウンド/長時間実行タスク(add_async_taskを使用)120~300s最大 28800s(8 時間)非同期タスク API は、追跡中の作業の間 VM を稼働状態に保ちます。アイドルタイムアウトはタスク間に適用されます。

トレードオフの概要:

  • アイドルタイムアウトが短い = maxVms の上限に対する余裕が増え、コストが下がります。リスク: 会話の途中で解放され、次のターンがコールドスタートになる可能性があります。
  • アイドルタイムアウトが長い = ウォーム状態のターンになり、レイテンシーが下がります。リスク: アイドル状態の VM がクォータを消費し、急増時に maxVms エラーが発生します。
  • 最大ライフタイムが短い = 予測可能な再作成が行われ、メモリーリークや古い状態を抑制できます。リスク: アクティブな長時間セッションがフローの途中で終了します。
  • 最大ライフタイムが長い = セッションが維持され、ウォーム状態による大幅な節約が得られます。リスク: 状態のずれや古いインメモリー状態が生じ、段階的なロールアウトが難しくなります。
ベストプラクティス

作業が完了したらStopRuntimeSessionを呼び出します。 エージェントがタスクを完了し、そのセッションへの追加リクエストを想定していない場合は、明示的に停止してください。アイドルタイムアウトを待たずに、環境をすぐに解放できます。

# After your invocation logic completes and you know the session is done:
client.stop_runtime_session(
    agentRuntimeArn=runtime_arn,
    runtimeSessionId=session_id,
)

関連する作業ではセッション ID を再利用します。 HTTP リクエストごとに新しいセッション ID を使うと、HTTP リクエストごとに新しい環境が作成されます。複数ターンの会話、バッチジョブ、ユーザーとの対話では、会話/バッチ/ユーザーとの対話ごとに 1 つのセッション ID を使い、関連するすべてのリクエストをその ID にルーティングしてください。

ワークロードに合わせてidleRuntimeSessionTimeoutを調整します。 デフォルトの 900 秒は、すぐにフォローアップのリクエストがあると想定される対話型ワークロードに適しています。セッションが短時間で終了するリクエスト/レスポンス型のワークロードでは、値を小さくしてください。

agentcore/agentcore.jsonのランタイムのエントリーを編集します。

{
  "runtimes": [
    {
      "name": "MyAgent",
      "lifecycleConfiguration": {
        "idleRuntimeSessionTimeout": 120,
        "maxLifetime": 3600
      }
    }
  ]
}

次にagentcore deployを実行して適用します。CLI と CDK が、内部のUpdateAgentRuntime呼び出しを処理します。

CLI を使う場合は、agentcore add agent ... --idle-timeout 120 --max-lifetime 3600を実行すると、同じフィールドがagentcore.jsonに書き込まれます。このファイルが信頼できる唯一の情報源です。ファイル内の各フィールドは、ファイル先頭の$schema URL(https://schema.agentcore.aws.dev/v1/agentcore.json)を通じて IDE の自動補完を利用できます。

タイムアウトを短くするほど、VM の解放が速くなり、maxVmsの上限に対する余裕が増えます。短すぎると、会話の途中で環境が解放され、次のターンがコールドスタートになります。

runtimeSessionIdとmcpSessionIdを同時に渡さないでください。 MCP エージェントでは、どちらか一方を使用します。両方を渡すと、同じ論理セッションに 2 つの別個の VM が紐付けられる可能性があります。

maxVmsの問題の診断

ServiceQuotaExceededException: maxVms limit exceededが発生した場合、まずクォータの引き上げを申請しないでください。CloudWatch の同時セッション数メトリクスは、稼働中の VM 数と同じではありません。アイドル状態の環境も、解放されるまではクォータを消費します。

次の順に対応してください。

  1. 論理リクエストが完了するたびにStopRuntimeSessionを追加します
  2. セッション ID の生成を見直します。再利用すべきリクエストごとに、新しい ID を作成していませんか?
  3. セッションの存続時間が短い場合は、idleRuntimeSessionTimeoutを短くします
  4. 上記すべてを実施しても上限に達する場合にのみ、引き上げを申請します

引き上げ申請の手順(Service Quotas コンソールを使用)と申請理由のテンプレートについては、references/limits.mdを参照してください。


長時間実行されるバックグラウンドタスク

エージェントが /invocations のレスポンス後も続く処理(バックグラウンド処理、非同期ジョブ、長時間のツールチェーンなど)を開始する場合、呼び出し元に結果を返した後は何もしないパターンでは不十分です。呼び出しのレスポンスが送信されると、ランタイムはセッションをアイドル状態と判断するため、バックグラウンドタスクの実行中であっても、環境は idleRuntimeSessionTimeout の時点で解放される可能性があります。

SDK の非同期タスク API を使用して「処理中」であることを通知する

bedrock-agentcore SDK には、追跡対象の処理が実行されている間、環境を稼働状態に保つタスク登録機能があります。Python の場合:

from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload, context):
    # Register the task BEFORE starting it
    task_id = app.add_async_task("background_work")

    # Kick off the work (in a thread, asyncio, etc.)
    start_background_work(task_id, payload)

    # Return the invocation response — the task is still tracked
    return {"status": "processing", "taskId": task_id}


def start_background_work(task_id, payload):
    try:
        # Long-running work here
        do_the_work(payload)
    finally:
        # Mark the task complete when done — this releases the "busy" signal
        app.complete_async_task(task_id)

if __name__ == "__main__":
    app.run()

登録済みのタスクが 1 つ以上アクティブな間、ランタイムは環境を稼働中とみなし、idleRuntimeSessionTimeout の時点では解放しません。ただし、maxLifetime(デフォルトは 8 時間)は引き続き上限として適用されます。

お使いの言語で同等の API を利用するには、bedrock-agentcore SDK のドキュメントを確認してください。TypeScript SDK にも同様のパターンがあります。

非同期タスク API を利用できない場合の代替策
  • idleRuntimeSessionTimeout を想定するタスクの所要時間に合わせて延長します。 タスクが最大 10 分かかるとわかっている場合は、タイムアウトを 12 分に設定します。maxLifetime を大きく下回る値にしてください。
  • ストリーミングレスポンスで HTTP 接続を開いたままにし、 定期的なハートビートイベントを送信します。ポーリングではなく、呼び出し元に結果が返るまで待ってもらいたい場合に便利です。agents-debug/SKILL.md の「接続がストリームの途中で切断される」セクションにある SSE キープアライブのパターンを参照してください。
  • 長時間の処理を複数回の呼び出しに分割し、 同じセッションで実行します。呼び出しのたびにアイドル時間がリセットされます。

クォータと制限

スロットリング、ServiceQuotaExceededException、その他のクォータ関連エラーが発生している場合、またはクォータによって起動が妨げられないよう事前に確認したい場合は、references/limits.md を読み込んでください。

このリファレンスでは、次の内容を説明しています。

  • 各エラーに対応するクォータ
  • 引き上げを申請する前に試すべき緩和策(重要:ほとんどの「クォータ」エラーは、実際にはセッションのライフサイクルに関する問題です)
  • Service Quotas コンソールから引き上げを申請する方法(直接 Support ケースが必要となる例外的なケースはまれです)
  • 審査担当者が承認に必要とする情報をすべて含んだ、コピーして使える申請理由のテンプレート

本番環境チェックリストの概要

プロジェクトに合わせたチェックリストを生成します。

Production Readiness Checklist for <AgentName>

IAM
[ ] Execution role Bedrock access scoped to specific model ARNs
[ ] ECR access scoped to specific repository
[ ] Trust policy scoped to your account ID

Authentication
[ ] Inbound auth is AWS_IAM or CUSTOM_JWT (not NONE)
[ ] If CUSTOM_JWT: discovery URL, audience, and client IDs configured

Shell Access (if using InvokeAgentRuntimeCommand)
[ ] InvokeAgentRuntimeCommand permission granted only to identities that need it
[ ] Separate IAM policy from InvokeAgentRuntime policy
[ ] CloudTrail / EventBridge alert configured for InvokeAgentRuntimeCommand calls
[ ] If commands constructed from user input: shell injection validation implemented

Code quality
[ ] Error handling wraps all agent logic
[ ] Input validation on payload fields (type, length, format)
[ ] No secrets hardcoded in agent code
[ ] Credentials registered via agentcore add credential

Observability
[ ] X-Ray tracing enabled (auto-configured)
[ ] CloudWatch log retention policy set
[ ] Eval baseline established

Performance
[ ] Agent initialization time measured and optimized
[ ] Deployment package size under 200 MB (target under 100 MB)
[ ] Dependencies audited — no unused packages
[ ] Heavy initialization deferred to request time
[ ] Session reuse strategy chosen for multi-turn / batch workloads
[ ] `StopRuntimeSession` called after work completes where applicable
[ ] `idleRuntimeSessionTimeout` tuned to workload (default 900s)
[ ] For long-running background tasks: `add_async_task` / `complete_async_task` used

Resources
[ ] Memory strategies appropriate for use case (if using memory)
[ ] Gateway auth configured (if using gateway)
[ ] Policy engine attached (if restricting tool access)

Testing
[ ] Agent tested with production-representative inputs
[ ] Error cases tested (tool failures, model errors)
[ ] Memory cross-session tested (if using LTM)

出力

  • プロジェクト固有の指摘を含むチェックリスト
  • 検出された問題を修正するための具体的なコマンド
  • 検出されたモデルとリソースに推奨される IAM ポリシー

コメント