AWS Lambda Web AdapterとFastAPIで作る!Amazon BedrockのストリームAPIを使ったチャットアプリ
生成AIのストリームAPIを活用したアプリケーション開発の機会が増えています。
- フロントエンドはAPIを動作確認できれば良い
- 当面(開発中)はランニングコストを抑えたい
- 将来的なECSへの実行基盤の移行に対して、改修コストを抑えたい
このような要件に対して、AWS Lambda Web Adapterを使ってSPAなウェブアプリケーションをLambda関数として動作させる方法を紹介します。
本記事では、サーバーサイドフレームワークにFastAPI、ストリームAPIにAmazon BedrockのInvokeModelWithResponseStream API、基盤モデルにClaude 3 Haikuを利用します。
動作確認は、以下の2ステップで行います。
- uvで環境構築してローカルで動作確認
- AWS SAMを使い、Lambda Function URLとAWS Lambda Web Adapterを組み合わせてLambda単体でアプリケーションを動作確認
以下のGitHubレポジトリで公開されているアプリで動作確認します
事前にレポジトリを clone しておきましょう。
$ git clone https://github.com/awslabs/aws-lambda-web-adapter.git
$ cd aws-lambda-web-adapter/examples/fastapi-response-streaming
$ tree .
.
├── README.md
├── app
│ ├── Dockerfile
│ ├── main.py
│ ├── requirements.txt
│ └── static
│ ├── index.html
│ ├── script.js
│ └── style.css
├── imgs
│ ├── demo.gif
│ └── serverless-storyteller-architecture.png
└── template.yaml
Amazon Bedrock の基盤モデルを有効化
基盤モデルとしてAnthropicのClaude 3 Haikuを利用します。まだ、モデルの利用申請を行っていない場合は、利用するリージョンで申請を済ませておきましょう。
モデルが利用可能でない場合は、以下のようなエラーが発生します
botocore.errorfactory.AccessDeniedException: An error occurred (AccessDeniedException) when calling the InvokeModelWithResponseStream operation: You don't have access to the model with the specified model ID.
ローカル環境でチャットアプリを動作させる
FastAPIのPythonランタイムとパッケージの管理には uv を利用します。
次のドキュメントに従い、uv をあらかじめインストールしておいてください
本記事では、 uv 0.5.2 で動作確認しました。
$ brew install uv
$ uv --version
uv 0.5.2 (Homebrew 2024-11-14)
繰り返しとなりますが、GitHubにある aws-lambda-web-adapter を clone し、サンプルアプリケーションのあるディレクトリに移動します。
$ git clone https://github.com/awslabs/aws-lambda-web-adapter.git
$ cd aws-lambda-web-adapter/examples/fastapi-response-streaming
後半のステップでは Python 3.12 ランタイムのLambdaを利用するため、ローカル環境でも同じバージョンを利用します。
$ uv init --app --python 3.12
Initialized project `fastapi-response-streaming`
$ uv add -r app/requirements.txt
Using CPython 3.12.3 interpreter at: /usr/bin/python3.12
Creating virtual environment at: .venv
...
+ uvicorn==0.23.2
$ cd app/
$ uv run main.py
INFO: Started server process [18764]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: 1.2.3.4:2809 - "POST /api/story HTTP/1.1" 200 OK
上記コマンド実行後、ブラウザから http://localhost:8080/ にアクセスしましょう。
チャット画面が表示されます。
キーワードを入力すると、ねんねんころりな話しを創作してくれます。
AWS API との bedrock.invoke_model_with_response_stream
のストリームイベントを yield
し、FastAPI のレスポンスとしては fastapi.responses.StreamingResponse
でラップします。
AWS Lambda環境でチャットアプリを動作させる
AWS Lambda Web Adapterは、WebアプリケーションとAWS Lambdaインターフェースのギャップを埋めるLambda Extensionです。
このツールを利用することで、Lambda向けにハンドラーやレスポンスを実装することなく、WebアプリケーションをそのままLambda関数として実行できるようになります。
AWS Lambda Web Adapterを使い、WebアプリケーションをLambda関数で動作させる決まり事は2つです
- Webアプリケーションを 8080 ポートで起動
- AWS Lambda Web Adapterを
/opt/extensions
以下で実行
たったこれだけです。
先ほどのアプリケーションをコンテナLambdaとしてDockerfileで定義したのが以下です。
FROM public.ecr.aws/docker/library/python:3.12.0-slim-bullseye
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
WORKDIR /app
ADD . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
Dockerfile 2行目の COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
が AWS Lambda Web Adapter 固有の対応です。
AWS LambdaにはLambda Function URLという機能があり、Lambda関数をパブリックなURLで公開することができます。
AWS Lambda Web AdapterとLambda Function URLを組み合わせることで、Lambdaを実行基盤にWebアプリケーションを簡単に公開できます。
AWS Serverless Application Model(AWS SAM) を使うと、このような構成を簡単にデプロイできます。
$ sam build
$ sam deploy --guided
次のドキュメントに従い、AWS SAM CLI をあらかじめインストールしておいてください
内部的には、以下のテンプレートを利用してIaC化してデプロイされています。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
Streaming Bedrock Response with FastAPI on AWS Lambda
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 300
Resources:
FastAPIFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
MemorySize: 512
Environment:
Variables:
AWS_LWA_INVOKE_MODE: RESPONSE_STREAM
FunctionUrlConfig:
AuthType: NONE
InvokeMode: RESPONSE_STREAM
Policies:
- Statement:
- Sid: BedrockInvokePolicy
Effect: Allow
Action:
- bedrock:InvokeModelWithResponseStream
Resource: '*'
Tracing: Active
Metadata:
Dockerfile: Dockerfile
DockerContext: ./app
DockerTag: v1
Outputs:
FastAPIFunctionUrl:
Description: "Function URL for FastAPI function"
Value: !GetAtt FastAPIFunctionUrl.FunctionUrl
FastAPIFunction:
Description: "FastAPI Lambda Function ARN"
Value: !GetAtt FastAPIFunction.Arn
AWS Lambda Web Adapterは何が嬉しい?
AWS Lambda Web Adapterツールを使ってWebアプリをLambda関数として実行する大きなメリットとして次の2点があります。
- WebアプリケーションをLambda向けにカスタマイズすることなく、そのままLambda関数として実行できる
- Lambda関数は、本記事で紹介したFunction URL以外にも、API GatewayやALB経由などでも呼び出せる
※ 画像引用元 https://github.com/awslabs/aws-lambda-web-adapter/blob/main/docs/images/lambda-adapter-overview.png
Function URLとして公開されたLambda関数は、CloudFrontとも容易に連携できます。
AWS Lambda Web Adapterを使ってWebアプリケーションをLambda関数として実行できるようにすることで、柔軟なサーバーレス構成を構築できます。
ローカル開発したものをLambdaに持っていったり、ALB配下でLambdaを動作させると、そこからさらにECSへシームレスに移行できます。
PoCのような検証時や社内ツールの簡易的な実行基盤としても非常に重宝できます。
GitHubのaws-lambda-web-adapterレポジトリの examples ディレクトリ配下には、今回紹介したFastAPI以外にも、Next.jsやSpring Bootなど人気フレームワークのサンプルコードが豊富に用意されています。
各サンプルはAWS SAMで簡単に実行できるので、ぜひ一度お試しください。