出勤状況AI報告ボット|Gemini API × Slack API × GAS
リモートワークの普及に伴い、多くの企業でSlackをはじめとするチャットツールでの勤怠報告が定着しています。これは業務開始を円滑にする一方で、新たな課題も生んでいます。報告時刻のばらつきや休暇連絡との混在により、メンバー同士で出勤状況を把握するには、確認が必要となるケースが少なくありません。特に、報告がないメンバーが休暇取得者なのか、あるいは報告を失念しているのかを判断するのは、見過ごされがちな管理コストとなっていました。
本稿では、これらの課題を解決するために開発した、Google Apps Script (GAS) と Gemini API、Slack API を活用した勤怠状況の自動レポートシステムについて、その仕組みを解説します。

システム概要
本システムは、Slackの勤怠報告チャンネルへの投稿を自動的に集計・分析し、日々の出勤状況を所定のチャンネルへレポートするものです。全体の処理フローは以下の通りです。
1. ログデータの蓄積
毎日定刻(AM 10:00)、GASがSlack APIを介して勤怠報告チャンネルの投稿を取得し、Googleドキュメントに時系列で記録します。

[09:44:45] オフィス勤務: <!here>
*`【出勤】`*2025年10月22日 9:44 JST
山田 花子 さんは*オフィス勤務*です。
本日午後から夏期休暇頂きます。
[09:51:27] 在宅勤務: <!here>
*`【出勤】`* 2025年10月22日 9:51 JST
山下 次郎 さんは*在宅勤務*です。
[09:52:05] オフィス勤務: <!here>
*`【出勤】`* 2025年10月22日 9:52 JST
鈴木 一郎 さんは*オフィス勤務*です。
2. 状況分析と判定
毎日定刻(AM 11:00)、別のGASが起動し、蓄積されたログデータと当日の出勤対象者リストを、プロンプトと共にGemini APIへ送信。APIは受け取った情報を基に、各メンバーの状況(出勤報告済み、休暇連絡済み、未報告)を判定します。

レポートの自動生成・投稿: Gemini APIによる分析結果を基に、GASがレポートを整形し、指定されたSlackチャンネルへ自動投稿します。
営業日判定: Googleカレンダーを参照し、システムの実行を営業日のみに限定。土日祝日には稼働しない設計としています。
この自動化により、管理者は手作業での確認業務から解放され、報告の確認漏れやコミュニケーションコストの削減が期待できます。
システム構成と技術詳細
本システムの実現にあたり、以下の技術要素を連携させています。
1. Google Apps Script (GAS) による自動化
定時実行機能を活用し、Slack APIからログデータ取得、Googleドキュメントへの書き込み、Gemini APIへのリクエスト、Slackへの通知という一連のプロセスを自動化しています。
Google cloudやFirebaseでの実装も考えましたが、社内向けなので、スタンドアローンのGASであればサーバーレスで実行できるため、インフラコストがかからない点が利点です。
2. Googleドキュメントによるデータストア
Slackから取得したテキストベースのログを保存する簡易的なデータベースとして利用しています。構造化データが不要な今回の要件においては、セットアップの容易さから最適な選択肢でした。
3. Gemini APIによる高度な文脈判断
本システムの中核を担うのがGemini APIです。単なるキーワードのマッチングでは困難な、「休暇」という意図を含む自然言語の投稿と、単に投稿が存在しない「未報告」という状態を、チャットログの文脈から判別します。この自然言語理解能力が、システムの信頼性を担保しています。
4. プロンプト設計の要点
生成AIから期待通りの出力を得るためには、プロンプトの設計が重要です。本システムでは、以下の要素を盛り込むことで、出力の精度と形式を制御しています。
明確なインプット/アウトプット定義: 入力データと、期待する出力形式を具体的に指示します。
厳密な条件設定: 判定ロジックを言語化して伝えます。
出力例の提示: 理想的な出力フォーマットの例を示すことで、AIの自己修正を促し、より安定した結果を得られるように設計しています。
( ${…}はGAS内で取得した変数です)
# Googleドキュメントに保存された勤怠ログ
${docText}
# 本日の日付
${today}
# 指示文章
出勤報告をしていない人を洗い出し、報告してください。
# 出力例:
${today}:勤怠連絡がない、または休暇を取得している方は以下の通りです。
山田 太郎さん: 体調不良のため休暇
鈴木 花子さん: 本日の出勤・休暇連絡がありません
また、本日の出勤報告は以下の方々です。
山田 太郎さん: 9:50
鈴木 花子さん: 9:50
# 条件:
該当者がいない場合は以下のメッセージにしてください
## 出力例:
${today}:本日は全員報告は以下の方々です。
山田 太郎さん: 9:50
鈴木 花子さん: 9:50

定時実行トリガーの強化
最後に、GASの時間主導型トリガーにはいくつか不便な点がある。
分ベース では1分おき・5分おき・10分おき・15分おき・30分おきのみ。
時間ベース では1時間おき・2時間おき・4時間おき・6時間おき・8時間おき・12時間おきのみ。
日付ベース では午前0時〜各1時間おき。
週ベース では特定の曜日に加えて午前0時〜1時間おき。
月ベース では特定の日に加えて午前0時〜1時間おき。
特定の日時 では「年」「月」「日」「時」「分」まで設定しないといけない。

つまりトリガーの設定画面だけでは「毎日午前10時15分」のような指定は実現できない。これを解消するため、GASで毎日実行後に、翌日の年月日時分を自動的にトリガー設定するようにした。
function setDailyTrigger() {
// 既存のトリガーを削除
const triggers = ScriptApp.getScriptTriggers();
for (const trigger of triggers) {
if (trigger.getHandlerFunction() === 'kintai_start') {
ScriptApp.deleteTrigger(trigger);
}
}
// 翌日のDateオブジェクトを作成
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
const day = now.getDate() + 1;
const date = new Date(year, month, day, 10, 15); // 翌日の時間指定
// トリガーを設定
ScriptApp.newTrigger('kintai_start').timeBased().at(date).create();
}
function kintai_start() {
// 定期実行したい処理をここに記述
kintai();
// 処理終了時に再び翌日のトリガーを設定
setDailyTrigger();
}
生成AIを既存の業務フローに組み込むことで、これまで自動化が困難であった非構造化データの扱いや、文脈判断を伴うタスクの効率化が可能となります。本事例が、皆様の業務改善の一助となれば幸いです。
