0.はじめに
こんにちは!KIYO Learningでスタディングの開発をしている @gawa32 です!
非エンジニアや事業部メンバーから「この画面の仕様どうなってる?」「この動きバグ?」と質問が来ることは多いですが、仕様確認にはコードを見る必要があるケースも多く、エンジニアが状況を確認して回答する場面は日常的にあります。
もっと気軽に質問できて、エンジニアの調査コストも下げたい——
そんな思いから、Slack で質問するとコードを読みながら回答してくれる Q&A Bot を作りました。
仕組みはシンプルで、
Slack → Google Apps Script → GitHub Actions → Claude Code
という“多段”で動きます。Slack にメンションするだけで、Claude Code がリポジトリを直接調査し、仕様や挙動の根拠を含んだ回答を返してくれます。
この記事では、この Bot をどんな構成で作ったのかを紹介します。
1. 全体構成
今回の Q&A Bot は、
Slack → Google Apps Script → GitHub Actions → Claude Code
という、シンプルな多段構成で動作します。
- Slack で質問すると Google Apps Script が受け取り、即応答
- GAS が GitHub Actions を起動し、裏でClaude Codeがリポジトリを調査
- 結果は Slack のスレッドに自動返信されます
ここでのポイントは、
自前サーバーを一切持たず、全部クラウドの仕組みだけで回しているところです。
インフラの用意や運用を気にしなくてよいので、あとから手を入れたり、小さく始めて徐々に育てていくのもやりやすい構成です。
次の章では、この流れの中でそれぞれのレイヤーが、どんな役割を持っているのかを簡単に紹介していきます。
※本記事のコードは構成イメージを伝えるための簡略版です。実際の運用では環境変数や権限設定などを適宜調整してください。
2. Slack(質問の入り口)
この Bot の入り口は Slack です。
ユーザーは Bot をメンションするだけで質問できるようにしており、BotユーザーのDMやチャンネルで動作します。
2-1. Slack 側で設定していること
Slack で行っている設定は主にこれだけです。
- Bot ユーザーの作成
- Events API を有効化
- app_mention
- message.im(DM)
- Bot Token(xoxb-)を発行
- chat:write / users:read など最低限の権限を付与
- GAS の公開 URL を Request URL に設定
つまり Slack 側は「イベントを GAS に投げるだけ」の役割になります。
3. Google Apps Script(イベント受付係)
Google Apps Script(GAS)は、この Bot の「受付窓口」です。
Slack からのイベントを受け取り、すぐにレスポンスを返して処理を GitHub Actions に引き渡す役割を担っています。
GAS が担当するのは以下の3つだけです。
- Slack Events API を受け取る
- 軽量なキューに積む
- GitHub Actions の workflow_dispatch を叩く
GAS 自体は重い処理を一切しない点がポイントです。
3-1. Slack Events API の受け取り(doPost)
Slack は「3秒以内に OK を返す」必要があるので、
GAS では受信 → 受付メッセージ → 即終了、の流れにしています。
function doPost(e) {
const json = JSON.parse(e.postData.contents);
// Slack の URL 検証用
if (json.type === "url_verification") {
return ContentService.createTextOutput(json.challenge);
}
if (json.type === "event_callback") {
const event = json.event;
const question = event.text.replace(/<@[A-Z0-9]+>/g, "").trim();
// 受付メッセージ
postSlackMessage(event.channel, `質問を受け付けました\n> ${question}`, event.ts);
// 非同期キューに積む
processEventAsync(json);
return ContentService.createTextOutput("OK");
}
}
3-2. イベントをキューに積んで順番に処理
GAS は同時実行や長時間処理に弱いので、
独自キュー(Script Properties(PropertiesService) + トリガー で安全に処理しています。
function processEventAsync(json) {
const lock = LockService.getScriptLock();
lock.waitLock(30000);
const key = "pendingEvents";
const queue = JSON.parse(PROPS.getProperty(key) || "[]");
queue.push(json);
PROPS.setProperty(key, JSON.stringify(queue));
lock.releaseLock();
ScriptApp.newTrigger("handleSlackEvent")
.timeBased()
.after(1)
.create();
}
この仕組みにより、
- Slack から複数のイベントが来ても落ちにくい構成にできる
- GAS の実行時間制限にかかりにくくなる
- イベントを FIFO で取り出すため、処理の順序を保ちやすい
といったメリットがあります。
3-3. キューから取り出して GitHub Actions を呼ぶ
トリガーで起動される handleSlackEvent() では、キューから1件だけ取り出して処理します。
function handleSlackEvent() {
const lock = LockService.getScriptLock();
lock.waitLock(30000);
const queue = JSON.parse(PROPS.getProperty("pendingEvents") || "[]");
const item = queue.shift();
PROPS.setProperty("pendingEvents", JSON.stringify(queue));
lock.releaseLock();
if (!item) return;
// Actions を叩く
triggerGitHubActions({
question: extractQuestion(item.event),
channel_id: item.event.channel,
thread_ts: item.event.ts,
slack_bot_token: PROPS.getProperty("SLACK_BOT_TOKEN"),
github_token: PROPS.getProperty("GITHUB_TOKEN"),
});
}
triggerGitHubActions() はシンプルで、GitHub Actions の workflow_dispatch を叩くだけです。
function triggerGitHubActions(inputs) {
const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/claude-code-qa.yml/dispatches`;
UrlFetchApp.fetch(url, {
method: "POST",
headers: {
Authorization: `token ${PROPS.getProperty("GITHUB_TOKEN")}`,
"Content-Type": "application/json",
},
payload: JSON.stringify({ ref: "main", inputs: inputs }),
});
}
3-4. GAS の役割まとめ
| やっていること | 説明 |
|---|---|
| イベント受付 | Slack のメンションを受信して即レス |
| キュー管理 | Script Properties を使った簡易キュー |
| Actions 呼び出し | workflow_dispatch で GitHub Actions に渡す |
GAS は「サーバーの代わりに Slack イベントを受け取るだけ」という最小限の役割にとどめています。
処理をシンプルに保つことで、後続のフローに余計な負荷をかけず、扱いやすい構成になります。
4. GitHub Actions(本処理の実行役)
Google Apps Script が workflow_dispatch を叩くと、
GitHub Actions が起動して Bot の本体処理 を担当します。
役割は大きく 3 つです。
- コードリポジトリをチェックアウト
- Claude Code CLI を実行して回答を生成
- Slack に結果を投稿
4-1. リポジトリの checkout
Claude Code に読ませたいリポジトリ(例:example-org/app-repo)を取得します。
- name: Checkout target repository
uses: actions/checkout@v4
with:
repository: example-org/app-repo
token: ${{ inputs.github_token }}
path: app-repo
private repositoryの場合でも、必要な権限を持つ GitHub Token を inputs 経由で渡せば checkout できます。
4-2. Claude Code CLI の実行(抜粋)
Actions 上で CLI を実行し、質問に対する回答を作成します。
※ Bedrock 経由での利用のため、モデル名は環境に合わせて読み替えてください。
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code
- name: Run Claude Code analysis
env:
QUESTION: ${{ inputs.question }}
run: |
cd app-repo
ANSWER="$(claude -p "$QUESTION" \
--model "global.anthropic.claude-opus-4-5-20251101-v1:0" \
--append-system-prompt "$SYSTEM_PROMPT")"
echo "$ANSWER"
4-3. Slack スレッドへの回答投稿(抜粋)
整形した回答テキストを Slack に返します。
- name: Post answer to Slack
env:
SLACK_BOT_TOKEN: ${{ inputs.slack_bot_token }}
run: |
curl -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "'"${{ inputs.channel_id }}"'",
"thread_ts": "'"${{ inputs.thread_ts }}"'",
"text": "'"${ANSWER//\"/\\\"}"'"
}'
4-4. GitHub Actions の役割まとめ
| やっていること | 説明 |
|---|---|
| リポジトリ取得 | Claude Code に読ませるコードを checkout |
| 回答生成 | Claude Code CLI を使ってコード調査・回答生成 |
| Slack 投稿 | スレッドに回答を書き込む |
GitHub Actions が Bot の「本体処理」を一手に引き受けることで、サーバーレスでコード調査フローを実現できます。
5. Claude Code への指示(System Prompt)
Claude Code はリポジトリを直接スキャンして回答を生成する非常に強力な仕組みです。
そのため、この Bot を通じて 「社内の誰でもコード調査の結果を間接的に得られる」 という性質があります。
便利である一方で、機密情報や認証情報の扱いには特に注意が必要になります。
そこでこの Bot では、回答ルールとセキュリティ制約を system prompt に明確に定義し、
AI が参照・出力してよい範囲を厳密にコントロールしています。
これにより、回答の品質を保ちながら、 機密情報や内部仕様が不適切に露出するリスクを防いでいます。
なお、GitHub 上のソースコードに機密情報や個人情報が含まれることはありませんが、
万が一それに近い情報が混入していた場合でも AI が出力しないよう、
system prompt 側でもセキュリティ制約を二重にかけています。
5-1. 実際に渡している system prompt(抜粋)
(要点だけ抜粋)
あなたはリポジトリを調査して回答するアシスタントです。
【回答ルール】
- 必ずコードを確認してから回答する(推測で答えない)
- 判断できない場合は「コードから確認できません」と返す
- 結論ファーストで、平易な日本語で説明する
- 必要に応じて GitHub の行番号リンクを付ける(例:#L10-L20)
- 引用するコードは最小限にとどめる
【セキュリティ制約(重要)】
- 機密情報(APIキー・パスワード・シークレット類)は絶対に出力しない
- `.env*` / `secrets.*` / `credentials.*` / `config` の“値”は出力禁止(概要のみ可)
- OS / Actions の環境変数は値を出さず、変数名のみ言及可(例:SLACK_BOT_TOKEN)
- ログなどに機密値が含まれる場合は必ずマスクする(例:`******`)
- 個人情報(氏名・メール等)は一般化して記述する
- 大量のコードをそのまま貼らず、必要最小限のみ引用する
この指示を最初に読み込ませることで、Claude Code の回答は以下のようになります
- 回答のトーンが安定する
- どこのコードを根拠にしているか明示される
- 「推測や勘」ではなく「コードを見た回答」になる
- 機密情報が回答に混じらないようコントロールされる
- 非エンジニアにも読みやすい説明になる
6. Slack API(回答の返却)
GitHub Actions で生成した回答は、Slack API を使って元のメンションのスレッドに返信 しています。
使っているのは主に chat.postMessage と chat.update の 2 つだけです。
6-1. 回答をスレッドに投稿(抜粋)
- name: Post answer to Slack
env:
SLACK_BOT_TOKEN: ${{ inputs.slack_bot_token }}
run: |
curl -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "'"${{ inputs.channel_id }}"'",
"thread_ts": "'"${{ inputs.thread_ts }}"'",
"text": "'"${ANSWER//\"/\\\"}"'",
"mrkdwn": true
}'
ポイントは thread_ts を指定して投稿すること。
これで自然に “返信” として扱われ、会話が散らばりません。
6-2. 受付メッセージの更新(任意)
GAS 側で「受付中です」と投稿しているので、処理が完了したらそのメッセージを置き換えています。
- name: Update processing message
if: success() && inputs.processing_msg_ts != ''
run: |
curl -X POST https://slack.com/api/chat.update \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "'"${{ inputs.channel_id }}"'",
"ts": "'"${{ inputs.processing_msg_ts }}"'",
"text": "回答を生成しました!スレッドをご確認ください。"
}'
6-3. このレイヤーのポイント
- Actions から Slack API を直接叩くだけなので構成はシンプル
- スレッドにまとめることで会話が追いやすい
- “受付→完了” の流れを更新すると使いやすさが上がる
Bot の UI はすべて Slack 上で完結しています。
7. まとめ
今回紹介した Q&A Bot は、Slack・GAS・GitHub Actions・Claude Code を組み合わせて
コード調査を自動化する仕組みです。
社内ではすでに毎日使われていて、
非エンジニアの方がそのまま Slack に質問を書ける機会は増えたように感じています。
「Bot の回答通りでした」というケースも何件かあり、少なくとも“ちょっと確認したい時の入口”としては機能し始めています。
エンジニア側の負担がどれくらい変わるかは、もう少し運用を続けてみないと分かりませんが、
少なくとも「とりあえず Slack に聞いてみる」という動線ができたのは良かった点かなと思います。
この構成はサーバーを持たずに動かせるので、同じように「Slack で気軽に仕様を確認したい」というケースで、参考になる部分があれば幸いです!
KIYOラーニング株式会社について
当社のビジョンは『世界一「学びやすく、分かりやすく、続けやすい」学習手段を提供する』ことです。革新的な教育サービスを作り成長させていく事で、オンライン教育分野でナンバーワンの存在となり、世界に展開していくことを目指しています。
プロダクト
- スタディング:「学びやすく・わかりやすく・続けやすい」オンライン資格対策講座
- スタディングキャリア:資格取得者の仕事探しやキャリア形成を支援する転職サービス
- AirCourse:受け放題の動画研修がついたeラーニングシステム(LMS)
KIYOラーニング株式会社では一緒に働く仲間を募集しています