社内FAQ chatbotの作り方
Slack×Claude API実践ガイド

実践ガイド社内DX

「経費精算のやり方は?」「有給の申請方法は?」「このツールの使い方は?」
社内で毎日繰り返される同じ質問。マニュアルはあるけど誰も読まない。 結局、詳しい人に聞く。その人の時間が奪われる。
この記事では、社内のナレッジをAIに学習させ、Slackで質問できるFAQ chatbotを コード付きで構築します。

⏱ 読了時間:約15分 | 実装時間:約2時間 | 前提:Slackワークスペース(無料プランでOK)

1. 社内FAQの課題

📊 こんな経験ありませんか?

1.8時間社員1人あたりの「情報探し」に費やす時間/日(McKinsey調査)
5回同じ質問が別の人から来る平均回数/週
67%の社員が「社内マニュアルが見つからない・見にくい」と回答

❌ 現状の問題

  • マニュアルがNotionの奥底に埋もれてる
  • 結局「○○さんに聞く」が最速になる
  • 詳しい人の時間が奪われ続ける
  • 新人が同じ質問を何度もする
  • 退職者のナレッジが消える

✅ FAQ bot導入後

  • Slackで質問→AIが即回答
  • マニュアルの該当箇所を引用表示
  • 24時間回答可能(夜勤・海外拠点も)
  • 同じ質問はAIが何度でも対応
  • ナレッジが組織に蓄積される

2. 仕組みの全体像

┌──────────┐ メンション ┌──────────┐  検索   ┌──────────┐
│  Slack   │ ───────> │  サーバー  │ ────> │ ナレッジ  │
│  社員     │          │ (Express) │       │   ベース   │
│          │ <─────── │           │       │(Markdown) │
│          │  回答     │           │       └──────────┘
└──────────┘          │           │
                      │  Claude   │
                      │   API     │
                      │  (回答生成)│
                      └──────────┘
  1. 社員がSlackでbotにメンションして質問
  2. サーバーがナレッジベース(Markdownファイル)を検索
  3. 関連するナレッジをClaude APIに渡して回答を生成
  4. Slackに回答を返信(出典のドキュメント名付き)

3. Step 1: ナレッジベースを準備する

AIに回答させるには、まず社内の情報をテキストデータとして整理する必要があります。 Markdownファイルで管理するのが最もシンプルです。

# ナレッジベースのディレクトリ構成

knowledge/
├── 総務/
│   ├── 経費精算.md
│   ├── 有給申請.md
│   ├── 出張申請.md
│   └── 備品購入.md
├── IT/
│   ├── PC初期設定.md
│   ├── VPN接続方法.md
│   ├── Slack使い方.md
│   └── パスワード変更.md
├── 人事/
│   ├── 評価制度.md
│   ├── 福利厚生.md
│   └── 入社手続き.md
└── 営業/
    ├── 見積もり手順.md
    ├── 契約フロー.md
    └── CRM操作方法.md

# knowledge/総務/経費精算.md の例

# 経費精算の方法

## 申請手順
1. freee経費精算にログイン
2. 「経費申請」→「新規作成」
3. 領収書の写真をアップロード
4. 勘定科目を選択(迷ったら「消耗品費」)
5. 上長に承認申請

## 締め日
- 毎月25日締め、翌月10日払い
- 25日を過ぎた分は翌々月になります

## よくある質問
Q. 領収書をなくした場合は?
A. 出金伝票で代用可能。上長の承認が必要。

Q. クレジットカード明細でもOK?
A. 3,000円以下ならOK。それ以上は領収書必須。

📌 既存のドキュメントの活用: NotionやGoogle Docsに既にマニュアルがあれば、 エクスポートしてMarkdownに変換するだけ。ゼロから書く必要はありません。 Notionなら「エクスポート」→「Markdown」で一括出力可能。

4. Step 2: Slack Appを作成する

1

Slack APIにアクセス

api.slack.com/apps → 「Create New App」→ 「From scratch」

2

Bot Token Scopesを設定

OAuth & Permissions → 以下のスコープを追加:

app_mentions:read   # メンション検知
chat:write          # メッセージ送信
channels:history    # チャンネル履歴
im:history          # DM履歴
3

Event Subscriptionsを有効化

Request URL にサーバーのURLを設定。イベント: app_mention を追加。

4

ワークスペースにインストール

「Install to Workspace」→ 許可 → Bot User OAuth Tokenをコピー

# .env に保存

SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxx
SLACK_SIGNING_SECRET=xxxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxx

5. Step 3: AIで回答を生成する

ナレッジベースを検索し、関連するドキュメントをClaude APIに渡して回答を生成します。

// knowledge.js — ナレッジ検索

const fs = require('fs');
const path = require('path');

const KNOWLEDGE_DIR = './knowledge';

/**
 * ナレッジベースからキーワードマッチで関連文書を検索
 */
function searchKnowledge(query) {
  const results = [];
  const files = getAllMarkdownFiles(KNOWLEDGE_DIR);

  for (const filePath of files) {
    const content = fs.readFileSync(filePath, 'utf-8');
    const fileName = path.basename(filePath, '.md');

    // シンプルなキーワードマッチ(本番ではベクトル検索推奨)
    const queryWords = query
      .toLowerCase()
      .split(/\s+/)
      .filter(w => w.length > 1);

    let score = 0;
    for (const word of queryWords) {
      const regex = new RegExp(word, 'gi');
      const matches = content.match(regex);
      if (matches) score += matches.length;
    }

    if (score > 0) {
      results.push({
        fileName,
        filePath: filePath.replace(KNOWLEDGE_DIR + '/', ''),
        content,
        score,
      });
    }
  }

  // スコア順にソートして上位3件
  results.sort((a, b) => b.score - a.score);
  return results.slice(0, 3);
}

function getAllMarkdownFiles(dir) {
  let files = [];
  const items = fs.readdirSync(dir);
  for (const item of items) {
    const fullPath = path.join(dir, item);
    if (fs.statSync(fullPath).isDirectory()) {
      files = files.concat(getAllMarkdownFiles(fullPath));
    } else if (item.endsWith('.md')) {
      files.push(fullPath);
    }
  }
  return files;
}

module.exports = { searchKnowledge };

// ai-answer.js — Claude APIで回答生成

const Anthropic = require('@anthropic-ai/sdk');
const { searchKnowledge } = require('./knowledge');

const anthropic = new Anthropic();

const SYSTEM_PROMPT = `あなたは社内FAQ AIアシスタントです。
社員からの質問に、提供されたナレッジベースの情報をもとに回答します。

ルール:
- ナレッジベースに情報がある場合はそれに基づいて回答
- 情報がない場合は「この件についてはナレッジベースに
  情報がありません。担当部署にお問い合わせください」と回答
- 回答の末尾に出典(参照したドキュメント名)を記載
- 推測や憶測で回答しない
- 簡潔に、でも必要な情報は漏らさず
- Slackのマークダウン形式で返す(*太字*、箇条書き等)`;

async function generateAnswer(question) {
  // ナレッジベースを検索
  const docs = searchKnowledge(question);

  if (docs.length === 0) {
    return {
      answer: 'この件についてはナレッジベースに情報が'
        + 'ありません。\n担当部署にお問い合わせください。',
      sources: [],
    };
  }

  // 関連ドキュメントをコンテキストとして構築
  const context = docs
    .map(d => '--- ' + d.filePath + ' ---\n' + d.content)
    .join('\n\n');

  const response = await anthropic.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 500,
    system: SYSTEM_PROMPT,
    messages: [{
      role: 'user',
      content: '【社内ナレッジ】\n' + context
        + '\n\n【質問】\n' + question,
    }],
  });

  return {
    answer: response.content[0].text,
    sources: docs.map(d => d.filePath),
  };
}

module.exports = { generateAnswer };

ポイント: 回答は必ずナレッジベースの情報に基づく(RAG: Retrieval-Augmented Generation)。 AIの「ハルシネーション」(もっともらしいウソ)を防ぎ、正確な社内情報を提供します。

6. Step 4: 全体を統合する

// server.js — Slack FAQ Bot

const express = require('express');
const crypto = require('crypto');
const { generateAnswer } = require('./ai-answer');
require('dotenv').config();

const app = express();

// Slack署名検証
function verifySlackSignature(req, res, buf) {
  const timestamp = req.headers['x-slack-request-timestamp'];
  const signature = req.headers['x-slack-signature'];

  const sigBase = 'v0:' + timestamp + ':' + buf.toString();
  const mySignature = 'v0=' + crypto
    .createHmac('sha256', process.env.SLACK_SIGNING_SECRET)
    .update(sigBase)
    .digest('hex');

  if (mySignature !== signature) {
    throw new Error('Invalid Slack signature');
  }
}

app.use(express.json({ verify: verifySlackSignature }));

// Slackイベントエンドポイント
app.post('/slack/events', async (req, res) => {
  // URL検証(初回のみ)
  if (req.body.type === 'url_verification') {
    return res.json({ challenge: req.body.challenge });
  }

  // イベント処理
  const event = req.body.event;
  if (!event || event.type !== 'app_mention') {
    return res.status(200).send('ok');
  }

  // 即座に200を返す(3秒ルール)
  res.status(200).send('ok');

  // メンション部分を除去して質問を抽出
  const question = event.text
    .replace(/<@[A-Z0-9]+>/g, '')
    .trim();

  if (!question) {
    await postToSlack(
      event.channel,
      '質問をどうぞ!例: 「経費精算のやり方は?」',
      event.ts
    );
    return;
  }

  // 「考え中...」のリアクション
  await addReaction(event.channel, event.ts, 'hourglass');

  try {
    // AI回答を生成
    const { answer, sources } = await generateAnswer(question);

    // 出典情報を追加
    let response = answer;
    if (sources.length > 0) {
      response += '\n\n'
        + '_📄 参照: ' + sources.join(', ') + '_';
    }

    // Slackに回答を投稿(スレッド返信)
    await postToSlack(event.channel, response, event.ts);
  } catch (error) {
    console.error('Error:', error);
    await postToSlack(
      event.channel,
      'エラーが発生しました。管理者に連絡してください。',
      event.ts
    );
  }

  // リアクション削除
  await removeReaction(event.channel, event.ts, 'hourglass');
  await addReaction(event.channel, event.ts, 'white_check_mark');
});

// Slackメッセージ送信
async function postToSlack(channel, text, threadTs) {
  await fetch('https://slack.com/api/chat.postMessage', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization':
        'Bearer ' + process.env.SLACK_BOT_TOKEN,
    },
    body: JSON.stringify({
      channel,
      text,
      thread_ts: threadTs,  // スレッド返信
    }),
  });
}

// リアクション追加/削除
async function addReaction(channel, ts, emoji) {
  await fetch('https://slack.com/api/reactions.add', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization':
        'Bearer ' + process.env.SLACK_BOT_TOKEN,
    },
    body: JSON.stringify({
      channel, timestamp: ts, name: emoji
    }),
  });
}

async function removeReaction(channel, ts, emoji) {
  await fetch('https://slack.com/api/reactions.remove', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization':
        'Bearer ' + process.env.SLACK_BOT_TOKEN,
    },
    body: JSON.stringify({
      channel, timestamp: ts, name: emoji
    }),
  });
}

app.listen(3000, () => {
  console.log('FAQ Bot running on port 3000');
});

🎯 UXのポイント:

  • 質問中は⏳リアクション → 回答後に✅に変更(処理中が分かる)
  • スレッド返信でチャンネルを汚さない
  • 出典ドキュメント名を表示(信頼性の担保)

7. ナレッジの管理と更新

FAQ botの品質はナレッジベースの鮮度で決まります。定期的な更新の仕組みを作りましょう。

📝 GitHub管理(推奨)

ナレッジのMarkdownファイルをGitHubリポジトリで管理。 更新はプルリクエストで行い、レビュー→マージで品質を担保。 マージ時にサーバーが自動更新(webhook経由)。

📊 回答できなかった質問のログ

「ナレッジベースに情報がありません」と回答した質問を記録。 週次でレビューし、必要なドキュメントを追加。botが答えられない質問 = 足りないナレッジを可視化。

👍👎 フィードバック機能

回答メッセージに👍👎のリアクションボタンを追加。 👎が多い回答は人間がレビューし、ナレッジを修正。 継続的な品質改善ループを実現。

8. 活用シーン別カスタマイズ

🏢 総務・バックオフィス

経費精算、有給申請、備品購入、入退社手続き

会話例:

社員: @faq-bot 経費精算の締め日いつ?

bot: 毎月25日締め、翌月10日払いです。25日を過ぎた分は翌々月になります。
📄 参照: 総務/経費精算.md

💻 IT・情シス

PC設定、VPN接続、ツール操作、トラブルシューティング

会話例:

社員: @faq-bot VPN繋がらないんだけど

bot: VPN接続のトラブルシューティング:
1. まずWi-Fiが接続されているか確認
2. VPNクライアントを再起動
3. それでも繋がらない場合は情シス(内線: 1234)へ
📄 参照: IT/VPN接続方法.md

📚 新人オンボーディング

入社手続き、ツール設定、社内ルール、自己紹介

会話例:

新入社員: @faq-bot Slackのプロフィール何書けばいい?

bot: Slackプロフィールの設定:
・表示名: 名前(チーム名)
・ステータス: 「入社○日目!」等
・自己紹介: #self-introduction チャンネルに投稿
📄 参照: IT/Slack使い方.md

📞 カスタマーサポート(社内向け)

商品情報、対応マニュアル、エスカレーション手順

会話例:

CS担当: @faq-bot 返品の条件って?

bot: 返品条件:
・到着後7日以内
・未使用・未開封品に限る
・お客様都合の場合は送料お客様負担
・不良品は着払いで受付
📄 参照: 営業/返品ポリシー.md

9. Microsoft Teams版

Teamsをお使いの場合も、同じ仕組みで構築可能です。 AI回答生成の部分はそのまま流用し、Slack APIの部分をTeams Bot Frameworkに置き換えます。

Slack vs Teams の違い

項目SlackTeams
Bot作成Slack APIBot Framework + Azure
イベント受信Events APIBot Framework Webhook
メッセージ送信chat.postMessageActivity.sendActivity
費用無料プランでOKMicrosoft 365必須

📌 どちらでも構築可能です。 Slackの方が開発がシンプルですが、 社内でTeamsを使っている場合はTeams版をお選びください。 AI回答ロジックは100%共通で使えます。

10. 運用コスト

社員50名、月間500件の質問を処理した場合:

Slack(無料プラン)¥0
Claude API(500回の回答生成)約 ¥500
VPSサーバー¥1,000〜3,000
合計¥1,500〜3,500/月

💡 比較: 人間 vs FAQ Bot

総務担当者の対応時間

月40時間

1件5分×500件/月=約40時間

人件費換算: 約¥100,000

FAQ Bot

¥3,500/月

24時間即回答

人間は複雑な案件だけに集中

まとめ

  • 社内ナレッジをMarkdownで管理しAIが即回答
  • RAG方式で正確な情報に基づく回答(ハルシネーション防止)
  • Slack/Teams両対応、スレッド返信+出典表示で実用的
  • 月500件対応でも¥3,500以下の運用コスト
  • フィードバック機能で継続的に品質向上

「社内のナレッジ共有をAIで効率化したい」

既存のマニュアルやドキュメントを活用して、 あなたの会社専用のFAQ botを構築します。

無料で相談する →

📚 関連記事