在 AWS 上启动您的 AI 代理:Bedrock、Lambda 和 API 网关

介绍

在这篇文章中,我将探讨如何实现一个允许我们发送问题并接收答案的服务。发送问题将作为提示发送到使用 Bedrock 的特定模型。

为了最大限度地降低实施成本,我将利用 API Gateway、AWS Lambda 和 Amazon Bedrock 的服务。

语境

当我们想要实现生成式人工智能服务时,有几种可能的途径,我们必须花时间了解最低要求,然后再做决定。

在这篇文章中,我将演示如何部署一个简单的 GenAI 解决方案,我们可以将其连接到我们当前的解决方案,或者通过 API 网关将其暴露给第三方。

TLDR

此实现的所有内容都可以在此存储库中找到。

范围

对于本次实现,我将定义一组非常简单的需求。

下图展示了我们将要构建的实现方案。

高层图

功能需求

我们应该能够发送提示并从 Nova Micro 收到模型完成形式的响应。

我们应该能够公开一个 HTTP 端点来触发生成响应的过程。

为了便于成本计算,我们估计每月将处理 100 个请求。

非功能性需求

由于我们使用的是 AWS 无服务器服务,我们可以依靠其内置功能来满足我们的非功能性需求。

为了实现自动化,部署应完全由 GitHub Actions 处理。在可用性方面,API 需要保持每月 99.9% 或更高的正常运行时间。

在安全方面,我们需要对 Bedrock 进行 IAM 范围的访问控制,使用 OpenID Connect 进行身份验证,并确保所有流量都仅使用 HTTPS。

最后,为了便于观察,我们必须有结构化的日志来捕获元数据、令牌使用情况和任何错误,所有这些都要通过 CloudWatch 仪表板进行可视化。

简单的。

超出范围是什么?

在此实现中,我将排除身份验证、清理、授权以及应用于请求的任何安全流程。这种方法使我能够专注于 GenAI 的总体实现流程。

每项服务的成本细分

根据预定义的数据量,我们可以估计每次请求平均使用 22 个输入令牌和 232 个输出令牌。

Amazon Bedrock(Nova Micro)

Nova Micro 模型每月处理 2200 个输入代币和 23200 个输出代币,每月运行成本约为 0.003 美元。输出代币的成本约为输入代币的 4 倍。

AWS Lambda

假设只有 100 次调用,每次调用耗时 3 到 5 秒**,内存为 512MB,我们远未达到免费套餐的限制。Lambda 每月提供 100 万次请求和 40 万 GB 秒的免费流量。

API 网关

第一年,我们可以免费获得 100 万次请求。之后,每月费用可能约为 0.0004 美元。

亚马逊 ECR

ECR 中那 300MB 的 Docker 镜像每月大约只需 1 美分。在 500MB 的免费额度用完后,我们实际需要为大约 136MB 的存储空间付费。

当规模扩大时会发生什么?

如果每月请求量达到 1,000 次,费用约为 0.04 美元。如果达到 10,000 次请求,费用约为 0.39 美元。即使达到 100,000 次请求,我们每月也只需支付约 3.76 美元。

好的,我们继续。

执行

要开始实施,我们可以先创建代理。

创建代理

要创建代理,我首先需要设置完整的 TypeScript 项目。

我们可以按照这些说明快速推进,并安装必要的依赖项,以便项目能够正常运行。

您可以根据自己的需要选择最合适的配置。

mkdir -p handler terraform

cd handler

pnpm init -y

pnpm --package=typescript dlx tsc --init

mkdir -p src tests

touch src/{app,env,index}.ts

pnpm add -D @types/node tsx typescript

pnpm add ai @ai-sdk/amazon-bedrock

pnpm add zod dotenv

在这个项目中,我将定义三个基本部分:

与 AWS Lambda兼容的逻辑。

将请求作为提示进行管理的逻辑,根据这些提示请求文本生成,并处理它们未来可能的发展演变。

使用@ai-sdk基岩调用生成实际文本的核心逻辑。

以下是项目主要文件的概要。

import { main } from "./app";

export const handler = async (event: any, context: any) => {

try {

const body = event.body ? JSON.parse(event.body) : {};

const prompt = body.prompt ?? "Welcome from Warike technologies - GenAI solutions architecture";

const response = await main(prompt);

return {

statusCode: 200,

body: JSON.stringify({

success: true,

data: response,

}),

};

} catch (error) {

console.error('Error in Lambda handler:', error);

return {

statusCode: 500,

body: JSON.stringify({

success: false,

error: error instanceof Error ? error.message : 'An unexpected error occurred'

}),

};

}

};

// app.ts

import { generateResponse } from "./utils/bedrock";

export async function main(prompt: string) {

try {

console.log('🚀 Starting Bedrock:');

return await generateResponse(prompt)

} catch (error) {

console.error('An unexpected error occurred running workflow:', error);

throw error;

}

}

// utils/bedrock.ts

import { config } from "./config";

import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';

import { generateText } from 'ai';

export async function generateResponse(prompt: string){

const { regionId, modelId } = config({ });

try {

const bedrock = createAmazonBedrock({

region: regionId

});

const { text, usage } = await generateText({

model: bedrock(modelId),

system: "You are a helpful assistant.",

prompt: [

{ role: "user", content: prompt },

],

});

console.log(`model: {modelId}, \\n response: {text}, usage: ${JSON.stringify(usage)}`);

return text;

} catch (error) {

console.log(`ERROR: Can't invoke '{modelId}'. Reason: {error}`);

}

}

为了进行本地测试,我定义了以下环境变量。我们可以使用 AWS Bedrock API 密钥进行测试。

AWS_REGION=us-west-2

AWS_BEDROCK_MODEL='amazon.nova-micro-v1:0'

AWS_BEARER_TOKEN_BEDROCK='aws_bearer_token_bedrock'

强烈建议仅使用短期 API 密钥,以避免损害系统安全。

定义基础设施

现在系统的逻辑已经可以正常运行,我们可以创建它的 Dockerfile,这将有助于将其部署到 AWS Lambda。

---- Build Stage ----

FROM node:22-alpine AS builder

WORKDIR /usr/src/app

RUN corepack enable

COPY package.json pnpm-lock.yaml* ./

RUN pnpm install --frozen-lockfile

COPY . .

RUN pnpm run build

---- Runtime Stage ----

FROM public.ecr.aws/lambda/nodejs:22

WORKDIR ${LAMBDA_TASK_ROOT}

COPY --from=builder /usr/src/app/dist/src ./

COPY --from=builder /usr/src/app/node_modules ./node_modules

CMD [ "index.handler" ]

所有组件都已准备就绪,我们可以开始在 Terraform 中定义资源了。

通过 API 网关公开服务

我们将首先定义 API 网关。我们将使用 HTTP 协议,并且只专注于创建 API、其阶段以及后续与 Lambda 的集成。

locals {

api_gateway_name = "dev-http-${local.project_name}"

}

module "warike_development_api_gw" {

source = "terraform-aws-modules/apigateway-v2/aws"

version = "5.4.1"

name = local.api_gateway_name

description = "API Gateway for ${local.project_name}"

protocol_type = "HTTP"

create_domain_name = false

create_certificate = false

create_domain_records = false

cors_configuration = {

allow_headers = ["*"]

allow_methods = ["*"]

allow_origins = ["*"]

}

Access logs

stage_name = "dev"

stage_description = "Development API Gateway"

stage_access_log_settings = {

create_log_group = false

destination_arn = aws_cloudwatch_log_group.warike_development_api_gw_logs.arn

format = jsonencode({

context = {

requestId = "$context.requestId"

requestTime = "$context.requestTime"

protocol = "$context.protocol"

httpMethod = "$context.httpMethod"

resourcePath = "$context.resourcePath"

routeKey = "$context.routeKey"

status = "$context.status"

responseLength = "$context.responseLength"

integrationErrorMessage = "$context.integrationErrorMessage"

error = {

message = "$context.error.message"

responseType = "$context.error.responseType"

}

identity = {

sourceIP = "$context.identity.sourceIp"

}

integration = {

error = "$context.integration.error"

integrationStatus = "$context.integration.integrationStatus"

}

}

})

}

Routes & Integration

routes = {

"POST /" = {

integration = {

uri = module.warike_development_lambda.lambda_function_arn

payload_format_version = "2.0"

}

}

}

stage_tags = merge(local.tags, {

Name = "${local.api_gateway_name}-dev"

})

tags = merge(local.tags, {

Name = local.api_gateway_name

})

depends_on = [

aws_cloudwatch_log_group.warike_development_api_gw_logs,

]

}

resource "aws_cloudwatch_log_group" "warike_development_api_gw_logs" {

name = "/aws/api-gw/${local.api_gateway_name}"

retention_in_days = 7

}

连接到 Amazon Bedrock

接下来,为了利用 Amazon Bedrock,此实现将使用美国地区的 Amazon Nova Micro 推理配置文件。

locals {

model_id = "amazon.nova-micro-v1:0"

}

data "aws_bedrock_inference_profile" "warike_development_lambda_bedrock_model" {

inference_profile_id = "us.${local.model_id}"

}

Bedrock Policy

data "aws_iam_policy_document" "warike_development_lambda_bedrock_policy_doc" {

statement {

effect = "Allow"

actions = [

"bedrock:InvokeModel",

]

resources = ["*"]

}

}

Lambda 服务

最后,我们将创建与我们创建的所有组件以及存储库中其他组件关联的 Lambda 函数。

locals {

lambda_function_name = local.project_name

lambda_env_vars = {

AWS_BEDROCK_MODEL = data.aws_bedrock_inference_profile.warike_development_lambda_bedrock_model.inference_profile_arn

}

}

module "warike_development_lambda" {

source = "terraform-aws-modules/lambda/aws"

version = "8.1.2"

function_name = local.lambda_function_name

description = "Lambda function for ${local.project_name}"

image_uri = "${aws_ecr_repository.warike_development_ecr.repository_url}:latest"

package_type = "Image"

create_package = false

ignore_source_code_hash = true

memory_size = 128

timeout = 900

environment_variables = merge(

local.lambda_env_vars,

{}

)

create_role = false

lambda_role = aws_iam_role.warike_development_lambda_role.arn

Cloudwatch logging

use_existing_cloudwatch_log_group = true

logging_log_group = aws_cloudwatch_log_group.warike_development_lambda_logs.name

logging_log_format = "JSON"

logging_application_log_level = "INFO"

logging_system_log_level = "WARN"

function URL

create_lambda_function_url = false

depends_on = [

aws_cloudwatch_log_group.warike_development_lambda_logs,

null_resource.warike_development_seed_ecr_image

]

tags = merge(local.tags, {

Name = local.lambda_function_name

})

}

resource "null_resource" "warike_development_seed_ecr_image" {

provisioner "local-exec" {

command = <<EOT

aws ecr get-login-password --region {local.aws_region} --profile {local.aws_profile} \

| docker login --username AWS --password-stdin {data.aws_caller_identity.current.account_id}.dkr.ecr.{local.aws_region}.amazonaws.com

docker pull public.ecr.aws/lambda/nodejs:22

docker tag public.ecr.aws/lambda/nodejs:22 ${aws_ecr_repository.warike_development_ecr.repository_url}:latest

docker push ${aws_ecr_repository.warike_development_ecr.repository_url}:latest

EOT

}

depends_on = [aws_ecr_repository.warike_development_ecr]

}

重要提示:warike_development_seed_ecr_image需要您本地运行 Docker。

我们继续前进。

创建资源

资源创建完成后,您应该会看到类似这样的消息:

terraform apply

...

Apply complete! Resources: 26 added, 0 changed, 0 destroyed.

使用 GitHub + ECR 配置 CI/CD

此外,我们需要一个包含我们项目的 Docker 镜像。以下 GitHub 流水线将允许我们构建镜像并将其推送到 ECR,然后将其部署到 Lambda。


name: Lambda CI/CD www.hdpjng.com Common

'on':

workflow_call:

inputs:

app-name:

type: string

lambda-function-secret-name:

required: true

type: string

pnpm-version:

required: false

type: number

default: 10

node-version:

required: false

type: number

default: 22

secrets:

PROJECT_NAME:

required: true

AWS_OIDC_ROLE_ARN:

required: true

AWS_REGION:

required: true

ECR_REPOSITORY:

required: true

AWS_LAMBDA_FUNCTION_NAME:

required: true

AWS_LAMBDA_FUNCTION_ROLE_ARN:

required: true

jobs:

build:

name: Build and Test

runs-on: ubuntu-latest

defaults:

run:

working-directory: ./handler

steps:

  • name: Checkout

uses: actions/checkout@v4

  • name: Install pnpm

uses: pnpm/action-setup@v4

with:

version: ${{ inputs.pnpm-version }}

  • name: Use Node.js ${{ inputs.node-version }}

uses: actions/setup-node@v4

with:

node-version: ${{ inputs.node-version }}

  • name: Install Dependencies

run: pnpm install --frozen-lockfile

  • name: Scan for critical vulnerabilities

run: pnpm audit --audit-level=critical

  • name: Run Tests

env:

DOTENV_QUIET: true

run: pnpm test:ci

  • name: Build

run: pnpm run build

build-docker:

name: Build and Push Docker Image

runs-on: ubuntu-latest

needs: build

permissions:

id-token: write

contents: read

outputs:

sha: ${{ steps.vars.outputs.sha }}

defaults:

run:

working-directory: ./handler

steps:

  • name: Checkout

uses: actions/checkout@v4

  • name: Configure AWS credentials

uses: aws-actions/configure-aws-credentials@v4

with:

role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}

aws-region: ${{ secrets.AWS_REGION }}

  • name: Login to Amazon ECR

id: login-ecr

uses: aws-actions/amazon-ecr-login@v2

with:

mask-password: 'true'

  • name: Set commit-sha

id: vars

run: |

calculatedSha=(git rev-parse --short {{ github.sha }})

echo "sha={calculatedSha}" \>\> GITHUB_OUTPUT

  • name: Build and Push Docker Image

env:

DOCKER_IMAGE: {{ secrets.ECR_REPOSITORY }}:{{ inputs.app-name }}-${{ steps.vars.outputs.sha }}

run: |

echo "Building Docker image $DOCKER_IMAGE"

docker build -t $DOCKER_IMAGE .

docker tag DOCKER_IMAGE "{{ secrets.ECR_REPOSITORY }}:{{ inputs.app-name }}-{{ steps.vars.outputs.sha }}"

docker tag DOCKER_IMAGE "{{ secrets.ECR_REPOSITORY }}:latest"

docker push $DOCKER_IMAGE

docker push "{{ secrets.ECR_REPOSITORY }}:{{ inputs.app-name }}-${{ steps.vars.outputs.sha }}"

docker push "${{ secrets.ECR_REPOSITORY }}:latest"

deploy-prod:

name: Deploy Production Lambda

runs-on: ubuntu-latest

if: github.ref == 'refs/heads/main'

needs: build-docker

permissions:

id-token: write

contents: read

defaults:

run:

working-directory: ./handler

steps:

  • name: Checkout

uses: actions/checkout@v4

  • name: Configure AWS credentials

uses: aws-actions/configure-aws-credentials@v4

with:

role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}

aws-region: ${{ secrets.AWS_REGION }}

  • name: Deploy Production Lambda

uses: aws-actions/aws-lambda-deploy@v1.1.0

with:

function-name: ${{ secrets.AWS_LAMBDA_FUNCTION_NAME }}

package-type: Image

image-uri: {{ secrets.ECR_REPOSITORY }}:{{ inputs.app-name }}-${{ needs.build-docker.outputs.sha }}

如果一切顺利,我们将能够测试亚马逊基岩版。

GitHub Actions

测试

我们来做个测试。在终端上,我们可以发出以下查询并观察结果:

curl -sS "https://123456.execute-api.us-west-2.amazonaws.com/dev/" \

-H "Content-Type: application/json" \

-d '{"prompt":"Heeey hoe gaat het?"}' | jq

预期输出结果类似于这样:

{

"success": true,

"data": "Hoi! Het gaat prima, bedankt voor het vragen. Hoe gaat het met jou? Is er iets waar ik je kan helpen of iets waar je graag over wilt praten?"

}

我认为这是一个成功。

可观测性

值得一提的是,我们可以通过CloudWatch监控任何错误,所以我们不必摸着石头过河。

CloudWatch 控制面板

打扫

最后,我们清理资源。

terraform destroy

...

Destroy complete! Resources: 26 destroyed.

结论

总的来说,使用 AWS 无服务器组件实现 GenAI 服务非常简单直接。

API Gateway、AWS Lambda 和 Amazon Bedrock 的组合,特别是搭配 Nova Micro 模型,不仅功能强大,而且极具成本效益。分析表明,即使手动大幅扩展流量,该解决方案的成本仍然非常低。

通过利用 Terraform 进行基础设施管理,利用 GitHub Actions 进行 CI/CD 流水线,我们实现了强大且完全自动化的部署流程。

最后,即使排除身份验证等因素,它也能为构建更复杂的生成式人工智能应用程序提供坚实且可扩展的基础。

相关推荐
说私域2 小时前
链动2+1模式、AI智能名片与S2B2C商城小程序:破解直播电商流量转化困局的创新路径
人工智能·小程序
想暴富,学技术2 小时前
AI提示词学习基础(一)
人工智能·学习
萤丰信息2 小时前
智慧园区:数字中国的“微缩实验室”如何重构城市未来
大数据·人工智能·科技·安全·重构·智慧园区
菠菠萝宝3 小时前
【AI应用探索】-7- LLaMA-Factory微调模型
人工智能·深度学习·大模型·llm·nlp·attention·llama
观测云3 小时前
阿里云 RDS PostgreSQL 可观测最佳实践
阿里云·postgresql·云计算
大模型真好玩3 小时前
低代码Agent开发框架使用指南(七)—Coze 数据库详解
人工智能·agent·coze
weixin_307779133 小时前
破解遗留数据集成难题:基于AWS Glue的无服务器ETL实践
开发语言·云原生·云计算·etl·aws
唐兴通个人3 小时前
金融保险银行营销AI数字化转型培训讲师培训老师唐兴通讲金融银保团队险年金险市场销售
大数据·人工智能
视界先声3 小时前
AIDAv2:重新定义DeFi的AI驱动金融基础设施
人工智能·金融