giftee Tech Blog

ギフティの開発を支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信しています。

Slackスレッドの議論サマリをClaude Code + GASで自動生成する

Slackスレッドの議論サマリをClaude Vode + GASで自動生成する

こんにちわ、ギフティのVPoEの大曽根です。

以前のエントリーで AI を活用して価値創造の取り組みの現在地をご紹介しましたが、今回はその中の1つのテーマについて、具体的な取り組みを紹介します。

tech.giftee.co.jp

背景

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 ツールを活用した業務効率化や事業価値創出を積極的に進めています。興味のある方はぜひお声がけください。

careers.giftee.co.jp