「外出先のスマートフォンからでもローカルLLMに話しかけたい」
そんな悩みを持つインフラエンジニアや情シス担当者は少なくない。この記事では、TelegramのBot機能とOllamaを組み合わせ、スマートフォンからでもローカルLLMへ安全にアクセスできる環境を構築する手順を解説する。
PythonとTelegram Bot APIを橋渡しするスクリプトを実装することで、インターネット上にプロンプトや応答を一切流さずに運用できる。会話履歴の保持、systemdによる常時起動、不正アクセスを防ぐユーザーID制限まで、本番で使えるレベルの実装を網羅する。
この記事のポイント
・OllamaのREST APIとTelegram Bot APIをPythonスクリプトで橋渡しし、スマートフォンからローカルLLMへ話しかけられるボットを構築する
・ユーザーID許可リストを実装してBot乗っ取りを防ぎ、外出先からでも安全に利用できる環境にする
・systemdユニットファイルで常時起動化すれば、サーバー再起動後もボットが自動復旧する
・会話履歴をメモリ内に保持してマルチターン対応にし、前後の文脈を考慮した応答が得られる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
OllamaとTelegramボットを組み合わせる理由
ローカルLLMをチームで使う場面では、アクセス手段の選択肢が意外と限られる。Ubuntu ServerでローカルLLMを構築した後、最初に試すのはブラウザで動くOpen WebUIだが、スマートフォンでの使い勝手は今ひとつだ。スマートフォンのブラウザでアクセスするには毎回URLを入力する手間があり、通知機能もない。SlackボットはAPI連携が手軽な反面、Slack側のプランや社内IT部門の制約に左右されやすい。会社のSlackワークスペースに個人でBotを追加できないというケースは珍しくない。
Telegramはその点で自由度が高い。Bot APIが完全無料で使え、スマートフォンアプリの完成度も非常に高い。会話UIとして使いやすく、コードブロックやMarkdownも綺麗に表示される。個人のTelegramアカウントで完結するため、会社のシステムに依存しない。
構成のポイントは、TelegramのメッセージをLinuxサーバー上のPythonスクリプトが受け取り、OllamaのREST APIへそのまま転送するだけという単純さだ。プロンプトや応答はサーバーとTelegramの間だけを行き来し、外部のLLMサービスには一切触れない。機密データをクラウドに送れない環境でも安心して使える構成になる。
この記事ではOllamaがUbuntu Server上ですでに動作していることを前提とする。モデルは `llama3.3:70b-instruct-q4_0` を例として使うが、7Bや14Bクラスのモデルでも同じ手順で動作する。
Step 1: Telegram BotTokenをBotFatherから取得する
Telegram Botを作るには、まずBotFatherという公式ボットに話しかけてトークンを発行してもらう必要がある。Telegramアプリを開き、検索欄に「BotFather」と入力してチャットを始める。スマートフォンのTelegramアプリで完結する作業だ。1. 新しいBotを作成する
BotFatherに `/newbot` と送ると、Botの表示名とユーザー名を順番に聞いてくる。ユーザー名は末尾が `bot` で終わらなければならない(例: `mycompany_llm_bot`)。入力し終えると以下のような形式でトークンが返ってくる。Use this token to access the HTTP API: 7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-example Keep your token secure and store it safely, it can be used by anyone to control your bot.
2. 自分のTelegramユーザーIDを確認する
BotFatherとは別に「@userinfobot」に `/start` と送ると、自分のユーザーIDが数字で返ってくる(例: `987654321`)。このIDを後でアクセス制限の許可リストに登録する。チームメンバーにも同じ手順でIDを確認してもらい、リストに追加する。3. グループチャットへの追加を禁止する
BotFatherで `/mybots` → ボット選択 → `Bot Settings` → `Allow Groups` を `Disabled` に設定しておく。この設定を怠ると、誰かがグループに追加した場合にグループメンバー全員がアクセスできてしまう。初期設定の段階で必ず行う。Step 2: Python環境と必要ライブラリをUbuntu Serverに準備する
Ollamaが稼働しているUbuntu Serverにログインして、Python仮想環境を用意する。1. 前提確認
Python 3.10以上が入っていること、Ollamaが起動していることを確認する。# バージョン確認 $ python3 --version Python 3.12.3 # Ollamaの動作確認 $ ollama list NAME ID SIZE MODIFIED llama3.3:70b-instruct-q4_0 xxxxxxxxxxxx 42 GB 2 days ago
2. 作業ディレクトリと仮想環境の作成
$ sudo mkdir -p /opt/ollama-telegram $ sudo chown $USER:$USER /opt/ollama-telegram $ python3 -m venv /opt/ollama-telegram/venv $ source /opt/ollama-telegram/venv/bin/activate $ pip install python-telegram-bot==20.8 httpx
Step 3: OllamaとTelegramを橋渡しするPythonスクリプトを実装する
スクリプトの核心部分を段階的に示す。まずシンプルな1往復の動作から確認する。1. 基本スクリプト(ollama_telegram_bot.py)
import os import httpx from telegram import Update from telegram.ext import ApplicationBuilder, MessageHandler, filters, ContextTypes TELEGRAM_TOKEN = os.environ["TELEGRAM_TOKEN"] OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434") MODEL_NAME = os.environ.get("OLLAMA_MODEL", "llama3.3:70b-instruct-q4_0") ALLOWED_USER_IDS = set( int(uid) for uid in os.environ.get("ALLOWED_USER_IDS", "").split(",") if uid ) async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: user_id = update.effective_user.id if ALLOWED_USER_IDS and user_id not in ALLOWED_USER_IDS: await update.message.reply_text("アクセスが許可されていません。") return await update.message.reply_text("考え中...") payload = { "model": MODEL_NAME, "prompt": update.message.text, "stream": False, } async with httpx.AsyncClient(timeout=120.0) as client: resp = await client.post(f"{OLLAMA_URL}/api/generate", json=payload) resp.raise_for_status() answer = resp.json()["response"] await update.message.reply_text(answer) app = ApplicationBuilder().token(TELEGRAM_TOKEN).build() app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) app.run_polling()
2. 環境変数ファイルを作成する
スクリプトと同じディレクトリに `.env` ファイルを作成する。# /opt/ollama-telegram/.env(パーミッション600に設定すること) TELEGRAM_TOKEN=7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-example OLLAMA_URL=http://localhost:11434 OLLAMA_MODEL=llama3.3:70b-instruct-q4_0 ALLOWED_USER_IDS=987654321,111222333
# .envのパーミッションをオーナー読み書きのみに制限する $ chmod 600 /opt/ollama-telegram/.env
3. 手動で動作確認する
$ source /opt/ollama-telegram/venv/bin/activate $ export $(grep -v '^#' /opt/ollama-telegram/.env | xargs) $ python3 /opt/ollama-telegram/ollama_telegram_bot.py
Step 4: 会話履歴を持たせてマルチターン対応にする
基本スクリプトは1往復で会話が完結する。「さっきの続きだけど」という形式の質問には対応できない。`context.user_data` に会話履歴を保存することでマルチターン対応にする。1. chat APIを使った実装に変更する
OllamaのChat APIは、ユーザーとアシスタントのメッセージ履歴をリストとして受け取る。`/api/generate` から `/api/chat` エンドポイントへ切り替える。async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: user_id = update.effective_user.id if ALLOWED_USER_IDS and user_id not in ALLOWED_USER_IDS: await update.message.reply_text("アクセスが許可されていません。") return history = context.user_data.setdefault("history", []) history.append({"role": "user", "content": update.message.text}) # 直近20メッセージ分(10往復)だけ保持してメモリを節約する if len(history) > 20: history = history[-20:] context.user_data["history"] = history await update.message.reply_text("考え中...") payload = {"model": MODEL_NAME, "messages": history, "stream": False} async with httpx.AsyncClient(timeout=120.0) as client: resp = await client.post(f"{OLLAMA_URL}/api/chat", json=payload) resp.raise_for_status() answer = resp.json()["message"]["content"] history.append({"role": "assistant", "content": answer}) await update.message.reply_text(answer)
2. 会話リセットコマンドを追加する
長い会話が続くとコンテキストが混乱することがある。`/reset` コマンドで履歴をクリアできるようにしておく。from telegram.ext import CommandHandler async def reset_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: context.user_data.clear() await update.message.reply_text("会話履歴をリセットしました。") # run_polling()の前に追加する app.add_handler(CommandHandler("reset", reset_command))
なお `context.user_data` はメモリ内にしか保存されないため、Botプロセス再起動で会話履歴はリセットされる。永続化が必要な場合はSQLiteやRedisに書き出す実装を別途追加することになる。日常的な補助用途であればメモリ内で十分だ。
Step 5: systemdでボットを常時起動サービスにする
手動でスクリプトを実行するだけでは、サーバー再起動のたびにボットが止まってしまう。systemdユニットファイルを作成して常時起動させる。1. ユニットファイルを作成する
# /etc/systemd/system/ollama-telegram.service [Unit] Description=Ollama Telegram Bot After=network.target ollama.service Requires=ollama.service [Service] Type=simple User=ubuntu WorkingDirectory=/opt/ollama-telegram EnvironmentFile=/opt/ollama-telegram/.env ExecStart=/opt/ollama-telegram/venv/bin/python3 ollama_telegram_bot.py Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target
2. サービスを有効化して起動する
$ sudo systemctl daemon-reload $ sudo systemctl enable ollama-telegram.service $ sudo systemctl start ollama-telegram.service # 状態確認 $ sudo systemctl status ollama-telegram.service * ollama-telegram.service - Ollama Telegram Bot Loaded: loaded (/etc/systemd/system/ollama-telegram.service; enabled) Active: active (running) since Fri 2026-08-15 09:00:00 JST; 5s ago
Step 6: ユーザーID許可リストでアクセスを制限する
Telegramボットは、ユーザー名さえわかれば誰でも話しかけられる仕様になっている。`ALLOWED_USER_IDS` の設定を怠ると、見知らぬ人がローカルLLMを無断利用したり、GPUリソースを食いつぶしたりするリスクがある。1. 許可リストの確認と追加方法
`.env` ファイルの `ALLOWED_USER_IDS` にカンマ区切りでユーザーIDを追加する。新しいメンバーが増えたら以下の手順で追加する。# .envのALLOWED_USER_IDSに追記した後、サービスをリスタートする $ sudo systemctl restart ollama-telegram.service
2. レートリミットで連続リクエストを制御する
許可ユーザーが大量のリクエストを短時間に送ると、GPUが飽和してサーバー全体の応答が遅くなる。前のリクエストが処理中の場合は待機させるフラグを持たせる実装が手軽で効果的だ。async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: user_id = update.effective_user.id if ALLOWED_USER_IDS and user_id not in ALLOWED_USER_IDS: await update.message.reply_text("アクセスが許可されていません。") return if context.user_data.get("processing"): await update.message.reply_text("前のリクエストを処理中です。少し待ってください。") return context.user_data["processing"] = True try: # ... LLM呼び出し処理(Step 4と同じ) ... pass finally: context.user_data["processing"] = False
3. システムプロンプトで回答範囲を限定する
業務用途であれば、システムプロンプトを設定してAIが答える範囲を限定するとよい。会話履歴の先頭に `role: system` のメッセージを追加する。SYSTEM_PROMPT = os.environ.get( "SYSTEM_PROMPT", "あなたはLinuxサーバー管理の専門アシスタントです。技術的な質問に日本語で回答してください。" ) # handle_message内の履歴初期化部分を以下に変更する history = context.user_data.setdefault( "history", [{"role": "system", "content": SYSTEM_PROMPT}] )
よくあるトラブルと対処法
このボット構成で頻出するトラブルと対処をまとめる。ボットがメッセージに反応しない
`systemctl status ollama-telegram` でプロセスが動いているか確認する。動いていてもメッセージが届かない場合は、サーバーからTelegramのAPIサーバー(api.telegram.org)へのアウトバウンド通信(443番ポート)がファイアウォールで遮断されていないか確認する。
# Telegram APIへの疎通確認 $ curl -s "https://api.telegram.org/bot${TELEGRAM_TOKEN}/getMe" | python3 -m json.tool
70Bクラスのモデルは初回応答まで数十秒かかることがある。`httpx.AsyncClient(timeout=120.0)` のタイムアウト値を `180.0` や `240.0` に増やすか、7B/14Bクラスの軽量モデルに切り替える。`ollama ps` でVRAM使用状況とCPUオフロード量を確認する。
「考え中...」の後に応答が来ない
モデル名のタイプミスや、量子化サフィックスの誤り(`:q4_0` 単独表記は実在しないので `llama3.3:70b-instruct-q4_0` のようにフルタグで指定する)が原因になりやすい。`ollama list` で実際のモデル名を確認してから `.env` の `OLLAMA_MODEL` に設定する。
EnvironmentFileの読み込みに失敗する
`.env` ファイルのシンタックスエラーでsystemdが値を読み込めないことがある。`.env` 内のコメント行(`#` で始まる行)や値の前後にスペースが入っていないかを確認する。`journalctl -u ollama-telegram` でエラーログを見ると原因が特定しやすい。
複数ユーザーが同時に使うと応答が遅い
Ollamaは同時リクエストを受け付けるが、VRAM容量が足りないとモデルがスワップアウトされて極端に遅くなる。環境変数 `OLLAMA_NUM_PARALLEL=1` を設定して並列数を1に制限するか、全員が使える軽量モデル(7B/14B)を別途用意するとよい。
まとめ
OllamaとTelegramボットを連携させることで、スマートフォンからでもプライベートなローカルLLM環境へアクセスできるようになる。Slackと違い社内IT部門の許可なく個人のLinuxサーバーに構築できる点が最大の利点だ。この記事で構築した内容を整理する。
| 手順 | 作業内容 | 主なコマンド・操作 |
|---|---|---|
| Step 1 | BotToken取得 | BotFatherで /newbot → トークン発行 |
| Step 2 | Python環境準備 | pip install python-telegram-bot==20.8 httpx |
| Step 3 | 基本スクリプト実装 | POST http://localhost:11434/api/generate |
| Step 4 | マルチターン対応 | POST http://localhost:11434/api/chat |
| Step 5 | systemd常時起動化 | systemctl enable ollama-telegram.service |
| Step 6 | アクセス制限 | ALLOWED_USER_IDS で許可リスト設定 |
20年以上現場でサーバーを管理してきた経験から言うと、こうした「外出先からでも使えるAIアシスタント」は、運用担当者が一人でチームを回す場面で特に効いてくる。サーバーの前に座っていなくても、スマートフォンで問題の切り分けを進められるのは、想像以上に助かる。
ローカルLLMとTelegramBot連携を実機GPUで2日間体験する
TelegramボットやRAG構築、systemd運用など、ローカルLLMの実践的な活用を手を動かしながら習得したい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。
・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:OllamaをDocker Composeで起動する方法|GPUパススルー・マルチサービス構成・環境変数管理で開発環境を整備する手順
- この記事の属するカテゴリ:ローカルLLMへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます