Strands AgentsのSteeringについて調べた
v1.19.0でリリースされたStrands AgentsのSteeringについて調べたので、その内容を書きます。
まだ、Experimental機能なので、本番環境での使用は推奨されていません。
Steeringとは
AIエージェントで複数ステップのタスクを実行する際に、従来だとプロンプトに全ての手順を記載していました。例えば、メールを送るエージェントの場合、以下のようなプロンプトを使用していました。
あなたはメール送信エージェントです。
以下の手順とルールを厳守してメールを送信してください。
## 手順
1. メールの宛先を取得する
2. メールの件名を取得する...(省略)
## 禁止事項とルール
- 社外秘の情報(APIキーやパスワード)は絶対に含めないこと
- 相手が「重要顧客」リストにある場合は、敬語を「尊敬語」レベルに引き上げること
- 深夜(22時以降)の場合は、送信せずにドラフト保存にとどめること
- 件名には必ず【重要】などの隅付き括弧を含めること
- ...(他20行続く)
このくらいの手順や禁止事項であれば特に問題はないのですが、ここから新しい禁止事項や手順を追加したい場合、プロンプトが非常に長くなってしまいます。当然、プロンプトが長くなるとエージェントが指示を無視したり、ハルシネーションが発生したりするリスクが高まります。
この問題を解決するために、ワークフローを構築する手段もあるのですが、これだとエージェント特有の柔軟性が失われます。
今回リリースされたSteeringは、プロンプトに手順やルールを直接記載するのではなく、適切なタイミングでフィードバックを行うことで、エージェントの挙動を制御します。
仕組み
公式ドキュメントによると、Steeringは下記のように使用します。
from strands import Agent, tool
from strands.experimental.steering import LLMSteeringHandler
@tool
def send_email(recipient: str, subject: str, message: str) -> str:
"""メールを送信する"""
return f"{recipient}にメールを送信しました"
# 明るいトーンを確保するためのSteeringハンドラーを作成
handler = LLMSteeringHandler(
system_prompt="""
あなたはメールが明るくポジティブなトーンを維持するようにガイダンスを提供します。
ガイダンス:
- メールの内容のトーンと感情を確認する
- メッセージがネガティブまたは中立的な場合は、より明るい表現を提案する
- ポジティブな言葉遣いとフレンドリーな挨拶の使用を促す
エージェントがメールを送信しようとしたとき、メッセージのトーンが
適切に明るいかどうかを確認し、改善が必要な場合はフィードバックを提供してください。
"""
)
agent = Agent(
tools=[send_email],
hooks=[handler] # Steeringハンドラーをhooksとして統合
)
# エージェントがメールのトーンについてガイダンスを受ける
response = agent("重要な会議を何度も直前にリスケする顧客のtom@example.comに、イライラしたメールを送って")
print(agent.messages) # "Tool call cancelled given new guidance..."(新しいガイダンスによりツール呼び出しがキャンセルされました)と表示
LLMSteeringHandlerクラスでSteeringを定義して、Agentのhooksに渡すことで、エージェントの挙動を制御できます。
使用されているLLMSteeringHandlerの実装を詳しく見てみましょう。
まず、LLMSteeringHandlerでは下記のパラメータが存在します。
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
system_prompt |
Steeringの判断基準となるシステムプロンプト。ガイダンスのルールを自然言語で記述 | - |
prompt_mapper |
ツール呼び出し情報(ツール名、引数など)をSteering用のプロンプトに変換する | DefaultPromptMapper |
model |
Steering判断用のLLMモデル | エージェントと同じモデル |
context_providers |
Steering判断時に参照する追加情報を収集する |
[LedgerProvider()](ツール呼び出しの履歴、タイミング、結果を追跡) |
Steeringは、エージェントがツールを呼び出そうとしたタイミング(BeforeToolCallEvent)で判断を行い、以下の3つのアクションのいずれかを返します。
(公式ドキュメントの画像を拝借しました)

画像をまとめると以下の通りです。
| Action | 動作 |
|---|---|
| Proceed | そのままツール実行を許可する |
| Guide | ツール呼び出しをキャンセルし、エージェントにフィードバックを返す。エージェントはこのフィードバックを受けて別のアプローチを試みる |
| Interrupt | ツール実行を一時停止し、人間の入力を待つ。承認されれば実行、拒否されればキャンセル |
このように、Steeringを使用することで、プロンプトに長々とルールを書かなくても、エージェントの挙動を柔軟に制御できるようになります。
使ってみた
「仕組み」セクションで紹介したコードを実際に実行してみました。実行すると、以下のようにトレースされました。

流れを整理すると以下の通りです。
- 人間がエージェントに「イライラしたメールを送って」と指示する
- エージェントがネガティブなトーンのメール文章を作成する
-
send_emailツールを呼び出そうとする - Steeringがツール呼び出しを検出し、内容を評価 → 修正するために
Guideアクションを返す - ツール呼び出しがキャンセルされ、「ポジティブなトーンに修正してください」とフィードバックが返る
- エージェントがフィードバックを受けて、ポジティブなトーンのメール文章に修正する
- 再度
send_emailツールを呼び出そうとする - Steeringがツール呼び出しを検出し、内容を評価 → 問題なしと判断し
Proceedアクションを返す - ツール実行が許可され、メールが送信される
1つ目のツール呼び出しの中身は以下のようになっています。

「非常に困惑」や「大きな支障」といったかなりネガティブなトーンでメールが生成されているので、Steeringが Guideアクションを返しています。このレスポンスを受けて、エージェントは再度メールを生成し、send_emailツール呼び出しを行っています。

2回目のツール呼び出しでは、statusがProceedとなっていますね。そのままツール実行が許可されたことを意味します。このように、Steeringを使用することで、エージェントの挙動を動的に制御できました。
次に、Interruptアクションも試してみました。LLMSteeringHandlerのsystem_promptを以下のように変更します。
handler = LLMSteeringHandler(
system_prompt="""
あなたはメール送信の安全性を確認するガイダンスを提供します。
ガイダンス:
- メールの宛先が社外ドメインの場合は、人間の確認を求める
- 添付ファイルがある場合は、人間の確認を求める
- 重要な内容(金額、契約、機密情報など)の場合は、人間の確認を求める
- 人間の確認を求める際は理由を含めたメッセージを表示する
- 上記以外で内容に問題がなければ、そのまま送信を許可する
"""
)
また、エージェントには「tom@external-company.com(社外)に契約更新の見積もり100万円についてメールを送って」と指示します。正しくSteeringが働くと、ツール呼び出しが一時停止され、人間に確認を求めるはずです。
実行結果は以下の通りです。

実行結果を見ると、Interruptアクションが返され、ツール呼び出しが一時停止されていることが確認できました。
Interruptを継続する場合は、下記のように記載するとツール実行が続行されます。
# interrupt_idの取得
interrupt_id = result.interrupts[0].id
# 実際にツール実行を続行する場合
responses = [
{
"interruptResponse": {
"interruptId": interrupt_id,
"response": "承認します。",
},
},
]
result = agent(responses)
langfuse上のinputはnullになっていますが、画像のようにきちんとツールが実行され、メールが送信されました。

まとめ
Strands AgentsのSteeringについて調べた内容を書きました。プロンプトに長々とルールを書かなくても、適切なタイミングでフィードバックを与えることで、エージェントの挙動を柔軟に制御できました。
しかし、毎回Tool呼び出しにSteering用のLLMが介入するので、もしかしたらレスポンスが遅くなったり、コストが増えたりする可能性があります。実際の運用で使用する場合は、このあたりも考慮する必要がありそうです。
Discussion