はじめに
Agents for Amazon Bedrock と Knowledge Base for Amazon Bedrock が CloudFormation によるデプロイをサポートしました。
以前 Agents for Amazon Bedrock を活用した Slack アプリを作成する記事を投稿したのですが、Agent はコンソール操作で作成していました。これが Agent 用の Lambda 関数などと一緒に一つのテンプレートでデプロイできるようになります。便利ですね!
この記事では CloudFormation 化する際に引っ掛かったポイントや、知っておくと良い点などを紹介します。
以下で公開しているテンプレートは Agent も CloudFormation で作成できるよう更新済です。記事の最後 にも載せています。
Ref や Fn::GetAtt で取得できる戻り値あれこれ
組み込み関数の Ref
や Fn::GetAtt
を活用すると、ARN や ID などの値が取得でき便利です。戻り値の一覧は テンプレートリファレンス を参照いただければと思いますが、特にエージェントおよびエイリアスの ID/ARN は使用頻度が高いのではないかと思います。
リソース | 宣言例 | 取得できる値 |
---|---|---|
AWS::Bedrock::Agent | !Ref ResourceName | エージェントの ID |
AWS::Bedrock::Agent | !GetAtt ResourceName.AgentArn | エージェントの ARN |
AWS::Bedrock::AgentAlias | !GetAtt ResourceName.AgentAliasId | エージェントのエイリアス ID |
AWS::Bedrock::AgentAlias | !GetAtt ResourceName.AgentAliasArn | エージェントのエイリアス ARN |
例えば Agent のアクショングループに設定する Lambda 関数には Agent からの呼び出しを許可するリソースベースポリシーが必要です。以下のように記述できます。
Resources:
BedrockAgentFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !Ref BedrockAgentFunction
Principal: "bedrock.amazonaws.com"
SourceArn: !GetAtt BedrockAgent.AgentArn
他にも Agent を呼び出す Lambda 関数を定義する場合、呼び出す対象のエージェント ID と エイリアス ID が必要です。以下のように環境変数として定義しておくことで、Lambda 関数内から参照可能になります。また Lambda 関数の実行ロールのポリシーで Resource を制限したい場合、エイリアス ARN を指定します。
Resources:
SlackAppFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-SlackAppFunction"
CodeUri: "slack_app/"
Environment:
Variables:
BEDROCK_AGENT_ID: !Ref BedrockAgent
BEDROCK_AGENT_ALIAS_ID: !GetAtt BedrockAgentAlias.AgentAliasId
Policies:
- Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- "bedrock:InvokeAgent"
Resource:
- !GetAtt BedrockAgentAlias.AgentAliasArn
Agent 用のサービスロール
Agent へ設定するサービスロールに必要な権限や信頼ポリシーは以下のドキュメントを参照してください。
CloudFormation で作成する場合、例として以下のような定義となります。
Parameters:
pModelId:
Type: String
Description: The Model ID of the Bedrock Agent.
Default: "anthropic.claude-v2:1"
AllowedValues:
- "anthropic.claude-v2:1"
- "anthropic.claude-v2"
- "anthropic.claude-instant-v1"
Resources:
BedrockAgentRolePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: "Policy for Bedrock Agent"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: "Allow"
Action:
- "bedrock:InvokeModel"
Resource:
- !Sub "arn:aws:bedrock:${AWS::Region}::foundation-model/${pModelId}"
BedrockAgentRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "AmazonBedrockExecutionRoleForAgents_${AWS::StackName}"
ManagedPolicyArns:
- !Ref BedrockAgentRolePolicy
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: "Allow"
Action: "sts:AssumeRole"
Principal:
Service: "bedrock.amazonaws.com"
Condition:
StringEquals:
aws:SourceAccount: !Ref "AWS::AccountId"
ArnLike:
aws:SourceArn: !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:agent/*"
ロール名の Prefix が AmazonBedrockExecutionRoleForAgents_
である必要があります。これに従っていない場合、Agent 作成時に次のようなエラーが発生します。
Properties validation failed for resource BedrockAgent with message:
#/AgentResourceRoleArn: failed validation constraint for keyword [pattern]
Agent が使用する機能により、ポリシーで許可すべきアクションは異なります。最低限必要なのが bedrock:InvokeModel
で、使用していない機能に対するアクションは省略できます。
例えば OpenAPI の Schema ファイルを S3 に配置している場合は対象のオブジェクトに対する権限が必要ですし、ナレッジーベースを使用する Agent の場合はナレッジベースにアクセスする権限が必要です。
OpenAPI Schema の指定方法
S3 にアップロードしたオブジェクトを指定する
バケット名とオブジェクトキーを指定します。
Resources:
BedrockAgent:
Type: AWS::Bedrock::Agent
Properties:
AgentName: !Ref "AWS::StackName"
AgentResourceRoleArn: !GetAtt BedrockAgentRole.Arn
FoundationModel: !Ref pModelId
Instruction: |
As an AWS Principal Engineer, you have been assigned the following tasks:
#以降省略
ActionGroups:
- ActionGroupName: "explainLatestAWSUpdate"
ActionGroupExecutor:
Lambda: !GetAtt BedrockAgentFunction.Arn
ApiSchema:
S3:
S3BucketName: "apischema-s3"
S3ObjectKey: "ApiSchema.json"
テンプレートに直接埋め込む
JSON or YAML 型式で直接埋め込むこともできます。
BedrockAgent:
Type: AWS::Bedrock::Agent
Properties:
AgentName: !Ref "AWS::StackName"
AgentResourceRoleArn: !GetAtt BedrockAgentRole.Arn
FoundationModel: !Ref pModelId
Instruction: |
As an AWS Principal Engineer, you have been assigned the following tasks:
#以降省略
ActionGroups:
- ActionGroupName: "explainLatestAWSUpdate"
ActionGroupExecutor:
Lambda: !GetAtt BedrockAgentFunction.Arn
ApiSchema:
Payload: |
openapi: 3.0.0
info:
title: Summarize an article API
version: 1.0.0
description: APIs for summarize an article from URL.
paths:
/summarize_article:
post:
summary: APIs for summarize an article from URL.
#以降省略
Function Schema の指定方法
2024/4/23 のアップデートで新たに使用可能になったエージェントの定義方法です。Function Schema については以下の記事でも紹介しています。
CloudFormation で定義する場合は以下のようになります。
Resources:
BedrockAgent:
Type: AWS::Bedrock::Agent
Properties:
AgentName: !Ref "AWS::StackName"
AgentResourceRoleArn: !GetAtt BedrockAgentRole.Arn
FoundationModel: !Ref pModelId
Instruction: |
As an AWS Principal Engineer, you have been assigned the following tasks:
#以降省略
ActionGroups:
- ActionGroupName: "explainLatestAWSUpdate"
ActionGroupExecutor:
Lambda: !GetAtt BedrockAgentFunction.Arn
FunctionSchema:
Functions:
- Name: "summarize_article"
Description: "summarize an article from url."
Parameters:
url:
Description: "url of article"
Required: false
Type: "string"
ユーザー入力の有効化/無効化
エージェントの設定にあるこれです。
ドキュメントによれば、この機能を有効にするには AWS::Bedrock::Agent AgentActionGroup
の ParentActionGroupSignature
プロパティに AMAZON.UserInput
を指定したアクショングループを作成する必要があります。その際、Description
、ApiSchema
、ActionGroupExecutor
は空白にする必要があります。例として以下のような定義となります。
Resources:
BedrockAgent:
Type: AWS::Bedrock::Agent
Properties:
AgentName: !Ref "AWS::StackName"
AgentResourceRoleArn: !GetAtt BedrockAgentRole.Arn
FoundationModel: !Ref pModelId
Instruction: |
As an AWS Principal Engineer, you have been assigned the following tasks:
#以降省略
ActionGroups:
- ActionGroupName: "userInputAction"
ActionGroupState: "ENABLED"
ParentActionGroupSignature: "AMAZON.UserInput"
- ActionGroupName: "explainLatestAWSUpdate"
ActionGroupExecutor:
Lambda: !GetAtt BedrockAgentFunction.Arn
ApiSchema:
Payload: |
openapi: 3.0.0
#以降省略
上記でデプロイは正常に完了するのですが、コンソール上のユーザー入力は「いいえ (DISABLED)」のままになります。もしかするとテンプレートの定義の仕方が間違っているかもしれません。
AWS::Bedrock::AgentAlias での Agent バージョンの指定
AWS::Bedrock::AgentAlias
の RoutingConfiguration
というプロパティでエイリアスに紐づけるエージェントのバージョンを指定できます。
Resources:
BedrockAgentAlias:
Type: AWS::Bedrock::AgentAlias
Properties:
AgentAliasName: "Default"
AgentId: !Ref BedrockAgent
RoutingConfiguration:
- AgentVersion: "1"
こんな時、CloudFormation を嗜む皆さんであれば以下のように書きたくなる方もいるのではないでしょうか。(- AgentVersion: !GetAtt BedrockAgent.AgentVersion
)
Resources:
BedrockAgent:
Type: AWS::Bedrock::Agent
Properties:
AgentName: !Ref "AWS::StackName"
AgentResourceRoleArn: !GetAtt BedrockAgentRole.Arn
AutoPrepare: true
FoundationModel: !Ref pModelId
# 以降省略
BedrockAgentAlias:
DependsOn: BedrockAgent
Type: AWS::Bedrock::AgentAlias
Properties:
AgentAliasName: "Default"
AgentId: !Ref BedrockAgent
RoutingConfiguration:
- AgentVersion: !GetAtt BedrockAgent.AgentVersion
しかしこのような定義でスタック作成するとエラーになります。Agent 作成後に存在するバージョンは 「作業中のドラフト」を示す DRAFT
のみであり、Fn::GetAtt で取得される値も DRAFT
です。このバージョンは TestAlias
というエイリアスにのみ紐づけが可能であるため、次のようなエラーが発生します。
The attribute routingConfiguration in AgentAlias is invalid.
DRAFT must not be associated with this alias.
エイリアスを新規作成するとエージェントのバージョンも自動で作成されるため、Agent 新規作成時はRoutingConfigration
プロパティの設定は不要です。
CloudFormation で Agent のバージョンをどう管理するか
Agent に対する更新を行った場合、その変更は作業中のドラフトに反映されます。運用環境に更新を反映するには新しいバージョンを作成し、そのバージョンを指すエイリアスを呼び出すようにアプリケーションを設定します。
最初は CloudFormation で AWS::Bedrock::Agent
リソースを更新したら自動でバージョンもあがるのかな?と思いましたがどうやらそういうことではないようです。AutoPrepare
というプロパティもありますが、これはあくまでエージェントの DRAFT バージョンを自動的に更新するかどうかを指定するものでした。
Specifies whether to automatically update the DRAFT version of the agent after making changes to the agent. The DRAFT version can be continually iterated upon during internal development. By default, this value is false.
検証したところ、AWS::Bedrock::AgentAlias
リソースを更新することでバージョンの発行が行われることを確認しました。
Resources:
DependsOn: BedrockAgent
BedrockAgentAlias:
Type: AWS::Bedrock::AgentAlias
Properties:
AgentAliasName: "Default"
AgentId: !Ref BedrockAgent
Description: "Enter some information about the updates and issue a new version."
Tags:
Key: "Value"
つまり Agent の更新が発生した際には AgentAliasName
, Description
, Tags
といった項目のいずれかを同時に更新することで新しいバージョンが発行され、エイリアスに紐づけられます。AgentAliasName を固定で運用したい場合は Description
まはた Tags
に都度 Agent 更新内容にに関する情報を入力するなどして、AgentAlias リソースの更新を行うとよいのではないかと思います。
参考: 今回作成したテンプレート全体
AWS SAM テンプレート (クリックで展開)
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: >
aws-update-navigator
Sample SAM Template for aws-update-navigator
Parameters:
pModelId:
Type: String
Description: The Model ID of the Bedrock Agent.
Default: "anthropic.claude-3-sonnet-20240229-v1:0"
AllowedValues:
- "anthropic.claude-3-opus-20240229-v1:0"
- "anthropic.claude-3-sonnet-20240229-v1:0"
- "anthropic.claude-3-haiku-20240307-v1:0"
- "anthropic.claude-v2:1"
- "anthropic.claude-v2"
- "anthropic.claude-instant-v1"
- "amazon.titan-text-premier-v1:0"
Globals:
Function:
Handler: app.lambda_handler
Runtime: python3.12
Architectures:
- x86_64
LoggingConfig:
LogFormat: JSON
ApplicationLogLevel: INFO
Resources:
SlackAppFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-SlackAppFunction"
CodeUri: "slack_app/"
MemorySize: 512
Timeout: 180
FunctionUrlConfig:
AuthType: NONE
Environment:
Variables:
BEDROCK_AGENT_ID: !Ref BedrockAgent
BEDROCK_AGENT_ALIAS_ID: !GetAtt BedrockAgentAlias.AgentAliasId
SLACK_BOT_TOKEN: "dummy"
SLACK_SIGNING_SECRET: "dummy"
Layers:
- !Ref SlackAppLayer
Policies:
- Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- "bedrock:InvokeAgent"
Resource:
- !GetAtt BedrockAgentAlias.AgentAliasArn
- Effect: Allow
Action:
- "lambda:InvokeFunction"
Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-SlackAppFunction"
SlackAppFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${SlackAppFunction}"
RetentionInDays: 14
SlackAppLayer:
Type: AWS::Serverless::LayerVersion
Properties:
Description: "boto3 & slack_bolt"
ContentUri: "slack_app_layer/"
CompatibleRuntimes:
- python3.12
Metadata:
BuildMethod: python3.12
BuildArchitecture: x86_64
BedrockAgentFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-BedrockAgentFunction"
CodeUri: "bedrock_agent/"
MemorySize: 128
Timeout: 10
Layers:
- !Ref BedrockAgentFunctionLayer
BedrockAgentFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !Ref BedrockAgentFunction
Principal: "bedrock.amazonaws.com"
SourceArn: !GetAtt BedrockAgent.AgentArn
BedrockAgentFunctionLayer:
Type: AWS::Serverless::LayerVersion
Properties:
Description: "beautifulsoup4"
ContentUri: "bedrock_agent_layer/"
CompatibleRuntimes:
- python3.12
Metadata:
BuildMethod: python3.12
BuildArchitecture: x86_64
BedrockAgentFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${BedrockAgentFunction}"
RetentionInDays: 14
BedrockAgentRolePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: "Policy for Bedrock Agent"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: "Allow"
Action:
- "bedrock:InvokeModel"
Resource:
- !Sub "arn:aws:bedrock:${AWS::Region}::foundation-model/${pModelId}"
BedrockAgentRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "AmazonBedrockExecutionRoleForAgents_${AWS::StackName}"
ManagedPolicyArns:
- !Ref BedrockAgentRolePolicy
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: "Allow"
Action: "sts:AssumeRole"
Principal:
Service: "bedrock.amazonaws.com"
Condition:
StringEquals:
aws:SourceAccount: !Ref "AWS::AccountId"
ArnLike:
aws:SourceArn: !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:agent/*"
BedrockAgent:
Type: AWS::Bedrock::Agent
Properties:
AgentName: !Ref "AWS::StackName"
AgentResourceRoleArn: !GetAtt BedrockAgentRole.Arn
AutoPrepare: true
FoundationModel: !Ref pModelId
IdleSessionTTLInSeconds: 120
ActionGroups:
- ActionGroupName: "explainLatestAWSUpdate"
ActionGroupExecutor:
Lambda: !GetAtt BedrockAgentFunction.Arn
FunctionSchema:
Functions:
- Name: "summarize_article"
Description: "summarize an article from url."
Parameters:
url:
Description: "url of article"
Required: false
Type: "string"
Instruction: |
As an AWS Principal Engineer, you have been assigned the following tasks:
1. Access the AWS service update URL and provide a summary of the English text in Japanese. There is no need to categorize the URL content.
2. Share your thoughts on the update in Japanese, focusing on the following points:
2.1. Discuss the advantages of this technology or service compared to existing technologies or services, and explain how it achieves these benefits.
2.2. Describe the technical challenges that this technology or service addresses.
3. Respond in json format.
Here’s an example:
summary, advantages, and addresses are all required fields
<example>
{
"summary": "Amazon EC2シリアルコンソールがすべてのAWSローカルゾーンで利用できるようになりました。",
"advantages": "インスタンスの起動やネットワークの接続の問題をトラブルシューティングするために、シリアルポートへのテキストベースのアクセスを簡単かつ安全に提供します。これにより、SSHやRDPで接続できない場合でも、対話形式でコマンドを実行して構成の問題を解決できます。",
"addresses": "これまでも管理コンソールやAPIを通じてシリアルコンソール出力にアクセスできましたが、それらは主に診断のためで、対話式のトラブルシューティングには適していませんでした。この新機能により、こうした制限がなくなり、はるかに使いやすくなります。"
}
</example>
If the tool did not return a summary, reply "Could not retrieve.".
BedrockAgentAlias:
DependsOn: BedrockAgent
Type: AWS::Bedrock::AgentAlias
Properties:
AgentAliasName: "Default"
AgentId: !Ref BedrockAgent
Outputs:
SlackAppFunctionUrl:
Description: "Function URL Endpoint"
Value: !GetAtt SlackAppFunctionUrl.FunctionUrl
以上です。
参考になれば幸いです。