
こんにちわ、ギフティのVPoEの大曽根です。
以前のエントリーで AI を活用して価値創造の取り組みの現在地をご紹介しましたが、今回はその中の1つのテーマについて、具体的な取り組みを紹介します。
背景
Slackでの議論は、スレッドが伸びるにつれて全体像を掴みにくくなります。特に長いスレッドでは「結局何が決まったのか」「誰がどのタスクを持っているのか」が埋もれてしまい、後から参照するのが困難です。
議論の結論やアクションアイテムを整理するために、手動でGoogle Docsにまとめるケースもありますが、これは地味に手間がかかります。会議には議事録の文化があるのに、Slackスレッドの議論にはそれがありません。
一方で、Slackのメッセージは自然言語であり、AIに読ませるコンテキストとしては好都合です。スレッドの会話をAIに要約させてGoogle Docsに出力すれば、「Slackスレッドの議事録」を自動で作れるのでは、と考えて仕組みを実装しました。
何を作ったか
ひとことで言えば、Slackスレッドの会話から、構造化されたサマリドキュメントをGoogle Docsに自動生成し、スレッドに通知する仕組みです。
Slackスレッドの親メッセージに :summarize: スタンプを押す ↓ Slack Event API が Webhook 送信 Google Apps Script が受信 ↓ スレッドの全メッセージを取得 ↓ Claude API でサマリ生成 Google Docs にドキュメント作成 ↓ スレッドにリンク付き通知 Slack スレッド内に完了通知が届く
スタンプを押すと、数分後にはスレッド内にこんな通知が届きます。
📝 サマリを作成しました https://docs.google.com/document/d/xxxxx 📁 全てのサマリはこちら https://drive.google.com/drive/folders/xxxxx
さらに、スレッドの議論が進んだ後に再サマリもできます。サマリ済みのスレッド内で :summarize: とメッセージを投稿すると、既存のGoogle Docsが最新内容で上書き更新されます。
🔄 サマリを更新しました https://docs.google.com/document/d/xxxxx
生成されるドキュメントの構造
Google Docsに出力されるサマリは以下のような構造です。
- トピック — 議論の要約タイトル
- 元スレッドURL — Slackの元スレッドへのリンク
- 参加者 — スレッドに参加したメンバー一覧
- 議論の経緯 — トピックごとに分類された議論の流れ
- 結論 — 最終的に合意された内容
- アクションアイテム — 担当者・タスク・期限の一覧表
ファイル名は [チャンネル名]_YYYY-MM-DD_サマリトピック名 の形式で自動生成されます。チャンネル名と日付がファイル名に含まれるため、後述するAIツールでの検索・活用に便利です。
技術構成
| 要素 | 採用技術 |
|---|---|
| 実行環境 | Google Apps Script |
| AI | Claude API(claude-opus-4-6) |
| トリガー・通知 | Slack Event API |
| ドキュメント出力 | Google Docs |
| キュー・ログ管理 | Google Spreadsheet |
| ローカル開発 | clasp(GASのCLIツール) |
GASを選んだ理由はシンプルで、出力先がGoogle DocsでSlack連携もある以上、Google Workspace内で完結するのが都合がいいからです。追加のサーバーもデータベースも不要で、スプレッドシートがキュー兼ログとして機能します。
アーキテクチャ
処理はイベント駆動+ポーリングのハイブリッド構成です。
[Slack] --Webhook--> [GAS doPost] --キュー追加--> [スプレッドシート]
↓ 5分おきトリガー
[processQueue]
↓
スレッド全文取得(Slack API)
↓
サマリ生成(Claude API)
↓
Google Docs 作成/更新
↓
完了通知(Slack API)
Slack Event API の Webhook はすぐに HTTP 200 を返す必要があるため、doPost ではキューに積むだけにしており、実際のサマリ生成は〜5分おきの時限トリガーで非同期に処理します。これにより Slack のリトライを回避しつつ、Claude API のような時間のかかる処理を安全に実行できます。
GASプロジェクトの構成
src/ ├── Config.gs — 設定値の一元管理 ├── Main.gs — エントリポイント(doPost / processQueue) ├── SlackClient.gs — Slack API ラッパー ├── ClaudeService.gs — Claude API 呼び出し・プロンプト管理 ├── DocsService.gs — Google Docs 作成・更新 ├── QueueManager.gs — スプレッドシートのキュー/ログ操作 └── Utils.gs — 共通ユーティリティ
作っていて詰まったところ
clasp push してもWebアプリに反映されない問題
開発初期、最もハマったのがこれです。clasp push でコードをアップロードしても、Webアプリとして公開されている doPost が古いコードのまま動き続けます。
原因は、GASのWebアプリがデプロイ時のバージョンに固定される仕組みだったことです。clasp push はプロジェクトのソースコードを更新するだけで、デプロイされているバージョンは変わりません。
clasp push → ソースコードは更新される Webアプリ(doPost) → デプロイ時のバージョン(例: v14)のまま
対策として、コードを更新したら毎回 GASエディタの「デプロイ > デプロイを管理」から新しいバージョンでデプロイする必要があります。
Google DocsのURL形式が2種類ある問題
再サマリ機能を実装した際、既存のGoogle DocsをURLから開いて更新する処理を書きました。ところが、ログシートに記録されたURLの形式が想定と違っていてエラーになります。
// 想定していた形式 // https://docs.google.com/document/d/{docId}/edit // 実際にログに記録されていた形式 // https://docs.google.com/open?id={docId}
DocumentAppの getUrl() が返すURLの形式と、ブラウザで開いたときのURLの形式が異なるケースがあります。extractDocId_ 関数で両方のパターンに対応しました。
function extractDocId_(url) { var match = url.match(/\/d\/([a-zA-Z0-9_-]+)/); if (match) return match[1]; match = url.match(/[?&]id=([a-zA-Z0-9_-]+)/); if (match) return match[1]; throw new Error('Invalid Google Docs URL: ' + url); }
Slackイベントのリトライで重複処理が走る問題
Slack Event API は、Webhook に対して3秒以内にレスポンスがないとリトライを送信します。GASの実行は必ずしも高速ではないため、同じイベントが複数回届くことがあります。
新規サマリ(リアクション)はキューとログの重複チェックで対応していましたが、再サマリ(メッセージ)で問題が発生。1回しかメッセージを送っていないのに、ログシートに複数の「更新成功」が記録される状況になります。
原因は、再サマリのキュー処理が完了して行が削除された後に、Slackのリトライイベントが到着し、再度キューに追加されていたこと。CacheServiceで10分間の重複排除キーを持つことで解決しました。
var cache = CacheService.getScriptCache(); var cacheKey = 'resummarize_' + threadTs; if (cache.get(cacheKey)) return; // 10分以内の重複は無視 cache.put(cacheKey, '1', 600);
必要な権限まわり
Slack Appのスコープ
channels:history - Publicチャンネルのメッセージ取得 channels:read - チャンネル情報の取得 chat:write - メッセージ投稿 reactions:read - リアクションイベントの受信 users:read - ユーザー情報の取得 users:read.email - ユーザーのメール取得
Event Subscriptionsでは reaction_added(新規サマリ用)と message.channels(再サマリ用)の2つを設定しています。
コスト感
| 項目 | コスト |
|---|---|
| GAS | 無料 |
| Claude API(サマリ生成) | 約 $0.05〜0.15/回 |
| Slack API | 無料 |
1スレッドあたり $0.05〜0.15 程度。月に100件サマリしても $15 以下です。
運用の工夫
サマリDocsの蓄積と横断検索
生成されたサマリはすべて共有フォルダに蓄積されます。ファイル名に [チャンネル名]_YYYY-MM-DD_トピック が含まれるため、AIツールとの相性がいいです。
NotebookLM で複数のサマリDocsをソースとして読み込ませれば、「過去にXXについて何が決まった?」「YYに関するアクションアイテムは?」といった横断的な質問ができます。
Gemini(Google Drive連携) では @Drive を使って、Drive内のサマリDocsを直接検索・参照できます。
@Drive [chanelname_1] のサマリから、過去1ヶ月の議論をまとめて @Drive [chanelname_2] で決まったアクションアイテムを一覧にして
チャンネル名でフィルタできるため、該当チャンネルのサマリをもれなく抜き出せます。
処理ログの可視化
スプレッドシートに処理結果を記録しています。
| ステータス | 意味 |
|---|---|
| 成功 | 新規サマリ作成 |
| 更新成功 | 再サマリによる更新 |
| 失敗 | エラー |
ログには処理日時、チャンネル名、スレッドURL、トリガーユーザー、トピック、ドキュメントURL、エラー詳細が記録されるため、問題が起きてもすぐに追跡できます。
排他制御とキュー処理
GASのスクリプトロック(LockService)で同時実行を防止し、5分おきのトリガーで最大5件ずつキューを処理します。GASの実行時間制限(6分)を超えないよう、1回のトリガーで処理する件数を制限しています。
振り返り
今回の仕組み構築で以下を実現できました。
- スタンプを押すだけという低い操作コストで、構造化されたサマリが自動生成される体験を実現できた
- 再サマリ機能により、議論が進行中のスレッドでも最新状態を保てるようになった
- サマリDocsの蓄積により、NotebookLMやGeminiを使った横断検索という二次活用の道が開けた
- GAS+スプレッドシートだけで完結する構成のため、追加のインフラコストがゼロ
運用していく中で今後やりたいこととして、
- Privateチャンネルへの対応(現在はPublicチャンネルのみ)
- サマリの品質向上(長大スレッドでの要約精度の改善)
- サマリDocsを起点にしたタスク管理連携
あたりがあります。
おわりに
やれそうかもというアイディアからClaude Codeと壁打ちをしながら実装計画を作成し、それをもとにAIに実装させ、躓いたら都度AIに聞きながら進めることでNon-devでも本番運用に乗る仕組みを作ることができました。
「Slackスレッドの議論をAIでサマリする」というアイデア自体はシンプルですが、実際に組んでみるとGAS特有の制約やSlack APIの挙動といった細かい課題が出てきます。
特にデプロイバージョンの管理やイベントリトライのような「動くけど正しく動かない」系の問題は、ログを丁寧に追わないと原因がわかりません。スプレッドシートをログとして活用できるのはGASの利点の一つです。
とはいえ、一度仕組みを作ってしまえば、あとはスタンプを押すだけで自動的にサマリが生成されます。
Slackでの議論が資産として蓄積される状態を、ほぼゼロコストで維持できます。
ギフティでは AI ツールを活用した業務効率化や事業価値創出を積極的に進めています。興味のある方はぜひお声がけください。