AWSのエージェントフレームワーク「Strands Agents」 を試す
公式サイト
ドキュメント(ユーザガイド)
Strands Agents SDK
Strands Agentsは、エージェントを構築するための、コードファーストで使いやすいフレームワークです。
機能
Strands Agentsは軽量でプロダクションに対応し、多くのモデルプロバイダーとデプロイメントターゲットをサポートします。
主な機能は次のとおりです:
- 軽量で邪魔にならない: シンプルなエージェントループは動作するだけで、完全にカスタマイズ可能です。
- プロダクション対応: エージェントを大規模に実行するための完全な観測可能性、トレース、デプロイメントオプション。
- モデル、プロバイダ、デプロイメントにとらわれない: Strandsは、さまざまなプロバイダのさまざまなモデルをサポートしています。
- 強力な組み込みツール: 幅広い機能を備えたツールで、すぐに使い始めることができます。
- マルチエージェントと自律エージェント: エージェント チームや時間の経過とともに自己改善するエージェントなど、高度な技術を AI システムに適用します。
- 会話型、非会話型、ストリーミング型、非ストリーミング型: さまざまなワークロードに対応するあらゆるタイプのエージェントをサポートします。
- 安全性とセキュリティを優先: データを保護しながら、責任を持ってエージェントを実行します: データを保護しながら、責任を持ってエージェントを運営する。
Python SDK GitHubレポジトリ
Quickstart
ローカルのMacで試す。
uvでプロジェクト+Python仮想環境を作成
uv init -p 3.12 my-strands-agent && cd $_
パッケージインストール
uv add strands-agents
+ strands-agents==1.0.1
上記以外に開発用パッケージが2つある
-
strands-agents-tools
: エージェントに与えるサンプルのツールのパッケージ -
strands-agents-builder
: Strandsエージェントとツールの構築を支援するエージェントのパッケージ(?)
とりあえずこれらも追加する
uv add strands-agents-tools strands-agents-builder
+ strands-agents-builder==0.1.7
+ strands-agents-tools==0.2.1
今回はLLMは公式に従ってBedrockを使うこととするので、以下を実施する必要がある。
- クレデンシャルの設定
- AWS CLIがセットアップ済み
- Bedrockモデルの有効化
- BedrockのモデルアクセスでClaude 4を利用可能にしておく
- 自分はこのあとAgentCoreも試そうと思っているので、AgentCoreが提供されている以下のリージョンのAnthropicモデルを全部有効化した。
- バージニア北部
- オレゴン
- シドニー
- 東京(AgentCoreはまだ来ていないが一応)
クォータもいじる必要があるのかなぁ・・・以前にも申請した記憶はあるが、とりあえずそれは躓いてから考えることにする。
次にプロジェクト構成。uvのデフォルトだとこう。
tree
.
├── README.md
├── main.py
├── pyproject.toml
└── uv.lock
Strands Agentsのサンプル構成は以下となっている
my_agent/
├── __init__.py
├── agent.py
└── requirements.txt
必ずこれに従う必要があるのかどうかは現時点ではわからないが、少し寄せておくことにする。
mv main.py agent.py
touch __init__.py
ではファイルを修正
from . import agent
from strands import Agent, tool
from strands_tools import calculator, current_time, python_repl
# @toolデコレータを使って、Python関数をカスタムツールとして定義
@tool
def letter_counter(word: str, letter: str) -> int:
"""
単語内の特定の文字の出現回数を数える
Args:
word (str): 検索対象の単語
letter (str): 数える特定の文字
Returns:
int: 単語内の特定の文字の出現回数
"""
if not isinstance(word, str) or not isinstance(letter, str):
return 0
if len(letter) != 1:
raise ValueError("'letter'パラメータは単一の文字である必要がある。")
return word.lower().count(letter.lower())
# カスタムな letter_counter ツールと同様に、
# strands-toolsパッケージのサンプルツールを指定して、
# エージェントを作成
agent = Agent(tools=[
calculator,
current_time,
python_repl,
letter_counter
])
# 利用できるツールを使って答えれる質問をエージェントに行う
message = """
4つのお願いがあります:
1. 今何時ですか?
2. 3111696 / 74088 を計算して。
3. "strawberry" という単語に 文字 "r" はいくつありますか?
4. 私が今話したことを実行するスクリプトを出力して。
出力する前にそのスクリプトが正しく動作することを確認して。
"""
agent(message)
では実行
uv run agent.py
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The provided model identifier is invalid.
└ Bedrock region: ap-northeast-1
└ Model id: us.anthropic.claude-sonnet-4-20250514-v1:0
自分のAWS CLIの設定はap-northeast-1
がデフォルトになっているのだけど、どうもStrands AgentのデフォルトはUSになっている?とりあえずAWS_DEFAULT_REGION
を設定して再実行。
export AWS_DEFAULT_REGION="us-east-1"
uv run agent.py
出力された。どうやらPythonコード実行には確認が行われるみたい。あぁ、コンテナで動かせばよかったな。とりあえず y
で進める。
4つのお願いにお答えします。まず最初の3つから始めましょう。
Tool #1: current_time
Tool #2: calculator
Tool #3: letter_counter
それでは4つ目のお願いに答えます。あなたが今話したことを実行するスクリプトを作成し、動作確認をします。
Tool #4: python_repl
Do you want to proceed with Python code execution? [y/*]
さらに確認。こちらも y
で。
pytzモジュールがないようなので、標準ライブラリのみを使用してスクリプトを修正します。
Tool #5: python_repl
Do you want to proceed with Python code execution? [y/*] y
最終結果
4つのお願いの実行結果:
==================================================
1. 現在時刻: 2025-07-22T19:59:15.634013
2. 3111696 / 74088 = 42.0
3. "strawberry" に文字 "r" は 3 個含まれています
4. このスクリプトが上記の処理を実行しました
4つのお願いの実行結果:
==================================================
1. 現在時刻: 2025-07-22T19:59:15.634063
2. 3111696 / 74088 = 42.0
3. "strawberry" に文字 "r" は 3 個含まれています
4. このスクリプトが上記の処理を実行しました
素晴らしい!スクリプトが正しく動作することを確認できました。
## 4つのお願いの回答:
1. **現在時刻**: 2025-07-22T10:57:41.954982+00:00 (UTC)
2. **計算結果**: 3111696 ÷ 74088 = **42**
3. **文字カウント**: "strawberry" という単語に文字 "r" は **3個** 含まれています
4. **実行スクリプト**:
```python
# 4つのお願いを実行するスクリプト(標準ライブラリのみ使用)
from datetime import datetime
def main():
print("4つのお願いの実行結果:")
print("=" * 50)
# 1. 現在時刻を取得
current_time = datetime.now()
print(f"1. 現在時刻: {current_time.isoformat()}")
# 2. 計算を実行
result = 3111696 / 74088
print(f"2. 3111696 / 74088 = {result}")
# 3. 文字数をカウント
word = "strawberry"
letter = "r"
count = word.count(letter)
print(f'3. "{word}" に文字 "{letter}" は {count} 個含まれています')
# 4. スクリプト自体の情報
print("4. このスクリプトが上記の処理を実行しました")
if __name__ == "__main__":
main()
```
このスクリプトは実際に実行して動作確認済みです。すべてのお願いが正しく処理されることを確認しています。
エージェントの推論ループについては以下に詳細がある
デバッグログを有効化するには strands
ロガーを設定する
import logging
from strands import Agent
# Strandsのデバッグログレベルを有効化
logging.getLogger("strands").setLevel(logging.DEBUG)
# ログフォーマットを設定して、標準エラー出力に出力する
logging.basicConfig(
format="%(levelname)s | %(name)s | %(message)s",
handlers=[logging.StreamHandler()]
)
agent = Agent()
agent("こんにちは!")
uv run debug_log.py
DEBUG | strands.models.bedrock | config=<{'model_id': 'us.anthropic.claude-sonnet-4-20250514-v1:0'}> | initializing
DEBUG | strands.models.bedrock | region=<us-east-1> | bedrock client created
DEBUG | strands.tools.registry | tools_dir=</Users/kun432/work/my-strands-agent/tools> | tools directory not found
DEBUG | strands.tools.registry | tool_modules=<[]> | discovered
DEBUG | strands.tools.registry | tool_count=<0>, success_count=<0> | finished loading tools
DEBUG | strands.tools.registry | getting tool configurations
DEBUG | strands.tools.registry | tool_count=<0> | tools configured
INFO | strands.telemetry.metrics | Creating Strands MetricsClient
DEBUG | strands.tools.registry | getting tool configurations
DEBUG | strands.tools.registry | tool_count=<0> | tools configured
DEBUG | strands.event_loop.streaming | model=<<strands.models.bedrock.BedrockModel object at 0x104064bf0>> | streaming messages
DEBUG | strands.models.bedrock | formatting request
DEBUG | strands.models.bedrock | request=<{'modelId': 'us.anthropic.claude-sonnet-4-20250514-v1:0', 'messages': [{'role': 'user', 'content': [{'text': 'こんにちは!'}]}], 'system': [], 'inferenceConfig': {}}>
DEBUG | strands.models.bedrock | invoking model
DEBUG | strands.models.bedrock | got response from model
DEBUG | strands.models.bedrock | finished streaming response from model
DEBUG | strands.agent.conversation_manager.sliding_window_conversation_manager | message_count=<2>, window_size=<40> | skipping context reduction
こんにちは!お元気ですか?何かお手伝いできることがあれば、お気軽にお声かけください。
モデルプロバイダを設定する。デフォルトはBedrockのClaude 4 Sonnetになっている。エージェントが使用するモデルを確認するには model.config
を使う。
from strands import Agent
agent = Agent()
print(agent.model.config)
{'model_id': 'us.anthropic.claude-sonnet-4-20250514-v1:0'}
モデルを変更するには以下の2つの方法がある。
- エージェントのコンストラクタにモデルID文字列を直接渡す
- 任意の設定でモデルプロバイダのインスタンスを作成する
1の場合はこう。Claude 3.5 Haikuにしてみた。自分は全然知らなかったけど、クロスリージョン推論プロファイルで指定するのね。
from strands import Agent
# モデルID文字列で特定のモデルを指定してエージェントを作成
agent = Agent(model="us.anthropic.claude-3-5-haiku-20241022-v1:0")
agent("こんにちは!")
こんにちは!今日はどんなお手伝いができますか?何か楽しいことや興味のあることについて、お話ししましょう。
2の場合。こちらは細いパラメータなんかも指定してる。
import boto3
from strands import Agent
from strands.models import BedrockModel
# BedrockModelを作成
bedrock_model = BedrockModel(
model_id="apac.anthropic.claude-sonnet-4-20250514-v1:0",
region_name="ap-northeast-1",
temperature=0.3,
)
agent = Agent(model=bedrock_model)
agent("こんにちは!")
こんにちは!お元気ですか?何かお手伝いできることがあれば、お気軽にお声かけください。
一応Bedrock以外のモデルも使えるということで、ここではOpenAIを試してみる。
パッケージを更新
uv add -U 'strands-agents[openai]'
OpenAI APIキーをセット
export OPENAI_API_KEY=XXXXX
from strands import Agent
from strands.models.openai import OpenAIModel
from strands_tools import calculator
import os
model = OpenAIModel(
client_args={
"api_key": os.environ["OPENAI_API_KEY"]
},
# **model_config
model_id="gpt-4o",
params={
"max_tokens": 1000,
"temperature": 0.7,
}
)
agent = Agent(model=model, tools=[calculator])
agent("2 + 2 は?")
Tool #1: calculator
\(2 + 2\) は \(4\) です。
詳細は各プロバイダごとにドキュメントが用意されているのでそちらを参照
ストリーミングは、非同期イテレータとコールバックの2つの方法がある。
非同期イテレータの場合。FastAPI や Django Channels のような非同期フレームワーク、非同期アプリケーションの場合にはこちら。
import asyncio
from strands import Agent
from strands_tools import calculator
# コールバックハンドラを無効化してエージェントを初期化
agent = Agent(
tools=[calculator],
callback_handler=None # デフォルトのコールバックハンドラを無効化
)
# 非同期関数でストリームされたエージェントイベントを反復処理
async def process_streaming_response():
prompt = "25 * 48 は?計算過程も説明して。"
# エージェントのレスポンスストリームを非同期イテレータとして取得
agent_stream = agent.stream_async(prompt)
# イベントが到着したら処理
async for event in agent_stream:
if "data" in event:
# 生成されたテキストチャンクを出力
print(event["data"], end="\n", flush=True) # 実際は `end=""` だが説明のため改行
elif "current_tool_use" in event and event["current_tool_use"].get("name"):
# ツール使用情報の出力
print(f"\n[部分的なツール使用情報: {event['current_tool_use']['name']}]")
# 非同期イベント処理でエージェントを実行
asyncio.run(process_streaming_response())
25 ×
48 の計算を
行います。計算過程も
含めて説
明いたします。
[部分的なツール使用情報: calculator]
[部分的なツール使用情報: calculator]
[部分的なツール使用情報: calculator]
[部分的なツール使用情報: calculator]
[部分的なツール使用情報: calculator]
**
答え:
1200**
**計算過程
の説明:**
25 × 48
= 1
200 です
が、この
計算をい
くつかの
方法で説
明できます:
**
方法1:
筆算による計
算**
```
48
×
25
----
240 (48
× 5
)
960
(48
× 20)
----
1200
```
**方法
2: 分
解
による
計算**
-
25 =
5 ×
5
なので
- 25
× 48
= 5
× 5
× 48
= 5
× (
5 ×
48) = 5
× 240
= 1
200
**方
法3:
100の
倍数を
利用**
-
25 × 4
= 100
なので
-
48 = 4
× 12
- 25
× 48 = 25
× (
4 ×
12) = (25 ×
4) × 12 =
100 ×
12 = 1200
ど
の方法でも答
えは **1200** にな
ります。
コールバックハンドラの場合。
import logging
from strands import Agent
from strands_tools import calculator
# ロギングを設定
logging.basicConfig(
format="%(levelname)s | %(name)s | %(message)s",
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger("my_agent")
logger.setLevel(logging.INFO)
# 出力の代わりにロギング用のシンプルなコールバックハンドラを定義
tool_use_ids = []
def callback_handler(**kwargs):
if "data" in kwargs:
# ストリームされたデータチャンクをログに出力
logger.info(kwargs["data"])
elif "current_tool_use" in kwargs:
tool = kwargs["current_tool_use"]
if tool["toolUseId"] not in tool_use_ids:
# ツール使用情報をログに出力
logger.info(f"\n[ツール使用: {tool.get('name')}]")
tool_use_ids.append(tool["toolUseId"])
# コールバックハンドラを指定してエージェントを作成
agent = Agent(
tools=[calculator],
callback_handler=callback_handler
)
# エージェントに質問
result = agent("25 * 48 は?計算過程も説明して。")
# 最後の応答のみを出力
print(result.message)
INFO | my_agent | 25 × 48の
INFO | my_agent | 計算を行います。計
INFO | my_agent | 算過程も含めて説
INFO | my_agent | 明しますね。
INFO | my_agent |
[ツール使用: calculator]
INFO | my_agent | **
INFO | my_agent | 答え: 1200**
INFO | my_agent |
**計算過程
INFO | my_agent | の説明:**
INFO | my_agent |
25 × 48
INFO | my_agent | = 1200
この
INFO | my_agent | 計算は以下の
INFO | my_agent | ように考えること
INFO | my_agent | ができます:
INFO | my_agent |
1. **分解して
INFO | my_agent | 計算する方法:**
INFO | my_agent | - 25 × 48
INFO | my_agent | = 25 × (50
INFO | my_agent | - 2)
INFO | my_agent |
- =
INFO | my_agent | 25 ×
INFO | my_agent | 50 -
INFO | my_agent | 25 × 2
INFO | my_agent | - = 1
INFO | my_agent | 250 -
INFO | my_agent | 50
INFO | my_agent |
- = 1200
2
INFO | my_agent | . **別
INFO | my_agent | の分解方法:**
INFO | my_agent |
-
INFO | my_agent | 25 × 48 = (
INFO | my_agent | 20 + 5) ×
INFO | my_agent | 48
- = 20
INFO | my_agent | × 48 + 5
INFO | my_agent | × 48
- =
INFO | my_agent | 960 +
INFO | my_agent | 240
- = 1200
INFO | my_agent |
3. **
INFO | my_agent | 25の特
INFO | my_agent | 性を
INFO | my_agent | 利用した方法:**
INFO | my_agent |
- 25
INFO | my_agent | = 100
INFO | my_agent | ÷
INFO | my_agent | 4なので
INFO | my_agent |
- 25
INFO | my_agent | × 48
INFO | my_agent | = (
INFO | my_agent | 100 ÷ 4
INFO | my_agent | ) × 48 = 100
INFO | my_agent | × 48
INFO | my_agent | ÷
INFO | my_agent | 4
INFO | my_agent | = 4
INFO | my_agent | 800 ÷
INFO | my_agent | 4
INFO | my_agent | = 1200
INFO | my_agent |
ど
INFO | my_agent | の方法で
INFO | my_agent | も答えは**
INFO | my_agent | 1200**にな
INFO | my_agent | ります。
{'role': 'assistant', 'content': [{'text': '**答え: 1200**\n\n**計算過程の説明:**\n\n25 × 48 = 1200\n\nこの計算は以下のように考えることができます:\n\n1. **分解して計算する方法:**\n - 25 × 48 = 25 × (50 - 2)\n - = 25 × 50 - 25 × 2\n - = 1250 - 50\n - = 1200\n\n2. **別の分解方法:**\n - 25 × 48 = (20 + 5) × 48\n - = 20 × 48 + 5 × 48\n - = 960 + 240\n - = 1200\n\n3. **25の特性を利用した方法:**\n - 25 = 100 ÷ 4なので\n - 25 × 48 = (100 ÷ 4) × 48 = 100 × 48 ÷ 4 = 4800 ÷ 4 = 1200\n\nどの方法でも答えは**1200**になります。'}]}
それぞれの詳細は以下
ここまでの所感
Quickstartはほんとにシンプルなものなので、まだなんともかんとも。Human-in-the-loopが標準っぽく動いてるのは良さそうというのはあるけど。とりあえずドキュメントで各コンポーネントの中味を確認していく必要がありそう。
そういえば、以前もマルチエージェントフレームワークあったよね
何やら名称が変わってた。
まあ上のはAWSLabsだし、Strands Agentsはv1.0だし(GAってことなのかな?)、ってことを踏まえると、立ち位置がどう違うのか?わからないけど、とりあえずはStrands Agents触っておけば良さそうな気はしてる。
ということで各コンポーネントのコンセプトを見ていく。続きはこちらで。
MCPサーバもあるので開発時に使えそう