🥓
TerraformでBedrockフローを構築してみた
Bedrockエージェントを運用する際にどこまでがIaCの責務なのかが気になったので、やってみました
構成図

フロー

以下コード
設定系
- リージョンは、Pinecone都合でオハイオを指定する
- PineConeは事前にアカウント作成しAPIキーを発行し、環境変数に
provider.tf
terraform {
required_version = "1.12.1"
required_providers {
pinecone = {
source = "pinecone-io/pinecone"
}
aws = {
source = "hashicorp/aws"
version = "6.5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
variable "pinecone_api_key" {
type = string
}
provider "pinecone" {
api_key = var.pinecone_api_key
}
export NAMESPACE=
export TF_VAR_namespace=$NAMESPACE
export TF_VAR_aws_region=$AWS_REGION
# Pinecone API Key (required)
export PINECONE_API_KEY=
export TF_VAR_pinecone_api_key=$PINECONE_API_KEY
main
- pineconeモジュールとawsモジュールに分割
- pinecone⇒awsに渡す情報
- apiキー : SecretManagerに保存する
- 接続文字列: DBのアレをナレッジベースで設定する
main.tf
locals {
# Pinnecone の都合でオハイオリージョン固定
aws_region = "us-east-1"
}
module "pinecone" {
source = "./pinecone"
aws_region = local.aws_region
}
module "aws" {
source = "./aws"
namespace = var.namespace
pinecone_api_key = var.pinecone_api_key
dense_connection_string = module.pinecone.dense_connection_string
aws_region = local.aws_region
}
pineconeモジュール
- リージョンは、
us-east-1を指定する - dimensionは、モデルに合わせて指定する
- 埋め込みモデルとして利用する Amazon Titan v2 が1024のため
./pinecone/main.tf
variable "aws_region" {
type = string
}
# https://registry.terraform.io/providers/pinecone-io/pinecone/latest/docs
resource "pinecone_index" "dense" {
name = "dense-index"
dimension = 1024
spec = {
serverless = {
cloud = "aws"
region = "us-east-1"
}
}
}
output "dense_connection_string" {
value = pinecone_index.dense.host
}
aws モジュール
- SecretManagerとBedrockが対象
- AWSサービスごとにモジュール化
- 依存関係
- pinecone APIキー & 接続文字列
- APIキー⇒AWS SecretManager
- SecretManager ARN ⇒ Bedrock
- 接続文字列⇒Bedrock
- APIキー⇒AWS SecretManager
- pinecone APIキー & 接続文字列
./aws/main.tf
module "secret_manager" {
source = "./modules/secret-manager"
namespace = var.namespace
pinecone_api_key = var.pinecone_api_key
}
module "bedrock" {
source = "./modules/bedrock"
namespace = var.namespace
pinecone_api_key = var.pinecone_api_key
dense_connection_string = var.dense_connection_string
secret_manager_arn = module.secret_manager.secret_manager_arn
aws_region = var.aws_region
account_id = data.aws_caller_identity.current.account_id
}
SecretManager
- シークレット名の接頭辞に
AmazonBedrock-を付けないといけない (2hハマった) - PineconeのAPIキーを暗号化し保存する
-
jsonencode({ apiKey = var.pinecone_api_key})で保存する - シークレット名の接頭辞に
AmazonBedrock-を付けないといけない
./aws/modules/secret-manager.tf
resource "aws_secretsmanager_secret" "pinecone_api_key" {
name = "AmazonBedrock-${var.namespace}-pinecone-api-key"
description = "Pinecone API Key for Bedrock Knowledge Base"
}
resource "aws_secretsmanager_secret_version" "pinecone_api_key" {
secret_id = aws_secretsmanager_secret.pinecone_api_key.id
secret_string = jsonencode({
apiKey = var.pinecone_api_key
})
}
Bedrock
ナレッジベース
- ナレッジベース本体、データソース(S3)の作成
- ナレッジベース本体で埋め込みモデルの指定とPineCone連携を設定する
-
applyし終わったら、マネコンからナレッジベースを開き「同期」を押すとPineConeにS3のデータが登録される

./aws/modules/bedrock/knowledge-base.tf
resource "aws_bedrockagent_knowledge_base" "bedrock_knowledge_base_v2" {
description = null
name = "${var.namespace}-knowledge-base-v2"
role_arn = aws_iam_role.knowledge_base_role.arn
tags = null
knowledge_base_configuration {
type = "VECTOR"
vector_knowledge_base_configuration {
embedding_model_arn = "arn:aws:bedrock:${var.aws_region}::foundation-model/amazon.titan-embed-text-v2:0"
embedding_model_configuration {
bedrock_embedding_model_configuration {
dimensions = 1024
embedding_data_type = "FLOAT32"
}
}
}
}
storage_configuration {
type = "PINECONE"
pinecone_configuration {
connection_string = "https://${var.dense_connection_string}"
credentials_secret_arn = var.secret_manager_arn
namespace = null
field_mapping {
metadata_field = "metadata"
text_field = "text"
}
}
}
}
resource "aws_s3_bucket" "name" {
bucket = "${var.namespace}-rag-bucket-v2"
}
resource "aws_bedrockagent_data_source" "rag_data_source" {
name = "${var.namespace}-rag-data-source-v2"
knowledge_base_id = aws_bedrockagent_knowledge_base.bedrock_knowledge_base_v2.id
data_source_configuration {
type = "S3"
s3_configuration {
bucket_arn = aws_s3_bucket.name.arn
}
}
depends_on = [aws_bedrockagent_knowledge_base.bedrock_knowledge_base_v2, aws_s3_bucket.name]
}
prompt
- テキストというより、LLMに渡すパラメタを一括りで「プロンプト」と呼んでいる
- LLMへの指示 + top p などの指定
./aws/modules/bedrock/prompt.tf
resource "aws_bedrockagent_prompt" "prompt" {
default_variant = "variantOne"
name = "${var.namespace}-test"
variant {
additional_model_request_fields = null
model_id = local.llm_model
name = "variantOne"
template_type = "TEXT"
inference_configuration {
text {
max_tokens = 512
stop_sequences = ""
temperature = 0.699999988079071
top_p = 0.8999999761581421
}
}
template_configuration {
text {
text = "This is my first text prompt. Please summarize on {{topic}}."
input_variable {
name = "topic"
}
}
}
}
}
flow
- フローは手書きでの実装はむずかしいので、GUIで作成⇒importがおすすめ
- 基本的にはIaCでの管理は不要の認識

./aws/modules/bedrock/flow.tf
resource "aws_bedrockagent_flow" "flow" {
execution_role_arn = aws_iam_role.agent_role.arn
name = "${var.namespace}-ai-agent-flow"
definition {
connection {
name = "FlowInputNodeFlowInputNode0ToKnowledgeBaseNode_1KnowledgeBaseNode0"
source = "FlowInputNode"
target = "KnowledgeBaseNode_1"
type = "Data"
configuration {
data {
source_output = "document"
target_input = "retrievalQuery"
}
}
}
connection {
name = "KnowledgeBaseNode_1KnowledgeBaseNode0ToPrompt_1PromptsNode0"
source = "KnowledgeBaseNode_1"
target = "Prompt_1"
type = "Data"
configuration {
data {
source_output = "retrievalResults"
target_input = "topic"
}
}
}
connection {
name = "Prompt_1PromptsNode0ToFlowOutputNodeFlowOutputNode0"
source = "Prompt_1"
target = "FlowOutputNode"
type = "Data"
configuration {
data {
source_output = "modelCompletion"
target_input = "document"
}
}
}
node {
name = "FlowInputNode"
type = "Input"
configuration {
input {
}
}
output {
name = "document"
type = "String"
}
}
node {
name = "FlowOutputNode"
type = "Output"
configuration {
output {
}
}
input {
category = null
expression = "$.data"
name = "document"
type = "String"
}
}
node {
name = "KnowledgeBaseNode_1"
type = "KnowledgeBase"
configuration {
knowledge_base {
knowledge_base_id = aws_bedrockagent_knowledge_base.bedrock_knowledge_base_v2.id
model_id = null
number_of_results = null
}
}
input {
category = null
expression = "$.data"
name = "retrievalQuery"
type = "String"
}
output {
name = "retrievalResults"
type = "Array"
}
}
node {
name = "Prompt_1"
type = "Prompt"
configuration {
prompt {
source_configuration {
inline {
additional_model_request_fields = null
model_id = local.llm_model
template_type = "TEXT"
inference_configuration {
text {
max_tokens = 512
stop_sequences = []
temperature = 0.699999988079071
top_p = 0.8999999761581421
}
}
template_configuration {
text {
text = "あなたは著名なポエマーです。受け取った文章をポエミーにしてください。\n {{topic}}."
input_variable {
name = "topic"
}
}
}
}
}
}
}
input {
category = null
expression = "$.data"
name = "topic"
type = "Array"
}
output {
name = "modelCompletion"
type = "String"
}
}
}
}
まとめ
- 変更頻度の低いイミュータブルリソースはTFで管理すると便利そう
- 反対に変更頻度の高いリソース & ローコードであるフローをTFで管理する必要はない
- 変更頻度の低いリソース
- PineConeDB
- SecretManager
- ナレッジベース関連
- IAMロール & ポリシー
- 変更頻度の高いリソース
- PineConeとS3に配置するデータ
- プロンプト
- TFで管理することでローコードの強みを打ち消してまうリソース
- Bedrockフロー
所感
サービス横断でも対応できるTerraformイイね!
Discussion