🐶
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