社内FAQ chatbotの作り方
Slack×Claude API実践ガイド
「経費精算のやり方は?」「有給の申請方法は?」「このツールの使い方は?」
社内で毎日繰り返される同じ質問。マニュアルはあるけど誰も読まない。 結局、詳しい人に聞く。その人の時間が奪われる。
この記事では、社内のナレッジをAIに学習させ、Slackで質問できるFAQ chatbotを コード付きで構築します。
⏱ 読了時間:約15分 | 実装時間:約2時間 | 前提:Slackワークスペース(無料プランでOK)
📖 目次
1. 社内FAQの課題
📊 こんな経験ありませんか?
❌ 現状の問題
- マニュアルがNotionの奥底に埋もれてる
- 結局「○○さんに聞く」が最速になる
- 詳しい人の時間が奪われ続ける
- 新人が同じ質問を何度もする
- 退職者のナレッジが消える
✅ FAQ bot導入後
- Slackで質問→AIが即回答
- マニュアルの該当箇所を引用表示
- 24時間回答可能(夜勤・海外拠点も)
- 同じ質問はAIが何度でも対応
- ナレッジが組織に蓄積される
2. 仕組みの全体像
┌──────────┐ メンション ┌──────────┐ 検索 ┌──────────┐
│ Slack │ ───────> │ サーバー │ ────> │ ナレッジ │
│ 社員 │ │ (Express) │ │ ベース │
│ │ <─────── │ │ │(Markdown) │
│ │ 回答 │ │ └──────────┘
└──────────┘ │ │
│ Claude │
│ API │
│ (回答生成)│
└──────────┘- 社員がSlackでbotにメンションして質問
- サーバーがナレッジベース(Markdownファイル)を検索
- 関連するナレッジをClaude APIに渡して回答を生成
- 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を作成する
Slack APIにアクセス
api.slack.com/apps → 「Create New App」→ 「From scratch」
Bot Token Scopesを設定
OAuth & Permissions → 以下のスコープを追加:
app_mentions:read # メンション検知 chat:write # メッセージ送信 channels:history # チャンネル履歴 im:history # DM履歴
Event Subscriptionsを有効化
Request URL にサーバーのURLを設定。イベント: app_mention を追加。
ワークスペースにインストール
「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 の違い
| 項目 | Slack | Teams |
|---|---|---|
| Bot作成 | Slack API | Bot Framework + Azure |
| イベント受信 | Events API | Bot Framework Webhook |
| メッセージ送信 | chat.postMessage | Activity.sendActivity |
| 費用 | 無料プランでOK | Microsoft 365必須 |
📌 どちらでも構築可能です。 Slackの方が開発がシンプルですが、 社内でTeamsを使っている場合はTeams版をお選びください。 AI回答ロジックは100%共通で使えます。
10. 運用コスト
社員50名、月間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を構築します。
無料で相談する →