コンテンツにスキップ

第13章 フルスタックエージェントを構築しよう — 学習メモ

書籍「Amazon Bedrock AgentCore実践入門」第13章のサンプルコード(このフォルダ)を読み解いた個人学習メモ。 AgentCore 全体像は ../../lectures/agentcore_basics/STUDY_NOTES.md 参照。 実行検証は伴わない。コードに現れた API 名だけ断定し、読めない挙動は「〜と推測」で明示する。

一言で

新刊チェッカー BookChecker = AgentCore の5機能(Runtime・Memory・Identity・Built-in Browser・Observability)を1つの Agent() に束ねて、Next.js/Amplify のフロントから叩く、ハンズオン全4章中もっとも機能を濃く積んだ章。

Gateway は使われない(agentcore.jsonagentCoreGateways / httpGateways は空配列、credentials も空配列)。ツールは「組み込み Browser」と「自作の Google カレンダー登録ツール(内部で Identity を使う)」の2つだけで、外部ツール集約の出番がないため。

全体像

flowchart TB
    U["ユーザー<br>ブラウザ"]
    COG["Cognito Authenticator<br>(providers.tsx)"]
    FE["Next.js 16 + Amplify<br>page.tsx"]
    RT["AgentCore Runtime<br>main.py invoke()"]
    AG["Strands Agent<br>Sonnet 4.6"]
    MEM["Memory<br>好み検索/保存<br>(4戦略・USER_PREFERENCE使用)"]
    BRW["Built-in Browser<br>新刊カレンダー巡回"]
    IDN["Identity<br>requires_access_token<br>(3LO)"]
    GCAL["Google Calendar API"]
    OBS["Observability<br>OTel自動計装(ADOT)"]

    U --> COG --> FE
    FE -->|"JWT + prompt<br>SSE POST"| RT
    RT --> AG
    AG --> MEM
    AG --> BRW
    AG -->|"カレンダー登録時"| IDN
    IDN -->|"未認可なら認可URLを<br>event_queue経由でSSE配信"| FE
    FE -->|"認可URLをクリック"| GCAL
    GCAL -.->|"3LOコールバック"| FE
    FE -->|"CompleteResourceTokenAuthCommand"| RT
    IDN -->|"認可完了後トークン取得"| GCAL
    RT -.->|"全実行を計装"| OBS

3LO(three-legged OAuth)の認可URL配信は、テキスト応答・ツール実行状況と同じ asyncio.Queue を共有して1本のSSEストリームに乗る。これが本章でいちばん凝った設計なので、シーケンスで別出し:

sequenceDiagram
    participant User as ユーザー
    participant FE as Next.js (page.tsx)
    participant RT as Runtime (main.py invoke)
    participant Agent as Strands Agent
    participant CalTool as calendar_tool
    participant Google as Google Calendar API

    User->>FE: 「登録して」
    FE->>RT: POST /invocations (JWT, prompt, session_id)
    RT->>Agent: agent.stream_async(prompt)
    Agent->>CalTool: add_calendar_event(...) 呼び出し
    CalTool->>CalTool: requires_access_token(...) デコレータ実行
    CalTool-->>RT: 未認可 → on_auth_url(url) → event_queue.put({"type":"auth_url"})
    RT-->>FE: SSEで auth_url イベント配信
    FE-->>User: 「Google連携を開始」ボタン表示
    User->>Google: ボタンクリック→OAuth同意
    Google-->>FE: /api/oauth2/callback?session_id=... へリダイレクト
    FE->>RT: CompleteResourceTokenAuthCommand(sessionUri, userToken)
    Note over CalTool: 認可完了でaccess_tokenが注入され<br>call_api()が再開・完了
    CalTool->>Google: POST /calendars/primary/events
    Google-->>CalTool: 登録結果
    CalTool-->>Agent: 成功/失敗メッセージ
    Agent-->>FE: テキスト応答(SSE)

使用ライブラリ・原理

ライブラリ / API 役割
strands (strands-agents) Agent クラス。tools=[...]system_promptsession_manager を渡すだけでReActループとメモリー連携が組み上がる
strands_tools.browser.AgentCoreBrowser AgentCore の組み込みBrowserサンドボックスをStrandsツール化。.browser 属性をそのまま tools=[] に渡す
bedrock_agentcore.runtime.BedrockAgentCoreApp Runtime機能。@app.entrypoint でHTTPエンドポイント化、app.run() でASGIサーバ起動
bedrock_agentcore.memory.integrations.strands.* Memory機能のStrands統合。AgentCoreMemoryConfig(設定)+ RetrievalConfig(名前空間ごとの検索設定)+ AgentCoreMemorySessionManager(Strandsのsession_managerインターフェース実装)
bedrock_agentcore.identity.requires_access_token Identity機能。デコレータがOAuthトークン取得・3LOフロー起動・トークン注入を肩代わり
asyncio.Queue テキスト・ツール実行状況・認可URLという種類の異なる3つのイベントを1本のストリームに合流させるための素朴な仕組み。生産者(agent_stream()タスクとcalendar_tool内の_on_auth_url)と消費者(invoke本体のwhileループ)が非同期に協調する
Next.js 16 (App Router) + React 19 フロントエンド。app/page.tsxがクライアントコンポーネントでSSEを手動パース
AWS Amplify Gen2 (defineBackend/defineAuth) Cognitoユーザープール定義とデプロイをコード化
@aws-amplify/ui-react Authenticator ログインUI一式(サインアップ・サインイン・パスワードリセット)を丸ごと提供
aws-amplify/auth fetchAuthSession ログイン中ユーザーのCognito JWT(アクセストークン)を取得し、Runtime呼び出しのAuthorization: Bearerに使う
@aws-sdk/client-bedrock-agentcore CompleteResourceTokenAuthCommand 3LOコールバックでAgentCore側のセッションバインディングを完了させるAPI(TypeScript側からも叩ける)
aws-opentelemetry-distro (ADOT) Observability機能。Dockerfileの起動コマンドをopentelemetry-instrument python -m mainにするだけでコード変更なしに自動計装

ファイル別の役割

ファイル 役割
bookchecker/agent/app/BookChecker/main.py エントリポイント。5機能を束ねる中心。SSE用イベントキューの生成・消費
bookchecker/agent/app/BookChecker/calendar_tool.py Identity(3LO)経由でGoogle Calendar APIにイベント登録するツール。make_calendar_tool(event_queue)でキューを注入
bookchecker/agent/app/BookChecker/memory/session.py 未使用のCLI雛形main.pyは同等のロジックを独自にインライン実装しており、こちらは呼ばれない(後述の落とし穴参照)
bookchecker/agent/app/BookChecker/model/load.py 未使用のCLI雛形load_model()global.anthropic.claude-sonnet-4-5-20250929-v1:0を返すが、実際のmain.pyAgent(model="us.anthropic.claude-sonnet-4-6")と文字列で直接指定しており呼ばれない
bookchecker/agent/app/BookChecker/mcp_client/client.py 未使用のCLI雛形(14行)。Gatewayを使わない本章では出番なし
bookchecker/agent/app/BookChecker/Dockerfile Container build。opentelemetry-instrumentでObservabilityを自動計装、非rootユーザーで起動
bookchecker/agent/app/BookChecker/pyproject.toml 依存パッケージ定義(bedrock-agentcorestrands-agentsstrands-agents-toolsplaywright等)
bookchecker/agent/agentcore/agentcore.json AgentCoreの宣言的スキーマ。Runtime定義(Container/PYTHON_3_14)とMemory定義(4戦略)。agentcore deployが読む正本
bookchecker/agent/agentcore/cdk/lib/cdk-stack.ts agentcore.jsonAgentCoreApplication/AgentCoreMcpのL3コンストラクトに橋渡しする薄いラッパー
bookchecker/agent/AGENTS.md agentcore createが生成するAIアシスタント向けコンテキスト(フラットリソースモデルの説明)
bookchecker/amplify/auth/resource.ts defineAuth({loginWith: {email: true}})。Cognitoのメールログインのみ定義
bookchecker/amplify/backend.ts defineBackend({auth})。Amplifyバックエンドは認証のみ(データ層はrm -rf amplify/dataで削除済み)
bookchecker/app/providers.tsx Amplify.configure(amplifyOutputs) + Authenticatorでアプリ全体をラップ、UIを日本語化
bookchecker/app/page.tsx チャットUI本体。JWT取得→Runtime呼び出し→SSEパース→text/tool_use/tool_result/auth_urlの4種イベントをUIに反映
bookchecker/app/api/set-token/route.ts CognitoトークンをHttpOnly Cookieに保存するRoute Handler(3LOコールバック時に読み出すため)
bookchecker/app/api/oauth2/callback/route.ts Googleからの3LOリダイレクト先。CookieのトークンでCompleteResourceTokenAuthCommandを実行しセッションを完了
handson-memo.txt ハンズオン中にメモするAWSリソースID等のテンプレ(プレースホルダのみ、実値は書かない)

中心コードの読み解き

main.py:49-69 — Memory設定からAgent生成までの、本章のハイライト(3機能が1呼び出しに集約される瞬間):

    # メモリーの設定を定義
    memory_config = AgentCoreMemoryConfig(              # ①
        memory_id=MEMORY_ID,
        session_id=session_id,
        actor_id="user",
        retrieval_config={
            "/users/{actorId}/preferences": RetrievalConfig(),
        },
    )

    # メモリーのセッションマネージャーを作成
    session_manager = AgentCoreMemorySessionManager(     # ②
        agentcore_memory_config=memory_config,
    )

    # Strands AgentsでAIエージェントを作成
    agent = Agent(                                       # ③
        model="us.anthropic.claude-sonnet-4-6",
        tools=[browser.browser, calendar_tool],          # ④
        system_prompt=SYSTEM_PROMPT,
        session_manager=session_manager,                 # ⑤
    )
やってること なぜ
AgentCoreMemoryConfig(...) /users/{actorId}/preferences 名前空間だけを検索対象に設定 agentcore.json は4戦略(SEMANTIC/USER_PREFERENCE/SUMMARIZATION/EPISODIC)を定義しているが、実際にAgentが読むのは好み(USER_PREFERENCE)だけ。設定と実装に差がある(後述の落とし穴)
AgentCoreMemorySessionManager(...) Strandsのsession_managerインターフェースを満たすメモリー統合オブジェクトを生成 ③に渡すだけで会話の自動保存・関連記憶の自動取得が有効になる(呼び出し側は保存/取得APIを直接叩かない)
Agent(...) Strands Agent本体を生成 この1呼び出しにBrowser・Calendar(Identity内包)・Memoryの3機能が集約される、本章の核
tools=[browser.browser, calendar_tool] 組み込みBrowserツールと、Identity連携済みカレンダー登録ツールを渡す browserはAgentCore Built-in Browser、calendar_toolは内部でrequires_access_tokenを使うためIdentity機能を内包している。Agent側からは「ただのツール2つ」に見える
session_manager=session_manager ①②で組み立てたメモリー統合をAgentに接続 Strands統合の設計思想(「渡すだけで機能が足される」)がここでも成立

学んだこと(要点)

  • AgentCoreの複数機能は、Agentの引数(tools/session_manager)に「渡すだけ」で合成できる。Runtime・Memory・Identity・Built-in Browserという別々の機能が、コード上はAgent()のキーワード引数2つに収束している。
  • SSEの多重化はasyncio.Queueという素朴な仕組みで実現されている。テキスト・ツール実行状況・OAuth認可URLという性質の異なる3種のイベントを、生産者(ストリーミングタスク/Identityコールバック)と消費者(invokeのwhileループ)で協調させるだけで1本のストリームに合流できる。フレームワーク側の特別なAPIは不要。
  • Identity(3LO)は「トークンが無ければ認可URLを投げて中断、認可後に再開」というデコレータパターン@requires_access_tokenが付いた内側の関数はトークンが未取得なら実行が保留され、on_auth_urlコールバックで通知だけ行う。呼び出し側(calendar_tool)はこの中断・再開を意識せず、await call_api()を1回呼ぶだけで完結しているように書ける。
  • フロントのCookie+Route Handlerが3LOの「橋渡し役」。AgentCore Runtime自体はブラウザとの対話手段を持たないため、認可URLへの遷移とコールバックの受け皿はNext.js側(/api/set-tokenでJWTを事前にCookie保存 → /api/oauth2/callbackでそのCookieを使ってセッションバインディングを完了)が担う。AgentCoreとフロントの責務分担が明確。
  • Observabilityは「起動コマンドを変えるだけ」で入るDockerfileCMDopentelemetry-instrument python -m mainにするだけでADOTの自動計装が有効になり、アプリコードに計装コードを書く必要がない(この点はmain.pyにOTel関連のimportが一切無いことからも確認できる)。

落とし穴・デプロイ前提

  • handson-memo.txt(ランタイムARN・Cognito ID・コールバックURL等)は絶対にGitHubへpushしない。本ハンズオンの13.4節向けメモ帳で、プレースホルダのまま配布されている。
  • env変数: フロントはNEXT_PUBLIC_AGENT_ARN(Amplifyデプロイ時)、RuntimeはCALLBACK_URL / CREDENTIAL_PROVIDER_NAME / AWS_DEFAULT_REGION(AgentCoreランタイム側)が必須。
  • aws bedrock-agentcore-control update-workload-identity --allowed-resource-oauth2-return-urls をコールバックURL登録のために実行する必要がある(コード上には現れない、README記載のインフラ操作)。
  • AmplifyのSSRコンピュートロールにカスタム信頼ポリシーが必要amplify.amazonaws.comをPrincipalとするsts:AssumeRole)。
  • Google Cloud側でOAuthクライアントを作成し、AgentCore Identityにクレデンシャルプロバイダーとして事前登録しておくことが前提agentcore.jsoncredentials配列は空のままで、クレデンシャルプロバイダーはこのJSONスキーマの管理外(マネージドコンソール/CLIで別途作成)。
  • agentcore createが生成するmcp_client/model/load.pymemory/session.pyは雛形のまま未使用。特にmemory/session.pymain.pyより広い名前空間(facts/preferences/summaries)を検索する完成度の高いコードに見えるが、実際に呼ばれるのはmain.pyにベタ書きされた(preferencesのみの)ロジック。読者が「どっちが本物か」で迷いやすい箇所。
  • model/load.pyload_model()は古いモデルID(claude-sonnet-4-5-20250929)を返す未使用スタブ。実際に使われるのはmain.py"us.anthropic.claude-sonnet-4-6"
  • CLI @aws/agentcore@1.0.0-preview.8はプレビュー版。バージョンアップでコマンド互換性が変わる可能性がある。
  • Gatewayは本章で不使用agentCoreGateways/httpGatewaysとも空配列)。ツールはBrowserとカレンダー登録の2つのみで完結しており、複数ツールの集約が要らない規模のため。

記事参照

  • 書籍 第13章「【ハンズオン】フルスタックエージェントを構築しよう」。
  • 関連: ../../lectures/agentcore_basics/STUDY_NOTES.md(AgentCore全体像・8機能カタログ・既存lectureとの対応地図)。
  • 関連lecture: Identity/OAuthの基礎パターンは同メモ「3-3. Identity」節、Memoryの短期/長期の分離は「3-2. Memory」節を参照。

作成: 2026-07-17 / 最終更新: 2026-07-17