SlackでAIを強化する | 第3章：会話分析：AIによるインサイト生成

## はじめに

第2章では、**Slack**にメッセージを送信し、チャンネルを作成するMCPサーバーを構築し、コミュニケーション自動化エージェントを実現しました。これにより、AIがメッセージ操作やチャンネル管理を処理できるようになり、チームの効率が向上しました。今回は、この基盤を活用して、Slackの会話データを解析する**会話分析エージェント**を構築します。

この第3章では、Slackチャンネルのメッセージを分析し、コミュニケーション頻度、主要トピック、ユーザーエンゲージメントなどのインサイトを生成します。たとえば、AIが「チームがプロジェクト計画に集中している」ことを検出したり、「特定のユーザーが会話に積極的でない」ことを指摘したりできます。コード例とステップごとのガイドで、会話分析AIの構築を体験しましょう。さあ、始めましょう！

## 会話分析エージェントとは？

会話分析エージェントは、Slackのメッセージデータを解析し、チームのコミュニケーションに関するインサイトを提供するAIです。MCPサーバーを介して、以下のような機能を実現できます：
- **頻度分析**：チャンネルごとのメッセージ投稿頻度を計算。
- **トピック抽出**：メッセージから主要なキーワードやテーマを特定。
- **エンゲージメント分析**：ユーザーごとの投稿数や反応（リアクション、返信）を評価。

### ユースケース
- チームパフォーマンス：AIがコミュニケーションの活発さや参加度を分析。
- プロジェクト管理：AIが会話からプロジェクトの優先事項や課題を抽出。
- プロセス改善：AIがコミュニケーションのボトルネック（例：特定のトピックが無視される）を特定。

## 開発環境の準備

第2章の環境を基に、以下の準備を行います：
- **Python 3.8以降**、**mcpライブラリ**、**requestsライブラリ**、**Claude Desktop**：これまでと同じ。
- **python-dotenv**：環境変数の管理（既にインストール済み）。
- **Slackチャンネル**：分析用のメッセージデータを含むチャンネル。

### Slackのセットアップ
1. **チャンネル準備**：
   - 第2章のチャンネル（例：`#mcp-project`）を使用。
   - メッセージを追加（例：プロジェクト計画、コードレビュー、タスク割り当てに関する会話）。
   - ユーザーエンゲージメントを高めるため、リアクション（絵文字）やスレッド返信を追加。
   - チャンネルIDを記録（第1章で取得済み）。
2. **Slackアプリの確認**：
   - 第2章のアプリを再利用（スコープ：`channels:history`、`channels:read`）。
   - 必要に応じて`reactions:read`（リアクション取得）を追加。
   - アプリがチャンネルに招待済みであることを確認。
3. **環境変数**：
   第2章の`.env`ファイルに以下を確認：
   ```bash
   SLACK_TOKEN=your_bot_token
   SLACK_CHANNEL_ID=your_channel_id
   ```

## コード例：会話分析用MCPサーバー

以下のMCPサーバーは、Slackチャンネルのメッセージを取得し、頻度やエンゲージメントを分析します。

```python
from mcp import MCPServer
import os
from dotenv import load_dotenv
import requests
from datetime import datetime, timedelta
import statistics
from collections import Counter

class SlackAnalysisServer(MCPServer):
    def __init__(self, host, port, token, channel_id):
        super().__init__(host, port)
        self.token = token
        self.channel_id = channel_id
        self.base_url = "https://slack.com/api"
        self.headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }
        self.register_resource("analyze_conversation", self.analyze_conversation)

    def get_messages(self, limit=100):
        try:
            url = f"{self.base_url}/conversations.history"
            query = {"channel": self.channel_id, "limit": limit}
            response = requests.get(url, headers=self.headers, params=query)
            response.raise_for_status()
            return response.json()["messages"]
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def get_reactions(self, message_ts):
        try:
            url = f"{self.base_url}/reactions.get"
            query = {"channel": self.channel_id, "timestamp": message_ts}
            response = requests.get(url, headers=self.headers, params=query)
            response.raise_for_status()
            return response.json().get("message", {}).get("reactions", [])
        except Exception as e:
            return []

    def analyze_conversation(self, params):
        try:
            messages = self.get_messages(limit=params.get("limit", 100))
            if isinstance(messages, dict) and "status" in messages:
                return messages

            user_counts = Counter()
            reaction_counts = Counter()
            timestamps = []
            keywords = Counter()
            stop_words = {"を", "は", "に", "の", "が", "です", "ます"}

            now = datetime.now()
            for msg in messages:
                user = msg.get("user", "unknown")
                text = msg.get("text", "")
                ts = float(msg["ts"])
                timestamp = datetime.fromtimestamp(ts)

                # ユーザーエンゲージメント
                user_counts[user] += 1

                # リアクション分析
                reactions = self.get_reactions(msg["ts"])
                for reaction in reactions:
                    reaction_counts[reaction["name"]] += reaction["count"]

                # メッセージ頻度
                timestamps.append(timestamp)

                # キーワード抽出（簡易）
                words = text.split()
                for word in words:
                    if word not in stop_words and len(word) > 1:
                        keywords[word] += 1

            # 頻度分析
            if timestamps:
                time_diffs = [(timestamps[i] - timestamps[i+1]).total_seconds() / 3600 
                             for i in range(len(timestamps)-1)]
                avg_interval = statistics.mean(time_diffs) if time_diffs else 0
                messages_per_day = len(timestamps) / ((now - min(timestamps)).total_seconds() / 86400)
            else:
                avg_interval, messages_per_day = 0, 0

            # トップキーワード
            top_keywords = [{"word": w, "count": c} for w, c in keywords.most_common(5)]

            return {
                "status": "success",
                "analysis": {
                    "message_count": len(messages),
                    "avg_message_interval_hours": f"{avg_interval:.2f}",
                    "messages_per_day": f"{messages_per_day:.2f}",
                    "user_engagement": [{"user": u, "count": c} for u, c in user_counts.most_common(3)],
                    "top_reactions": [{"reaction": r, "count": c} for r, c in reaction_counts.most_common(3)],
                    "top_keywords": top_keywords
                }
            }
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    load_dotenv()
    server = SlackAnalysisServer(
        host="localhost",
        port=8124,
        token=os.getenv("SLACK_TOKEN"),
        channel_id=os.getenv("SLACK_CHANNEL_ID")
    )
    print("Slack分析MCPサーバーを起動中: http://localhost:8124")
    server.start()
```

### コードの説明
- **get_messages**：チャンネルのメッセージを取得（最大100件）。
- **get_reactions**：メッセージごとのリアクションを取得。
- **analyze_conversation**：メッセージを解析し、以下のインサイトを生成：
  - メッセージ総数：取得したメッセージの数。
  - 平均投稿間隔：メッセージ間の平均時間（時間単位）。
  - 1日あたりのメッセージ数：会話の活発さを評価。
  - ユーザーエンゲージメント：投稿数が多い上位3ユーザー。
  - トップリアクション：使用頻度の高いリアクション（例：👍）。
  - トップキーワード：メッセージから頻出単語を抽出（簡易トピック分析）。
- **register_resource**：会話分析をリソースとして登録。

### 前提条件
- Slackチャンネルにメッセージとリアクションが存在。
- Slackアプリに`channels:history`、`channels:read`、`reactions:read`スコープが付与されている。
- `.env`ファイルに正しい`SLACK_TOKEN`と`SLACK_CHANNEL_ID`が設定済み。

## サーバーのテスト

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

1. **サーバー起動**：
   ```bash
   python slack_analysis_server.py
   ```
   コンソールに「Slack分析MCPサーバーを起動中: http://localhost:8124」と表示。

2. **会話分析のテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8124"
   payload = {
       "jsonrpc": "2.0",
       "method": "analyze_conversation",
       "params": {"limit": 50},
       "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",
       "analysis": {
         "message_count": 50,
         "avg_message_interval_hours": "2.50",
         "messages_per_day": "10.00",
         "user_engagement": [
           {"user": "U123456", "count": 20},
           {"user": "U789012", "count": 15},
           {"user": "U345678", "count": 10}
         ],
         "top_reactions": [
           {"reaction": "thumbsup", "count": 25},
           {"reaction": "smile", "count": 10}
         ],
         "top_keywords": [
           {"word": "プロジェクト", "count": 8},
           {"word": "タスク", "count": 6},
           {"word": "コード", "count": 5}
         ]
       }
     },
     "id": 1
   }
   ```

## Claude Desktopとの接続

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

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

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   チャンネル #mcp-project の会話データを分析してください。
   ```
   レスポンス例：
   ```
   チャンネル #mcp-project の会話分析：
   - メッセージ数：50
   - 平均投稿間隔：2.50時間
   - 1日あたりのメッセージ数：10.00
   - アクティブユーザー：U123456（20投稿）、U789012（15投稿）、U345678（10投稿）
   - 人気のリアクション：👍（25）、😊（10）
   - 主要トピック：プロジェクト（8）、タスク（6）、コード（5）
   ```

## 実装のコツと注意点

- **データ品質**：メッセージが少ない場合、分析結果が制限される。十分なデータ（例：50件以上）を用意。
- **レートリミティング**：Slack APIの制限（例：`conversations.history`は1秒に1リクエスト）に注意。
- **セキュリティ**：本番環境では、`auth: none`を避け、トークン認証を導入。
- **テスト**：テスト用チャンネルを作成し、本番データに影響を与えない。
- **拡張性**：大量のメッセージを処理する場合、キャッシュ（例：Redis）やNLPライブラリ（例：spaCy）でキーワード抽出を強化。

## 試してみよう：挑戦課題
以下の機能を追加して、エージェントを強化してみてください：
- 特定ユーザー（例：U123456）のメッセージだけを分析するフィルター。
- メッセージの感情分析（ポジティブ/ネガティブ）を行う機能。
- 分析結果をチャンネルに投稿するツール。

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

この第3章では、Slackの会話データを活用して会話分析エージェントを構築しました。メッセージ頻度やエンゲージメントを分析することで、AIがチームのコミュニケーションを評価し、プロセス改善を支援できるようになりました。

次の第4章では、SlackのWebhookを活用して**リアルタイム管理AI**を構築します。たとえば、AIが新しいメッセージやリアクションをリアルタイムで検知し、通知や応答を生成します。リアルタイム管理AIに興味がある方は、ぜひお楽しみに！

---

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