はじめに:ボットに「身体」と「神経」を授ける時間
これまでの道のりを振り返ってみましょう。
第1回では、賢いボットの「設計図」であるModel Context Protocol(MCP)を学びました。
第2回では、その設計図を元に、ボットの「脳」となるMCPEngineをわずか50行のPythonコードで実装しました。
そして今日、私たちはシリーズの中で最も心躍るステップに足を踏み入れます。
それは、私たちが作り上げた「脳」に、「身体」と「神経」を授けることです。
具体的には、以下の2つの大きな目標を達成します。
- 「身体」を作る
Pythonの高速なWebフレームワークFastAPIを使い、MCPEngineをホストするWebサーバーを構築します。これがボットの身体となります。 - 「神経」を繋ぐ
作成したサーバーをSlack Events APIに接続します。これにより、ボットはSlackワークスペース内の出来事(特に@メンション)を「聞く」ことができるようになります。
この記事を最後まで読み終え、コードを実行したとき、あなたのボットは初めてSlackのチャンネルにその姿を現し、あなたの呼びかけに最初の返事をくれるはずです。
多くの部品を組み合わせる大きな一歩ですが、一つ一つ丁寧に進めていくので、ぜひ一緒に「誕生の瞬間」を体験しましょう!
ステップ1:Slackのセットアップ - ボットの「耳」を作る
まず、ボットがSlackの会話を聞き取れるように、Slack側でアプリの設定を行います。このプロセスは、ボットに「耳」を取り付ける作業だと考えてください。
1. Slackアプリの新規作成
Slack APIのページにアクセスし、「Create an App」ボタンをクリックします。
「From scratch」を選択し、アプリ名(例: My-MCP-Bot)と、インストールしたいワークスペースを選んで、「Create App」をクリックします。
2. Socket Modeの有効化【重要】
Slackからのイベントを受け取るには、通常、サーバーをインターネットに公開し、Webhook URLを設定する必要があります。これはngrokなどを使えば可能ですが、開発段階では少し手間がかかります。
そこで今回は、開発を劇的に簡単にする 「Socket Mode」 を利用します。これは、私たちのサーバーからSlackへ直接WebSocket接続を確立する方式で、サーバーを外部に公開する必要がありません。
- 左側メニューの「Settings」セクションにある「Socket Mode」をクリックします。
- 「Enable Socket Mode」のトグルをONにします。
- トークン名の入力を求められるので、分かりやすい名前(例: socket-mode-token)を付けて「Generate」をクリックします。
- xapp-で始まるApp-Level Tokenが生成されます。これは後で使うので、必ずコピーして安全な場所に保管してください。
3. イベントの購読設定
次に、ボットがどんな出来事を「聞く」べきかを設定します。
- 左側メニューの「Features」セクションにある「Event Subscriptions」をクリックします。
- 「Enable Events」のトグルをONにします。
- 「Subscribe to bot events」という項目で、「Add Bot User Event」ボタンをクリックします。
- 今回はメンションに反応させたいので、app_mentionを検索して選択し、「Save Changes」をクリックします。
4. ボットに必要な権限(スコープ)の付与
ボットがメッセージを読んだり、返信を書き込んだりするには、そのための権限が必要です。
- 左側メニューの「Features」セクションにある「OAuth & Permissions」をクリックします。
- 「Scopes」という項目までスクロールします。
- 「Bot Token Scopes」セクションで、「Add an OAuth Scope」ボタンをクリックし、以下の2つの権限を追加します。
- app_mentions:read: ボットへのメンションを読み取る権限
- chat:write: チャンネルにメッセージを書き込む権限
5. アプリのインストールとBotトークンの取得
- 設定が完了したので、アプリをあなたのワークスペースにインストールします。
- 「OAuth & Permissions」ページの一番上に戻り、「Install to Workspace」ボタンをクリックします。
許可を求められるので、「許可する」をクリックします。 - インストールが完了すると、xoxb-で始まるBot User OAuth Tokenが表示されます。これも後で使う最重要トークンなので、必ずコピーして保管してください。
- ボットをチャンネルに招待
最後に、テストを行うSlackチャンネルで、今作成したボットを招待します。
/invite @<あなたのボット名>
これでSlack側の準備は完了です!
ステップ2:サーバー実装 - ボットの「身体」と「声」を作る
いよいよ、ボットの本体となるPythonサーバーを構築します。
1. 必要なライブラリの追加インストール
第2回までのライブラリに加え、Slackとの連携を簡単にするためのライブラリをインストールします。
# FastAPIとUvicornはインストール済みのはず
pip install slack_bolt python-dotenv
- slack_bolt: Slackの公式Python SDK。イベント処理やAPI呼び出しを驚くほど簡単にしてくれます。
- python-dotenv: .envファイルから環境変数を安全に読み込むために使います。
2. プロジェクトの構成
以下のようなファイル構成で進めます。
my-slack-bot/
├── .env
├── mcp_engine.py # 第2回で作成したファイル
└── main.py # 今回作成するサーバーのメインファイル
.envファイルに、先ほど取得した2つのトークンを記述します。
.env
# "OAuth & Permissions"ページで取得したBotトークン
SLACK_BOT_TOKEN="xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"
# "Socket Mode"ページで取得したApp-Levelトークン
SLACK_APP_TOKEN="xapp-x-xxxxxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
3. main.pyの実装
ここが今回のクライマックスです。slack_boltとFastAPIを組み合わせ、メンションに反応するサーバーを構築します。
- main.py
import os
import asyncio
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from openai import AsyncOpenAI
# slack_boltから必要なクラスをインポート
from slack_bolt.async_app import AsyncApp
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
# 第2回で作成したMCPエンジンをインポート
from mcp_engine import MCPEngine
# .envファイルから環境変数を読み込む
load_dotenv()
# --- 1. 初期化セクション ---
# Slack Boltアプリを初期化
# SLACK_BOT_TOKENはBoltが内部で利用
app = AsyncApp(token=os.environ.get("SLACK_BOT_TOKEN"))
# OpenAIクライアントを非同期モードで初期化
openai_client = AsyncOpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
# 各会話のMCPエンジンインスタンスを保持する辞書
# 注意:これはデモ用のインメモリ実装です。サーバーを再起動すると記憶は失われます。
# 第4回でこれを永続化します。
mcp_engines: dict[str, MCPEngine] = {}
# --- 2. Slackイベントリスナーの実装 ---
@app.event("app_mention")
async def handle_app_mention_events(body: dict, say):
""" @メンションイベントを処理する """
# 1. イベント情報から必要なデータを抽出
user_id = body["event"]["user"]
user_input = body["event"]["text"]
channel_id = body["event"]["channel"]
# スレッド内の会話を識別するため、thread_ts(なければts)をキーにする
thread_ts = body["event"].get("thread_ts", body["event"]["ts"])
# 2. 会話セッションIDを定義
session_id = f"{channel_id}-{thread_ts}"
# 3. このセッション用のMCPエンジンを取得または新規作成
if session_id not in mcp_engines:
# 新しい会話が始まったので、新しいエンジンを作成
system_prompt = "あなたは、関西弁で話す親しみやすいAIアシスタント『まいど君』です。ユーザーの質問には、必ずユーモアを交えて答えてください。"
mcp_engines[session_id] = MCPEngine(system_prompt=system_prompt)
print(f"新しいセッションを開始しました: {session_id}")
engine = mcp_engines[session_id]
# 4. LLMに応答を問い合わせ
try:
# MCPエンジンでAPI用のメッセージリストを構築
messages = engine.build_messages(user_input)
# OpenAI APIを非同期で呼び出し
response = await openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
ai_response = response.choices[0].message.content
# 5. MCPエンジンの対話履歴を更新
engine.add_message("user", user_input)
engine.add_message("assistant", ai_response)
except Exception as e:
print(f"エラーが発生しました: {e}")
ai_response = "すまん、ちょっと調子が悪いみたいや…。"
# 6. Slackに応答を投稿(スレッド内に返信する)
await say(text=ai_response, thread_ts=thread_ts)
# --- 3. FastAPIとSocket Modeの連携 ---
# FastAPIアプリを定義
api = FastAPI()
# Socket Modeハンドラを初期化
# SLACK_APP_TOKENはSocket Modeの接続に利用
socket_handler = AsyncSocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN"))
@api.on_event("startup")
async def startup():
# FastAPIの起動時に、Socket Modeのリスナーをバックグラウンドで起動
asyncio.create_task(socket_handler.start_async())
サーバーの起動
準備は整いました。ターミナルで以下のコマンドを実行し、サーバーを起動します。
uvicorn main:api --reload
コンソールにUvicorn running on http://127.0.0.1:8000のようなメッセージが表示されれば、ボットの「身体」は正常に起動しています。同時に、Socket ModeハンドラがSlackとの「神経」を接続し、メンションを待ち受けている状態になります。
いよいよ、ボットとの初対話!
Slackのテスト用チャンネルに戻り、ボットにメンションしてみましょう。
@My-MCP-Bot こんにちは!君は誰?
数秒待つと…ボットがスレッド内で返信をくれるはずです!
まいど!わいはAIアシスタントの「まいど君」や!よろしくな!
さらに、スレッド内で会話を続けてみてください。
僕の好きなアーティストはB'zやで。覚えててな。
B'zか!ええ趣味しとるやん!稲葉さんのシャウトは魂震えるもんな!
僕の好きなアーティスト、誰やったか覚えてる?
あったりまえやん!B'zやろ?忘れるわけないがな!
おめでとうございます!
あなたの手によって、 会話の文脈を記憶し、Slack上で応答するAIボットが誕生した瞬間 です。
まとめ:生命の誕生、そして次なる進化へ
今回は、シリーズのクライマックスとも言える大きなマイルストーンを達成しました。
Slackアプリを設定し、Socket Modeで安全にローカルサーバーと接続した。
slack_boltライブラリを使い、@app_mentionイベントをリッスンするロジックを実装した。
第2回で作成したMCPEngineを組み込み、会話セッションごとに記憶を管理する仕組みを構築した。
FastAPIサーバーを起動し、Slack上で実際にボットと対話することに成功した。
あなたのボットは今、Slackという世界で生命を宿しました。しかし、彼はまだ生まれたての赤ん坊のようなものです。現在の実装には、実用化に向けた大きな課題が残されています。
- 記憶の揮発性: サーバーを再起動すると、mcp_engines辞書に保存された全ての会話履歴は綺麗さっぱり消えてしまいます。
- 限定的な文脈理解: スレッド内でメンションされた際に、そのスレッドの過去のやり取りを読み込む機能がまだありません。新しい会話として扱ってしまっています。
ボットを赤ん坊から、チームを支える頼れるアシスタントへと「育成」していくのが、次回のテーマです。
最終回となる第4回『実用的なSlackボットへ!会話履歴の永続化とスレッド対応でチームのAIアシスタントに育てる』 では、インメモリの記憶をRedisなどの永続的なストレージに置き換え、ボットに本当の「長期記憶」を授けます。さらに、スレッド内の文脈を深く理解させ、より高度な対話を実現します。
シリーズの集大成を、ぜひお楽しみに!
この記事が、皆さんのAIアプリケーション開発の安定化に少しでも貢献できれば嬉しいです。役に立ったと感じたら、ぜひ Like をお願いします!
Written by A.H.