第01回 学習メモ: Chainlitを使った基本的なチャットボット¶
全体像¶
3ファイルが「だんだん本格化していくチュートリアル」になっており、「枝豆の妖精」というキャラ設定(system prompt)が共通。
学習ステップの全体構成¶
flowchart LR
Step1["chatgpt.py<br/>OpenAI API を単発で叩く<br/>(CLI / 状態なし)"]
Step2["app.py<br/>Chainlit で UI だけ立てる<br/>(LLM 未接続 / 固定応答)"]
Step3["chatbot.py<br/>Step1 + Step2 を統合<br/>+ 会話履歴の保持"]
Step1 -->|LLM 呼び出しを覚える| Step3
Step2 -->|Chainlit UI を覚える| Step3
「LLM 単体」と「UI 単体」を別々に学んでから合体する、というステップ分割が綺麗。
chatbot.py のメッセージ往復(ステートレス API でどう会話を成立させるか)¶
sequenceDiagram
autonumber
participant Browser as ブラウザ (Chainlit UI)
participant Server as chatbot.py (Chainlit ハンドラ)
participant Session as cl.user_session<br/>(プロセス内 dict)
participant OpenAI as OpenAI API<br/>(ステートレス)
Browser->>Server: チャットセッション開始
Server->>Session: history = [system prompt]
Note over Session: 妖精キャラの設定だけ入った状態
Browser->>Server: ユーザー発話「こんにちは」
Server->>Session: history.append(user)
Server->>OpenAI: ChatCompletion.create(messages=history)
Note over OpenAI: 履歴を毎回全送り<br/>これが「ステートレスでも会話できる」理由
OpenAI-->>Server: assistant 応答
Server->>Session: history.append(assistant)
Server->>Browser: cl.Message(...).send()
Browser->>Server: 次の発話「枝豆って美味しい?」
Server->>Session: history.append(user)
Server->>OpenAI: ChatCompletion.create(messages=history)
Note over OpenAI: 1ターン目の応答も含めて<br/>すべての履歴を再送
OpenAI-->>Server: assistant 応答
Server->>Session: history.append(assistant)
Server->>Browser: cl.Message(...).send()
重要なポイント: OpenAI API 自体はステートレス(過去の会話を覚えていない)。会話が成立しているように見えるのは、クライアントが毎リクエストで履歴を丸ごと送り直しているから。history の所在は Chainlit のプロセス内メモリ(cl.user_session)であって、永続化はされていない(タブを閉じれば消える)。
使用ライブラリ・原理¶
OpenAI Chat Completion API¶
messages配列に{"role": "system" | "user" | "assistant", "content": "..."}を積んでLLMに送る- API自体はステートレス: 過去の会話を覚えていないので、クライアントが履歴を毎回まるごと送る
temperature: 0で決定的、1で創造的(0.7はキャラ会話の定番)max_tokens: 出力上限。コスト制御と暴走防止
Chainlit¶
- StreamlitのLLMチャット特化版のようなフレームワーク
chainlit run app.pyでブラウザにチャットUIが立ち上がる@cl.on_message: ユーザー送信時に呼ばれるハンドラ@cl.on_chat_start: セッション開始時に1回だけ呼ばれるフックcl.user_session.get/set: ユーザーごとに独立したセッションストアcl.Message(content=...).send(): チャット欄に返信を出す
ファイル別の役割¶
| ファイル | 役割 |
|---|---|
chatgpt.py |
OpenAI APIをPythonから叩く最小例(CLI、状態なし) |
app.py |
Chainlitの最小例。LLM未接続で固定応答を返すUI骨格 |
chatbot.py |
上記2つを統合 + cl.user_session で会話履歴を永続化 |
学んだこと(要点)¶
- Chat APIのステートレス性: 毎リクエストで履歴を全送するから「会話している」体験になる
- トークン消費は履歴に比例して増える → 後の回で要約・圧縮の必要性につながる伏線
- UIとLLMを分けて学ぶ構成: フレームワークの責務を切り分けて理解できる、教科書的に綺麗な順序
拡張アイデア¶
- ストリーミング表示(
cl.Message().stream_token(...)を使う) - 履歴のトークン数を概算し、N件を超えたら古いものを切り詰める
- system promptをチャット内で動的に切り替える(キャラ選択UI)
現代版に移植するなら¶
- APIキーは
os.environ["OPENAI_API_KEY"]+.envで読む(ハードコード禁止)。本リポジトリは 1Password CLI 経由 openai.ChatCompletion.create(...)→client.chat.completions.create(...)(v1.x SDK)model="gpt-3.5-turbo"→gpt-4o-mini/gpt-4.1-nano(コスト・性能とも良い)@cl.on_messageの引数はmessage: cl.Message型推奨(message.contentで本文取得)
記事参照¶
- Software Design 2023年〜の連載 第01回
- 動かし方は
README.mdを参照
作成: 2026-05-17 / 最終更新: 2026-06-10