はじめに
Amplify Gen 2 と Amazon Bedrock AgentCore で、業務で使える AWS 運用アシスタントを作ってみました。ブラウザのチャットから自然言語で AWS リソースを問い合わせ・操作できるアプリです。コーディングは Kiro に全面的にお任せしています。
まずは結論から
- AWS MCP Server を Web に立てて標準化。ローカルの MCP 設定なしで、誰でもブラウザから AWS を自然言語で操作できる
- 複数の IAM ロールをセッション単位で選択。アカウントを横断した調査・比較ができる
- 操作の境界を守っているのは IAM ロールの権限そのもの。デプロイは
git pushの1系統
想定される読者
- AWS の運用を AI エージェントで効率化したい方
- 複数アカウントをまたいだ運用をしている方
- AI エージェントの権限設計に悩んでいる方
背景
最近のちょっとした悩み
業務で取り扱う AWS アカウントがだんだん増えてきました。アカウントが増えると、トラブルやセキュリティ対応、AWS からの通知で調査する機会もそれなりに増えてきます。
個人的には Kiro に AWS MCP Server の設定をして、自然言語で調査するのを便利に使っているんですが、チームの AWS 運用者も同じように使えるようにできないか? と思い、Web アプリにできないか試してみました。
本当はローカルAIで十分
AWS MCP Server を使う一番手軽な方法は、手元の AI エージェント(Kiro や Claude Code など)の設定ファイルに MCP サーバーを登録することです。個人で使うならこれが最短ルートで、むしろセットアップできる人にはベストな選択です。
チームで使おうとすると、事情が変わる
ただ、これをチームの運用ツールにしようとすると課題が出てきます。
まず 環境構築が属人的になります。各自の端末の設定はバラバラで、ローカルAIの習熟度もそれぞれ異なるため、チーム展開が難しい。
また、実際に AWS アカウントに繋ぐ権限のコントロールが大変です。利用者のローカル認証情報を使ってしまうと権限が強すぎる可能性がありますし、複数アカウントをまたいだ操作や、意図したアカウントへの接続など、利用者が相当意識して権限をコントロールする必要があります。
じゃあ Web アプリにしてみよう
Web に立てて 使えるロールを管理画面で登録する形にすれば、ブラウザとログインアカウントだけで全員が同じ環境を使えて、権限の管理も一箇所に集約できます。だからこそ、Web アプリとして提供する価値がある、と判断しました。
システム構成
ということで、今回作成した AWS 運用アシスタントについて紹介していきます。
画面イメージ
良くあるチャットボットの画面構成となっております。セッション管理で過去の履歴から会話を再開できたり、エージェントからの回答に簡単な Generative UI 機能を取り入れて、図や表を使った可視化機能も取り入れています。
アーキテクチャ図
AWSアーキテクチャ図はこんな感じです。
メインアカウント(図の Account A)は3つのブロックに分かれています。
| ブロック | 内容 |
|---|---|
| Amplify Hosting | Next.js の配信と SSR。サーバーサイド処理は /api/roles だけ |
| Auth & Data | Cognito / AppSync / DynamoDB。Amplify Gen 2 が作るバックエンド |
| AgentCore | Runtime / Memory と、そこから伸びる AssumeRole の経路 |
図の右側にある Account B は、エージェントが操作する別アカウントです。クロスアカウントでの操作もできる構成にしています。
経路は3系統あります。
①チャットのストリーミング(図のオレンジ太線)
ブラウザ → 中継 Lambda(関数 URL)→ AgentCore Runtime と流れる、メインの経路です。
- ブラウザは Cognito の JWT を
Authorization: Bearerで中継 Lambda に送ります - 中継 Lambda が SigV4 署名を付けて AgentCore Runtime を
InvokeAgentRuntimeで呼びます - Runtime 上の Strands Agent が Bedrock(既定は Claude Sonnet 5)で推論し、AG-UI プロトコルの
text/event-streamで応答を返します - 応答はそのままブラウザまでストリーミングで流れます
ブラウザから AgentCore Runtime を直接叩く経路はありません。 SigV4 署名と Cognito トークンの署名検証は、この中継 Lambda の中だけで行われます。
②画面データの取得(図のピンク線・破線)
チャット以外の画面データは、Amplify Gen 2 の標準的な経路です。
- セッション一覧・ロール管理・フィードバックは AppSync(GraphQL)経由で DynamoDB へ
- 例外は
/api/rolesで、ここだけ Next.js の SSR が DynamoDB を直接 Scan します(roleArnを除外して返すため) - 会話履歴の復元は中継 Lambda の
/memory/eventsから AgentCore Memory のListEventsを辿ります
③AWS 操作と AssumeRole(図の青太線)
エージェントが AWS を触るときの経路です。
- Runtime がセッションで選択されたロールに
sts:AssumeRole - 得た一時認証情報で AWS MCP Server に SigV4 接続し、AWS サービスを操作
- 同一アカウントのロールなら、対象ロールの信頼ポリシーに Runtime 実行ロールを追加するだけで有効になります(コード変更も再デプロイも不要)
- 別アカウントのロールを使う場合は、Runtime 実行ロール側のポリシーにも追記が必要です
AgentCore Runtime と Memory は Amplify バックエンドスタックの一部です。同一スタックなので、テーブル名・Runtime ARN・Memory ID・IAM ポリシーの Resource がすべて synth 時に解決されます。詳細は後ほど。
主な機能一覧
| 機能 | 説明 |
|---|---|
| AWS 操作チャット | 自然言語で AWS リソースを問い合わせ・操作。裏側は AWS MCP Server |
| ロール選択 | セッション開始時に使用する IAM ロールを複数選択。呼び出しごとにエージェントが選ぶ |
| ロール管理画面 | 管理者(Cognito ADMINS グループ)が利用可能なロールを追加・編集・無効化 |
| チャット履歴 | サイドバーから過去のセッションを再開。履歴の正は AgentCore Memory |
| Generative UI | 数量的な回答を棒グラフ・折れ線・円グラフ・表として描画 |
| 画像入力 | ファイル選択とクリップボード貼り付けに対応。エラー画面のスクショを貼って質問できる |
| Good/Bad フィードバック | 回答への評価とコメントを収集し、全ユーザー横断の集計画面で確認 |
| 基本ツール(tools) | 現在時刻取得・JST 変換・経過時間計算・四則演算・バイト数の単位変換など、AWS 操作の補助に使うユーティリティ |
業務ツールとして必要そうなものは一通り入れました。履歴が残ることも、Good/Bad を集計できることも、「チームで使う」前提だと意味が出てきます。
技術スタック
| レイヤー | 技術 |
|---|---|
| フロントエンド | Next.js 15(App Router) + TypeScript + React 19 |
| バックエンド | AWS Amplify Gen 2(Cognito / AppSync / DynamoDB) |
| エージェント UI | CopilotKit(@copilotkit/react-core/v2) + AG-UI プロトコル |
| エージェント | Python 3.13 / Strands Agents SDK + ag-ui-strands
|
| LLM | Amazon Bedrock(既定は Claude Sonnet 5) |
| エージェント実行基盤 | Amazon Bedrock AgentCore Runtime / Memory |
| AWS 操作 | AWS MCP Server(mcp-proxy-for-aws) |
| ホスティング | Amplify Hosting |
| テスト | Vitest + fast-check / pytest + ruff |
| IDE 支援 | Kiro |
AG-UI は、エージェントとフロントエンドの間でイベント(テキストの断片、ツール呼び出し、ツール結果など)をやり取りするプロトコルです。CopilotKit がこのプロトコルのクライアント側を担当してくれるので、ストリーミング表示やツールカードの描画を自分で作らなくて済みます。
データモデル
Amplify Gen 2 の amplify/data/resource.ts に定義しているモデルは3つです。
RoleConfig の認可を ADMINS グループのみにしているのがポイントです。
| モデル | 認可 | 内容 |
|---|---|---|
| ChatSession | allow.owner() |
セッション名・更新日時・選択したロール名の配列 |
| RoleConfig | allow.group("ADMINS") |
ロール定義(表示名・アカウントラベル・ARN・スコープ・有効フラグ) |
| MessageFeedback | 所有者は CRUD、認証済みユーザーは read | Good/Bad と任意コメント |
権限設計と操作の境界
今回、いちばんの肝となるロール管理についてです。
ロールの追加(ADMINS 専用)
エージェントに付与するロールを管理できる管理者は、ADMINS グループに属する人に限られます。管理者は画面右上の「ロール設定管理」からロールの追加・編集・無効化ができます。
ロール設定可能な管理者ユーザは、Cognito の ADMINS グループに手動で追加します。

ロール設定で登録する項目は次のとおりです。
| 項目 | 説明 |
|---|---|
| 表示名 | ユーザーに見せるロールの名前(例: 「メイン垢読み取り」「サブ垢管理者」) |
| アカウントラベル | どのアカウントのロールか識別する文字列(例: main_account、sub_account) |
| ロール ARN | AssumeRole 先の IAM ロールの ARN |
| 操作スコープ | 読み取り専用 / 読み書き / 管理者 |
一般ユーザー向けのロール一覧は GraphQL API を通さず、/api/roles という Route Handler が DynamoDB を直接 Scan して返します。このとき roleArn は必ず除外しているので、ロールの ARN や AWS アカウント ID が非管理者に渡る経路が設計上存在しません。ユーザーに見えるのは表示名・アカウントラベル・操作スコープだけです。
RoleConfig の削除は isActive を false にする論理削除です。物理削除して同じ名前で作り直せるようにすると、過去セッションの復元時に文字列照合が別のロールに紐づいてしまう危険があるためです。
セッション開始時の設定
画面でロールを選んでセッションを始める
新規チャットを始めるとき、ダイアログで使用するロールを選びます。各ロールは 操作スコープ(読み取り専用 / 読み書き / 管理者)を持っています。
エージェントが持てる権限は、ここに登録されたロールの範囲だけです。
管理者がロールを追加しても過去のセッションには影響しません(セッション作成時に選んだロールの組み合わせは固定されるため)。ロールを無効化した場合、そのロールは新規チャットの選択肢に表示されなくなりますが、過去セッションの履歴は引き続き閲覧できます。
複数アカウントを横断して AWS を操作する
選べるロールはセッションにつき1つではありません。新規チャットのダイアログで複数のロールを選ぶと、そのセッションではその一群(ロールセット)が使えます。ロールの登録項目にはアカウントラベル(「メイン」「サブ」など)があるので、メインアカウントの読み取り専用ロールとサブアカウントの読み取り専用ロールを一緒に選んでおく、といった使い方ができます。
それぞれのアカウントで最近作られた S3 バケットを教えて
こうお願いすると、エージェントはそれぞれのアカウントで適切なロールを使って AWS を操作します。1回の質問の中で複数のアカウントに同時にアクセスできるのが、運用でいちばんやりたかったことです。
しっかりと最近作られたバケット情報を取得することができました。

また、以下のように、書き込み権限を持つロールを設定することで、S3 バケットの作成をお願いすることも可能です。

AssumeRole の設定方法
エージェントが AWS を操作するには、Runtime 実行ロールから対象ロールへの sts:AssumeRole が通る必要があります。設定は IAM 側の作業だけで、コードの変更も再デプロイも不要です。
同一アカウントの場合
対象ロールの信頼ポリシーに Runtime 実行ロールを追加するだけです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ACCOUNT_ID>:role/<Runtime 実行ロール名>"
},
"Action": "sts:AssumeRole"
}
]
}
クロスアカウントの場合
クロスアカウントでは両方向の設定が必要です。
① 接続先アカウント側(ターゲットロール)の信頼ポリシー:
他アカウントの Runtime 実行ロールからの AssumeRole を許可します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<メインアカウントID>:role/<Runtime 実行ロール名>"
},
"Action": "sts:AssumeRole"
}
]
}
② メインアカウント側(Runtime 実行ロール)の IAM ポリシー:
接続先アカウントのロールを AssumeRole する権限を付与します。
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::<接続先アカウントID>:role/AgentMCPReadOnlyRoleForMainAcc",
"arn:aws:iam::<接続先アカウントID>:role/AgentMCPAdminRoleForMainAcc"
]
}
設定を追加したら、アプリのロール管理画面から接続先のロール ARN を登録するだけで使えるようになります。
エージェントの動作
ここからは、エージェント内部の動きについてです。
ツール呼び出しごとにロールを選んで AssumeRole する
セッションで選択されたロール名は X-Role-Names ヘッダーとして Runtime に渡ります。エージェントは BeforeToolCallEvent フックでツール呼び出しごとにどのロールを使うかを解決し、sts:AssumeRole で一時認証情報を取得してから AWS MCP Server に接続します。
複数ロールが選ばれている場合は、ツールのスキーマに role_name 引数を差し込む RoleSelectingToolWrapper が動き、モデルが呼び出しごとに適切なロールを指定します。
シーケンスで示すとこうなります。
複数ロールを使うときは、モデルが結果を混同しないような手当ても必要でした。成功したツール結果の先頭にどのロールで実行したかを注記する(AfterToolCallEvent)ようにして、「並列実行した複数ロールの結果を混同して同じ内容を取得し続ける」という挙動を抑えています。
操作の境界を守っているのは IAM ロールの権限
エージェントが AWS を操作するとき、実際に呼ばれるツールは aws___call_aws という単一の汎用ツールです。mcp-proxy-for-aws が公開している AWS CLI 相当のツールで、読み取りも書き込みも削除もすべて同じツール名です。コマンド文字列だけが違います。
そのため、エージェント内のスコープ強制フックはツール名からは操作の危険度を判別できません。読み取り専用スコープのセッションで「バケットを作って」と依頼すると、フックはツール呼び出しを通してしまいます。実際に拒否するのは AssumeRole 先の IAM ロールの権限です。
TOOL_CALL_START aws___call_aws ← スコープフックは通過する
TOOL_CALL_RESULT AccessDenied: assumed-role/AgentMCPReadOnlyRole/mcp-agent-readonly
is not authorized to perform: s3:CreateBucket
層に分けて整理するとこうなります。
| 層 | 役割 | 実効性 |
|---|---|---|
| 画面のロール選択 | セッションで使えるロールの限定 | ユーザーの選択に依存 |
| スコープ強制フック | ツール名で判別できる範囲、ロール未選択時の拒否(確実性は無し) | 汎用ツールの内部操作は判別不能 |
| IAM ロールの権限 | 実際の AWS API 呼び出しの許否 | 唯一の確実な境界 |
読み取り専用で使わせたいロールには、必ず読み取り専用の IAM 権限だけを付けてください。スコープの登録値を readonly にすることは、UI 上の表示とフックによる補助的な抑止であって、権限の制限そのものではありません。
ロールの切り替えは MCP サブプロセスの再起動を伴う
mcp-proxy-for-aws は stdio のサブプロセスとして起動します。AWS の認証情報はサブプロセス起動時に環境変数で渡す仕組みのため、ロールを切り替えるにはサブプロセスの再起動が必要です。
ここで問題になるのが、複数ロールを使うセッションです。エージェントがロール A でツールを呼び出している最中にロール B への切り替え(stop → start)が走ると、実行中のツール呼び出しが Connection to the MCP server was closed で失敗します。
# 概念的にはこういう流れ(gateway/manager.py)
def ensure_role(self, role_name: str) -> None:
if role_name == self._current_role:
return # 同じロールなら何もしない
self._wait_in_flight() # 実行中の呼び出しが終わるまで待つ
self._client.stop() # サブプロセスを止める
creds = assume_role(role_name) # boto3 sts:AssumeRole
self._reassign_transport(creds) # 新しい認証情報でトランスポートを作り直す
self._client.start() # 起動し直す
対処として in-flight カウンタを持たせて、ロール変更時はカウンタがゼロになるまで待つようにしました。カウンタのデクリメントは AfterToolCallEvent フックで行っています。同一ロールへの並列呼び出しは並列に流れて、直列化されるのは異なるロールへの切り替えを挟む場合だけです。
バックエンドの設計ポイント
ここからはバックエンドの処理で特筆すべき点の紹介です。
中継サーバーとしての独立Lambda
そもそもなぜ中継サーバーが要るのか
AG-UI プロトコルでストリーミング配信するだけなら、ブラウザから AgentCore Runtime を直接叩けば中継は要りません。ただし今回は CopilotKit をフロントエンドの UI ライブラリに使っており、CopilotKit のアーキテクチャ上、ブラウザとエージェントの間にサーバーサイドの CopilotRuntime(中継サーバー)を置く必要があります。
加えて、セキュリティの観点でもサーバーサイドは外せません。AgentCore Runtime の呼び出しには SigV4 署名が必要で、その署名に使う IAM 認証情報をブラウザに持たせるわけにはいかないためです。Cognito トークンの署名検証もここで行っています。
中継サーバー自体は Lambda でなくても(Express、Next.js Route Handler など)構いません。
Next.js Route Handler ではストリーミングできなかった
当初は Next.js の Route Handler(/api/copilotkit)で中継していました。ローカルの npm run dev では普通にストリーミングで流れるので問題に気づかなかったのですが、Amplify Hosting にデプロイすると応答がバッファリングされる問題にぶつかりました。
原因は、Amplify Hosting の SSR Compute が awslambda.streamifyResponse() を有効化しないことです。AgentCore Runtime からの text/event-stream が全部溜まってから一括で返ってきていました。
独立 Lambda + 関数 URL で解決した
解決策は、Node.js マネージドランタイム上で awslambda.streamifyResponse() をネイティブに使う独立 Lambda を立てて、Lambda 関数 URL(InvokeMode: RESPONSE_STREAM)として公開することでした。
この Lambda に寄せた責務は次のとおりです。
- Cognito トークンによる認証ゲート
- AgentCore Runtime 向けの SigV4 署名
- セッションヘッダー(
X-Role-Namesなど)の伝播 - 過去セッションの履歴取得(
GET /memory/events)
デプロイは既存のフローに統合されていて(amplify/backend.ts の defineBackend に含まれる)、別のコマンドや CDK スタックは不要です。
関数 URL のセキュリティについて
Lambda 関数 URL は認証タイプを NONE にすると URL を知っている人なら誰でも叩ける公開エンドポイントになるため、一般的にはセキュリティ上の懸念から利用を避ける場面もあります。
今回は関数 URL の認証タイプこそ NONE ですが、Lambda のコード内で aws-jwt-verify を使って Cognito JWT の署名を検証しています。有効な JWT を持たないリクエストはここで弾かれるので、未認証のアクセスはできません。AWS レベルの認証(IAM_AUTH)ではなくアプリケーションレベルで認証を行っている形です。
IAM_AUTH を使わなかった理由は、CopilotKit のクライアントがブラウザから直接この URL を叩く構成のため、ブラウザに IAM 認証情報を持たせたくなかったからです。Bearer トークン(Cognito JWT)を送るだけで認証が完結する設計にしています。
CORS は Lambda 関数 URL 側(cors: { allowedOrigins: ["*"] })に一本化しています。CopilotKit Runtime 側のヘッダー付与は cors: { origin: [] } で無効化しました。両方が付くとブラウザが重複ヘッダーで CORS エラーになるためです。オリジン制限ではなく JWT 検証で認証しているので、["*"] でも問題ありません。
会話履歴と AgentCore Memory
発言本文(ユーザー発言・アシスタント応答)の唯一の正は AgentCore Memory に置いています。actor_id に Cognito の sub、session_id に ChatSession.id を使ってスコープしています。
DynamoDB の ChatSession が持つのはメタデータ(セッション名・更新日時・選択したロール)だけです。発言本文を二重管理しない形にしました。
過去セッションを開いたときの履歴復元は、中継 Lambda の専用経路から読み出します。
GET {functionUrl}/memory/events?sessionId=...
ListEvents の nextToken を全ページ辿って古い順に整列して返し、フロントエンドは選択したセッションの分だけ遅延取得します。ツール呼び出し(toolUse)と実行結果(toolResult)からツールカードも再構築されるので、リロードしても「どのツールが何を返したか」まで復元されます。
セキュリティ面では、JWT の署名検証に加えて、ChatSession.ownerUserId と認証済みユーザーの actor_id を照合し、不一致なら ListEvents を発行せず 403 を返します。他ユーザーの履歴を読み出せないようにする二重チェックです。
Memory には長期記憶戦略(semantic_facts、SEMANTIC)も有効化しているので、actor_id 単位でセッションをまたいだ記憶抽出ができます。短期記憶の保持期間は 365 日です。
Memory は RemovalPolicy.RETAIN にしています。Runtime はコードから作り直せますが、Memory の中身は作り直せません。スタックの削除やロールバックで会話履歴が消えるのを避けるためです。Amplify アプリを削除しても Memory は残るので、不要になったら手動で削除してください。
AgentCore Gateway をやめた理由
当初は AgentCore Gateway を経由して AWS MCP に接続していたのですが、tools/list は成功するのに tools/call が "Tool invocation failed" で失敗する問題にぶつかりました。
Gateway を経由せず、Runtime から mcp-proxy-for-aws で AWS MCP に直接 SigV4 接続する形に変更して解決しました。
mcp_client = build_aws_mcp_client(
endpoint=AWS_MCP_ENDPOINT, # https://aws-mcp.us-east-1.api.aws/mcp
region=AWS_MCP_REGION, # us-east-1
)
agent = Agent(model=load_model(), tools=[mcp_client])
AWS MCP エンドポイントは us-east-1 固定です。これは AWS MCP というサービス自身のエンドポイントで、デプロイ先リージョンとは別物です。1つのエンドポイントから全リージョンのリソースを操作できるので、別リージョンにデプロイしても変更は不要です。
UI/UX の作り込み
Generative UI(グラフ・表の描画)
「リージョンごとの EC2 インスタンス数」のような数量的な答えは、テキストで並べられるより図で見たいところです。そこで、エージェント側に emit_visualization というツールを持たせています。
エージェントがこのツールを呼ぶと、検証・正規化されたペイロードが AG-UI の TOOL_CALL_RESULT としてフロントに流れ、対応するチャートや表が描画されます。棒グラフ・折れ線・円グラフ・データ表の4種類に対応しています。
@tool(name="emit_visualization")
def emit_visualization(payload: dict[str, Any]) -> dict[str, Any]:
"""Emit a chart or table for the frontend to render as Generative UI."""
if not is_valid_visualization(payload):
return {
"status": "error",
"content": [{"text": "Invalid Visualization_Payload: ..."}],
}
normalized = normalize_visualization(payload)
return {
"status": "success",
"content": [
{"json": {"name": VISUALIZATION_EVENT_NAME, "value": normalized}},
{"text": json.dumps(normalized, ensure_ascii=False)},
],
}
ペイロードが不正な場合はツールエラーとして返すので、モデルが自分で修正して呼び直せます。フロントエンドが壊れた描画をするのではなく、エージェント側で自己修正が回るようにしたかった部分です。フロント側もスキーマ検証に失敗したら文字列でのフォールバック表示に切り替えます。
画像入力
エラー画面のスクリーンショットを貼って質問できるように、画像入力にも対応しました。ファイル選択とクリップボード貼り付け(Ctrl+V / Cmd+V)の両方が使えます。
S3 などのオブジェクトストレージは使わず、AG-UI プロトコル上で base64 インライン画像として送っています。ただし Amplify Hosting の SSR Lambda には約 6MB のトランスポート制限があるので、制限を段階的に置いています。
- 1枚あたり 3MB まで
- 1メッセージ合計 3MB まで
- 1メッセージ 3枚まで
加えて、同じ会話の2ターン目以降で過去の画像バイナリを再送しないようにしています。会話が続くほどリクエストが膨らんで制限を超える、という事故を避けるためです。
Good/Bad フィードバック
CopilotKit 標準の Good/Bad ボタンに実際の挙動を実装して、MessageFeedback に保存しています。

認可の設計はこうしました。
MessageFeedback: a
.model({ /* ... */ })
.authorization((allow) => [
allow.owner().to(["create", "read", "update", "delete"]),
allow.authenticated().to(["read"]),
]),
read だけを認証済みユーザー全体に開放しているので、誰でも全ユーザー横断の集計画面を見られる一方、作成・更新・削除は所有者だけです。なりすまし投稿や他人の評価の改ざんはできません。
フィードバックの集計画面はこんな感じ。とりあえず組み込んでみただけですが、実運用が始まったら意外と使えるかも?

今後の改善ポイント
2つ考えています。
1つ目は ユーザーごとに使えるアカウント/ロールを絞る仕組みです。現状は登録済みのロールが全ユーザーに見える設計なので、業務で本格的に使うなら「このユーザーには本番ロールは見せない」といった制御が必要です。やり方としては、Cognito の ADMINS グループと同じ仕組みでロールごとにグループを増やし、ユーザーの所属グループに基づいてロール一覧をフィルタする形がやりやすそうです。今回のリポジトリには入れていませんが、/api/roles のフィルタ条件を1つ足すだけなので、拡張はしやすい構成にはなっています。
2つ目は 長時間調査のタイムアウト対策です。広範囲な調査(複数アカウントを横断してリソースを洗い出すなど)を依頼すると、中継 Lambda の 15 分制限に引っかかって途中で途切れることがあります。10 分程度で一旦会話を区切って中間報告を返し、続きは次のターンで再開するような仕組みを取り入れたいと思っています。エージェントのシステムプロンプトで時間を意識させるか、AgentCore 側のタイムアウト検知でイベントを挟むか、やり方はまだ検討中です。
まとめ
今回は、業務で本当に使える AWS 運用アシスタントを目指して、新しい AI エージェントを作ってみました。個人利用の延長ではなく、チームで使える AWS 運用アシスタントにしたかった、というのが今回の狙いとなります。
エージェント自体はそんなにすごくないです。AWS MCP Server につないで、複数ロールを AssumeRole できるようにしただけ、とも言えます。でも、使える形にすると業務の武器になる。そんなことを目指してひとつのエージェントとして形にしてみました。
今回の AI エージェントはテンプレートとして使えるように GitHub にソースを公開しているので、よければ覗いてみてください。










