PoetryでFastAPI + LangServe + Bedrock開発の環境構築

## ３行で経緯

- 久しぶりにPythonを触ることになったら、
- いつの間にかパッケージ管理に便利そうなもの（Poetry）が登場していたので
- 今時らしい題材（FastAPI + LangServe）で開発環境を構築してみる

## 前提

```zsh
sysctl -n machdep.cpu.brand_string
# => Apple M1 Pro
python3 --version
# => Python 3.12.4
```

## この記事で扱う技術セットの概要

### Poetryとは

https://python-poetry.org/

Pythonのパッケージマネージャー。より簡単に依存関係の管理や追跡、パッケージのビルド・配布を行えるのが強みで、Pythonの仮想環境のセットアップまで容易にしてくれる。pyproject.tomlを使用して依存パッケージやプロジェクト設定を一元管理する。

筆者は普段Node.js界隈中心に活動しているので、Node.js界隈にすぐ伝わる言い方をするなら、pyproject.tomlはpackage.jsonに相当。ちなみにpackage-lock.jsonに相当するものもあって、poetry.lockというファイルがそれに該当する。

## LangServeとは

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

LangChainをREST APIとして簡単にデプロイするためのツール。LangChain公式が開発しており、LangChainテンプレートもこのLangServeで公開されている。FastAPIと統合されており、少ないコードでSwaggerUI付きの分かりやすいREST APIをすぐに実装できる。

## LangChainとは

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

LLM（大規模言語モデル）を利用したアプリケーション開発のためのフレームワーク。各社の言語モデルを統合してくれていたり、様々なデータソースからモデルに情報を渡すことができたり、過去の会話を記録するメモリ機能があったりと、LLM開発において何かと手間のかかる部分をフレームワークとして容易かつ効率よく実装できるようになっている。

## FastAPIとは

https://fastapi.tiangolo.com/ja/

シンプルなコードで迅速にHTTP APIを開発可能なPythonのWebフレームワーク。FastAPIの公式ドキュメントは自身のことを次のように説明している。

> FastAPI は、Pythonの標準である型ヒントに基づいてPython 以降でAPI を構築するための、モダンで、高速(高パフォーマンス)な、Web フレームワークです。
>
> 主な特徴:
> - 高速: NodeJS や Go 並みのとても高いパフォーマンス (Starlette と Pydantic のおかげです)。 最も高速な Python フレームワークの一つです.
> - 高速なコーディング: 開発速度を約 200%~300%向上させます。 *
> - 少ないバグ: 開発者起因のヒューマンエラーを約 40％削減します。 *
> - 直感的: 素晴らしいエディタのサポートや オートコンプリート。 デバッグ時間を削減します。
> - 簡単: 簡単に利用、習得できるようにデザインされています。ドキュメントを読む時間を削減します。
> - 短い: コードの重複を最小限にしています。各パラメータからの複数の機能。少ないバグ。
> - 堅牢性: 自動対話ドキュメントを使用して、本番環境で使用できるコードを取得します。
> - Standards-based: API のオープンスタンダードに基づいており、完全に互換性があります

https://fastapi.tiangolo.com/ja/

## Amazon Bedrockとは

https://aws.amazon.com/jp/bedrock/

AWSで生成AIアプリケーションを開発するならこれ。各社の生成AIモデルを提供してくれている。AWSなので権限制御はIAMで可能。プロダクトのその他のリソースと合わせて権限管理するならIAM制御可能なのは便利。

## Poetryのインストールと設定

筆者はMacを使用しているので、次のようにインストールしました。

```zsh
curl -sSL https://install.python-poetry.org | python3 -
```

パスを通すべく `.zshrc` に下記を追加。

```zsh:.zshrc
export PATH="$HOME/.local/bin:$PATH"
```

ターミナルを開き直すと無事にpoetryのバージョンを確認できました。

```zsh
poetry --version
# => Poetry (version 1.8.3)
```

タブ補完を有効にして、

```zsh
mkdir ~/.zfunc
poetry completions zsh > ~/.zfunc/_poetry
```

あとはパッケージ依存関係の管理をプロジェクト内に閉ざすべく、Pythonの仮想環境 `.venv` をプロジェクトディレクトリごとに作成するよう下記を設定すれば完了！

```zsh
poetry config virtualenvs.in-project true
```

## Poetry + FastAPIで開発できるようにする

まずはPoetryで新しいプロジェクトを作成。

```zsh
poetry new poetry-fastapi-langserve-sample

# $ tree poetry-fastapi-langserve-sample/
# poetry-fastapi-langserve-sample/
# ├── README.md
# ├── poetry_fastapi_langserve_sample
# │   └── __init__.py
# ├── pyproject.toml
# └── tests
#     └── __init__.py
```

Pythonの仮想環境に入って、

```zsh
cd poetry-fastapi-langserve-sample
poetry shell
# ※ちなみに仮想環境から出る時は `exit` コマンドを実行すればOK
```

必要なパッケージ２つを追加。

```zsh
poetry add fastapi uvicorn
```

最小限のエントリーポイントを書いて、

```python:main.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}
```

FastAPIを起動。

```zsh
uvicorn main:app --reload
```

リクエストを投げるとちゃんと応答が返ってくる。

```zsh
curl http://localhost:8000
# => {"Hello":"World"}
```

これでFastAPIが動作していることが確認できた。

## FastAPIにLangServe + Amazon Bedrockを組み込んでみる

まずはLangServeを導入。

```zsh
poetry add 'langserve[server]'
```

LangServeはadd_routesにRunnableを継承したクラスを渡すと自動でルートを定義できる。今回はAmazon Bedrock経由で生成AIモデルを使いたいので、langchain-awsパッケージを導入する。

https://python.langchain.com/docs/integrations/chat/bedrock/

```zsh
poetry add langchain-aws
```

このlangchain-awsパッケージにはBedrockをLangChainに統合可能な「ChatBedrock」が含まれている。このChatBedrock自体が前述のRunnableを継承したクラスなので、これさえあればすぐにBedrockのAPIルートを作成できる。モデルは今回、Claude 3.5 Sonnetを指定。

```python:main.py
from fastapi import FastAPI
from langchain_aws.chat_models.bedrock import ChatBedrock
from langserve import add_routes

app = FastAPI()

add_routes(app, ChatBedrock(
    model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",
    region_name="us-east-1"),
    path="/bedrock")
```

実装はこれだけ！　あとは動かすのみ。

ChatBedrockコンポーネントはAWS SDK for Python (Boto3) を介してBedrockにリクエストを投げるので、動かすためにはAWSのクレデンシャルが必要。環境変数でAWSプロファイルを指定するなら、次のようにする（もちろん他のやり方でもOK）

```zsh
export AWS_PROFILE=自分のプロファイル名
```

AWSのクレデンシャルを指定できたなら、あとは起動して動作確認するだけ。

```zsh
uvicorn main:app --reload
```

![スクリーンショット 2024-09-30 14.43.08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267156/3877e84b-952d-b721-0483-d41535374918.jpeg)

FastAPIが `http://localhost:8000/docs` にSwaggerUIを表示してくれるので、アクセスしてみよう。

![スクリーンショット 2024-09-30 15.15.08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267156/dfff2c9b-7141-936d-a093-2852138001d8.jpeg)


一通りのAPIルートが作成されている！　試しに動かしてみると、ちゃんとBedrock経由で生成AIモデルのAPIを動かせた。

![スクリーンショット 2024-09-30 15.18.43.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267156/cf055571-fa92-6982-6ea7-6a88f4c0a31b.jpeg)

これで、PoetryでのFastAPI + LangServe + Bedrockの環境構築ができた。

単にBedrock経由で生成AIのモデルを利用可能なREST APIを用意したいだけなら、これだけでも便利だし、何かプロダクトとしてLangChainで作ったカスタムチェーンを実行可能なREST APIにしたいなら、これを起点に開発していけばいいですね。

## まとめ

PoetryでFastAPI + LangServe + Bedrockの環境構築とmain.py実装までを一気通貫するなら、こう。

```zsh
# Poetry自体のインストールと設定までは割愛
poetry new poetry-fastapi-langserve-sample
cd poetry-fastapi-langserve-sample
poetry shell
poetry add fastapi uvicorn 'langserve[server]' langchain-aws
```

```python:main.py
from fastapi import FastAPI
from langchain_aws.chat_models.bedrock import ChatBedrock
from langserve import add_routes

app = FastAPI()

add_routes(app, ChatBedrock(
    model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",
    region_name="us-east-1"),
    path="/bedrock")
```

```zsh
# AWSクレデンシャルの指定とFastAPIの起動
export AWS_PROFILE=自分のプロファイル名
uvicorn main:app --reload
```

これで完了！　この手数の少なさはプロダクトの中核を開発するまでの道のりが短くていいですね。

次回はLangChainでカスタムチェーンを実装し、LangServeで実際にREST API化するところまで記事にします。

## 参考: 最終的なディレクトリ構造やpyproject.tomlの状態など

最終的には、今回のプロジェクトは次のようなディレクトリ構造になっています。

```
.
├── README.md
├── __pycache__
│   ├── main.cpython-312.pyc
│   └── main.cpython-39.pyc
├── main.py
├── poetry.lock
├── poetry_fastapi_langserve_sample
│   └── __init__.py
├── pyproject.toml
└── tests
    └── __init__.py
```

そしてpyproject.tomlの中身は次のようになっています。

```toml:pyproject.toml
[tool.poetry]
name = "poetry-fastapi-langserve-sample"
version = "0.1.0"
description = ""
authors = ["shigeru.hyodo <41170561+gel1123@users.noreply.github.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.115.0"
uvicorn = "^0.31.0"
langchain-aws = "^0.2.1"
langserve = {extras = ["server"], version = "^0.3.0"}


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```

## おまけ: LangServeのPlayground機能

LangServeにはadd_routesに渡したrunnableのPlaygroundを提供してくれる機能がある。

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

実際にFastAPIを起動してPlaygroundにアクセスしてみると、こんな感じ。ちょっとした動作確認をしたいときに便利そう。

![スクリーンショット 2024-09-30 14.55.08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267156/ef1f9cf9-81c6-d695-0ffa-d35e3831aac1.jpeg)

本番環境で使う時など、PlaygroundをOFFにしたい場合は、次のようにdisabled_endpointsオプションを指定してやればOK。

```diff
 add_routes(app, ChatBedrock(
     model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",
     region_name="us-east-1"),
+    disabled_endpoints=["playground"],
     path="/bedrock")
```

LangChainでカスタムチェーンを作った時の動作確認としても、あると便利そうですね！