20
5

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 & Bedrockガードレールハンズオン!堅牢なChatBotを作る

Last updated at Posted at 2025-09-24

前書き

この記事は、LINE Developer Communityの Bedrockフル活用で一歩進んだBotを作ろう! というイベントのハンズオン手順書です。

Amazon Bedrockガードレールの機能を活かして、LINE Botを作る手順を記載しています。

オーバービュー

事前準備

AWSアカウント

ハンズオンでは数円の課金が発生するかもしれませんので、自己責任でお願いします。
IAMユーザーの発行方法につきましては、補足説明をご確認ください。

GitHubアカウント

サンプルコード用意してありますので、GitHub Codespaceを利用するためです。

LINE公式アカウント

LINE Messaging APIを利用するためです。申請方法は補足説明をご確認ください。

GitHub Codespacesの説明

今回のハンズオンで使用するリポジトリは以下の通りです。

画面右上の緑色ボタン Code > Create codespace on main

環境初期化の準備をお待ちください。必要なライブラリを一括でインストールするため、1分程度の時間が必要です。

Use Cmd/Ctrl + Shift + P -> View Creation Log to see full logs
✔ Finishing up...
⠸ Running postCreateCommand...
  › bash .devcontainer/setup.sh

初期化完了したら、.envファイルが作られます。
AWSの環境変数をセットアップしてください。

.env
AWS_ACCESS_KEY_ID=your_aws_access_key_id
AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key
AWS_DEFAULT_REGION=us-west-2
AWS_REGION=us-west-2

# AWS Session Token(必要に応じて)
AWS_SESSION_TOKEN=your_session_token

Amazon Bedrock AgentCore入門

Strands Agentsを使用して、簡単なAIエージェントを作成し、Bedrock AgentCoreにデプロイします。

まず、プロジェクト配下にあるagent_basic.pyをご確認ください。
これはStrands Agentsの基本形態で、AgentインスタンスにモデルのIDを渡して、LINE Developer Communityって何?を質問しています。

agent_basic.py
from dotenv import load_dotenv
from strands import Agent

load_dotenv()

agent = Agent("us.anthropic.claude-sonnet-4-20250514-v1:0")
agent("LINE Developer Communityって何?")

下記のコマンドで実行します。

 uv run agent_basic.py

環境変数の設定や権限に問題がなければ、
LINE Developer Communityは、LINEのAPIやサービスを活...のような出力がコンソール上で確認できます。

Bedrock AgentCoreランタイムへのデプロイ

agent.pyファイルをデプロイに使います。
このファイルは先ほどのagent_basic.pyと基本的に同じですが、AgentCoreのライブラリを追加してるのと、AIエージェントに渡すプロンプトもリクエストから渡せるようにしました。

agent.py
from dotenv import load_dotenv
from strands import Agent
from bedrock_agentcore.runtime import BedrockAgentCoreApp

load_dotenv()

agent = Agent("us.anthropic.claude-sonnet-4-20250514-v1:0")
app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
     """Process user input and return a response"""
     prompt = payload.get("prompt", "Hello")
     result = agent(prompt)
     return {"result": result.message}

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

下記のコマンドを実行し、AIエージェントの名前をlinebotにします。

# .env の内容をターミナルの環境変数に設定
export $(cat /workspaces/line-guardrail/.env | grep -v ^# | xargs)

# デプロイ準備
agentcore configure --entrypoint agent.py --name linebot

ウィザードで色々聞かれますが、全部Enterで大丈夫です。

🔐 Execution Role
Press Enter to auto-create execution role, or provide execution role ARN/name to use existing
Execution role ARN/name (or press Enter to auto-create):
✓ Will auto-create execution role

🏗️  ECR Repository
Press Enter to auto-create ECR repository, or provide ECR Repository URI to use existing
ECR Repository URI (or press Enter to auto-create):
✓ Will auto-create ECR repository

🔍 Detected dependency file: requirements.txt
Press Enter to use this file, or type a different path (use Tab for autocomplete):
Path or Press Enter to use detected dependency file:
✓ Using detected file: requirements.txt

🔐 Authorization Configuration
By default, Bedrock AgentCore uses IAM authorization.
Configure OAuth authorizer instead? (yes/no) [no]:
✓ Using default IAM authorization
Configuring BedrockAgentCore agent: linebot

実施完了したら、.bedrock_agentcore.yamlDockerfileがフォルダにできます。
中身を確認して、相違がなければ、下記のコマンドでデプロイを実施します。

agentcore launch

デプロイ完了後、下記の画像のように表示されます。
AGENT_RUNTIME_ARNの値を控えてください、LINE MessagingAPIと連携する際に使います。

D92473C7-624C-4F67-B4AB-A69AE68E21DD_4_5005_c.jpeg

また、AWSコンソールのAgentCoreエージェントランタイムで、linebotエージェントが作成されたことを確認できます。

0F5F6C42-B717-4EF0-A580-0FE214F720AD.jpeg

AgentCoreランタイムにデプロイされたAIエージェントを実行してみます。

agentcore invoke '{"prompt": "Hello"}'  

下記のレスポンスが表示するはずです。

Response:
{"result": {"role": "assistant", "content": [{"text": "Hello! How are you doing today? Is there anything I can help you with?"}]}}

LINE MessagingAPIとAIエージェントの連携

AWS SAMを使って、LINE MessagingAPI用のAPIをデプロイします。

まずはチャネルシークレットチャネルアクセストークンをLINE Developersコンソールから取得します。

チャネル基本設定 -> チャネルシークレット

B9B02B35-A71E-4362-B552-EBA13C7FC6CE_4_5005_c.jpeg

Messaging API設定 -> チャネルアクセストークン

45FFB2B5-BFCA-4F20-9BFA-24A3E7A434D0_4_5005_c.jpeg

それらの情報を控えて、.envに記入してください。

.env
# LINE Messaging API Configuration
BEDROCK_AGENT_RUNTIME_ARN=your_bedrock_agent_runtime_arn
LINE_CHANNEL_SECRET=your_line_channel_secret
LINE_CHANNEL_ACCESS_TOKEN=your_line_channel_access_token

LINE MessagingAPI使って、AIエージェントにメッセージを送るため、お持ちのLINEアプリから、QRコードをスキャンする必要があります。

EBBD5A0F-B0C5-4D6E-85C6-F2916D72A428.jpeg

プロジェクト配下のapiフォルダにSAMの設定ファイルとLambdaの設定ファイルがあります。

api/lambda_handler.pyは、Webhook設定に設定されているAPI Gatewayが受信したリクエストを受け取り、リクエストbodyを処理してから、boto3を使ってAgentCoreランタイムを呼び出し、リクエストを処理してユーザーに返すシンプルな構成です。

APIをデプロイします。

cd api
make deploy

デプロイ完了したら、コンソールから下記の出力が確認できます。

Value               https://xxxxx.execute-api.us-west-2.amazonaws.com/prod/invoke                                                                                      
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Successfully created/updated stack - line-guardrail-stack in us-west-2

✅ Deployment completed successfully!
💡 You can now test your API using the endpoint shown above

Valueに記載されているエンドポイントをLINEのWebhook URLに貼り付けてください。

E63A331D-7151-46AE-AD8C-E4350998A966_4_5005_c.jpeg

正しくAPIがデプロイされていれば、検証ボタンをクリックすると成功のポップアップが表示されます。

7F9372DF-C1EB-4BB0-AEF9-C942C9E26172.jpeg

それからLINE Botにメッセージを送れば、AIエージェントからの返信が確認できるはずです。

IMG_1781.jpeg

ここまでで、ガードレールを試すための準備が終わりました。

Amazon Bedrock Guardrails

まず、Amazon Bedrockガードレールのワードフィルター機能を活用して、基本的なガードレールを実装をします。今回はdogを禁止ワードとして設定します。

ディレクトリ配下にscripts/create_dog_guardrail.pyが存在します、下記のコマンド使って、デプロイします。

cd ..
uv run scripts/create_dog_guardrail.py --create-version

デプロイ完了後、Guardrail IDが出力されます、控えておいてください。

次にagent.pyを修正して、作成したガードレールを使用できるようにします。

agent.py
from dotenv import load_dotenv
from strands import Agent
from bedrock_agentcore.runtime import BedrockAgentCoreApp
+ from strands.models import BedrockModel

load_dotenv()

+ bedrock_model = BedrockModel(
+    model_id="us.anthropic.claude-sonnet-4-20250514-v1:0",
+    guardrail_id="GUARDRAIL_ID",              # 犬禁止 guardrail ID
+    guardrail_version="1",                    # Guardrail version
+    guardrail_trace="enabled",
+)

agent = Agent(
+    system_prompt="You are a helpful assistant.",
+    model=bedrock_model,
)
app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
     """Process user input and return a response"""
     prompt = payload.get("prompt", "Hello")
     result = agent(prompt)
     return {"result": result.message}

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

その後、再度デプロイしてください。

agentcore launch

デプロイ完了後、dogを含むワードをAIエージェントに送信すると、ここはねこねこショップなので、犬用の商品は取り扱い禁止です。とのメッセージが表示されます。

例えばDo you sell dog food?を送ってみよう。

IMG_1783.jpeg

Amazon Bedrockガードレールの料金体系は以下の通りです。

ガードレールのポリシー 料金
コンテンツフィルター(テキストコンテンツ) 1,000テキストユニットあたり0.15USD
コンテンツフィルター(画像コンテンツ) 処理された画像あたり0.00075USD
拒否されたトピック 1,000テキストユニットあたり0.15USD
機密情報フィルター 1,000テキストユニットあたり0.10USD
機密情報フィルター(正規表現) 無料
ワードフィルター 無料
コンテキストグラウンディングチェック 1,000テキストユニットあたり0.10USD

今回デプロイしたワードフィルターのみが機能するガードレールは無料です。

特定のトピックを禁止するガードレール

とあるヘアサロンの予約AIエージェントを例とします。

このAIエージェントは、顧客対応とツールを使って、スタッフのスケジュールを調査する機能を持っています。

特定の用途に限定されるため、ヘアサロンへの問い合わせ以外のすべての問い合わせをフィルタリングし、また、ユーザーの個人情報もフィルタリングする必要があります。

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_320164_b9c19e2e-7a4f-4d1c-a6a3-49f84e954996のコピー.tiff

例えば、上記の画像のように、ヘアサロンのAIエージェントのはずなのに、プログラミングの質問に答えてしまってはいけません。

scripts/create_beauty_salon_guardrail.pyを以下のコマンドでデプロイします。

uv run scripts/create_beauty_salon_guardrail.py --create-version

デプロイ後、出力されるガードレールのIDを控えましょう。

次に、AIエージェントをカスタマイズします。
AIエージェントの指示を美容室AIエージェントらしく修正し、ダミーツールを持たせてスタッフのスケジュールを検索できるようにします。

具体的には、agent.tsを編集して以下を行います:

  • ツールをimportし、AIエージェントに追加する
  • 使用するガードレールIDとシステムプロンプトを更新する

今回追加するツールは、fetch APIをモックしたものです。
スタッフ名を指定して個別スケジュールを取得することも、一括で全スタッフのスケジュールを取得することもできます。

tools.py
from strands import tool

@tool
def get_staff_schedule(date: str, staff_name: str = None) -> dict:
    """Get staff schedule and availability for Hair Salon MIKA.

    Args:
        date: The date to check schedule (YYYY-MM-DD format)
        staff_name: Optional specific staff name to check
    """
    try:
        # Sample schedule data for Hair Salon MIKA
        schedule_data = {
            "2025-09-24": {
                "田中美香": {
                    "10:00-12:00": {"status": "予約済", "service": "カット+カラー"},
                    "13:00-14:00": {"status": "予約済", "service": "カット"},
                    "15:00-17:00": {"status": "空き"},
                    "17:00-19:00": {"status": "予約済", "service": "パーマ"},
                },
                "佐藤裕子": {
                    "10:00-11:00": {"status": "予約済", "service": "カット"},
                    "12:00-13:30": {"status": "予約済", "service": "カラー"},
                    "14:00-15:00": {"status": "空き"},
                    "15:30-16:15": {"status": "予約済", "service": "トリートメント"},
                },
            },
            "2025-09-25": {
                "田中美香": {
                    "10:00-11:00": {"status": "空き"},
                    "11:30-13:30": {"status": "予約済", "service": "カット+カラー"},
                    "14:30-15:30": {"status": "空き"},
                    "16:00-17:00": {"status": "予約済", "service": "カット"},
                },
                "佐藤裕子": {
                    "10:00-12:00": {"status": "予約済", "service": "パーマ"},
                    "13:00-14:00": {"status": "空き"},
                    "14:30-16:00": {"status": "予約済", "service": "カラー"},
                    "16:30-18:00": {"status": "空き"},
                },
            },
        }

        # Get schedule for the specified date
        date_schedule = schedule_data.get(date, {})

        if not date_schedule:
            return {
                "status": "error",
                "content": [
                    {"text": f"スケジュールデータが見つかりません: {date}"}
                ]
            }

        # Return specific staff schedule if requested
        if staff_name:
            staff_schedule = date_schedule.get(staff_name, {})
            if not staff_schedule:
                return {
                    "status": "error",
                    "content": [
                        {"text": f"スタッフ「{staff_name}」のスケジュールが見つかりません: {date}"}
                    ]
                }

            result_data = {
                "date": date,
                "staff": staff_name,
                "schedule": staff_schedule
            }
        else:
            # Return all staff schedules
            result_data = {
                "date": date,
                "allStaff": date_schedule
            }

        return {
            "status": "success",
            "content": [
                {"json": result_data}
            ]
        }

    except Exception as e:
        return {
            "status": "error",
            "content": [
                {"text": f"Error: {e}"}
            ]
        }
agent.py
from dotenv import load_dotenv
from strands import Agent
from strands.models import BedrockModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp
+ from tools import get_staff_schedule

load_dotenv()

bedrock_model = BedrockModel(
    model_id="us.anthropic.claude-sonnet-4-20250514-v1:0",
+    guardrail_id="GUARDRAIL_ID",              # ヘアサロン guardrail ID
    guardrail_version="1",                     # Guardrail version
    guardrail_trace="enabled",
)

agent = Agent(
+    system_prompt="あなたは美容室MIKAの予約システムの親切なアシスタントです。お客様のスタッフのスケジュール確認や空き状況の確認をお手伝いします。",
    model=bedrock_model,
+    tools=[get_staff_schedule],
)
app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
     """Process user input and return a response"""
     prompt = payload.get("prompt", "Hello")
     result = agent(prompt)
     return {"result": result.message}

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

修正完了したら、再度デプロイしてください。

agentcore launch

デプロイ後、メッセージを再度送信してみてください。
サロン関連の問い合わせは正常に処理される一方、関係のないトピックはガードレールによってブロックされることが確認できます。

コピー.tiff

コンテキストグラウンディングチェック

LLMのレスポンスに含まれるハルシネーションをフィルタリングする機能です。

曖昧な回答が許されない場面で有効に活用できます。モデルの回答とソースデータを比較し、スコアが閾値を下回る場合、その回答をリジェクトすることができます。

この機能を試すため、まずAIエージェントを以下のように修正してください。

agent.py
from dotenv import load_dotenv
from strands import Agent
from strands.models import BedrockModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp

load_dotenv()

bedrock_model = BedrockModel(
    model_id="us.anthropic.claude-sonnet-4-20250514-v1:0",
)

agent = Agent(
+    system_prompt="あなたはLINE Developer Communityに関する質問に答えるアシスタントです。",
    model=bedrock_model,
)
app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
     """Process user input and return a response"""
     prompt = payload.get("prompt", "Hello")
     result = agent(prompt)
     return {"result": result.message}

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

修正完了したら、デプロイしてください。

agentcore launch

デプロイ完了後、
LINE Developer Communityはフレンドリーなイベントで、懇親会のピザを目当てに参加することも可能ですか?
というメッセージをLINE Botに送信してみてください。

IMG_1813.jpeg

ただし、LINE Developer Communityの禁止事項には食事のみを目的とした参加が明記されているため、曖昧な回答は許されません。

EF955D84-D05C-4752-9BA4-5F577D2F0964_4_5005_c.jpeg

ここで、コンテキストグラウンディングチェックの登場です。

この機能を利用する際に少し注意が必要です。機能を有効にしたガードレールをAIエージェントに直接渡しても動作しないため、ツールとして渡す必要があります。

下記のガードレールをデプロイしてください。

uv run scripts/create_contextual_grounding_guardrail.py --create-version

デプロイ後、ガードレールのIDを控えてください。

tools.pyの45行目のIDを修正してください。

tools.py
...
        guardrail_response = bedrock_runtime.apply_guardrail(
+            guardrailIdentifier="GUARDRAIL_ID", # コンテキストグラウンディングチェック用に作成したガードレールIDを指定
            guardrailVersion="1", # 必要に応じてバージョンを指定
            source="OUTPUT",
            content=[
                {
                    "text": {
                        "text": grounding_source,
                        "qualifiers": ["grounding_source"]
                    }
                },
                {
                    "text": {
                        "text": question,
                        "qualifiers": ["query"]
                    }
                },
                {
                    "text": {
                        "text": answer_text
                    }
                }
            ]
        )
...

AIエージェントも修正してください、ツールを追加します。

agent.py
from dotenv import load_dotenv
from strands import Agent
from strands.models import BedrockModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp
+ from tools import grounded_answer

load_dotenv()

bedrock_model = BedrockModel(
    model_id="us.anthropic.claude-sonnet-4-20250514-v1:0",
)

agent = Agent(
    system_prompt="あなたはLINE Developer Communityに関する質問に答えるアシスタントです。",
    model=bedrock_model,
+    tools=[grounded_answer],
)
app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
     """Process user input and return a response"""
     prompt = payload.get("prompt", "Hello")
     result = agent(prompt)
     return {"result": result.message}

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

再度デプロイしてください。

agentcore launch

同じ質問を再度投げてみます。

IMG_1814.jpeg

ちゃんとブロックされましたね!
今回は便宜上、参照ドキュメントをベタ書きしましたが、この参照ドキュメントは動的に読み込むことも可能です。
S3から取得したり、PDFなどを変換して渡すこともできます。この機能を活用することで、モデルのハルシネーションを大幅に減らすことができます。

補足説明

お片付け

AWS SAMで作られたリソースを削除する方法

sam delete --stack-name line-guardrail-stack
        Are you sure you want to delete the stack line-guardrail-stack in the region us-west-2 ? [y/N]:  y
        Are you sure you want to delete the folder line-guardrail in S3 which contains the artifacts? [y/N]: y

Amazon Bedrock AgentCoreのリソース削除

存在するエージェントを確認する

agentcore configure list
Configured Agents:
  ✅ linebot (default) - Ready
     Entrypoint: main.py
     Region: us-west-2

削除確認

agentcore destroy --agent linebot --dry-run

削除実行

agentcore destroy --agent linebot --delete-ecr-repo

Amazon Bedrock Guardrails削除

存在するガードレール一覧表示

uv run scripts/manage_guardrails.py --list

ID指定削除

uv run scripts/manage_guardrails.py --delete GUARDRAIL_ID

LINEメッセージングAPIの申請

ビジネスアカウントの登録、もしくはLINEアカウントでログインしてください。

49C43D44-B6CF-4CD1-8CAB-91F7D623BCDE.jpeg

0B755694-56B7-456C-8819-A1EFE17DE38A.jpeg

8DD9A77C-A9F9-4901-B179-765F86F610FE.jpeg

CCA26E72-086D-442F-B1C4-637A5940CD4B_1_201_a.jpeg

F2977291-11CE-4D34-B7ED-CD858120FEFB.jpeg

BF0A6363-57A1-4A97-BBDE-3B68477DACDC.jpeg

5C4A8DE3-F2B7-4811-9656-62324CD269C0.jpeg

991784C6-E71D-4D57-A2E0-A66F69F8A902.jpeg

646A56C8-F538-4FC3-AC70-579CC95A7E69.jpeg

198643BC-B0D7-445F-9EFE-043D14C14AB5.jpeg

AWSアカウントのセットアップ

ハンズオン用のユーザーはハンズオン完了後、削除することおすすめです。

ハンズオンで使用するモデルの有効化

AWSコンソールでリージョンを us-west-2 に設定、Amazon Bedrock のモデルアクセス画面を開く、
AnthropicのClaude Sonnet 4を有効化してください。

2BD5F4D6-356B-465B-BE98-3BB4AA418FC7.jpeg

IAMユーザーの作成

  1. AWSマネジメントコンソールにサインイン(https://console.aws.amazon.com/)
  2. このハンズオンは、すべてオレゴンリージョンで行います。最初に画面右上のリージョンを切り替えておいてください。
  3. IAMを検索してアクセス
  4. サイドバーユーザーより新規ユーザーを作成
  • ユーザー名: codespaces
  • 許可のオプション: ポリシーを直接アタッチする
  • 許可ポリシー: AdministratorAccessにチェック
  • 後はそのまま進む

IAMアクセスキーの作成

  • IAMユーザー作成後、作成したユーザー名をクリックして開く
  • 概要セクションよりアクセスキーを作成
  • ユースケース: コマンドラインインターフェイス (CLI)
  • 上記のレコメンデーションを理解し、アクセスキーを作成します。にチェック
  • 後はそのまま進む

作成後、アクセスキーとシークレットアクセスキーを、コードスペースの.envに貼り付ける

20
5
1

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
20
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?