0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

買い忘れ防止 LINE アプリを作った【4/4】— Strands Agent デプロイ・LINE 通知完成編

0
Posted at

第3章で AgentCore Gateway を作りました。最終章では、エージェントコードを書いて AgentCore Runtime にデプロイし、iPhone の位置情報トリガーと LINE プッシュ通知まで繋げてアプリを完成させます。
※第3章へのリンク
https://qiita.com/toshi-arch/items/f5bfdb388c29a4a95334


Step 1. エージェントコードの作成

依存パッケージのインストール:

uv add bedrock-agentcore strands-agents mcp-proxy-for-aws python-dotenv

main.py:

import os
from dotenv import load_dotenv
from mcp_proxy_for_aws.client import aws_iam_streamablehttp_client
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from bedrock_agentcore.runtime import BedrockAgentCoreApp

load_dotenv()
GATEWAY_URL = os.environ['GATEWAY_URL']  # 第3章の setup_gateway.py の出力値

app = BedrockAgentCoreApp()


@app.entrypoint
def run_agent(payload: dict, context) -> dict:
    store_name = payload.get('store_name', '')

    mcp_client = MCPClient(
        lambda: aws_iam_streamablehttp_client(
            endpoint=GATEWAY_URL,
            aws_service='bedrock-agentcore',
        )
    )

    with mcp_client:
        tools = mcp_client.list_tools_sync()

        agent = Agent(
            model=BedrockModel(model_id='jp.anthropic.claude-haiku-4-5-20251001-v1:0'),
            tools=tools,
            system_prompt=f"""
あなたは買い物アシスタントです。
ユーザーが「{store_name}」に近づいています。

以下の手順でツールを呼び出し、今日買うべきものを提案してください。
1. search_purchase_history で「{store_name}」の購買履歴を取得する
2. calculate_consumption で購入提案リストを生成する
3. 提案を自然な日本語でまとめる(箇条書き、100文字以内)
""",
        )

        result = agent(f'{store_name}で今日買うべきものを教えて')

    return {'message': str(result)}


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

ローカルで動作確認

# ターミナル1
GATEWAY_URL=https://xxx.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp \
  uv run python main.py

# ターミナル2
curl -X POST http://localhost:8080/invocations \
  -H "Content-Type: application/json" \
  -d '{"store_name": "イオン"}'

Step 2. AgentCore Runtime へのデプロイ

CLI のインストール

uv add bedrock-agentcore-starter-toolkit

⚠️ ハマりポイント: デプロイ CLI は pip の bedrock-agentcore-starter-toolkit です。npm の @aws/agentcore とは別物で仕様が異なります。

configure

uv run agentcore configure \
  --entrypoint main.py \
  --execution-role arn:aws:iam::[ACCOUNT_ID]:role/SmartShopperAgentCoreRuntimeRole \
  --requirements-file requirements.txt \
  --deployment-type container \
  --name smartShopperAgent \
  --region ap-northeast-1 \
  --non-interactive

⚠️ ハマりポイント: エージェント名は英数字とアンダースコアのみ、48文字以内。ハイフンが含まれるとエラーになります。

configure を実行すると .bedrock_agentcore/smartShopperAgent/Dockerfile が自動生成されます。

Dockerfile に GATEWAY_URL を追記

ENV GATEWAY_URL=https://[gateway-id].gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp

deploy

uv run agentcore deploy

デフォルトは CodeBuild モード(AWS 上でコンテナをビルドするため、ローカルの Docker は不要)。完了すると Agent Runtime ARN が表示されます。

⚠️ ハマりポイント: --deployment-type CodeZip にすると pip install がコールドスタート時に走り「Runtime initialization time exceeded」でタイムアウトします。必ず container を使いましょう。

動作確認

uv run agentcore invoke '{"store_name": "イオン"}'

.gitignore に追加

.bedrock_agentcore.yaml
.bedrock_agentcore/

これらにはアカウント ID が平文で含まれます。


Step 3. 位置情報トリガーと LINE プッシュ通知

既存の LINE Webhook Lambda(第1章で作ったもの)に、位置情報トリガーの受け口・AgentCore 呼び出し・LINE プッシュ通知を追加します。

import json, uuid, os, boto3
from linebot.v3.messaging import (
    Configuration, ApiClient, MessagingApi,
    PushMessageRequest, TextMessage
)

AGENT_RUNTIME_ARN = os.environ.get('AGENT_RUNTIME_ARN')
configuration = Configuration(access_token=LINE_ACCESS_TOKEN)  # 第1章の設定を流用


def lambda_handler(event, context):
    body = event.get('body', '')
    parsed = json.loads(body) if isinstance(body, str) else (body or {})

    # 位置情報トリガー
    if parsed.get('trigger_type') == 'location_trigger':
        store_name = parsed.get('store_name', '')
        user_id = parsed.get('user_id', '')
        if not store_name or not user_id:
            return {'statusCode': 400, 'body': 'store_name and user_id are required'}
        return handle_location_trigger(store_name, user_id)

    # 既存の LINE Webhook 処理(第1章のコード)...


def handle_location_trigger(store_name: str, user_id: str) -> dict:
    try:
        message = invoke_agent(store_name)
    except Exception as e:
        print(f"AgentCore 呼び出しエラー: {e}")
        return {'statusCode': 500, 'body': 'Agent invocation failed'}

    try:
        send_push_message(user_id, message)
    except Exception as e:
        print(f"LINE プッシュ通知エラー: {e}")
        return {'statusCode': 500, 'body': 'LINE push notification failed'}

    return {'statusCode': 200, 'body': 'OK'}


def invoke_agent(store_name: str) -> str:
    """AgentCore Runtime を呼び出してエージェントの回答を取得する"""
    client = boto3.client('bedrock-agentcore', region_name='ap-northeast-1')
    response = client.invoke_agent_runtime(
        agentRuntimeArn=AGENT_RUNTIME_ARN,
        qualifier='DEFAULT',
        runtimeSessionId=str(uuid.uuid4()),
        payload=json.dumps({'store_name': store_name}),
    )
    raw = response['response']
    body = raw.read() if hasattr(raw, 'read') else raw
    return json.loads(body).get('message', '提案が取得できませんでした。')


def send_push_message(user_id: str, message: str) -> None:
    """LINE Messaging API でプッシュ通知を送る"""
    with ApiClient(configuration) as api_client:
        MessagingApi(api_client).push_message(
            PushMessageRequest(
                to=user_id,
                messages=[TextMessage(text=message)]
            )
        )

Lambda の環境変数に AGENT_RUNTIME_ARN(Step 2 で表示された ARN)を追加。


Step 4. iOS オートメーションの設定

iPhone の「ショートカット」アプリで位置情報トリガーを設定します。

  1. 「ショートカット」→「オートメーション」→「+」→「場所」
  2. 店舗を地図で指定 →「到着」を選択
  3. アクション:「URLの内容を取得」
    • URL: API Gateway の Webhook エンドポイント
    • 方法: POST
    • 本文(JSON):
      {
        "trigger_type": "location_trigger",
        "store_name": "イオン",
        "user_id": "あなたのLINEユーザーID"
      }
      
  4. 「実行の前に確認」→ オフ

完成したフロー

iPhone が「イオン」の半径内に入る
  → ショートカットが API Gateway に POST
  → Lambda が trigger_type を検知
  → AgentCore Runtime を invoke
      → Strands Agent が推論開始
      → Gateway 経由で search_purchase_history を呼び出す
          → S3 から「イオン」の購買 JSONL を全件取得
      → Gateway 経由で calculate_consumption を呼び出す
          → 消費サイクルを計算して提案リストを生成
      → エージェントが自然な日本語でまとめる
  → Lambda が LINE プッシュ通知を送信
  → iPhone に「今日はシャンプーと牛乳を買うといいです」が届く

全4章 ハマりポイント総まとめ

# 症状 原因と対処
① Lambda リソースポリシーで権限エラー setup_gateway.py より前に aws lambda add-permission を実行
② Gateway が作れない / 後で認証エラー create_gateway に authorizerType='AWS_IAM' が必須
③ ステータス待ちが終わらない 待つべきは READY(ACTIVE では永久ループ)
④ ツールが呼び出されない targetConfiguration は {'mcp': {'lambda': {...}}} とネストが必要
⑤ ツールが常に Unknown tool Gateway は ターゲット名___ツール名 形式で渡す。___ でプレフィックスを除去
⑥ agentcore configure でエラー エージェント名にハイフン不可(英数字とアンダースコアのみ)
⑦ コールドスタートでタイムアウト --deployment-type container を使う。CodeZip は起動時に pip install が走る
⑧ npm の agentcore CLI で詰まる デプロイは pip の bedrock-agentcore-starter-toolkit。npm 版は別物

おわりに

全4章を通じて、LINE でレシートを送ると購買履歴が記録され、店舗に近づいたら購入提案が届くアプリを作りました。
IPhoneのオートメーションでHTTP request送れるっていうのが意外でしたね。全く知らんかったです。LINE Messaging API使うことでほぼバックエンド側のサーバーレス構成組めてコストも抑えられたのもよかったです。

そしてAgentCore を使うことで、エージェントのコンテナビルド・ホスティング・IAM 認証を自前で実装せずに済みました。一方でドキュメントがまだ薄く、特に Gateway 周りは試行錯誤が必要でした。同じところで詰まっている方の助けになれば幸いです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?