統計ロードマップ
2026-04-30·実装·⏱ 約 6

Slack Bot を LLM で作る ─ FastAPI + OpenAI で社内ツール化

Slack の Slash Command と Events API を使い、社内チャンネル内で動く LLM Bot を構築。FastAPI バックエンド + OpenAI で 1 日で完成。

社内チャンネルに LLM Bot を 1 つ置く だけで、検索・要約・コード生成の効率が劇的に上がります。本記事では FastAPI + OpenAI で最短実装。

アーキテクチャ

  1. ユーザーが Slack で `@ai-bot 統計検定 2 級は?` とメンション
  2. Slack が Events API でこちらの Webhook を叩く
  3. FastAPI がリクエスト受信 → 署名検証 → LLM 呼び出し
  4. 結果を Slack API でチャンネルに投稿

Slack App セットアップ

  • [api.slack.com/apps](https://api.slack.com/apps) で App 作成
  • Event Subscriptions で `app_mention` イベント購読
  • Bot Token Scopes: `chat:write`, `app_mentions:read`
  • Install to Workspace で `xoxb-*` トークン取得
  • Signing Secret をコピー(検証用)

FastAPI 実装

main.py
import os, hmac, hashlib, time
from fastapi import FastAPI, Request, HTTPException
from openai import OpenAI
import httpx

app = FastAPI()
openai = OpenAI()

SLACK_TOKEN = os.environ['SLACK_BOT_TOKEN']
SLACK_SECRET = os.environ['SLACK_SIGNING_SECRET']

async def verify_slack(req: Request, body: bytes):
    ts = req.headers.get('X-Slack-Request-Timestamp', '')
    if abs(time.time() - int(ts)) > 60 * 5:
        raise HTTPException(403, 'Stale timestamp')
    sig_basestring = f'v0:{ts}:{body.decode()}'.encode()
    expected = 'v0=' + hmac.new(SLACK_SECRET.encode(), sig_basestring, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, req.headers.get('X-Slack-Signature', '')):
        raise HTTPException(403, 'Bad signature')

@app.post('/slack/events')
async def events(req: Request):
    body = await req.body()
    await verify_slack(req, body)
    payload = await req.json()

    # Slack の URL 検証
    if payload.get('type') == 'url_verification':
        return {'challenge': payload['challenge']}

    if payload.get('event', {}).get('type') == 'app_mention':
        text = payload['event']['text']
        channel = payload['event']['channel']

        # LLM 呼び出し
        resp = openai.chat.completions.create(
            model='gpt-4o-mini',
            messages=[{'role': 'user', 'content': text}],
        )
        reply = resp.choices[0].message.content

        # Slack に投稿
        async with httpx.AsyncClient() as c:
            await c.post(
                'https://slack.com/api/chat.postMessage',
                headers={'Authorization': f'Bearer {SLACK_TOKEN}'},
                json={'channel': channel, 'text': reply},
            )

    return {'ok': True}

デプロイ ─ Vercel(無料枠)

  • FastAPI を Vercel Functions として動かす
  • 環境変数 `SLACK_BOT_TOKEN` `SLACK_SIGNING_SECRET` `OPENAI_API_KEY` を設定
  • 発行された URL を Slack の Event Subscriptions に登録

発展

  • スレッド対応: メンション元のスレッドに返信(`thread_ts`)
  • Slash Command: `/ask 質問` で起動
  • 会話履歴: スレッド全体を context にして文脈考慮
  • RAG: 社内ドキュメントを参照([RAG 入門](/blog/rag-introduction))
  • ツール呼び出し: DB / GitHub / Notion を自動で読み書き

学習リソース

  • [OpenAI API 実装入門](/blog/openai-api-implementation)
  • [FastAPI 入門](/blog/fastapi-introduction)
  • [Vercel デプロイ](/blog/vercel-deployment-for-ai)
  • [AI エージェント 入門](/blog/ai-agents-introduction)
Related Articles

関連記事