はじめに
前回の記事「Bedrockを使って、S3に置いた独自データに応答してくれるチャットボットを作ってみた。」では、Knowledge Bases for Amazon BedrockとAgents for Amazon Bedrock、Pineconeを使用してS3バケット配置したオブジェクトを対象としたRAGを構築しました。
本記事では、Agents for Amazon BedrockのAction groupsを使用し、自然言語でBacklogの課題やWikiを検索するチャットボットを作成します。
今回は、AgentsがBacklog APIを使用して課題やWikiをキーワード検索し、その結果をもとにAgentsが回答を組み立てます。RAGではないので、キーワードに対して類似度の高いものを検索するようなことはできません。検索の精度や結果はBacklog APIに異存します。そのため、Backlogの検索にAgents for Amazon Bedrockが向いているかはさておき、Action groupsの構築方法や運用の理解を深めることに重点を置きました。
デモ画面
契約について検索してください
というメンションに対して、契約
というキーワードでBacklogの課題とWikiを検索し、その結果をSlackに返しています。Backlogの課題はプロジェクトをまたいで検索できますが、Wikiはプロジェクトキーを指定して検索する必要があります。そのため、チャットボットはプロジェクトキーを問う返信を行います。検索結果には、課題の検索結果が10件とWikiの検索結果が3件表示されています。この画面は、最も期待どおりに動作した例です。毎回、課題とWikiの検索結果が得られるわけではありませんでした。
また、このデモ画面内には福島
という名称が含まれていますが、Wikiに記述している名称は別のものです。同様に、鈴木建設
や鈴木様
、池田様
もWikiに書かれている内容とは異なります。Agentsが回答を作成する際に勝手に情報を書き換えたようです。この点はプロンプトで制御できるのかもしれません。
構成図
参考情報
環境構築
- 以下のリソースを作成・削除・変更できる権限をもつAWSユーザーを利用すること
- AWS IAM
- AWS Lambda
- AWS CloudFormation
- AWS Secrets Manager
- Amazon API Gateway
- Amazon S3
- Amazon CloudWatch Logs
- Amazon Bedrock
- 使用するAWSリージョンは、us-east-1
- Slack Appを作成するためのアカウントや権限を持っている
開発環境構築
作業環境のOSバージョン
Windows 11上のWSLでUbuntu 23.04を動かしています。
$ cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Ubuntu 23.04"
Python環境
$ python3 --version
Python 3.12.0
$ python3 -m venv .venv
source .venv/bin/activate
$ pip3 install --upgrade pip
$ pip3 --version
pip 24 from /home/xxx/.venv/lib/python3.12/site-packages/pip (python 3.12)
AWS環境構築
aws configureコマンドでデフォルトのリージョンやクレデンシャルを設定するか、もしくは~/.aws/configや~/.aws/credentialsを用意します。
AWS SAM CLIインストール
AWS上でサーバーレスアプリケーションを構築、実行するAWS SAMを使用します。
Installing the AWS SAM CLI の手順に従い、AWS SAM CLIをインストールします。今回はx86_64環境でLinux OSを使用するため、x86_64 - command line installerの手順を実行します。
$ sam --version
SAM CLI, version 1.108.0
version 1.104.0 ~ 1.106.0ではビルド時に不必要なWarningメッセージが出力されるため修正パッチが適用された1.107.0以降を使用します。
この件は以下のissueで解決しています。
Slack Appの作成
Slac APIを開き、From scratchからSlack Appを作成します。ここでは、App Nameをaws-sam-bedrock-action-group
とします。
Basic Information画面のApp Credentialsに表示されているSigning SecretはSlackSigningSecret
として次のSecret Managerのシークレット登録に使用します。
OAuth & Permissions画面のOAuth Tokens for Your WorkspaceにあるBot User OAuth Tokenは、SlackBotToken
として次のSecret Managerのシークレット登録に使用します。
OAuth & Permissions画面のBot Token Scopesにapp_mentions:read
とchat:write
を追加します。
Slackシークレット情報をSecret Managerに登録
あらたにシークレットを作成し、ここまでの手順で作成した以下のシークレット情報を登録します。シークレット名は、Bedrock-sam-secrets-action-group
とします。このシークレット名は後述のtemplate.yaml内のSECRET_NAMEに定義しているため、異なるシークレット名を使用する場合はtemplate.yaml内のSECRET_NAMEも変更します。
シークレットキー | 値 |
---|---|
SlackSigningSecret | 前項のSlackのSigning Secret |
SlackBotToken | 前項のSlackのBot User OAuth Token |
アプリケーションの構築
ディレクトリ構造は以下のとおりです。
.
├── README.md
├── assets
│ ├── backlogSearch.json
│ └── backlogSearch.yml
├── backlogSearch
│ ├── __init__.py
│ └── app.py
├── bedrock_action_group_slack_app
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── samconfig.toml
└── template.yaml
上記のソースコード類はGithubリポジトリにアップロードしています。
assets/backlogSearch.yml, assets/backlogSearch.jsonの構成
assets/backlogSearch.ymlもしくはassets/backlogSearch.jsonは、Backlogの課題やWikiを検索するためのOpenAPIスキーマです。これらを任意のS3バケットに配置します。後述のAction groupsの設定で使用します。
OpenAPIスキーマは、Backlog API とはで公開されているAPI仕様をもとに作成しました。ほぼ、GitHub Copilotが生成してくれたものを使用しています。
__init__.py
は空のファイルです。
bedrock_action_group_slack_app/requirements.txtは以下のとおりです。boto3やrequestsも必要ですが、それらはLambdaレイヤーで追加するようtemplate.yamlに記述します。
slack-bolt
slack-sdk
template.yamlの構成
AWS SAM テンプレートファイル(template.yaml)に、作成するAWSリソースを定義します。
ここでは、ふたつのLambda関数を作成します。ひとつはSlack Event Subscriptionsを受け取り、もうひとつはBacklogの課題やWikiを検索するための関数です。それぞれ、Lambda関数用ロールやポリシー、Lambdaの環境変数などを記述します。その他に、以下のレイヤーやリソースベースポリシーが含まれます。
- Lambda関数からSecrets Managerにアクセスするための
AWS-Parameters-and-Secrets-Lambda-Extension
レイヤー - Lambda関数内からimportするためのboto3やrequestsをパッケージにしたレイヤー
- BedrockkからLambda関数を扱うためのリソースベースポリシー
AWS-Parameters-and-Secrets-Lambda-Extension
レイヤーのARNはUsing Parameter Store parameters in AWS Lambda functionsのドキュメントにリージョンやアーキテクチャ毎に掲載されています。
boto3やrequestsのレイヤーは、keithrozario/KlayersのリポジトリにあるList of ARNs
に、Pythonバージョンとアーキテクチャ毎に掲載されています。今回は、Python 3.12, arm64を使用するので、Layers for Python 3.12-arm64の一覧からus-east-1用のARNを使用します。
Lambdaレイヤーは以下のようにtemplate.yamlに記述しています。
Layers:
# Layer for AWS Parameter Store and Secrets Manager
- arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64:11
# Layer for requests and boto3
- arn:aws:lambda:us-east-1:770693421928:layer:Klayers-p312-arm64-requests:1
- arn:aws:lambda:us-east-1:770693421928:layer:Klayers-p312-arm64-boto3:1
適用すると、Lambda関数のLayersに以下のように表示されます。
リソースベースポリシーは、以下のようにtemplate.yamlに記述しています。
BacklogSearchFunction:
Type: AWS::Serverless::Function
Properties:
(途中省略)
# Resouse based policy for lambda.
PermissionForBacklogSearchToInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt BacklogSearchFunction.Arn
Action: lambda:InvokeFunction
Principal: bedrock.amazonaws.com
適用すると、Lambda関数の設定のResource-based policy statementsに以下のように表示されます。
template.yaml内のEnvironmentにあるSECRET_NAME
とREGION_NAME
には、それぞれ先ほど作成したSecrets Managerのシークレットの名前とリージョンを設定します。
Lambda関数の実行ロール内のポリシーではResource: "*"
としていますが、本番環境などでは許可するリソースを特定したほうがよいでしょう。例えば、AgentsのResourceを特定する場合は以下のように指定します。
Resource: "arn:aws:bedrock:{Region}:{AWS Account ID}:agent-alias/{Agents ID}/{Agents Alias}"
samconfig.tomlの構成
SAM CLIの実行設定ファイル(samconfig.toml)に、SAM CLIを実行する際の設定を定義します。AWS SAMのチュートリアル: Hello World アプリケーションのデプロイを実行した際に作成されるsamconfig.tomlをもとにしています。今回の例では、以下の点を変更しています。
-
[default.global.parameters]
セクションのstack_nameを"sam-app"から"bedrock-slack-rag-appt"に変更 -
[default.deploy.parameters]
セクションにregion指定を追加 -
[default.deploy.parameters]
セクションのcapabilitiesを"CAPABILITY_IAM"から"CAPABILITY_NAMED_IAM"に変更
bedrock_action_group_slack_app/app.pyの構成
bedrock_action_group_slack_app/app.pyは、Slack Event Subscriptionsを受け取り、Slackのメンションイベントに応答するための関数です。boto3クライアントを使用してAgentsのinvoke_agentメソッドを呼び出し、Agentsにメッセージを送信します。そして、レスポンスをSlackに返します。
client = boto3.client(
service_name='bedrock-agent-runtime',
region_name="us-east-1",
config=Config(
read_timeout=180,
)
)
response = client.invoke_agent(
inputText=prompt,
agentId=agent_id,
agentAliasId=agent_alias_id,
sessionId=session_id,
enableTrace=enable_trace,
)
backlogSearch/app.pyの構成
backlogSearch/app.pyは、Agentsからイベントデータを受け取り、Backlogの課題やWikiを検索するための関数です。
event["apiPath"]が'/issues'の場合は課題を検索し、'/wikis'の場合はWikiを検索します。このapiPathは、AgentsのAction groupsの設定で読み込むOpenAPIスキーマのpathsに記述されています。それぞれの関数の結果をAgentsの形式に合わせて返します。
if api_path == "/issues":
body = issue_search(parameters)
elif api_path == "/wikis":
body = wiki_search(parameters)
response_body = {"application/json": {"body": json.dumps(body)}}
action_response = {
"actionGroup": event["actionGroup"],
"apiPath": event["apiPath"],
"httpMethod": event["httpMethod"],
"httpStatusCode": 200,
"responseBody": response_body,
}
api_response = {"messageVersion": "1.0", "response": action_response}
return api_response
課題検索を行うissue_search関数では、レスポンスボディの中から課題キー、課題の件名、課題の状態、担当者、期限日を返します。全ての情報を返そうとすると検索結果の件数や課題本文のサイズなどでLambda response exceeds maximum size 25KB
のエラーが発生するため返す情報を絞っています。
Wiki検索を行うWiki_serach関数では、レスポンスボディの中からWiki名と本文の先頭100文字を返します。これも同様にエラーを回避するために情報を絞っています。
ビルド
template.yamlがあるディレクトリで、ビルドコマンドを実行します。
sam build
ビルドに成功すると、以下のようなメッセージが表示されます。
Starting Build use cache
Building codeuri: /home/xxx/aws-sam-bedrock-action-group-slack-app/backlogSearch runtime: python3.12
metadata: {} architecture: arm64 functions: BacklogSearchFunction
Manifest is not changed for (BedrockAssitantFunction), running incremental build
Building codeuri: /home/xxx/aws-sam-bedrock-action-group-slack-app/bedrock_action_group_slack_app
runtime: python3.12 metadata: {} architecture: arm64 functions: BedrockAssitantFunction
requirements.txt file not found. Continuing the build without dependencies.
Running PythonPipBuilder:CopySource
Running PythonPipBuilder:CopySource
Running PythonPipBuilder:CopySource
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
デプロイ
ビルドでエラーがなければsam deployコマンドを実行し、デプロイを行います。
sam deploy
デプロイが成功すると、CloudFormationがチェンジセットを実行する様子とともに以下のような情報がコンソールに出力されます。
CloudFormation outputs from deployed stack
----------------------------------------------------------------------------------------------------------------------------------
Outputs
----------------------------------------------------------------------------------------------------------------------------------
Key BacklogSearchFunction
Description Backlog Search Lambda Function ARN
Value arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:bedrock-action-group-slack-a-BacklogSearchFunction-xxxxxxxxxxxx
Key BedrockAssitantApi
Description The URL of Slack Event Subscriptions
Value https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/slack/events
Key BedrockAssitantFunction
Description Bedrock Assistant Lambda Function ARN
Value arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:bedrock-action-group-slack-BedrockAssitantFunction-xxxxxxxxxxxx
Key BedrockAssitantFunctionIamRole
Description Implicit IAM Role created for Bedrock Assistant function
Value arn:aws:iam::xxxxxxxxxxxx:role/bedrock-action-group-slack-app-lambda-role
----------------------------------------------------------------------------------------------------------------------------------
Slack Appの設定
メンションイベントに応答するために、Event Subscriptions画面のSubscribe to bot eventsにapp_mentionを追加します。
Event Subscriptions画面のEnable EventsをOnにし、Request URLにデプロイ時に出力されたURL https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/slack/events を入力します。
Verified ✓
と表示されれば、正しいURLが入力されています。レスポンスが得られるようになるまで時間がかかる場合があります。正しいURLを入力しているにもかかわらずVerifiedとならない場合は、時間をおいて再試行します。
設定を追加後、画面最下部にあるSave Changesをクリックし内容を保存します。
Slack Workspaseにアプリをインストール
Install App画面のInstall to Workspaceをクリックし、Slack AppをWorkspaceにインストールします。
インストールに成功すると、Thank you!画面が表示されます。Slackアプリをインストールしている場合はclick here
のリンク、Webブラウザを使用している場合は、this link
をクリックしてSlackを開きます。
Bedrock環境構築
Agentsの設定
Agentsを開き、Agentを作成します。
入力/選択項目 | 内容 |
---|---|
Agent name | 任意のAgents名 |
Agent description | Agentsの説明文 |
User Input | Yes |
IAM Permissins | 新規にロールを作成する場合は、Create and use a new service role。既存のロールを利用する場合は Use an existing service role |
Idle session timeout | 30 Minutes |
"User Input" は、Agentsが回答するための十分な情報がない場合に、ユーザーに追加の情報を求めることができるかを選択します。Yes,Noのどちらを選択しても良いのですが、Yesのほうが会話らしくなると思います。
"Idle session timeout"は、入力が何もない状態でセッションを維持する時間。セッションが有効な間であれば、以前の会話履歴を引き継いで会話を続けることができます。
Agentsが使用する言語モデルを選択します。記事執筆時点では、Anthropic社の"Claude instant V1","Claude instant V2", "Claude instant V2.1"のみが選択可能です。
Instructions for the Agent
には、エージェントが実行するタスクについて明確かつ具体的な指示を入力します。
ここでのポイントは、Agentsがタスクを実行する順番や実行するLambda関数の理解を深めるために具体的な指示を与えることです。例えば、 Agentsは実行するLambda関数に個々のクエリパラメータを処理する実装を見つけられない場合に"依頼内容を実現する実装がない"と判断し処理を中断することがありました。そのため、 それを防ぐ指示を含めます。また、依頼内容に対してOpenAPIスキーマのどのパスを使用するかを指示することで、Agentsが適切な処理を行う助けになるようです。ただし、この指示は40~1,200文字以内という制限があります。
上記のポイントや指示を踏まえて、以下のような指示を入力します。
指示内にあるGET::backlogSearch::getIssueListやGET::backlogSearch::getWikiListのbacklogSearch
は、Action groupsの設定名です。getIssueList
やgetWikiList
は、OpenAPIスキーマのpathsに記述されているoperationIdです。
上記のポイントや以下の指示は、Agentsの反応を試す中で経験的に得たものです。おおよそ期待した結果が得られたので、おそらく考え方は合っていると思います。
あなたはBacklogの課題やWikiを検索するするプロフェッショナルです。
以下の手順でデータを検索してください。一度にすべてを行ってはいけません。user::askuser を通じて1つずつ実行してください。
1. 私の依頼内容を正しく理解してください。例えば、私が"点検に関する課題を10件探し、件名の昇順で一覧にしてください。"と依頼した場合、検索キーワード=点検, 課題を取得する件数=10, 並び替えに使用する属性=件名, 並び順=昇順 のように依頼内容を適切に分解してください。
2. あなたは依頼内容に基づいてfunctionを実行します。functionのコードを参照してfunctionが依頼内容を処理可能か否かを推論する場合、コードの内のコメントも参照しコードの仕様を理解する必要があります。例えば、コード内に個々のクエリパラメータを処理する実装がない場合でも短絡的に"依頼内容を実現する実装がない"と判断してはいけません。クエリパラメータを辞書形式のリストで受け取って値を処理している可能性があります。functionがクエリパラメータをどのように処理しているか仕様がコメントに書かれているので、コメントも参照しfunctionの仕様を理解してください。
3. 与えられたAPIスキーマを正しく理解してください。そのうえで、分解した依頼内容をAPIスキーマをもとにパラメータに当てはめます。例えば、「1. 私の依頼内容の理解」で与えられた依頼内容の例の場合は、GET::backlogSearch::getIssueListに対してはkeyword=点検, count=10, sort=件名(summary), order=asc のようにパラメータに値を当てはめます。GET::backlogSearch::getWikiListに対しては、keyword=点検を当てはめます。
4. 私の依頼に対して、はじめに GET::backlogSearch::getIssueListを実行し続けてGET::backlogSearch::getWikiList実行します。それぞれの結果を提示してください。GET::backlogSearch::getIssueListの結果は、`課題キー`, `課題名`, `課題のステータス`, `担当者`, `期日`のみ提示してください。課題の期日が未設定の場合は、'なし'と回答します。GET::backlogSearch::getWikiListの結果は、Wiki名とWikiの内容を提示してください。
出力例:
```
検索結果
{課題キー}, {課題名}, {課題のステータス}, {担当者}. {期日}
Wikiの検索結果
{Wiki名}, {Wikiの内容}
```
続いて、Action groupsの設定を行います。ここで先ほどデプロイしたLambda関数や、OpenAPIスキーマを設定します。
assets/backlogSearch.jsonもしくはassets/backlogSearch.yamlを任意のS3バケットにアップロードし、そのオブジェクトパスをS3 Urlに設定します。
入力/選択項目 | 内容 |
---|---|
Action group name | 任意のAction groups名。先ほどの指示内容に合わせてbacklogSearch とします。 |
Description | Action groupsの説明文 |
Select Lambda function | 先ほどデプロイしたLambda関数のうち、名前にBacklogSearchFunction が含まれる関数を選択。 |
S3 Url | S3バケット内にあるbacklogSearch.yamlかbacklogSearch.jsonのオブジェクトパス |
今回はknowledge baseは使用しないので、何も入力せず次に進みます。
Agentsの設定が完了すると、概要がこのように表示されます。APIからAgentsを呼び出す際にID
を使用します。 AgentsのIDは後述のSecret Managerのシークレット登録に使用します。
右上のEditからAgentsの設定を変更することができます。
Agents エイリアスの作成
AgentsをAPIから呼び出す際は、Agents本体ではなくエイリアスを使用します。そこで、Agentsに対するエイリアスを作成します。
右上のCreateから新規作成画面に進みます。
エイリアスの情報を入力します。
入力/選択項目 | 内容 |
---|---|
Alias name | 任意のエイリアス名 |
Description | エイリアスの説明文 |
Create a new version and associate it to this alias. / Use an existing version to associate this alias. | エイリアスを新しいバージョンと既存バージョンのどちらに紐付けるか |
エイリアスを作成すると、バージョンとともに以下のようにリストが表示されます。APIからAgentsを呼び出す際にAlias ID
を使用します。 Alias IDは後述のSecret Managerのシークレット登録に使用します。
Agentsの設定変更はのWorking draft
の項目から行えます。なかでもAdvanced promptsでは、言語モデルに対して前処理や後処理の詳細な指示テンプレートを上書き/拡張できます。
設定を変更した際はplaygroundで動作確認が行ったあと、エイリアスを作成します。
Working draft内にあるAdvanced promptsの項目で、Editをクリック。
プロンプトや、Temperature、出力の長さなどがカスタマイズできます。
Agentsのシークレット情報をSecret Managerに登録
AgentsのIDとAlias IDをSecret Managerに登録します。登録先のシークレットは、SlackのSlackSigningSecretやSlackBotTokenを登録したものです。
あわせて、BacklogのAPIキーと自身のBacklogのURLを登録します。BacklogのURLは、https://[スペースID].backlog.com/ もしくはhttps://[スペースID].backlog.jp/ です。
動作確認
任意のチャンネンルに、@aws-sam-bedrock-action-group
(Slack Appの作成時に設定した名前)を招待し、メンション形式で依頼をポストします。動作しない場合のログやOpenAI APIが返すレスポンスは CloudWatch Logs に出力されているログが参考になります。
作成したリソースの削除
最後に、作成したアプリケーションを削除する手順です。リソースを削除するには sam delete コマンドを実行します。
sam delete
まとめ
Agents for Amazon BedrockとAction groupsを使用することで、SlackアプリとBacklogのAPIを組み合わせて自然言語での問い合わせに対してBacklog課題やWikiを検索することができました。Backlog公式のOpenAPIスキーマがないため自分で作成する必要がありましたが、その作成もGitHub Copilotの助けを借りてスムーズに進めることができました。
このアプリケーションは、RAGと異なり検索の精度や結果はBacklog APIに異存するため、検索キーワードと類似性が高い課題やWikiを抽出するといったことができません。Knowledge Bases for Amazon Bedrockで構築したRAGと組み合わせ、相互の結果をもとにAgentsに回答を求めることで、より高度な問い合わせに対応することができるかもしれません。
前回の記事に引き続きAWS SAMを使用してアプリケーションを構築しましたが、今回はLambdaレイヤーやリソースベースポリシーを使用することで、Lambda関数の設定をより細かく行う設定を知ることができました。