
Strands AgentsでAmazon Bedrock Guardrailsと連携して意図しないリクエストをブロックする
はじめに
こんにちは、ラ・ムーが大好きなコンサルティング部の神野です。
正月休みももう終わりですね。AgentCoreやStrands Agentsの検証やブログを書いていたらあっという間にお休みが終わってしまい驚愕しています。
話は変わって、最近個人的にナレッジ収集のAIエージェントを作成しています。
Slackで書き込みしたナレッジを構造化やカテゴライズしてGitHubに自動的にコミットする仕組みです。
ここで課題になるのが、関係ない書き込みを弾きたいということです。
「今日の天気は?」とか「おすすめの映画教えて」みたいな、ナレッジ収集とは関係ない質問が来たときにはブロックしたいですよね。
最初はシンプルに下記のようにシステムプロンプトで「関係ない質問には答えないでください」と指示していて、判断用のサブエージェントをツールとして用意してブロックしていました。

ただシステムプロンプトが複雑になりエージェントの手数が増えるのは嫌だなと感じ、ビルトインの機能で何かないかなと思った時に、思いついたのが下記のようなBedrock Guardrailsの活用です。(他にもステアリング機能とかあると思いますが、そもそも前段で弾きたいなと思い関心事を分離したかったのです)

今回はStrands AgentsとBedrock Guardrailsを連携させて、
要件となるドメイン外のリクエストをブロックする方法を検証してみたいと思います。
Bedrock Guardrailsとは
Bedrock Guardrailsは、Amazon Bedrockで利用できるセーフガード機能です。
LLMアプリケーションに対して、不適切なコンテンツのフィルタリングや特定トピックのブロックなどを設定できます。
わかりやすく解説してある記事があるので、詳細はこちらをご参照ください。
Guardrailsでは幅広いポリシーを設定できますが、今回は拒否トピックポリシーを活用します。
拒否トピックポリシーを使うと、特定のトピックに関する会話をブロックできます。
今回のAIエージェントはナレッジの収集がメインなので、それ以外の日常的な会話などはブロックしたいです。
それをこのBedrock Guardrailsの拒否トピックポリシーで実現してみます。
Strands Agentsとの連携
Strands AgentsではBedrock Guardrailsとの連携がビルトインでサポートされています。
下記公式ドキュメントにやり方があるのでみてみましょう。
import json
from strands import Agent
from strands.models import BedrockModel
# Create a Bedrock model with guardrail configuration
bedrock_model = BedrockModel(
model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0",
guardrail_id="your-guardrail-id", # Your Bedrock guardrail ID
guardrail_version="1", # Guardrail version
guardrail_trace="enabled", # Enable trace info for debugging
)
# Create agent with the guardrail-protected model
agent = Agent(
system_prompt="You are a helpful assistant.",
model=bedrock_model,
)
# Use the protected agent for conversations
response = agent("Tell me about financial planning.")
# Handle potential guardrail interventions
if response.stop_reason == "guardrail_intervened":
print("Content was blocked by guardrails, conversation context overwritten!")
print(f"Conversation: {json.dumps(agent.messages, indent=4)}")
BedrockModelの初期化時にguardrail_idでコンソールで作成したGuardrailsのIDを、guardrail_versionでバージョン番号を、guardrail_traceでトレース情報の有効/無効を設定するだけでGuardrailsが適用されます。
めちゃくちゃシンプルに実装できていいですね。
前提
今回使用したバージョンは下記のとおりです。
| 項目 | バージョン |
|---|---|
| Python | 3.12 |
| strands-agents | 1.21.0 |
今回Guardrailsを作成したリージョンはus-west-2(オレゴン)です。
実装
それでは実際に実装していきます。
Guardrailsの作成(コンソール)
まずはAWSコンソール上でBedrock Guardrailsを作成していきます。
今回は拒否されたトピックしか設定しませんが、それ以外もどういった設定項目があるか設定を進めながら確認していきます。
ガードレールの詳細
Bedrockのコンソールから Guardrails を選択し、Create guardrail をクリックします。

名前と説明を入力して次に進みます。
今回は knowledge-agent-guardrail という名前にしました。拒否したトピックの内容が入力された際は下記を返却するようにします。
このトピックはAI/生成AI系ナレッジの対象外です。
対象:AI駆動開発、プロンプト設計、Claude/GPT活用、MCP、Strands Agents等
クロスリージョン推論のオプションも有効にします。

コンテンツフィルターの設定
次にコンテンツフィルターの設定画面が表示されます。

コンテンツフィルターでは、有害なコンテンツ(暴力、性的表現、誹謗中傷など)をフィルタリングする強度を設定できます。
一般公開するアプリケーションでは適切に設定しておくと安心ですね。
拒否トピックの設定
次に拒否トピックを設定します。ここで「拒否したいトピック」を定義します。
今回のメイン設定箇所ですね!Add denied topicを選択します。

今回は以下のような内容をブロック対象として設定しました。
名前はnon-development-topicとします。tierはStandardとします。
否定するトピックを入力します。トピックは下記アドバイスがあるのでそれに従って日常会話を指定します。
- 「トピックの定義」はトピックを説明するものであるため、「◯◯の話題は話させない」など否定的な記述はしない
- 「トピックの定義」に「例」や「手順」を含めない
最初は上記をクリアしていない指定(具体例を入れたり、否定系)をしていたので今後も注意したいなと思います。否定系を使えないので考えられる拒否トピックを含めたほうが良さそうです。全てを考えるのは少し難しいですね・・・なので、明示的に拒否したい話題がある場合に使う方法がよさそうです。
日常会話、雑談、エンターテインメント、ライフスタイル、個人相談に関する一般的な話題。
サンプルフレーズには以下のような例を設定しました。
5つまで設定できるので実際に適応する際はよく使われそうなフレーズを入れ込んでおくといいですね。今回はサンプル的に入力しています。
- 今日は暑いですね
- お疲れ様です
- 週末何する予定?
- 昨日のサッカー見た?
- おすすめの映画教えて

ちなみにactionはInput/Outputそれぞれ選択可能かつ、Detectも選択可能で、検知はするがブロックはしないモードもあります。まずはブロックはしたくないけど評価を見たいケースには良いですよね。
ワードフィルターの設定
次にワードフィルターの設定画面が表示されます。

ワードフィルターでは、特定の単語やフレーズを直接ブロックできます。不適切な表現や社内用語の漏洩防止などに使えますね。
AWSが用意したワードフィルターや自分で言葉を登録することもできます。
機密情報フィルターの設定
次に機密情報フィルターの設定画面が表示されます。

機密情報フィルターでは、個人情報(PII)やカスタム定義した機密情報をマスキングまたはブロックできます。
メールアドレスやクレジットカード番号などを自動で検出してくれるのはありがたいですね。
また正規表現でのチェックも可能で特定フォーマットのIDなども検知することも可能です。
コンテキストグラウンディングチェックの設定
次にコンテキストグラウンディングチェックの設定画面が表示されます。

コンテキストグラウンディングチェックでは、RAGなどで参照ソースがある場合に、回答がソースに基づいているか(Grounding)、関連性があるか(Relevance)をチェックできます。
一定の閾値以上を情報として採用したいケースなどに使いたいですね。
実際に試した記事も弊社ブログにあるので、必要に応じてご参照ください。
自動推論チェックの設定
次に自動推論チェック(Automated Reasoning Check)の設定画面が表示されます。

自動推論チェックでは、Automated Reasoningポリシーを使用してLLMの出力を検証できます。
ポリシーはこの画面とは別に作成する必要があり、業務ルールなどが書かれたソースドキュメントとルールを抽出して定義します。この画面では作成済みのポリシーを選択する形ですね。
現時点では検出モード(Detect mode)のみの動作となっており、ブロックはせずフィードバックを提供する形になります。
信頼度の閾値(Confidence threshold)も設定可能です。
公式ドキュメントによると、以下の制限事項があります。
- 利用可能リージョン: US (N. Virginia, Oregon, Ohio) と EU (Frankfurt, Paris, Ireland) のみ
- 言語サポート: 英語(US)のみ
- ストリーミングAPI: サポートしていない
規制の厳しい業界(ヘルスケア、HR)や複雑なルールを扱うアプリケーション(住宅ローン承認等)で特に価値があるとのことです。
今回は設定しませんが、より厳密な出力検証が必要なケースでは活用を検討してみてください。
今後、このポリシーも検証してブログ化したいですね。
確認と作成
設定内容を確認して、Create guardrail をクリックします。これでGuardrailsの作成は完了です!
コンソールでのテスト
作成できたらコンソール上でテストしてみます。Haiku4.5を選択して、「チーズナンって美味しいよね」と話しかけてみます。

期待通りブロックされましたね!!
試しに今度は「Claude Codeのカスタムスラッシュコマンドを使うと定型作業のスピードが向上し業務効率につながります。」と開発系のことを入力してみます。

こちらも問題なく通過しました!Guardrailsが正しく動作していることが確認できましたね!
バージョンの作成
テストが完了したら、バージョンを作成します。Create versionから、バージョン1として展開します。


これでGuardrailsの設定は終わりました。今度はStrands Agentsから使ってみましょう!
IDが必要になるので控えておきます。

Strands Agentsのセットアップ
まずはuvで環境を作成していきます。
uv init
ライブラリもインストールします。
uv add strands-agents
これで準備ができたので、早速コードを書いて試してみます!
from strands import Agent
from strands.models import BedrockModel
# コンソールで作成したGuardrailsを指定
bedrock_model = BedrockModel(
model_id="us.anthropic.claude-haiku-4-5-20251001-v1:0",
guardrail_id="<実際のガードレールIDを入れてください>",
guardrail_version="1",
guardrail_trace="enabled",
region_name="us-west-2"
)
agent = Agent(
model=bedrock_model
)
response = agent("チーズナンって美味しいよね")
model_idで使用するモデル(今回はHaiku 4.5)を指定し、guardrail_idとguardrail_versionでコンソールで作成したGuardrailsのIDとバージョン番号を設定しています。guardrail_traceはトレース情報の出力設定で、デバッグ時に有効化しておくと便利です。
動作確認
それでは実行してみましょう。コンソールでテストしたときと同じ質問をしてみます。
uv run main.py
このトピックはAI/生成AI系ナレッジの対象外です。
対象:AI駆動開発、プロンプト設計、Claude/GPT活用、MCP、Strands Agents等
問題なく動きました!Guardrailsで設定したブロックメッセージがそのまま返ってきていますね!
ちなみにguardrail_trace="enabled"の場合だと、どのポリシーでブロックされたかの詳細情報がログに出力されます。
PythonのログレベルをDEBUGに設定すると確認できます。
import logging
logging.basicConfig(level=logging.DEBUG)
{
"trace": {
"guardrail": {
"actionReason": "Guardrail blocked.",
"inputAssessment": {
"topicPolicy": {
"topics": [{
"action": "BLOCKED",
"name": "non-development-topic",
"type": "DENY"
}]
}
}
}
}
}
どのトピックでブロックされたかがわかるので、デバッグ時に便利ですね。
ちなみに拒否トピックのactionをDetectに変更すると、トレース情報も変わります。
| 項目 | Blocked | Detected |
|---|---|---|
actionReason |
"Guardrail blocked." | "No action." |
action |
"BLOCKED" | "NONE" |
detected |
true | true |
{
"trace": {
"guardrail": {
"actionReason": "No action.",
"inputAssessment": {
"topicPolicy": {
"topics": [{
"action": "NONE",
"detected": true,
"name": "non-development-topic",
"type": "DENY"
}]
}
}
}
}
}
Detectモードでは検知しつつもモデルの応答は生成されるので、本番適用前の評価に活用できますね。
シャドウモードも試してみる
いきなりリクエストをブロックするのは怖い、まずはどんなリクエストがブロックされるか確認したいというケースもありますよね。
もちろんGuardrails側でDetectで検知するでもいいですが、Hooksを活用したシャドウモード運用も可能です。
実際にはブロックせず、違反があった場合にログを出力するだけなので、Guardrailsの挙動を確認しながら段階的に導入できます。
実際に試してみましょう。
import boto3
from strands import Agent
from strands.hooks import HookProvider, HookRegistry, MessageAddedEvent, AfterInvocationEvent
class NotifyOnlyGuardrailsHook(HookProvider):
def __init__(self, guardrail_id: str, guardrail_version: str):
self.guardrail_id = guardrail_id
self.guardrail_version = guardrail_version
self.bedrock_client = boto3.client("bedrock-runtime", "us-west-2")
def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(MessageAddedEvent, self.check_user_input)
registry.add_callback(AfterInvocationEvent, self.check_assistant_response)
def evaluate_content(self, content: str, source: str = "INPUT"):
"""ApplyGuardrail APIでコンテンツを評価(シャドウモード)"""
try:
response = self.bedrock_client.apply_guardrail(
guardrailIdentifier=self.guardrail_id,
guardrailVersion=self.guardrail_version,
source=source,
content=[{"text": {"text": content}}]
)
if response.get("action") == "GUARDRAIL_INTERVENED":
print(f"\n[GUARDRAIL] WOULD BLOCK - {source}: {content[:100]}...")
for assessment in response.get("assessments", []):
if "topicPolicy" in assessment:
for topic in assessment["topicPolicy"].get("topics", []):
print(f"[GUARDRAIL] Topic Policy: {topic['name']} - {topic['action']}")
except Exception as e:
print(f"[GUARDRAIL] Evaluation failed: {e}")
def check_user_input(self, event: MessageAddedEvent) -> None:
"""ユーザー入力をチェック"""
if event.message.get("role") == "user":
content = "".join(block.get("text", "") for block in event.message.get("content", []))
if content:
self.evaluate_content(content, "INPUT")
def check_assistant_response(self, event: AfterInvocationEvent) -> None:
"""アシスタントの応答をチェック"""
if event.agent.messages and event.agent.messages[-1].get("role") == "assistant":
assistant_message = event.agent.messages[-1]
content = "".join(block.get("text", "") for block in assistant_message.get("content", []))
if content:
self.evaluate_content(content, "OUTPUT")
# Hooksを使ったエージェントの作成
agent = Agent(
system_prompt="You are a helpful assistant.",
hooks=[NotifyOnlyGuardrailsHook("<実際のガードレールIDを入れてください>", "1")]
)
# 通常どおり使用(違反があってもブロックせずログ出力のみ)
agent("チーズナンって美味しいよね")
上記コードでは、ApplyGuardrail API(LLM を介さず Amazon Bedrock Guardrails の機能を利用できるAPI)を直接呼び出して評価を行っています。
GUARDRAIL_INTERVENEDが返ってきた場合でも実際にはブロックせず、ログを出力するだけです。
実行してみると下記結果となり、ログからブロックされたことがわかり、意図通りの設計になっているかチェックできます。
いきなりGuardrailsを適用せず、まずは評価したい時とかに活用を検討したいですね。
uv run hooks_guardrail.py
[GUARDRAIL] WOULD BLOCK - INPUT: チーズナンって美味しいよね...
[GUARDRAIL] Topic Policy: non_ai_topics - BLOCKED
はい、チーズナンは本当に美味しいですね!ふわふわのナンの中にとろけるチーズが入っていて、インド料理の中でも特に人気の高いメニューだと思います。
カレーと一緒に食べると、スパイシーなカレーとマイルドなチーズの組み合わせがとても良く合いますよね。チーズが伸びる様子も見た目にも楽しいですし、食べ応えもあります。
どんなカレーと合わせて食べるのがお好みですか?バターチキンカレーやほうれん草カレーなどと特に相性が良いと言われていますが。
[GUARDRAIL] WOULD BLOCK - OUTPUT: はい、チーズナンは本当に美味しいですね!ふわふわのナンの中にとろけるチーズが入っていて、インド料理の中でも特に人気の高いメニューだと思います。
カレーと一緒に食べると、スパイシーなカレーとマイルドな...
[GUARDRAIL] Topic Policy: non_ai_topics - BLOCKED
安定したらビルトイン方式に切り替える、という段階的な導入も考えられますね!
ApplyGuardrailAPIについての詳細は下記ブログをご参照ください。
具体的な運用などはまた別途検証や考察したいですね。
おわりに
Strands Agentsのビルトイン機能で簡単にGuardrailsが使えるのは嬉しいですよね。
今回は拒否トピックポリシーのみを使いましたが、Bedrock Guardrailsにはコンテンツフィルターや機密情報のマスキングなど、他にも便利な機能があります。
本番環境でエージェントを運用していく上で積極的に活用していきたいですね!何を設定したら良いか奥が深そうなのでいつか徹底検証したいですね・・・
本記事が少しでも参考になりましたら幸いです。最後までご覧いただきありがとうございました!








