こんにちは!
先日、Streamlit を使ってみたので備忘録です。
最後は、実際にローカルLLM(Ollama)と会話できるチャットUIまで作ります。
元ネタはこちら:Streamlit 公式ドキュメント
Streamlitとは
Streamlitは、データサイエンスや機械学習、生成AIなどのスクリプトを数分でインタラクティブなWebアプリに変換できるオープンソースのPythonライブラリです。HTMLやCSS、JavaScriptを書く必要がありません。
フロントエンドの知識がなくても、PythonコードだけでUIが完結するのが最大のメリットです。
主な使用目的
- 分析データやグラフの可視化ダッシュボードを素早く作りたい場合
- ローカルで動かす生成AIやLLMのプロトタイプUIを構築したい場合
- 社内向けの簡単なツールや管理画面を爆速でデプロイしたい場合
基本的なメソッド(ウィジェット)
| ウィジェット | メソッド | 説明 |
|---|---|---|
| テキスト・Markdown | st.write() / st.markdown() | テキストやマークダウンを出力 |
| データフレーム表示 | st.dataframe() | 表データをインタラクティブに表示 |
| テキスト入力 | st.text_input() | ユーザーからの文字列入力を受け付ける |
| ボタン | st.button() | クリックされたらTrueを返す |
| チャット入力 | st.chat_input() | チャット形式の入力欄を表示 |
| ストリーミング表示 | st.write_stream() | ジェネレータの出力を逐次描画 |
環境構築
今回の検証環境は以下のとおりです。(やや古めのPC)
| 項目 | バージョン |
|---|---|
| macOS | 26.6 (Tahoe) |
| CPU | Intel Core i5-1038NG7(x86_64) |
| Python | 3.12.8 |
| Streamlit | 1.61.1 |
| Ollama | 0.32.6 |
1. 作業ディレクトリと仮想環境の作成
プロジェクトごとに仮想環境を切っておくと、後片付けが楽になります。
mkdir my_work
cd my_work
python -m venv .venv
source .venv/bin/activate
Windows の場合、仮想環境の有効化は .venv\Scripts\activate です。
2. ライブラリのインストール
Streamlit本体に加えて、データ表示用の pandas と、後述のOllama連携で使う requests を入れておきます。
pip install streamlit pandas requests
インストールできたか確認します。
streamlit version
3. ローカルLLM(Ollama)の準備
上級編で使うローカルLLMを用意します。Ollama公式サイトからインストーラを入手するか、Homebrewで導入します。
brew install ollama
インストール後、モデルを取得します。今回は軽量な gemma4:e2b-it-qat(約4GB)を使いました。
ollama pull gemma4:e2b-it-qat
サーバーを起動します。アプリ本体とは別のターミナルで実行し、起動したままにしておいてください。
ollama serve
Ollamaは既にバックグラウンドで常駐している場合があります。その場合 ollama serve は address already in use で失敗しますが、問題ありません。次のコマンドでモデル一覧が返ってくれば準備完了です。
curl http://localhost:11434/api/tags
Ollamaが動いているかの確認方法
以下のコマンドで、取得済みのモデルが一覧表示されれば正常です。
ollama list
NAME ID SIZE MODIFIED
gemma4:e2b-it-qat 07ea59a47401 4.3 GB 7 weeks ago
qwen2.5-coder:latest dae161e27b0e 4.7 GB 2 months ago
基本編:テキストとデータの表示
まずは基本的な表示から試してみます。
import streamlit as st
import pandas as pd
st.title("Streamlitの基本テスト")
st.write("これは最初のStreamlitアプリです。")
# データフレームの表示
df = pd.DataFrame({
"名前": ["Alice", "Bob", "Charlie"],
"年齢": [25, 30, 35]
})
st.dataframe(df)
実行方法と結果
ターミナルで以下のコマンドを実行します。
streamlit run app.py
ブラウザが自動的に立ち上がり、http://localhost:8501 にアクセスされます。タイトルとテーブルが表示されていれば成功です。
応用編:インタラクティブなウィジェットと状態管理
次に、よく使うウィジェット(入力フォームなど)を組み合わせてみます。ボタンを押すと状態が更新される仕組みを作ります。
import streamlit as st
st.header("応用編:カウンターアプリ")
# セッションステート(状態管理)の初期化
if "count" not in st.session_state:
st.session_state.count = 0
# ボタンの配置
if st.button("カウントアップ"):
st.session_state.count += 1
st.write(f"現在のカウント: {st.session_state.count}")
Streamlitはユーザーが操作するたびにスクリプト全体を上から下へ再実行します。変数の状態を保持したい場合は、必ず st.session_state を使用してください。
上級編:ローカルLLM連携のチャットUI構築
最後に、チャットUIコンポーネントを使って、実際にローカルLLMと会話できるアプリを作ります。
Ollamaが提供するREST APIを requests で叩き、返ってきたトークンを st.write_stream() で逐次描画します。
Ollama APIの使い方
Ollamaは http://localhost:11434 でREST APIを公開しています。今回使うのは2つだけです。
| エンドポイント | 用途 |
|---|---|
| GET /api/tags | インストール済みモデルの一覧を取得 |
| POST /api/chat | チャット形式で応答を生成 |
/api/chat に "stream": true を渡すと、レスポンスが 1行1JSON(NDJSON) で少しずつ返ってきます。この各行から message.content を取り出して繋げていけば、タイピング風のストリーミング表示になります。
実装
import json
import requests
import streamlit as st
OLLAMA_URL = "http://localhost:11434"
st.header("上級編:ローカルLLMチャットUI")
@st.cache_data(ttl=60, show_spinner=False)
def list_models() -> list[str]:
"""Ollama にインストール済みのモデル名を取得する。"""
res = requests.get(f"{OLLAMA_URL}/api/tags", timeout=5)
res.raise_for_status()
return [m["model"] for m in res.json().get("models", [])]
def stream_chat(model: str, messages: list[dict]):
"""Ollama の /api/chat を stream=True で叩き、生成トークンを逐次 yield する。"""
with requests.post(
f"{OLLAMA_URL}/api/chat",
json={"model": model, "messages": messages, "stream": True},
stream=True,
timeout=300,
) as res:
res.raise_for_status()
for line in res.iter_lines(decode_unicode=True):
if not line:
continue
chunk = json.loads(line)
if chunk.get("done"):
break
yield chunk.get("message", {}).get("content", "")
# 利用可能なモデルの取得(Ollama が起動していない場合はここで気付ける)
try:
models = list_models()
except requests.RequestException as e:
st.error(f"Ollama に接続できません({OLLAMA_URL})。`ollama serve` を確認してください。\n\n{e}")
st.stop()
if not models:
st.warning("モデルがありません。`ollama pull gemma4:e2b-it-qat` などで取得してください。")
st.stop()
model = st.selectbox("モデル", models)
# チャット履歴の初期化
if "messages" not in st.session_state:
st.session_state.messages = []
# 過去のメッセージを表示
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# ユーザーの入力
if prompt := st.chat_input("メッセージを入力してください...", submit_mode="disable"):
# ユーザーメッセージを表示・保存
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# ローカルLLMの応答をストリーミング表示
with st.chat_message("assistant"):
try:
answer = st.write_stream(stream_chat(model, st.session_state.messages))
except requests.RequestException as e:
st.error(f"生成に失敗しました: {e}")
st.stop()
# アシスタントメッセージを保存
st.session_state.messages.append({"role": "assistant", "content": answer})
実装のポイント
-
会話履歴をそのまま渡す
st.session_state.messagesは{"role": ..., "content": ...}の配列で、Ollamaのmessagesパラメータの形式と同じです。そのまま渡すだけで、文脈を踏まえた会話になります。 -
st.write_stream()にジェネレータを渡す
自前でst.empty()とループを書かなくても、文字列をyieldするジェネレータを渡すだけでタイピング風の表示になります。戻り値には連結済みの全文が入るので、そのまま履歴に保存できます。 -
@st.cache_data(ttl=60)でモデル一覧をキャッシュ
Streamlitは操作のたびに全体を再実行するため、キャッシュがないと毎回APIを叩いてしまいます。 -
submit_mode="disable"
生成中は入力欄を無効化し、応答の途中で次のメッセージが割り込むのを防ぎます。 -
接続エラーを握りつぶさない
Ollamaが起動していないケースが一番よくあるので、起動時にst.error()で明示的に知らせます。
実行方法と結果
Ollamaを起動した状態で、別のターミナルからアプリを実行します。
streamlit run app3.py
モデルを選んでメッセージを送ると、ローカルLLMからの応答が1文字ずつ流れてきます。
うまく動かないとき
| 症状 | 原因と対処 |
|---|---|
| 「Ollama に接続できません」と表示される | Ollamaが起動していません。ollama serve を実行してください |
| 「モデルがありません」と表示される |
ollama pull gemma4:e2b-it-qat でモデルを取得してください |
| 応答が返るまで非常に遅い | モデルの初回ロードに時間がかかります。2回目以降は高速になります |
ModuleNotFoundError: requests |
仮想環境を有効化した上で pip install requests を実行してください |
応用のヒント
OLLAMA_URL を差し替えれば、vLLMやllama.cppのサーバーなど、OpenAI互換APIを持つ他のバックエンドにも同じ構成で接続できます。完全にオフラインで動作するセキュアなAIチャット環境をすぐに構築可能です!
まとめ
Streamlitを使うと、簡単なデータ表示から状態管理が必要なチャットUIまで、素早く綺麗に実装できます。
特にローカルLLMとの連携は st.chat_input() と st.write_stream() の2つだけで完結するので、プロトタイピングには最適でした。
このツールを活用して、サクサク開発を行いましょう!



