🐶

Laravel 経験者のための FastAPI チャットボット構成ガイド(初心者向け)

に公開

Laravelに慣れた筆者が FastAPIでチャットボットアプリを開発する際、「慣れ親しんだ構造で学びたい」という思いから、AIとの壁打ちで「Laravel風FastAPI ディレクトリ構造」考えたので紹介する。


ディレクトリ構成(筆者のような初心者向け)

chatbot_app/
├── main.py                    # アプリのエントリーポイント
├── app/
│   ├── routers/               # URLルーティング
│   │   └── chat.py
│   ├── controllers/           # 表示ロジック
│   │   └── chat_controller.py
│   ├── services/              # ビジネスロジック / LangChain実行
│   │   ├── chat_service.py
│   │   └── langchain_chain.py
│   ├── models/                # スキーマ定義(Pydantic/ORM)
│   │   └── schemas.py
│   ├── templates/             # Jinja2テンプレート(ビュー)
│   │   └── chat.html
│   ├── static/                # 静的ファイル
│   │   └── style.css
│   └── core/                  # アプリ設定など
│       └── config.py
└── tests/
    └── test_chat.py

Laravel との対応表

Laravel構成 FastAPI構成
routes/web.php routers/*.py
Controllers controllers/*.py
Services services/*.py
外部APIラッパー or Provider langchain_chain.py
resources/views templates/(Jinja2)
public static/

動作例コード

main.py

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from app.routers import chat

app = FastAPI()

app.mount("/static", StaticFiles(directory="app/static"), name="static")
templates = Jinja2Templates(directory="app/templates")

app.include_router(chat.router)

chat.py(ルーター)

from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from app.controllers import chat_controller

router = APIRouter()

@router.get("/chat", response_class=HTMLResponse)
async def show_chat(request: Request):
    return await chat_controller.show(request)

chat_controller.py

from fastapi import Request
from fastapi.templating import Jinja2Templates
from app.services.chat_service import get_chat_response

templates = Jinja2Templates(directory="app/templates")

async def show(request: Request):
    user_input = "こんにちは"
    bot_reply = await get_chat_response(user_input)
    context = {"request": request, "message": bot_reply}
    return templates.TemplateResponse("chat.html", context)

chat_service.py

from app.services.langchain_chain import qa_chain

async def get_chat_response(user_input: str) -> str:
    response = qa_chain.invoke({"question": user_input})
    return response["answer"]

langchain_chain.py

from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings

db = FAISS.load_local("db", OpenAIEmbeddings())
llm = ChatOpenAI()
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=db.as_retriever())

chat.html(View)

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="/static/style.css">
    <title>チャットページ</title>
</head>
<body>
    <h1>{{ message }}</h1>
</body>
</html>

まとめ

  • 最初は Controller + View(Jinja2) を中心に構成し、アプリが複雑化したら Service や Repository を導入することも視野に入れる。
  • 役割ごとにディレクトリを分けることで、拡張性・可読性の高いアーキテクチャを実現。

参考リンク

Discussion