きっかけ
これまで、Bedrock Agentを使って、AIエージェントを遊びで作ってみたりしていました。
(同窓会の出欠確認用サイトを作った際に、出欠の回答状況をLambda経由でDBを確認して回答するAIエージェントを作成した)
ところが、AWS Summit 2026に行った際、どこもかしこも、Bedrock AgentCore、Strands Agent、…という感じで、時代に取り残されている感を感じたのがきっかけです。
気づいたら、Bedrock Agentは新規ユーザの利用はできなくなっていますね・・・
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/agents-how.html
Amazon Bedrock エージェント (2023 年 11 月リリース) は「Amazon Bedrock Agents Classic」となり、
2026 年 7 月 30 日以降、新規のお客様は利用できなくなります。
Bedrock Agents Classic と同様の機能については、Amazon Bedrock AgentCore をご覧ください。
Bedrock Agents Classic を使用する場合は、その日より前にサインアップします。
既存のお客様は、通常どおりサービスを引き続き使用できます。
詳細については、「Amazon Bedrock エージェント Classic メンテナンスモード」を参照してください。
作ってみたもの
- 野球選手の年別データを持ったAurora DSQLに野球選手のデータを確認する
- Managed KnowledgeBaseに格納された野球のルールを確認する
- Managed KnowledgeBaseに格納された野球の戦術を確認する
- Cognitoグループがtigersのユーザには全球団の情報を、carpのユーザにはカープだけの情報を返す
- 巨人に有利な情報を返さない
なお、生成AIも手助けに使いつつ、自分の理解のために、生成AIに全部書かせるようなことはしないようにしました。
デモ
- ログインします。
- Cognito Managed Loginです。
- ショート2人、4番中野という前衛的なオーダーを返してくれました。
- システムプロンプトだったり、オーダーの組み方のルールをRAGに置くだったりをするべきかもしれませんが・・・いったん返ってきたのでよしとします。
思ったこと
-
Bedrock Agentでもツール群は使わせることはできた(Target)のですが、リクエストを受けた後は生成AIに任せることになり、確定的な動きはさせることができないため、
アプリからAgentに投げるまでの間に確定的な処理は行ったうえで投げて、ツールのLambdaの中でまた確定的な処理は書く必要がありますが、
そのあたりをStrands AgentのPythonの中で完結できるのは楽だなと思いました。- 前処理でユーザ属性を確認しています
@app.entrypoint async def invoke_agent(payload, context): # frontend から送られた token を受け取る token = payload.get("token") session_start_time = payload.get("sessionStartTime") # tokenから、ユーザ情報を取得 try: user = get_user(token) user_sub = user.get("sub") user_group = user.get("cognito:groups", []) except Exception as e: print(f"Error in get_user: {e}") return # ユーザがtigersに所属しているか確認 tigers = False carp = False for g in user_group: if g == "tigers": tigers = True if g == "carp": carp = True- そのあと、Tool群を定義して(後述)
- Agentを呼び出します
agent = Agent( model=model, tools=[ search_baseball_players, search_player_position, search_player_batter_stats, search_player_pitcher_stats, get_team_mst, get_position_mst, search_player_by_team_and_year, search_player_by_position_and_year, get_streamable_http_mcp_client() ], session_manager=session_manager, system_prompt=system_prompt ) # エージェントをストリーミング呼び出し stream = agent.stream_async( payload.get("prompt") )- Agentに呼び出されたTool内で、また確定的な処理をします(ここはCarp用とTigers用にToolを分けて、Identityで認可を行うほうが良いのかもしれません)
@tool def search_baseball_players(first_name: str = "", last_name: str = ""): """ # 概要 野球選手(プロ野球選手)のデータをDBから検索するツール。 #引数 - 姓(last_name) : 部分一致、未指定可 - 名(first_name) : 部分一致、未指定可 # 戻り値 JSON形式で返される - player_id : 選手ID - first_name : 名 - last_name : 姓 - team_id : 所属チームID - team_name : 所属チーム名 - start_year : 在籍開始年 - end_year : 在籍終了年 - ren_no : 一年の間で移籍した場合の連番 """ query = """ SELECT pm.player_id, pm.first_name, pm.last_name, pt.team_id, tm.name as team_name, pt.start_year, pt.end_year, pt.ren_no FROM player_mst as pm INNER JOIN player_team as pt ON pm.player_id = pt.player_id LEFT JOIN team_mst as tm ON pt.team_id = tm.team_id WHERE 1=1 """ params = {} if carp and not tigers: # カープファンの場合は、検索結果をカープに強制絞り込み query += " AND pt.team_id = %(carp_id)s" params["carp_id"] = carp_id elif tigers: # タイガースファンは何でも見れる pass else: return "検索権限がありません。" if first_name: query += " AND pm.first_name LIKE %(first_name)s" params["first_name"] = f"%{first_name}%" if last_name: query += " AND pm.last_name LIKE %(last_name)s" params["last_name"] = f"%{last_name}%" print(f"Executing query: {query}") print(f"With params: {params}") rows = execute_query_with_logging(query, params) result_list = [] if rows: for row in rows: result_list.append({ "player_id": row[0], "first_name": row[1], "last_name": row[2], "team_id": row[3], "team_name": row[4], "start_year": row[5], "end_year": row[6], "ren_no": row[7] }) return result_list -
ここが目玉なのかな、と思いますが、Memoryなど、いろんな機能があって便利だなと思いました。
- 下記のようにしています
session_id = f"{user_sub}_{session_start_time}" memory_config = AgentCoreMemoryConfig( memory_id=memory_id, session_id=session_id, actor_id=user_sub )session_manager = AgentCoreMemorySessionManager( agentcore_memory_config=memory_config )
- 下記のようにしています
ストリーミングについて
今回、フロントエンド側にもストリーミングでレスポンスが表示されるようにしました。
が、その中で、API Gatewayの時間制限(30秒)に引っかかったためクォータ申請をしたのですが、
SAMが30秒以上は対応しておらず、ここは手でマネジメントコンソールから更新せざるを得ませんでした。
InvokeAgentRuntimeServerlessFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${ResourceNamePrefix}-lambda-invoke-agent-runtime"
Runtime: nodejs22.x
Handler: index.handler
Role: !GetAtt LambdaInvokeAgentRuntimeRole.Arn
Timeout: 179 ##ここで179秒としても、API Gatewayのタイムアウトが30秒から動かない
CodeUri: ../../app/invoke-runtime/
Environment:
Variables:
AGENT_ARN: !Ref AgentCoreArn
Events:
AgentApi:
Type: Api
Properties:
RestApiId: !Ref ServerlessApi
Path: /agent
Method: post
ResponseTransferMode: RESPONSE_STREAM
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
EntryPoints:
- index.ts
追加で書きたいことがあれば追記します。




