Lambda SnapStartがPythonに対応したので改めてLangServe on Lambdaをやってみた

今まで、Javaのみに提供されていたLambda SnapStartがPythonと.Netに対応しました🎉🎉🎉

https://aws.amazon.com/jp/blogs/aws/aws-lambda-snapstart-for-python-and-net-functions-is-now-generally-available/

さすがクラスメソッドさん、もうブログが出ていますので、「SnapStartって何？」みたいな方はこちらを参照いただければと思います。

https://dev.classmethod.jp/articles/snap-start-support-python-and-dotnet/

以前、Lambda上でLangServeを動作させる方法（[こちら](https://qiita.com/moritalous/items/ab61f52c490f82fdb72f)）を投稿したのですが、1年ぐらいたつので、改めてやってみました。

https://python.langchain.com/docs/langserve/

:::note
LangServeは、LangChainのチェーンをいい感じにWeb APIにするやつです
:::

## 手順

### LangServeのLambdaレイヤーを作成する

CloudShellで作業します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/c25d3657-3f34-739f-534e-f685fe8a6e47.png)

pythonのSnapStartは、Pythonの3.12以降が対応とのことで、今回は最新のPython3.13を使用します。

CloudShellにインストールされているPythonは3.9なので、Dockerを使いつつ、無理やりPython3.13のレイヤーを作成します。

```shell:CloudShell
mkdir python

docker run \
-v $PWD/python:/python \
--user $(id -u):$(id -g) \
python:3.13-bullseye pip install -t python langserve[server]==0.3.0 langchain-aws==0.2.7

rm -r python/boto*

zip -r9 langserve-layer.zip python

aws lambda publish-layer-version \
--layer-name langserve-layer-p313 \
--compatible-runtimes python3.13 \
--compatible-architectures x86_64 \
--zip-file fileb://langserve-layer.zip --no-cli-pager
```

:::note warn
`python:3.13`だとLambda環境で実行時エラーになるので`python:3.13-bullseye`を指定しています。
:::

## Lambda関数を作成する

マネジメントコンソールで頑張ります。

ランタイムは「Python 3.13」を選択して、「関数の作成」をクリックします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/0a01a666-27a2-bd19-4f62-3006a48af9c5.png)

### ラインタイム設定

ハンドラを「run.sh」に変更します。（run.shは後ほど作成します）

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/e12fc356-8fa5-f5b9-5cd9-4028b912303a.png)

### レイヤーを指定する

先ほど作成したLangServeのレイヤーに加え、「AWS Lambda Web Adapter」を追加します。

https://github.com/awslabs/aws-lambda-web-adapter

:::note
AWS Lambda Web Adapterは、FastAPIなどのWebアプリをLambda上で動作させるAWS製のライブラリーです。LangServeも内部的にFastAPIを使用しています。
:::

* LangServeレイヤー（langserve-p313）

    バージョンはそれぞれの環境で変わってくると思います。

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/c82fe1b8-13e8-c049-0e31-4d55caa4a17d.png)

* Lambda Web Adapter

    ARNを直接指定します。（リージョンはLambdaのリージョンに合わせます）

    `arn:aws:lambda:us-east-1:753240598075:layer:LambdaAdapterLayerX86:23`

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/78cbaa25-85ce-8be5-64ee-511689240973.png)



### ソースコードを記述する

ソースコードを記述します。

`lambda_function.py`は不要なので削除します

* main.py

    ```python:main.py
    #!/usr/bin/env python
    
    from fastapi import FastAPI
    from langchain_aws.chat_models import ChatBedrockConverse
    from langchain_core.output_parsers import StrOutputParser
    
    from langserve import add_routes
    
    app = FastAPI(
        title="LangServe",
        version="1.0",
        description="LangChain Server",
    )
    
    chain = ChatBedrockConverse(model="us.anthropic.claude-3-5-haiku-20241022-v1:0") | StrOutputParser()
    
    add_routes(
        app,
        chain,
        path="/bedrock",
    )
    
    if __name__ == "__main__":
        import uvicorn
    
        uvicorn.run(app)
    
    ```

* run.sh
    
    ```shell:run.sh
    #!/bin/bash
    
    PATH=$PATH:$LAMBDA_TASK_ROOT/bin \
        PYTHONPATH=$PYTHONPATH:/opt/python:$LAMBDA_RUNTIME_DIR \
        exec python -m uvicorn --port=$PORT main:app
    ```

ソースは自動保存されます。
ソースが完成したら、必ず「Deploy」ボタンを押してください。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8c6d7e93-8138-dc8e-8d4d-015775ade063.png)


### 諸々設定する

設定を色々します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/950c83ab-d4cb-750c-fb82-3a1c7b3a6a4a.png)

* 一般設定

    * メモリを256MBにします（お好みで）
    * SnapStartを「PublishedVersions」にします
    * タイムアウトを30秒にします（お好みで）

    
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/0146bb36-f4af-44a7-c6f7-19f49b889577.png)

    また、画面下部の「既存のロール」のリンクからIAMロールの画面に遷移し、「BedrockFullAccess」を追加します。
    
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/4599887f-7a9a-e7e4-904b-7366d68864e5.png)

* 環境変数

    Lambda Web Adapterの動作に必要なので以下の環境変数をセットします。

    * **AWS_LAMBDA_EXEC_WRAPPER** : /opt/bootstrap
    * **AWS_LWA_INVOKE_MODE** : response_stream
    * **PORT** : 8000

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/92f653ee-42d5-2761-1a77-40248a5c3f17.png)

### バージョンとエイリアス、関数URLを作成する

SnapStartを使用するには「バージョン」を作成する必要があります。
関数URLを使用するには「エイリアス」を作成する必要があります。

ということで、「バージョン」を作ったうえで「エイリアス」を作ります。

まずはバージョンを作成します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/044f8e62-3a8a-cb14-f29c-bf82be7169b5.png)

そしてエイリアスを作成します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/964edae0-acf7-5504-8a84-1751f3e8e265.png)

最後に関数URLを作成します。

認証タイプを「NONE」にし、その他の設定の中にある「呼び出しモード」を「RESPONSE_STREAM」に設定します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/43cf27d3-8c2a-a132-54a7-6e5cb5e9ba8b.png)

これで、SnapStartを有効化した関数URLが発行されます。

## ブラウザでアクセスしてみる

関数URLのURLの末尾に`/docs`をつけてブラウザでアクセスします。

OpenAPIのドキュメントが表示されます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/4b10c172-e817-5a28-9970-bec96e79f77f.png)

今回は「/bedrock」としてAPIを作成したので、「/bedrock/playground」にアクセスすると、プレイグラウンドが表示されます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/364bb74b-0556-72ec-dd9e-fc308329441b.png)

もちろん、呼び出しも可能です。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/ab6157d4-150f-3a79-3134-65ac7a1f101a.png)

:::note
普通にWeb画面が表示されてますが、これ、Lambdaのレスポンスですよ。すごくないですか？？
:::

SnapStartの検証はできてませんすいませんw