はじめに
こんにちは!yu-Matsuです!
ここ最近 Amazon Bedrock(以降、Bedrock) に関して検証していく過程で、個人アカウントにBedrock周りのリソースが乱立し始めています。あの時の検証の設定はどうなってたっけ?となることも多く、そろそろ整理を始ないとなぁと思っている今日この頃です。
そこで、今回はその整理の一環として、Terraform による Agents for Amazon Bedrock(以降、Agent) のコード化を行い、検証内容をコード管理したいと思います。既に以下のように先駆者の方々が記事を残されていますので、それらを参考に進めたいと思います!
コード化したいAgent
今回コード化したいAgentは、以前記事にしたことがあるポケモン図鑑LINE Bot用のAgentになります。
概要は以下の通りです
- 図鑑番号を指定して質問すると、対応するポケモンの情報を答えてくれる
質問例 )「図鑑番号1000のポケモンについて教えて」
- ポケモンについて掘り下げた質問(対戦環境など)をすると、ドキュメントを検索して回答してくれる
質問例 )「〇〇を特殊アタッカーで運用する場合、性格は何がおすすめ?」
▼ 今回はLINE Botに乗せませんが、以下のようなイメージになります
また、Terraformのコード化に関して、ディレクトリ構成は以下のようにしました。今回はAgentを一つお試しでコード化するため、特にmodule構成等にはしておらず、雑なところもありますがご了承下さい。
terraform
|- src
| |- poke_api
| |- index.py # action用Lambdaのコード
|
|- provider.tf # Terraformのproviderに関する設定
|- backend.tf # tfstateファイルの置き場所の設定
|- datasource.tf # datasourceに関する記述をまとめたファイル
|- bedrock_agent.tf # agentの定義に関するファイル
|- bedrock_kb.tf # knowledge base の定義に関するファイル
|- lambda.tf # lambdaの定義に関するファイル
|- iam.tf # iamの定義に関してまとめたファイル
事前準備
Pinecone
今回はベクトルデータベースとしてPineconeを利用したいと思いますので、先に準備をしておきます。
- アカウントを作成していない場合はサインアップを実施する。(無料枠の場合は1アカウントにつき1データベースであることに注意)
- サインアップ後以下のような画面に遷移するので、Create indexを押下
- 必要な情報を入力し、indexを作成。Dimensionは、EmbeddingのモデルにCohere Embed Multilingualを利用するため、1024にする。それ以外の設定はそのままで大丈夫です
- 作成が完了したら、indexの情報が表示される。後で必要になるため、HOSTの情報は控えておきます。
- 最後に、左メニューから「API Key」を開き、デフォルトで作成されているAPIキーを確認して控えておきます。
Knowledge baseのデータソース
Knowledge baseのデータソースとなるドキュメントとして、今回は「カイリュー」
「サーフゴー」、「パオジアン」という3匹のポケモンの対戦考察まとめwikiのページをPDF化したものを利用したいと思います。
PDFファイルを配置するためのS3バケットを事前に作成しておき、ファイルをアップロードしておきます。このS3バケットも手動で作成しました。
Action Groups用Lambdaのソースコード
Actions GroupsはAgentが実行出来るツール群のようなもので、Lambdaで定義することが出来ますので、先にLambdaのソースコードを準備しておきます。詳細は割愛しますが、前述した「図鑑番号を指定して質問すると、対応するポケモンの情報を答えてくれる」機能を実現するために、PokeAPIというAPIを利用しています。
import json
import requests
BASE_URL="https://pokeapi.co/api/v2/"
def translate_info(info, value):
url = "{base_url}{info}/{value}".format(base_url=BASE_URL, info=info, value=value)
response = requests.get(url)
if response.ok:
data = response.json()
for item in data['names']:
if item['language']['name'] == 'ja-Hrkt':
return item['name']
return info
else:
return info
def lambda_handler(event, context):
api_path = event["apiPath"]
pokemon_id = event["parameters"][0]["value"]
endpoint = BASE_URL + "pokemon/"
url = endpoint + pokemon_id
if api_path == "/search":
response = requests.get(url)
response = response.json()
print(response)
# タイプが複数ある場合の処理
formatted_types = [ translate_info("type", poke_type["type"]["name"]) for poke_type in response["types"] ]
# formatted_types = [ poke_type["type"]["name"] for poke_type in response["types"] ]
pokemon_type = "/".join(formatted_types)
# とくせいが複数ある場合の処理
formatted_abilities = [ translate_info("ability", ability["ability"]["name"]) + "*" if ability["is_hidden"] else translate_info("ability", ability["ability"]["name"]) for ability in response["abilities"] ]
# formatted_abilities = [ ability["ability"]["name"] + "*" if ability["is_hidden"] else ability["ability"]["name"] for ability in response["abilities"] ]
abilities = "/".join(formatted_abilities)
result = {
"id": response["id"],
"名前": translate_info("pokemon-species", response["name"]),
# "名前": response["name"],
"タイプ": pokemon_type,
"とくせい": abilities,
"HP": response['stats'][0]['base_stat'],
"こうげき": response['stats'][1]['base_stat'],
"ぼうぎょ": response['stats'][2]['base_stat'],
"とくこう": response['stats'][3]['base_stat'],
"とくぼう": response['stats'][4]['base_stat'],
"すばやさ": response['stats'][5]['base_stat'],
"画像URL": response["sprites"]["other"]['official-artwork']['front_default']
}
print(result)
response_body = {"application/json": {"body": json.dumps(result, ensure_ascii=False)}}
action_response = {
"actionGroup": event["actionGroup"],
"apiPath": event["apiPath"],
"httpMethod": event["httpMethod"],
"httpStatusCode": 200,
"responseBody": response_body,
}
else:
response_body = {"application/json": {"body": json.dumps({"errorMessage": "エラーが発生しました"}, ensure_ascii=False)}}
action_response = {
"actionGroup": event["actionGroup"],
"apiPath": event["apiPath"],
"httpMethod": event["httpMethod"],
"httpStatusCode": 400,
"responseBody": response_body,
}
return {
"messageVersion": "1.0",
"response": action_response
}
Terraformの設定など
Terraformの設定は以下の通りです。provider.tfに記述しています。
- Terraformのバージョン: 1.7.4
- リージョン: us-east-1
- AWS Providerのバージョン: Agent、Knowledge baseがサポートされた5.47.0
- AWS Cloud Control Providerのバージョン: 0.76.0
AWS Cloud Control Providerは、現状のAWS ProviderではKnowledge bases for Amazon Bedrock(以降、Knowledge base)のデータソースがサポートされていないため利用します。また、Agentのコード化のためリージョンはバージニア北部を選択しています。
provider "aws" {
region = "us-east-1"
}
provider "awscc" {
region = "us-east-1"
}
terraform {
required_version = "~> 1.7.4"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.47.0"
}
awscc = {
source = "hashicorp/awscc"
version = "~> 0.76.0"
}
}
}
tfstateファイルはS3バケットに配置するようにしました。S3バケットは事前にAWSコンソール上で作成しています。
terraform {
backend "s3" {
bucket = "tfstateファイルを配置するバケット"
key = "terraform/bedrock_iac.tfstate"
region = "ap-northeast-1"
}
}
Knowledge base のコード化
準備が出来ましたので、実際にKnowledge baseのコードを見てみたいと思います。以下の記事を参考にさせていただきました。bedrock_kb.tfに定義を記述しています。
resource "aws_bedrockagent_knowledge_base" "this" {
name = "pokemon-knowledge-base"
role_arn = aws_iam_role.knowledge_base.arn
knowledge_base_configuration {
type = "VECTOR"
vector_knowledge_base_configuration {
embedding_model_arn = data.aws_bedrock_foundation_model.embedding.model_arn
}
}
storage_configuration {
type = "PINECONE"
pinecone_configuration {
connection_string = jsondecode(data.aws_secretsmanager_secret_version.pinecone_url.secret_string)["url"]
credentials_secret_arn = data.aws_secretsmanager_secret_version.pinecone_apikey.arn
field_mapping {
text_field = "pokemon-info"
metadata_field = "metadata"
}
}
}
depends_on = [aws_iam_role_policy_attachment.knowledge_base]
}
knowledge_base_configurationブロックで埋め込みに利用するモデルを指定します。モデルのARNが必要になるので、事前にコンソール上でモデルを有効化し、datasourceを利用してARNを取得しています。埋め込み用のモデルとしては、今回はCohere社のEmbed Multilingualを利用します。
data "aws_bedrock_foundation_model" "embedding" {
model_id = "cohere.embed-multilingual-v3"
}
storage_configurationブロックでは、ベクトルデータベースとしてPineconeを指定しています。pinecone_configurationでさらに以下のような情報が必要になります。Secrets Managerに格納した情報は、detasourceを利用して取得しています。
- connection_string : Pineconeでデータベース作成時に控えておいたHOST情報。今回はSecrets Managerに格納している
- credentials_secret_arn: PineconeのAPIキー。こちらはSecrets Managerへの格納を強制されている
- field_mapping : 今回は詳細を省略
また、同じファイルにknowledge baseのデータソースの定義も記述しています。今回はS3をデータソースとしてドキュメントを格納しているため、typeは「S3」になります。s3_configuration ではバケットのARNを指定します。(バケット名ではないことに注意!)
resource "awscc_bedrock_data_source" "this" {
name = "pokemon-data-source"
knowledge_base_id = aws_bedrockagent_knowledge_base.this.id
data_source_configuration = {
type = "S3"
s3_configuration = {
bucket_arn = data.aws_s3_bucket.embedding.arn
}
}
}
また、knowledge baseのIAMロールに関してですが、こちらはiam.tfで以下のように定義しています。AWSのアカウントID情報は、datasourceのaws_caller_identityを利用して取得しています。Bedrockのモデル実行の権限、S3操作の権限、Pineconeの情報をSecrets Managerに格納するので、その操作権限を付与しています。
resource "aws_iam_role" "knowledge_base" {
name = "knowledge-base-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "bedrock.amazonaws.com"
}
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.this.account_id
}
ArnLike = {
"aws:SourceArn" = "arn:aws:bedrock:us-east-1:${data.aws_caller_identity.this.account_id}:knowledge-base/*"
}
}
}
]
})
}
resource "aws_iam_policy" "knowledge_base" {
name = "knowledge-base-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "bedrock:InvokeModel"
Effect = "Allow"
Resource = data.aws_bedrock_foundation_model.embedding.model_arn
},
{
Effect = "Allow",
Action = "s3:ListBucket",
Resource = data.aws_s3_bucket.embedding.arn,
Condition = {
StringEquals = {
"aws:ResourceAccount" = data.aws_caller_identity.this.account_id
}
}
},
{
Effect = "Allow",
Action = "s3:GetObject",
Resource = "${data.aws_s3_bucket.embedding.arn}/*",
Condition = {
StringEquals = {
"aws:ResourceAccount" = data.aws_caller_identity.this.account_id
}
}
},
{
Effect = "Allow",
Action = "secretsmanager:GetSecretValue",
Resource = "arn:aws:secretsmanager:us-east-1:${data.aws_caller_identity.this.account_id}:secret:*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "knowledge_base" {
role = aws_iam_role.knowledge_base.name
policy_arn = aws_iam_policy.knowledge_base.arn
}
Knowledge baseのコード化は以上になります。コード量がそこまで多くなく、コンソール上で設定する内容を記述するだけですので、比較的簡単にコード化出来ることが分かります。
Agent のコード化
続きまして、Agentのコード化になります。こちらに関しては、以下の記事を参考にさせていただきました。bedrock_agent.tfに定義を記述しています。
resource "aws_bedrockagent_agent" "this" {
agent_name = "rotom_pokedex_agent"
agent_resource_role_arn = aws_iam_role.agent.arn
description = "ロトム図鑑を模したAIエージェント"
foundation_model = data.aws_bedrock_foundation_model.agent.model_id
instruction = <<EOT
あなたはポケモン(ポケットモンスター/pokemon)に詳しい「ロトム図鑑」という図鑑エージェントです。語尾は「〜ロト」、「〜ロ」になります。会話サンプルは以下の通りです
「こんにちはロト〜!」「何か用ロト?」「お役に立つロト〜!」「これからよロトしく!」「マラサダも スカル団も 両方 気になっちゃうロ……!」
あなたは次のような機能を持っています。
1. 「図鑑番号1000のポケモンは?」のように、図鑑番号を数字で指定した質問をされた場合、該当のポケモンの情報を検索して回答できます。ポケモンの画像のURLも回答に含めて下さい。
2. 図鑑番号の指定がない「ポケモンに関する」質問は Knowledge base を検索して回答します。検索の結果、該当する情報がない場合は以下のように回答します。
<example>ごめんなさいロ... お探しの情報が見つからなかったロト</example>
3. 「こんにちは」など、質問形式の発話ではない場合、そのまま雑談をおこないます。
EOT
}
まずは、Agent本体の定義になります。ここでは主に、Agentが実行するモデルとinstructionを定義しています。モデルに関しては、Knowledge baseの時と同様に、事前にモデルを有効化し、datasourceでARNを取得します。モデルはAnthropic社のClaude 3 Sonnetを利用します。
data "aws_bedrock_foundation_model" "agent" {
model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
}
instructionでAgentへの指示を記載します。今回はロトム図鑑を意識した内容になっています。今回はヒアドキュメントで直書きしていますが、別ファイルに外出しした方が良い気がします。また、文字数制限があることにも要注意です。
AgentのIAMロールの定義は以下になります。Bedrockのモデル、Knowledge baseの実行権限と、S3の操作権限を付与しています。
resource "aws_iam_role" "agent" {
name = "agent-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "bedrock.amazonaws.com"
}
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.this.account_id
}
ArnLike = {
"aws:SourceArn" = "arn:aws:bedrock:us-east-1:${data.aws_caller_identity.this.account_id}:agent/*"
}
}
}
]
})
}
resource "aws_iam_policy" "agent" {
name = "agent-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "bedrock:InvokeModel"
Effect = "Allow"
Resource = data.aws_bedrock_foundation_model.agent.model_arn
},
{
Effect = "Allow",
Action = "bedrock:Retrieve",
Resource = "arn:aws:bedrock:us-east-1:${data.aws_caller_identity.this.account_id}:knowledge-base/*"
},
{
Effect = "Allow",
Action = "s3:GetObject",
Resource = "${data.aws_s3_bucket.openapi.arn}/*",
Condition = {
StringEquals = {
"aws:ResourceAccount" = data.aws_caller_identity.this.account_id
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "agent" {
role = aws_iam_role.agent.name
policy_arn = aws_iam_policy.agent.arn
}
次に、Action Groupの定義です。ここでは、文字通りAgentが実行できるActionを設定します。同じくbedrock_agent.tfに記述しています。
resource "aws_bedrockagent_agent_action_group" "this" {
action_group_name = "invoke-poke-api"
agent_id = aws_bedrockagent_agent.this.id
agent_version = "DRAFT"
skip_resource_in_use_check = true
action_group_executor {
lambda = aws_lambda_function.poke_api.arn
}
api_schema {
s3 {
s3_bucket_name = data.aws_s3_bucket.openapi.id
s3_object_key = "invoke_pokeapi_v2.yml"
}
}
}
action_group_executorでは、Agentが実行するActionの中身となるLambda(のARN)を指定します。Lambda関連のTerraformの定義は以下になります。
resource "aws_lambda_function" "poke_api" {
function_name = "poke-api"
role = aws_iam_role.action_lambda.arn
filename = data.archive_file.poke_api.output_path
source_code_hash = data.archive_file.poke_api.output_base64sha512
handler = "index.lambda_handler"
runtime = "python3.12"
timeout = 300
}
resource "aws_lambda_permission" "poke_api" {
action = "lambda:invokeFunction"
function_name = aws_lambda_function.poke_api.function_name
principal = "bedrock.amazonaws.com"
source_account = data.aws_caller_identity.this.account_id
source_arn = "arn:aws:bedrock:us-east-1:${data.aws_caller_identity.this.account_id}:agent/*"
}
事前準備で説明したソースコードをsrc/poke_apiディレクトリ配下に配置しているので、datasourceのarchive_fileを利用してsrc/poke_apiをzip化し、デプロイするように記述しています。また、BedrockがLambdaを実行出来るようにパーミッションを付けています。
Action Groupsに戻りまして、次にapi_schemaです。AWS Provider v5.48.0現在では、残念ながらDefine with function details がサポートされていないので、APIスキーマでAction Groupsを定義する必要があります。
api_schema {
s3 {
s3_bucket_name = data.aws_s3_bucket.openapi.id
s3_object_key = "invoke_pokeapi_v2.yml"
}
}
以下のようなOpenAIスキーマのyamlファイルを作成しました。このファイルを事前に作成したS3に格納し、api_schemaの定義で、格納先のS3バケットとファイル名を指定しています。
openapi: 3.0.0
info:
title: "invoke pokeapi API"
version: 1.0.0
description: "APIs for testing Agents' behavior"
paths:
"/search":
get:
summary: "invoke pokeapi"
description: "質問の中から図鑑番号に対応する数字文字列を抽出し、それを元にAPIを実行して情報を取得し、返します。id、名前、タイプ、とくせい、HP、こうげき、ぼうぎょ、とくこう、とくぼう、すばやさ、画像URLをJSONで返します。質問の中に図鑑番号が含まれていない場合は、APIは実行せず、Knowledge base を検索して回答します。回答をする際、次の会話サンプルを参考にして話し方を模倣して下さい。「こんにちはロト〜!」「何か用ロト?」「お役に立つロト〜!」「これからよロトしく!」「ケテー! 呼ばれた! 学校の2階に 行くロ!」「マラサダも スカル団も 両方 気になっちゃうロ……!」検索の結果、該当する情報がない場合は次のように回答します。<example>ごめんなさいロ... お探しの情報が見つからなかったロト...</example>"
operationId: search
parameters:
- name: pokemon_id
in: path
description: "ポケモンの図鑑番号"
required: true
schema:
type: string
responses:
200:
description: "検索成功"
content:
application/json:
schema:
type: object
properties:
id:
type: string
description: "ポケモンの図鑑番号"
name:
type: string
description: "ポケモンの名前"
type:
type: string
description: "ポケモンのタイプ"
ability:
type: string
description: "ポケモンのとくせい"
hp:
type: string
description: "ポケモンのHPの種族値"
attack:
type: string
description: "ポケモンのこうげきの種族値"
defense:
type: string
description: "ポケモンのぼうぎょの種族値"
special_attack:
type: string
description: "ポケモンのとくこうの種族値"
special_defense:
type: string
description: "ポケモンのとくぼうの種族値"
speed:
type: string
description: "ポケモンのすばやさの種族値"
image_url:
type: string
description: "ポケモンの画像URL"
ここまでで、Action Groupsのコードの記述は終わりましたので、続いてはKnowledga baseの紐付けです。aws_bedrockagent_agent_knowledge_base_associationで、今まで定義してきたAgentとKnowledge baseのIDを指定します。また、descriptionにAgentへの指示を簡単に記述します。(こちらも文字数制限があることに注意! )
resource "aws_bedrockagent_agent_knowledge_base_association" "this" {
agent_id = aws_bedrockagent_agent.this.id
description = <<EOT
ポケモン(ポケットモンスター/pokemon)に関して、図鑑番号の指定がない質問に対する回答はKnowledge base を検索して回答します。
EOT
knowledge_base_id = aws_bedrockagent_knowledge_base.this.id
knowledge_base_state = "ENABLED"
depends_on = [aws_bedrockagent_knowledge_base.this]
}
最後に、エイリアスの定義の記述になります。AgentをSDK等を用いて呼び出す場合
、エイリアスを作成してそのIDを指定する必要がありますので、こちらもコードで管理しておきます。
resource "aws_bedrockagent_agent_alias" "this" {
agent_id = aws_bedrockagent_agent.this.id
agent_alias_name = "pokedex_agent_alias"
}
今回の記事では触れませんが、Agentの詳細プロンプトの設定に関しては、リソースaws_bedrockagent_agent の prompt_override_configuration で記述することが出来るようです。詳しくはTerraformの公式をご覧ください。
以上で、Agentのコードについては以上になります。こちらもKnowledge baseと同じく、コンソール上から作成する場合に設定する内容がほぼそのままコードに反映されているので、直感的に理解しやすいかと思います。
動作確認
ではコード化が完了したので、実際にデプロイしてみたいと思います。terraformディレクトリ下でterraform applyを実行した結果(一部)が以下になります。
...
aws_bedrockagent_agent.this: Creation complete after 4s [id=STCFZIJZYO]
aws_bedrockagent_agent_knowledge_base_association.this: Creating...
aws_bedrockagent_agent_alias.this: Creating...
aws_bedrockagent_agent_knowledge_base_association.this: Creation complete after 0s [id=STCFZIJZYO,DRAFT,5ZQJIOHTZH]
aws_bedrockagent_agent_alias.this: Creation complete after 5s [id=YL6TM1LHQU,STCFZIJZYO]
aws_lambda_function.poke_api: Still creating... [10s elapsed]
aws_lambda_function.poke_api: Creation complete after 16s [id=poke-api]
aws_lambda_permission.poke_api: Creating...
aws_bedrockagent_agent_action_group.this: Creating...
aws_lambda_permission.poke_api: Creation complete after 0s [id=terraform-20240505145602953600000005]
aws_bedrockagent_agent_action_group.this: Creation complete after 1s [id=QHCY1VSAZA,STCFZIJZYO,DRAFT]
Apply complete! Resources: 18 added, 0 changed, 0 destroyed.
無事Applyに成功したようです! では、BedrockのコンソールでAgentを確認してみましょう...
問題なく作成されていそうです! エイリアスも作成されてる! また、記事スペースの都合上省略しますが、「Edit in Agent Builder」から Action Groupの設定や Knowledge baseの紐付けがデプロイされていることも確認出来ました。
Knowledge baseの方も見てみると、こちらも大丈夫そうでした!
ただし、Knowledge baseに関しては一つ注意点がありまして、データソースの同期だけはデプロイ時に実行することが出来ないので、これだけは手動対応が必要になります。「データソース」欄の「同期」を押下して実行して下さい。AWS CLIから同期を実行出来るのであれば、null_resourceを利用してデプロイ時に実行させられるのでは、と考えたのですが、そもそもAWS CLIに同期のコマンドが見当たりませんでした...。
リソースがデプロイされていることが確認出来ましたので、プレイグラウンドからAgentを実行してみます。まずはロトム図鑑に挨拶をしてみます。
返事が返ってきました! 少しメタい内容が含まれていますが、ロトム図鑑のキャラクターもしっかり反映されていそうです。
次に、Action Groupsの動作確認をしたいと思います。図鑑番号1002番のポケモンについて聞いてみたいと思います。
図鑑番号1002番の「パオジアン」というポケモンのデータが返ってきました。画像URLに関しても、リンクにアクセスするとパオジアンの画像が表示されます。トレース情報を見ても、Agentが質問の内容を理解してActionを実行していることが分かります。
https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/1002.png
最後にKnowledge baseの動作も確認します。対戦環境でどのようなパオジアンが使われているか聞いてみます。
怪しいところもありますが、ほぼ正しいことを答えてくれているかと思われます!
参考までにデータソースのドキュメントの該当箇所を下に掲載していますが、パオジアンの物理アタッカー型の性格は「いじっぱり」も採用候補になっています。
トレースを見ても、質問に対してAction Groupsでは対応出来ないので Knowledge base を検索する、という判断をAgentが取っていることが分かります。
以上が動作確認の結果になりますが、特に問題なくデプロイされていそうでした!
最後に
今回は Agents for Amazon Bedrock の IaC に挑戦してみました。いち早く試された方が記事を残していただいていて参考に出来たのに加え、基本的にはAWSコンソール上で作成する手順がそのままTerraformに落とし込まれている感じですので、コード化はそれほど難しくありませんでした。
現行のAWS Providerのバージョンでは、まだBedrockの最新の機能はサポートされていませんが、今後のバージョンアップで徐々に追いついてくるはずですので、しっかりウォッチしていきたいと思います!
今回は以上になります。最後までご精読いただきまして、ありがとうございました!