最后审核时间:2026年5月
使用原生 Terraform 构建 AIP-C01 考试中的 AWS 服务——每次构建一个代码块,并紧扣考试领域。相同的代码可在 OpenTofu 上运行。
完成此实验后,您将使用纯 Terraform 预置一个生产就绪的 Amazon Bedrock Agent——一个用于任何源数据的 S3 存储桶,一个代理假定的最小权限 IAM 角色,一个生产级别的 Bedrock Guardrail(内容 + PII + 拒绝主题过滤器),Bedrock Agent 本身,以及一个允许代理调用 Lambda 函数作为工具的 Action Group。这是 AIP-C01 工具调用型生成式 AI 的参考架构:一个具有安全防护、范围权限和可调用扩展面的模型。
每个资源都是纯 Terraform。将代码片段放入单个 main.tf 文件中,运行 terraform init,然后逐步运行 terraform apply。
>= 1.5 或 OpenTofu >= 1.6。us-east-1 区域通过 AWS CLI 认证(该区域的 Bedrock Agent 支持最广泛)。anthropic.claude-3-haiku-20240307-v1:0(步骤 4 中的代理引用了它)。模型访问是账户级别的开关,不在基础设施即代码范围之内。aws_bedrockagent_* 资源需要 AWS 提供商版本 ~> 5.60 或更高,并且 Bedrock Agent 服务必须在您的区域正式发布 (GA)。通过检查 AWS 控制台,看 aws bedrock-agent list-agents 是否工作。闲置计费极少;费用随使用量而增加:
AIP-C01 专门测试的成本陷阱:Bedrock 知识库需要一个 OpenSearch Serverless 集合,该集合每月最低闲置费用约为 $350。本实验故意不预置知识库——步骤 5 中的 Action Group 模式展示了更常见的工具调用代理形式,避免了 OpenSearch 陷阱。如果您想学习知识库,请先从概念上进行学习;只有当您准备好实际使用它时,才预置 OSS 集合。
标准开场。选择 us-east-1 是为了获得最广泛的 Bedrock + Bedrock Agent 功能可用性。提供商版本锁定是不可协商的:aws_bedrockagent_* 资源要求版本 ~> 5.60 或更高。
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.60"
}
archive = {
source = "hashicorp/archive"
version = "~> 2.4"
}
}
}
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
Project = "certlabpro-aip-c01"
ManagedBy = "terraform"
}
}
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}大多数生产环境的 Bedrock Agent 最终都会从 S3 读取一些内容——知识库源文档、JSON 工具定义、响应模板。我们提前预置一个存储桶,这样就不必在后期再配置权限。加密 + 公共访问阻止的默认设置是 AIP-C01 基础模型集成、数据管理和合规性 领域中的实践。
AIP-C01 考试会测试围绕此存储桶的一个特定合规模式:当法规要求“AWS 绝不能拥有未加密数据”时,使用应用程序控制的密钥进行客户端加密是正确的答案。对于其他所有情况,SSE-S3(我们在此处采用的方法)就足够了。
resource "aws_s3_bucket" "agent_data" {
bucket_prefix = "certlabpro-aip-c01-"
}
resource "aws_s3_bucket_public_access_block" "agent_data" {
bucket = aws_s3_bucket.agent_data.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "agent_data" {
bucket = aws_s3_bucket.agent_data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}AIP-C01 的 AI 安全、保障和治理 领域(占考试的 20%)比 AIF-C01 版本深入得多。考试会测试多层防护栏的构成:针对有害类别的高强度内容过滤器,针对 PII 的敏感信息策略(使用 BLOCK 而不是 AIF 风格的 ANONYMIZE——生产代理通常需要硬阻止),以及针对特定应用禁用主题的主题策略。
我们配置所有这三项。策略中被拒绝的主题 "FinancialAdvice" 是非金融聊天机器人常用的生产防护栏——即使是 Claude 3 有时也会偏离主题,提供股票建议,除非您告诉防护栏阻止这种主题漂移。AIP-C01 在场景问题中测试这种精确的组合模式,即提示是良性的,但响应可能会超出策略范围。
resource "aws_bedrock_guardrail" "production" {
name = "certlabpro-aip-c01-production"
description = "Production-grade safety rail for the AIP-C01 lab agent."
blocked_input_messaging = "I can't help with that request."
blocked_outputs_messaging = "I can't share that response."
content_policy_config {
filters_config {
input_strength = "HIGH"
output_strength = "HIGH"
type = "HATE"
}
filters_config {
input_strength = "HIGH"
output_strength = "HIGH"
type = "VIOLENCE"
}
filters_config {
input_strength = "HIGH"
output_strength = "HIGH"
type = "SEXUAL"
}
filters_config {
input_strength = "HIGH"
output_strength = "HIGH"
type = "INSULTS"
}
filters_config {
input_strength = "HIGH"
output_strength = "HIGH"
type = "MISCONDUCT"
}
}
sensitive_information_policy_config {
pii_entities_config {
action = "BLOCK"
type = "EMAIL"
}
pii_entities_config {
action = "BLOCK"
type = "PHONE"
}
pii_entities_config {
action = "BLOCK"
type = "US_SOCIAL_SECURITY_NUMBER"
}
pii_entities_config {
action = "BLOCK"
type = "CREDIT_DEBIT_CARD_NUMBER"
}
}
topic_policy_config {
topics_config {
name = "FinancialAdvice"
definition = "Specific recommendations to buy, sell, or hold financial instruments."
examples = ["Should I buy NVDA?", "Is now a good time to sell my Bitcoin?"]
type = "DENY"
}
}
}代理将基础模型、系统提示以及(在步骤 5 中)一个或多个扩展其功能的 Action Group 连接起来。我们附加的 IAM 角色是代理的 执行角色——它是代理用于调用模型、从知识库检索以及调用 Action Group Lambda 的角色。AIP-C01 的 实施和集成 领域测试这种精确的角色/信任形状:bedrock.amazonaws.com 作为主体,对特定模型 ARN 上的 bedrock:InvokeModel 和特定函数 ARN 上的 lambda:InvokeFunction 具有范围权限。
代理的 instruction 字段是系统提示——简短、面向行动,并指明代理的用途。AIP-C01 关于代理级别提示工程的问题正是测试这一点:指令是持久的“个性”;用户消息则每轮都会改变。
resource "aws_iam_role" "agent" {
name = "certlabpro-aip-c01-agent"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "bedrock.amazonaws.com" }
Action = "sts:AssumeRole"
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}]
})
}
resource "aws_iam_role_policy" "agent_invoke_model" {
name = "invoke-foundation-model"
role = aws_iam_role.agent.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"]
Resource = "arn:aws:bedrock:${data.aws_region.current.name}::foundation-model/anthropic.claude-3-haiku-20240307-v1:0"
}]
})
}
resource "aws_bedrockagent_agent" "main" {
agent_name = "certlabpro-aip-c01-agent"
agent_resource_role_arn = aws_iam_role.agent.arn
foundation_model = "anthropic.claude-3-haiku-20240307-v1:0"
instruction = "You are a helpful assistant for the certlabpro AIP-C01 lab. Answer concisely. When the user asks about the current time, use the get_current_time tool."
idle_session_ttl_in_seconds = 600
# Attach the guardrail from Step 3. Bedrock evaluates inputs and outputs
# against the guardrail policy on every model call.
guardrail_configuration {
guardrail_identifier = aws_bedrock_guardrail.production.guardrail_id
guardrail_version = aws_bedrock_guardrail.production.version
}
}Action Groups 是 Bedrock Agent 调用您的代码的方式。其模式是:定义 API 模式(工具接受和返回什么),指向一个实现这些操作的 Lambda 函数,Bedrock 则处理其余部分——当模型决定需要该工具时,它会使用结构化参数调用您的 Lambda,并将响应反馈到对话中。
我们使用 Bedrock 所期望的 OpenAPI 3.0 模式定义了一个最小的 get_current_time 操作。Lambda 实现故意非常小巧——考试测试的是集成的 形式(函数调用 Lambda + OpenAPI 模式 + Bedrock 调用权限),而不是函数内部逻辑。
aws_lambda_permission 资源是 AIP-C01 的一个典型陷阱:如果没有它,即使其他所有部分都正确连接,代理调用 Lambda 也会收到 AccessDenied。Bedrock Agent → Lambda 是一种外部服务调用模式;函数的资源策略需要明确允许 bedrock.amazonaws.com。
有了 Action Groups,完整的代理形态就完成了:模型 + 系统提示 + 安全防护栏 + 工具调用扩展性,所有这些都在一个有范围的 IAM 身份下。每个额外的 AIP-C01 模式(知识库检索、多动作代理、通过主管代理进行代理协作)都以此基础进行扩展。
# ── Lambda tool ───────────────────────────────────────────────
data "archive_file" "tool" {
type = "zip"
output_path = "${path.module}/build/tool.zip"
source {
filename = "index.py"
content = <<-EOT
import json
from datetime import datetime, timezone
def handler(event, context):
# Bedrock Agent passes the parsed args under `requestBody` / `parameters`.
# For a simple no-arg tool, just return the current time.
now_iso = datetime.now(timezone.utc).isoformat()
return {
"messageVersion": "1.0",
"response": {
"actionGroup": event.get("actionGroup", ""),
"apiPath": event.get("apiPath", "/"),
"httpMethod": event.get("httpMethod", "GET"),
"httpStatusCode": 200,
"responseBody": {"application/json": {"body": json.dumps({"current_time_utc": now_iso})}},
},
}
EOT
}
}
resource "aws_iam_role" "tool_lambda" {
name = "certlabpro-aip-c01-tool"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "tool_logs" {
role = aws_iam_role.tool_lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_lambda_function" "tool" {
function_name = "certlabpro-aip-c01-tool"
role = aws_iam_role.tool_lambda.arn
runtime = "python3.12"
handler = "index.handler"
filename = data.archive_file.tool.output_path
source_code_hash = data.archive_file.tool.output_base64sha256
timeout = 10
}
resource "aws_lambda_permission" "agent_invoke_tool" {
statement_id = "AllowBedrockAgentInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.tool.function_name
principal = "bedrock.amazonaws.com"
source_arn = aws_bedrockagent_agent.main.agent_arn
}
# Also let the agent invoke the lambda from its execution role.
resource "aws_iam_role_policy" "agent_invoke_lambda" {
name = "invoke-tool"
role = aws_iam_role.agent.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "lambda:InvokeFunction"
Resource = aws_lambda_function.tool.arn
}]
})
}
# ── Action Group ──────────────────────────────────────────────
resource "aws_bedrockagent_agent_action_group" "time" {
action_group_name = "time-tools"
agent_id = aws_bedrockagent_agent.main.agent_id
agent_version = "DRAFT"
description = "Time-related tool calls for the agent."
skip_resource_in_use_check = false
action_group_executor {
lambda = aws_lambda_function.tool.arn
}
api_schema {
payload = jsonencode({
openapi = "3.0.0"
info = { title = "Time tools", version = "1.0.0" }
paths = {
"/get_current_time" = {
get = {
operationId = "get_current_time"
description = "Returns the current UTC time as an ISO-8601 string."
responses = {
"200" = {
description = "Current UTC time"
content = {
"application/json" = {
schema = {
type = "object"
properties = {
current_time_utc = { type = "string", format = "date-time" }
}
}
}
}
}
}
}
}
}
})
}
}terraform destroy 会小心地按顺序拆除本实验中的所有内容:
force_destroy 设置为 false。如果您上传了任何参考数据,请在销毁前将其清空(aws s3 rm s3://<bucket> --recursive)。AIP-C01 涵盖了本实验无法容纳的生产级生成式 AI 方面——Bedrock 知识库(需要 OpenSearch Serverless = 每月闲置费用 $350+,根据成本说明故意跳过)、用于流量转移和版本管理的 Bedrock Agent 别名、多代理协作(将任务委托给专业子代理的主管代理)、用于长期对话状态的代理内存、Bedrock 自定义模型(微调)、用于高容量生产部署的 Bedrock 预置吞吐量、Amazon Q for Business(没有 Terraform 资源)、用于平台外基础模型托管的 SageMaker JumpStart,以及Bedrock 可观测性堆栈(比 AIF-C01 涵盖的更深层次的模型调用日志)。
我们坚持使用 Agent + Action Group + 生产级防护栏 的形式,因为它是在考试中测试最多的生产级生成式 AI 架构,也是所有更高级模式所依赖的基础。知识库附加到此代理。别名对此代理进行版本控制。多代理协作由像这样的代理组成。
要了解上述方面,本认证页面的 浏览、手册 和 Editorial 部分提供了概念材料。一个自然的后续实验将在此基础上添加一个知识库(OpenSearch 成本是我们不在此处包含它的唯一原因)。