🐙 LangServeで作成したAPIをRenderにデプロイするまでの流れ


## 参照したドキュメント

[LangServe](https://python.langchain.com/docs/langserve/)

[Reder へ FastAPI のデプロイ方法](https://render.com/docs/deploy-fastapi)

## 今回作ったもの

日記のデータをリクエストボディにつけて送信すると、成長した点などをまとめて返してくれる API を作成しました。

<details><summary> リクエスト body につける json の例</summary>

```json
{
    "内省ノート": [
        {
            "日付": "2025年3月24日（月）",
            "良かった点": [
                "朝のスタンドアップミーティングで自己紹介ができた",
                "開発環境のセットアップを完了した",
                "先輩エンジニアからGitの使い方について丁寧に教えてもらえた"
            ],
            "反省点": [
                "質問するタイミングで躊躇してしまい、無駄な時間を使ってしまった",
                "チーム用語や略語がわからず、会話についていけない場面があった",
                "メモを取る習慣がまだできていない"
            ],
            "感想": "初日は緊張したが、チームメンバーが親切に迎えてくれて安心した。明日からはもっと積極的に質問していきたい。わからないことが多すぎて少し不安だが、一つずつ学んでいこう。"
        },
        {
            "日付": "2025年3月25日（火）",
            "良かった点": [
                "昨日より早く質問できるようになった",
                "チームのドキュメントを読み込み、プロジェクト構成の理解が深まった",
                "簡単なバグ修正タスクを一つ完了できた"
            ],
            "反省点": [
                "コードレビューでコーディング規約に関する指摘を多く受けた",
                "テスト環境の使い方を誤解していた",
                "ブランチ戦略について理解が浅かった"
            ],
            "感想": "小さな成功体験があって嬉しい。バグ修正は小さなものだったが、実際のコードベースに貢献できたことが自信になった。コードレビューでの指摘は多かったが、学びに変えていきたい。チーム用語集を作り始めたのも良かった。"
        },
        {
            "日付": "2025年3月26日（水）",
            "良かった点": [
                "朝一番で昨日の指摘を全て修正し、承認をもらえた",
                "ペアプログラミングでDOの設計パターンについて学べた",
                "チーム勉強会で質問ができた"
            ],
            "反省点": [
                "デバッグに時間がかかりすぎた",
                "APIの使い方を誤解して実装を進めてしまった",
                "集中力が途切れる時間帯があった"
            ],
            "感想": "知識不足を痛感する日だった。特にAPIの理解不足は反省点。ただ、先輩方が根気強く教えてくれるので救われている。明日はもっと事前調査をしっかりしてから実装に入りたい。少しずつだが成長している気がする。"
        },
        {
            "日付": "2025年3月27日（木）",
            "良かった点": [
                "朝のスタンドアップで昨日の作業を簡潔に説明できた",
                "新たに2つのタスクを完了できた",
                "単体テストの書き方を理解し、自分で追加できた"
            ],
            "反省点": [
                "コミットメッセージの書き方がまだ不十分",
                "エラーハンドリングの考慮が足りなかった",
                "タスク見積もりが甘く、予定より時間がかかった"
            ],
            "感想": "少しずつ開発速度が上がってきている気がする。単体テストを書く重要性も理解できてきた。まだまだ基本的なミスが多いが、先週よりは確実に成長している。明日はコードの品質にもっと注意を払いたい。"
        },
        {
            "日付": "2025年3月28日（金）",
            "良かった点": [
                "新機能の設計レビューで自分の意見を伝えることができた",
                "バグの根本原因を自力で特定できた",
                "リファクタリングで重複コードを削減できた"
            ],
            "反省点": [
                "コードレビューのコメントを見落としていた",
                "ドキュメント更新を忘れていた",
                "週報の提出が遅れた"
            ],
            "感想": "一週間があっという間に過ぎた。技術的な面では少しずつ成長を感じるが、プロジェクト管理や作業の優先順位付けなど、開発以外のスキルも磨く必要があると感じた。週末はJavaScriptの非同期処理について勉強したい。"
        },
        {
            "日付": "2025年3月31日（月）",
            "良かった点": [
                "週末に学んだ非同期処理の知識を活かして実装できた",
                "朝一番でTo-Doリストを作成し、計画的に作業できた",
                "他のチームメンバーのコードレビューに参加できた"
            ],
            "反省点": [
                "DBのクエリ最適化についての知識不足を感じた",
                "会議の準備が不十分だった",
                "マージ後のテストを忘れるところだった"
            ],
            "感想": "新しい週が始まったが、先週よりも落ち着いて作業できるようになった。自分の役割も少しずつ理解できてきた。特に、コードレビューに参加して他のメンバーのコードから学べたのは大きな収穫。これからも積極的に参加したい。"
        },
        {
            "日付": "2025年4月1日（火）",
            "良かった点": [
                "複雑な機能の実装を一人で完了できた",
                "チームリーダーから実装アプローチを褒められた",
                "新しく入った研修生に環境構築を教えることができた"
            ],
            "反省点": [
                "エッジケースの考慮が足りなかった",
                "コードの可読性よりも効率を優先してしまった部分があった",
                "Gitのrebaseでコンフリクト解決に手間取った"
            ],
            "感想": "入社から一週間以上が経ち、少しずつ自信がついてきた。特に今日は他の人に教える立場になれたことが大きな自信になった。まだ知識不足は多いが、一歩一歩前進している実感がある。明日も頑張ろう。"
        }
    ]
}
```

</details>

<details><summary> レスポンスで帰ってくる評価の例</summary>

```
あなたの内省ノートを拝見し、成長した点を以下にリストアップしました。

成長した点
コミュニケーション能力の向上

朝のスタンドアップミーティングで自己紹介や作業内容を簡潔に説明できるようになった。
チーム勉強会や設計レビューで質問や意見を伝えることができた。
技術的スキルの向上

Gitの使い方を学び、実際にコードレビューやブランチ戦略を理解するようになった。
バグ修正や新機能の実装を通じて、実践的なプログラミングスキルが向上した。
単体テストの書き方を理解し、自分でテストを追加できるようになった。
問題解決能力の向上

バグの根本原因を自力で特定できるようになり、リファクタリングで重複コードを削減することができた。
複雑な機能の実装を一人で完了し、実装アプローチを評価された。
自己管理能力の向上

To-Doリストを作成し、計画的に作業を進めることができた。
週末に自主的に学習を行い、知識を実装に活かすことができた。
チームへの貢献

他のチームメンバーのコードレビューに参加し、学びを得るとともに、チームの成長に寄与した。
新しく入った研修生に環境構築を教えることで、教える立場としての経験を積んだ。
評価
あなたの内省ノートからは、短期間での成長が非常に明確に見て取れます。特に、コミュニケーション能力や技術的スキルの向上は、チームでの協力やプロジェクトの成功に直結する重要な要素です。また、自己管理能力や問題解決能力の向上も、今後のキャリアにおいて大きな強みとなるでしょう。

反省点についても、具体的に挙げられており、次に何を改善すべきかが明確です。これらの反省を活かして、さらなる成長を目指す姿勢は素晴らしいです。引き続き、学びを深め、チームに貢献し続けることを期待しています
```

</details>

## ローカルで動かす

まずはローカルで動かしてみましょう。

```python
from fastapi import FastAPI
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langserve import add_routes
from fastapi.middleware.cors import CORSMiddleware
import os
from dotenv import load_dotenv
load_dotenv()

app = FastAPI(
    title="LangChain Server",
    version="1.0",
    description="This is a LangChain server for evaluating introspection logs.",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["*"],
)

model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = ChatPromptTemplate.from_template("""あなたは人々の相談により人生をよりよくするカウンセラーです。
                                        そこであなたには一定期間の内省の記録をもとに成長した点をリストアップしてから評価していただきたいです。
                                        {intro_log}
                                        """)
add_routes(
    app,
    prompt | model,
    path="/eval",
)

if __name__ == "__main__":
    import uvicorn
    port = int(os.environ.get("PORT", 8000))
    host = "0.0.0.0"
    uvicorn.run(app, host=host, port=port)
```

評価してくれるエンドポイントしかないシンプルな API です。
このプログラムを動かすのに必要なライブラリは以下です。
以下のテキストをコピーして、requirements.txt に貼り付けてください。

```
aiohappyeyeballs==2.6.1
aiohttp==3.11.16
aiosignal==1.3.2
annotated-types==0.7.0
anthropic==0.49.0
anyio==4.9.0
async-timeout==4.0.3
attrs==25.3.0
certifi==2025.1.31
charset-normalizer==3.4.1
click==8.1.8
dataclasses-json==0.6.7
distro==1.9.0
exceptiongroup==1.2.2
fastapi==0.115.12
frozenlist==1.5.0
h11==0.14.0
httpcore==1.0.7
httpx==0.28.1
httpx-sse==0.4.0
idna==3.10
jiter==0.9.0
jsonpatch==1.33
jsonpointer==3.0.0
langchain==0.3.22
langchain-anthropic==0.3.10
langchain-community==0.3.20
langchain-core==0.3.50
langchain-openai==0.3.12
langchain-text-splitters==0.3.7
langserve==0.3.1
langsmith==0.3.23
marshmallow==3.26.1
multidict==6.3.2
mypy-extensions==1.0.0
numpy==2.0.2
openai==1.69.0
orjson==3.10.16
packaging==24.2
propcache==0.3.1
pydantic==2.10.1
pydantic-settings==2.8.1
pydantic_core==2.27.1
python-dotenv==1.1.0
PyYAML==6.0.2
regex==2024.11.6
requests==2.32.3
requests-toolbelt==1.0.0
sniffio==1.3.1
SQLAlchemy==2.0.40
sse-starlette==2.2.1
starlette==0.46.1
tenacity==9.1.2
tiktoken==0.9.0
tqdm==4.67.1
typing-inspect==0.9.0
typing_extensions==4.13.1
urllib3==2.3.0
uvicorn==0.34.0
yarl==1.18.3
zstandard==0.23.0
```

requirements.txt を作成したら`pip install -r requirements.txt`で依存関係をインストールしましょう。

そしてローカルに.env ファイルを作成して以下の項目を記述します。
`<ここに API キーを記入>`の部分にはそれぞれの API キーを記入してください。

```
OPENAI_API_KEY=<ここにAPIキーを記入>
ANTHROPIC_API_KEY=<ここにAPIキーを記入>
LANGSMITH_API_KEY=<ここにAPIキーを記入>
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
```

では、ローカルで動かしてみましょう。
`main.py`という名前で保存している場合`python main.py`を実行してサーバーを起動します。
おそらく以下のようなメッセージが表示されると思います。

![](/images/langserve_launched.png)

では、`http://0.0.0.0:8000/docs`にアクセスして、API が動いているか確認してみましょう。

![](/images/fastapi_langseve_docs.png)

そして今回注目するのは`/eval/invoke`のエンドポイントです。
他にも batch(複数の入力を一度に処理する)や streaming(ストリーミングでレスポンスを受け取る)のエンドポイントもありますが、今回は`/eval/invoke`のエンドポイントを利用します。

`/eval/invoke`エンドポイントの仕様を確認してみましょう。
![](/images/langserve_eval_invoke_definition.png)
RequestBody の項目を見ると input キーがあることがわかります。その中に`intro_log`というキーがあり、そこに評価してほしい内容を入れます。
ちなみに`intro_log`に渡した内容が Python コードの中の`{intro_log}`に入ります。

```python
ChatPromptTemplate.from_template("""あなたは人々の相談により人生をよりよくするカウンセラーです。
                                        そこであなたには一定期間の内省の記録をもとに成長した点をリストアップしてから評価していただきたいです。
                                        {intro_log} # ここに入る
                                        """)
```

では先ほどの`http://0.0.0.0:8000/docs`の`/eval/invoke`の項目の中の`Try It out`と書かれたボタンを教えてください
そして`intro_log`の項目に評価して欲しいデータを入れて`Execute`ボタンを教えてみましょう。しばらくするとレスポンスが帰ってきて評価が表示されると思います。

動くことが確認できたら、Render でデプロイするための準備として GitHub にリポジトリを作成して、先ほどの`main.py`と`requirements.txt`をコミットしておきましょう。（.env ファイルは GitHub にあげないように注意してください。）
今回はこの部分を詳しく説明しませんが、時間があれば追記するかもしれないです。

## Render でデプロイ

この記事は 2025 年 4 月 5 日に書いているのでこの記事を見ている時点の Render の画面の UI などが変わっている可能性があるので適宜読み替えてください。

1. Render のアカウントを作成(https://render.com/ にアクセスして右上の`Sign In`をクリック)後程 GitHub のリポジトリからデプロイするので GitHub アカウントを利用して Render のアカウントを作成することをおすすめします。　今回はここの説明は省略します。
2. アカウントを作成できたら、右上のダッシューボードボタンを教えてダッシュボードに移動します。
3. ダッシュボードに移動したら Projects の項目の Add New ボタンを教えて`Web Service`を選択します。
   ![](/images/render_add_webservice_button.png)
4. WebService の作成画面では最初にデプロイするリポジトリを選択することになると思うのでリポジトリを選択します。
   ![](/images/render_new_webservice_choice_repository.png)
5. `Start Command`という項目に`python main.py`と入力します。
6. `Instance Type`は`Free`を選択します。
7. `Environment Variables`という項目があるので、先ほどローカルで作成した`.env`ファイルの内容から追加しましょう。
   ![](/images/render_new_webservice_add_environment_variable.png)
8. 最後に一番下にある`Deploy Web Service`ボタンを教えてデプロイします。
9. デプロイが完了するまでしばらく待ちます。デプロイが完了してからデプロイしたプロジェクトの詳細ページにあるリンクからデプロイした API にアクセスできるようになります。
   ![](/images/render_deployed_project_page.png)

ここまでで Python で LangChain を使って作成した API を Render でデプロイすることができました。