SupabaseでリアルタイムAIを構築する | 第4章：コミュニティAIの強化：チャットボットの構築

## はじめに

第3章では、**Supabase**のデータを活用してデータ分析エージェントを構築し、プロジェクトの進捗や傾向を分析するMCPサーバーを実装しました。これにより、AIが統計を生成し、優先度の提案を行うことができました。今回は、Supabaseと**Model Context Protocol（MCP）**を使って、コミュニティを強化する**リアルタイムチャットボット**を構築します。このチャットボットは、Supabaseに保存されたチャットログを基に、ユーザーからの質問に即座に回答します。

この第4章では、Supabaseにチャットメッセージを保存し、MCPサーバーを通じてAIがメッセージを取得・処理する仕組みを作ります。たとえば、ユーザーが「プロジェクトAの状況は？」と尋ねると、AIがSupabaseのデータから関連情報を抽出し、適切な回答を返します。コード例とステップごとのガイドで、コミュニティAIの構築を体験しましょう。さあ、始めましょう！

## コミュニティAIとチャットボットの概要

コミュニティAIは、ユーザー間の対話を支援し、情報を提供するエージェントです。Supabaseをデータストアとして使用することで、チャットログを効率的に管理し、リアルタイムでAIに提供できます。MCPサーバーは、以下の役割を果たします：
- **メッセージ保存**：ユーザーのメッセージをSupabaseに記録。
- **メッセージ取得**：最新のチャットログをAIに提供。
- **アクション実行**：AIが生成した回答をSupabaseに保存または外部ツール（例：Slack）に送信。

### ユースケース
- **コミュニティ管理**：DiscordやSlackでユーザーからの質問に自動回答。
- **カスタマーサポート**：問い合わせをリアルタイムで処理し、履歴を保存。
- **チームコラボレーション**：プロジェクト関連の質問に即座に答える。

## 開発環境の準備

第3章の環境を基に、以下の準備を行います：
- **Python 3.8以降**、**mcpライブラリ**、**supabaseライブラリ**、**Claude Desktop**：これまでと同じ。
- **Supabaseテーブル**：チャットメッセージを保存する新しいテーブルを作成。
- **python-dotenv**：環境変数の管理（既にインストール済み）。

### Supabaseテーブルの作成
チャットログを保存する`chat_messages`テーブルを作成します。SQLエディタで以下を実行：
```sql
CREATE TABLE chat_messages (
  id SERIAL PRIMARY KEY,
  user_id TEXT NOT NULL,
  message TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  project_id INTEGER REFERENCES projects(id)
);
```
テストデータを挿入：
```sql
INSERT INTO chat_messages (user_id, message, project_id) VALUES
('user1', 'プロジェクトAの進捗はどう？', 1),
('user2', 'タスクの期限を確認したい', 1),
('user3', 'プロジェクトBのデータ分析について', 2);
```

### 環境変数
第1章の`.env`ファイル（`SUPABASE_URL`、`SUPABASE_KEY`）を再利用。

## コード例：チャットボット用MCPサーバー

以下のMCPサーバーは、Supabaseの`chat_messages`テーブルにメッセージを保存し、最新のメッセージを取得する機能を提供します。さらに、プロジェクトデータと連携してコンテキストを生成します。

```python
from mcp import MCPServer
from supabase import create_client
import os
from dotenv import load_dotenv

class ChatbotServer(MCPServer):
    def __init__(self, host, port, supabase_url, supabase_key):
        super().__init__(host, port)
        self.supabase = create_client(supabase_url, supabase_key)
        self.register_resource("get_recent_messages", self.get_recent_messages)
        self.register_tool("send_message", self.send_message)

    def get_recent_messages(self, params):
        try:
            limit = params.get("limit", 10)
            project_id = params.get("project_id", None)
            query = self.supabase.table("chat_messages").select("*").order("created_at", desc=True).limit(limit)
            if project_id:
                query = query.eq("project_id", project_id)
            response = query.execute()
            messages = response.data
            return {"status": "success", "messages": messages}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def send_message(self, params):
        try:
            user_id = params.get("user_id", "ai_bot")
            message = params.get("message", "")
            project_id = params.get("project_id", None)
            if not message:
                return {"status": "error", "message": "メッセージが必要です"}
            data = {
                "user_id": user_id,
                "message": message
            }
            if project_id:
                data["project_id"] = project_id
            response = self.supabase.table("chat_messages").insert(data).execute()
            return {"status": "success", "message_id": response.data[0]["id"]}
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    load_dotenv()
    server = ChatbotServer(
        host="localhost",
        port=8097,
        supabase_url=os.getenv("SUPABASE_URL"),
        supabase_key=os.getenv("SUPABASE_KEY")
    )
    print("チャットボットMCPサーバーを起動中: http://localhost:8097")
    server.start()
```

### コードの説明
- **get_recent_messages**：最新のチャットメッセージを取得。`project_id`でフィルタリング可能。
- **send_message**：AIまたはユーザーが新しいメッセージをSupabaseに保存。
- **register_resource/tool**：メッセージ取得をリソース、送信をツールとして登録。
- **データ構造**：`chat_messages`テーブルにユーザーID、メッセージ、プロジェクトIDを保存。

### 前提条件
- `chat_messages`と`projects`テーブルがSupabaseに存在。
- `.env`ファイルに正しい`SUPABASE_URL`と`SUPABASE_KEY`が設定済み。

## サーバーのテスト

サーバーが正しく動作するか確認します：

1. **サーバー起動**：
   ```bash
   python chatbot_server.py
   ```
   コンソールに「チャットボットMCPサーバーを起動中: http://localhost:8097」と表示。

2. **メッセージ取得のテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8097"
   payload = {
       "jsonrpc": "2.0",
       "method": "get_recent_messages",
       "params": {"limit": 5, "project_id": 1},
       "id": 1
   }
   response = requests.post(url, json=payload)
   print(json.dumps(response.json(), indent=2, ensure_ascii=False))
   ```
   期待されるレスポンス：
   ```json
   {
     "jsonrpc": "2.0",
     "result": {
       "status": "success",
       "messages": [
         {
           "id": 1,
           "user_id": "user1",
           "message": "プロジェクトAの進捗はどう？",
           "created_at": "2025-04-16T00:00:00",
           "project_id": 1
         },
         {
           "id": 2,
           "user_id": "user2",
           "message": "タスクの期限を確認したい",
           "created_at": "2025-04-16T00:00:00",
           "project_id": 1
         }
       ]
     },
     "id": 1
   }
   ```

3. **メッセージ送信のテスト**：
   ```python
   payload = {
       "jsonrpc": "2.0",
       "method": "send_message",
       "params": {
           "user_id": "ai_bot",
           "message": "プロジェクトAの進捗を確認中です",
           "project_id": 1
       },
       "id": 2
   }
   response = requests.post(url, json=payload)
   print(json.dumps(response.json(), indent=2, ensure_ascii=False))
   ```
   期待されるレスポンス：
   ```json
   {
     "jsonrpc": "2.0",
     "result": {
       "status": "success",
       "message_id": 4
     },
     "id": 2
   }
   ```

## Claude Desktopとの接続

サーバーをClaude Desktopに接続します：

1. **設定ファイルの編集**：
   Claude Desktopの設定ファイル（例：`claude_desktop_config.json`）に以下を追加：
   ```json
   {
     "mcp_servers": [
       {
         "name": "ChatbotServer",
         "url": "http://localhost:8097",
         "auth": "none"
       }
     ]
   }
   ```

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   プロジェクトAの最近のメッセージを教えてください。
   ```
   レスポンス例：
   ```
   プロジェクトAの最近のメッセージ：
   - ユーザー1：プロジェクトAの進捗はどう？
   - ユーザー2：タスクの期限を確認したい
   ```

   別のプロンプト：
   ```
   プロジェクトAについて「進捗を確認しました」と返信してください。
   ```
   レスポンス例：
   ```
   メッセージ「進捗を確認しました」をプロジェクトAに送信しました。
   ```

## エージェントロジックの構築

実際のチャットボットでは、メッセージを処理して適切な回答を生成するロジックが必要です。以下は、簡略化したロジック例：

```python
def process_message(self, message, project_id):
    if "進捗" in message:
        stats = self.supabase.table("tasks").select("status").eq("project_id", project_id).execute().data
        completed = len([t for t in stats if t["status"] == "completed"])
        total = len(stats)
        response = f"プロジェクトの進捗：{completed}/{total} タスク完了"
        self.send_message({"user_id": "ai_bot", "message": response, "project_id": project_id})
        return response
    return "処理可能な質問をしてください"
```

このロジックは、「進捗」というキーワードを含むメッセージに対して、タスクの完了状況を返します。

## 実装のコツと注意点

- **メッセージフィルタリング**：スパムや不適切なメッセージを防ぐフィルタを追加。
- **リアルタイム統合**：第2章のサブスクリプションを活用し、新着メッセージを即座に処理。
- **セキュリティ**：本番環境では、ユーザー認証を追加し、`auth: none`を避ける。
- **テスト**：テスト用テーブルで動作確認し、本番データに影響を与えない。
- **スケーラビリティ**：大量のメッセージを処理する場合、キャッシュやキュー（例：Redis）を検討。

## 試してみよう：挑戦課題
以下の機能を追加して、チャットボットを強化してみてください：
- メッセージに「タスク追加」とある場合、自動で`tasks`テーブルにタスクを追加。
- 特定のキーワード（例：ヘルプ）を検出し、定型回答を返す機能。
- メッセージを外部ツール（例：Slack）に転送するツールを登録。

## まとめと次のステップ

この第4章では、Supabaseを活用してリアルタイムチャットボットを構築しました。チャットログの保存と取得を通じて、AIがコミュニティの対話に参加できるようになりました。

次の第5章では、MCPサーバーの**最適化とコミュニティへの貢献**に焦点を当てます。サーバーのパフォーマンス向上、セキュリティ強化、そしてSupabase用MCPサーバーをオープンソースとして共有する方法を学びます。コミュニティAIの未来に興味がある方は、ぜひお楽しみに！

---

役に立ったと思ったら、「いいね」や「ストック」をしていただけると嬉しいです！次の章でまたお会いしましょう！