Agents for Amazon Bedrockでのエージェント作成を試す① 〜初期設定・アクショングループ作成編〜
はじめに
Amazon Bedrock上で簡単にエージェントを作成・デプロイすることができる、Agents for Amazon Bedrockを試していきます。
Amazon Bedrockを使うところまでは、前記事をご参考ください。
Agents for Amazon Bedrockとは
Agents for Amazon Bedrockは、Amazon Bedrock上で提供されるエージェントの構築のためのツールです。これを使うことで、数ステップの設定で簡単にエージェントを構築・デプロイすることができます。
LangChainで構築するエージェントのツールに当たるものはアクショングループと呼ばれ、Lambdaを使って作成します。Amazon Bedrockの利点同様、AWSの各種サービスとの連携が得意なようなので、AWSで色々作る上では重宝しそうです。
また、Knowledge BaseというRAG構築ツールとの連携も簡単にできます。チャットでサポートしてもらいつつ、社内文書に対する質問にも答えてくれるようなエージェントがさっくり作成できる優れものです。
今回は、このAgents for Amazon Bedrockを使って簡単なアクションを実行できるエージェントを作成し、動作確認してみたいと思います。
エージェントを作成する
早速エージェント本体を使っていきます。
Bedrockを選択し、左側、オーケストレーション>エージェントを選択します。 ただし、対応しているリージョンでしか表⽰されないので注意が必要です。今回は現時点(06/30)でClaude 3に対応しているバージニア北部を使っています。
※7月から東京リージョンでもClaude 3シリーズが使用可能となりました。
「Agent builder」が起動します。
「エージェントを作成」をクリックし、適当に名前をつけて「作成」をクリック。
Agent builderが立ち上がるので、適当にエージェントに担ってもらう設定を書きます。今回は、Alexaのような家電の操作ができるという設定を与えてみます。
情報が足りない場合は適宜やり取りしてくれる方が望ましいので、ユーザー入力はEnabledにしておきます。
各種情報が記載できたら、一度保存しておきます。
これでとりあえずエージェントの作成は完了です。続いて、エージェントが使えるツールを作成していきます。
アクショングループの作成
アクショングループはLambdaを使って作成します。エージェントによってツールの利用が必要と判断すれば、引数の指定とともにこのLambdaが実行されることになります。
今回は、擬似的に家電の状態を返すLambdaを作ってエージェントに渡してみます。
アクショングループの項目より、追加をクリックします。
アクショングループの定義方法には、機能詳細の定義するとスキーマを生成してくれる簡単な方法(Difine with function details)と、LambdaやAPI Getewayの呼び出しを行うようにAPIスキーマを自分で定義する方法(Difine with API schemas)があります。
権限周りの設定など自動でやってくれて簡単っぽいので、前者の方を使うこととします。
アクショングループ内で実際に動かす関数を設定することができます。これが、エージェントによって実行されるツール部分となります。今回は、機器の情報を取得できる関数というものを作成してみます。
1つのアクショングループ内で関数は複数定義することができますが、Lambdaは1つしか割り当てることができないようです。なので、Lambdaの中で、エージェントによって指定された関数の名前に応じて実行する処理を切り替えるような仕組みを作っておく必要がありそうです。
今作成したアクショングループをクリックして、編集画面に入ります。
Action group invocation項目内で、アクショングループを作成したときに作成されたLambda関数が選択されている状態になっているので、表示をクリックしてLambdaの編集画面に入ります。
この時点で下記のようなダミーコードが記載されています。
import json
def lambda_handler(event, context):
agent = event['agent']
actionGroup = event['actionGroup']
function = event['function']
parameters = event.get('parameters', [])
# Execute your business logic here. For more information, refer to: https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html
responseBody = {
"TEXT": {
"body": "The function {} was called successfully!".format(function)
}
}
action_response = {
'actionGroup': actionGroup,
'function': function,
'functionResponse': {
'responseBody': responseBody
}
}
dummy_function_response = {'response': action_response, 'messageVersion': event['messageVersion']}
print("Response: {}".format(dummy_function_response))
return dummy_function_response
このダミーコードを編集する形でLambdaの中身を作成していきます。今回は下記のような形で、機器の種類を指定すると、その状態っぽい値をランダムに返してくれる関数を作成してみました。
import json
import random
def lambda_handler(event, context):
# eventから各種パラメータの取り出し
agent = event['agent']
actionGroup = event['actionGroup']
function = event['function']
parameters = event.get('parameters', [])
# 関数の存在チェック
if function not in globals() or not callable(globals()[function]):
return_body = f"Error: Function '{function}' does not exist."
else:
return_body = globals()[function](parameters)
# 関数の実行結果部分
responseBody = {
"TEXT": {
"body": return_body
}
}
# action実行の結果部分
action_response = {
'actionGroup': actionGroup,
'function': function,
'functionResponse': {
'responseBody': responseBody
}
}
function_response = {'response': action_response, 'messageVersion': event['messageVersion']}
print("Response: {}".format(function_response))
return function_response
# 実際に実行される関数部分
def get_device_status(parameters):
device_type = next(p for p in parameters if p['name'] == 'device_type')['value']
out = {}
# 証明
if device_type == "light":
light_status = random.choice(["ON", "OFF"])
out["status"] = light_status
if light_status == "ON":
light_brightness = random.choice(range(1, 5))
out["brightness"] = light_brightness
# エアコン
elif device_type == "air-conditioner":
ac_mode = random.choice(["cooling", "heating", "blast", "OFF"])
out["mode"] = ac_mode
if ac_mode not in ["blast", "OFF"]:
ac_temp = random.choice(range(16, 30))
out["temperature"] = ac_temp
else:
out = "NoDevice"
return "The device status of {} is {}".format(device_type, out)
前述のように、このスクリプト内でエージェントが選択するAction group functionに応じて処理を切り替える必要があります。その部分を、コンソールで設定したAction group functionと同じ名前を持つ関数を定義し、それをlambda_handlerで呼び出すようにしました。
関数の呼び出し部分は関数名に応じた分岐で書いても良かったですが、globals()[function]とすることで、lambda_handler内がスッキリするし、今後機能追加していきたい時はその関数を追加するだけでいいので便利です。
次に、このLambdaがちゃんと動くかをテストします。AgentがLambdaを呼び出す際のイベント形式については、下記に記述があります。このイベント形式に合わせてテストケースを作成すれば、テストを行うことができます。
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/agents-lambda.html
Define with function detailsを利用した場合のイベント形式は、このようになります。
{
"messageVersion": "1.0",
"agent": {
"name": "string",
"id": "string",
"alias": "string",
"version": "string"
},
"inputText": "string",
"sessionId": "string",
"actionGroup": "string",
"function": "string",
"parameters": [
{
"name": "string",
"type": "string",
"value": "string"
},
...
],
"sessionAttributes": {
"string": "string",
},
"promptSessionAttributes": {
"string": "string"
}
}
Lambda画面の「テスト」タブをクリックし、下記のようなテストケースを作ってテストしました。
無事に通ったのでアクションの設定は完了です。最後に、動作確認をします。
エージェントをテストする
Agent builder画面の右端に、「テスト」という画面があります。そこにチャットをプロンプトを打ち込めば、作成したエージェントの動作確認をすることができます。
最初はエージェントに変更を加えた状態だと、「エージェントが最新の変更をテストできるように準備します。」と表示されているので、「準備」をクリックします。
準備が完了したら、適当にプロンプトを打ち込んでみます。
「エアコンの状態を教えて」という依頼に対し、エアコンの状態を返してくれています。うまく動いています。
「トレースを表示」をクリックすると、モデルに入力されているプロンプト(の一部)や中間の出力などを確認することができます。上記の例では、モデルがget_device_status関数をparameterのdevice_typeにair-conditionに設定して呼び出しができているのがわかります。
ちなみに、エージェントは会話履歴を保持してくれているので、「もう一度確認して。」と言った依頼でも、エアコンの状態のことであると理解してくれています。
この辺り、特別な設定をしなくてもよしなにやってくれるのが地味に助かりますね。
最後に
Agents for Amazon Bedrockを使って、簡単なエージェントを作成することができました。
LangChainとは異なり、エージェントの構築にはほとんどコーディングを必要としません。ツール部分はLambdaで構築する必要があり、自分はあまり慣れていないので、少し身構えてしまうところはありますが、AWSの各種サービスをうまく連携させることができるのは使い勝手が良いです。
簡単に構築することができる一方、カスタマイズ性は少し落ちますので、求めることが増えてくると、LangChainで自作する必要が出てきそうだな、と言うのが今の所の感想です。
とはいえ、まだまだこれから機能が充実していくとも思うので、キャッチアップして色々試していきたいです。
ちなみに、ちんたら記事に起こしている間にAmazon Bedrockに関する素晴らしい本が発売されました。Agentの内容も載っています。私もまだ全部は読めてはいませんが網羅的でわかりやすいのでおすすめです。
やることが溜まりまくっていますが、次はRAGの構築やもう少し使えるエージェントを作ってみるなどやってみようと思います。