これまでの自動化を“合体”させたら、自分専用のAI秘書になった【応用編その1・全体像】

話しかけるだけで、秘書に(AI秘書アイキャッチ) つまずき帳

予定とタスクは、気づけば頭の中だけでは持ちきれない量になっています。会議や外回りで席を離れることが多い人ほど、共有された連絡や予定を見落としやすい。「あれ、あの予定っていつだっけ」と確認しに戻る数分が、地味に積み重なる。大事な用事をうっかり忘れて、あとでヒヤッとする。働いていれば、たぶん誰にでも覚えがあります。

とくに地味にこたえるのが、前の晩です。明日の段取りが前の夜に分かっていれば、朝はずいぶんラクになる。でも予定表は職場のパソコンや手帳の中にあって、家ではパッと確認できない。だから夜のうちに、安心しきれない。——正直に言うと、これはぜんぶ、わたし自身のことでした。「秘書がひとりいてくれたら」と、ずっと思っていました。

そこで頼ったのが、AIです。このブログでこれまで一つずつ作ってきた小さな自動化——フォームの通知、LINEへの通知、毎朝のレポート——が、実は「秘書の部品」でした。それを一つにつなげて、ひと工夫だけ足したら、LINEに話しかけるだけで予定もタスクも整う「自分専用のAI秘書」になりました。名前は「ぽけっと手帳」。アイコンは、白くて笑った可愛いロボットです。しかも、コードは一行も書いていません。ぜんぶAIにお願いして、わたしは貼って、許可しただけ。

できあがったもの

完成したのは、こういう秘書です。

  • LINEに話しかけると、予定もタスクも勝手に登録してくれる
  • 毎晩、明日の予定を向こうから送ってきてくれる
  • いつものスプレッドシートが「今日ダッシュボード」に進化した
  • 登録した予定は、Googleカレンダーにも自動で入っている

予定とタスクの本体は、ずっとスプレッドシートです。シートが常にマスターで、AIはそこへ書き込む係。だから「AIが暴走して訳が分からなくなる」ことがありません。元はいつものシートのままです。

ぽけっと手帳の今日ダッシュボード。今日・今週の予定や残タスク、早出の注意がひと目で分かる(架空データ)。

わたしの一日で見せます

朝。 スマホを見ると、LINEにもう今日の予定が届いています。早番や朝の早い会議の日は、いちばん上に⚠️マークつきで強調されている。だから朝礼に出られなくても、今日が「いつもと違う日」かどうかが一目で分かります。

前の晩。 毎晩、明日の予定が向こうから届きます。ここで「明日は早い日だ」と分かるのが、わたしにとっては最大のポイントでした。前夜に分かれば、保育園送りの段取りに間に合う。この一通のために作ったと言ってもいいくらいです。

思いついた時。「来週水 ○○面談 15時」「金曜までに報告書」——こんなふうに、思いついた言葉のままLINEに送るだけ。あとはAIが、それが予定なのかタスクなのか問い合わせなのかを判断して、ちゃんと該当するシートに登録し、その場で「登録しました」と復唱して返してくれます。同じ予定が既にあれば、登録前に「はい/いいえ」で確認してくれる。間違えても対話で消せるし、変えられる。だから雑に放り込めます。

LINEに「来週水 部内ミーティング 15時」と送ると、AIが予定として自動登録して返信する画面(架空データ)。

パソコンで作業中。 ボタンを押すのも面倒な時は、シートに直接、内容と締切を打ってチェックを1つ入れるだけでタスクが追加できます。LINEを開かずに、作業の手を止めずに足せる。リッチメニューのボタンを押せば、今日の予定・今週の予定・残タスクが吹き出しカードでワンタップです。

LINEのリッチメニュー。今日・明日・今週の予定、残タスク、予定一覧、今週の要注意の6ボタン。

週末。 週に一度、今週の予定がまとめて届きます。日曜の夜に「今週はこういう一週間だな」と俯瞰できる。これで、月の予定表をいちいち見にいく習慣そのものが消えました。

これは、新しい魔法ではありません(これまでの“組み合わせ”です)

正直に打ち明けると、何かすごい新技術を覚えたわけではないんです。このブログで一つずつ書いてきた部品を、組み合わせただけ。並べてみると、こうなります。

  • 03「毎朝レポート」でやった「決まった時間に、自動でLINEへ送る」——これがそのまま、毎朝の「今日の予定」、毎晩の「明日の予定」、週1の「今週まとめ」になりました。
  • 02「LINE通知」で作った“LINEに送る”土台を、もう一歩進めて、LINEの言葉を受け取る双方向にしました。フォームの回答を読む代わりに、LINEに送った自然文をAI(Gemini)に読ませて、予定かタスクかに仕分けています。
  • 01「フォーム回答をメールで通知」は、捨てずに“保険”へ回しました。LINEへの通知が失敗した時、自動でメールに切り替わって届きます。
  • 04「差し込みメール」でやった「名簿を1件ずつ処理して、済んだら✅で記録する」考え方は、タスクを1件ずつ管理して状態を残す仕組みに生きています。

新しく足したのは、ほんのひと工夫だけ——LINEの言葉をAIに読ませること、予定をGoogleカレンダーへ同期すること、シートを“今日ダッシュボード”に整えること。土台はぜんぶ、これまでの記事の中にありました。だから、01から順番にやってきた人なら、ここまで地続きでたどり着けます。

どうやって作ったか(おおまかに)

仕組みの土台は、拍子抜けするほど素朴です。紙の月の予定表をスマホで撮影して、AIに読ませて表のデータに変換し、シートに貼る。それだけ。あとは、これまでの記事で覚えた通知や受け取りの部品を、上に重ねていきました。最後の仕上げに、シートを今日ダッシュボードにリッチ化し、予定をGoogleカレンダーへ約15分ごとに一方向で同期するようにしています。

言葉の解析も、写真の読み取りも、ぜんぶAI(Gemini)の担当です。そして大事な告白を一つ。コードはぜんぶAIが書きました。わたしは、貼って、許可しただけです。「自分で開発した」なんて、とても言えません。詳しい組み合わせ方は、次回からのシリーズで一つずつ分解していきます。

お金とAIの、正直な話

気になるお金の話を正直に書きます。AIエンジンはGeminiの無料枠で統一していて、わたしの使い方では総額0円です。たくさんの人が一斉に使う前提だと話は変わるはずなので、あくまで「個人がひとりで使う、わたしの使い方では」という範囲の話として読んでください。

無料枠なので、混んでいる時間帯はAIの返事が一瞬詰まることがあります。それでも、よく使うボタン操作はAIを呼ばずローカルだけで処理する作りにしてあるので、肝心なところは止まりません。

そしてもう一つ正直に言うと、これで稼いでいるわけではありません。収益はまだゼロです。いまは、作って、こうして発信して、知ってもらう段階。それでも、自分の一日がラクになった実感は本物です。

つまずきと、その工夫

作ってみて一番こだわったのは、「壊れても気づける」ことでした。便利な道具が黙って壊れているのが、いちばん怖い。だから「沈黙の故障を作らない」を設計の柱にしました。

  • 写真の中に読めない行があったら、行番号つきで「ここが読めません」と警告する
  • LINEへの通知が失敗したら、自動でメールに切り替えて届ける
  • 同じ予定は登録前に確認する
  • 間違えても「取消」で元に戻せる
  • エラーは英語の暗号ではなく、日本語で知らせる

完璧ではありません。読み取りをミスることもあります。でも、壊れた時に黙り込まず、ちゃんと声を上げてくれる。安心して任せられる線は、たぶんここでした。賢いより、裏切らない。秘書に求めるのは、結局それなんだと思います。

まとめと、次回予告

朝礼に出られなくても、前の夜に翌日の段取りが決まる。思いついた用事はLINEに一言投げておけば、忘れない。それだけで、毎日の気の重さがずいぶん軽くなりました。

創刊号01(フォーム通知)02(LINE通知)03(毎朝レポート)04(差し込みメール)と積み上げてきた部品が、この05でようやく一つにつながって、「自分専用のAI秘書」になりました。これは、ゼロから作った特別なものではなく、これまでの応用編です。

次回からは、この「ぽけっと手帳」を、どの記事のどの部品を組み合わせたのか、作り方シリーズとして一つずつ分解していきます。写真をどう読ませるか、LINEとどうつなぐか、許可画面で気後れしないために——コードが書けなくても、AIに任せて同じものにたどり着けるところまで、ご一緒できればうれしいです。

🧩 設計図:ぜんぶの“組み立て方”(上級者向け)

ここまでの「部品」が、1つのボットの中でどう組み合わさっているか——全体の組み立て図です。コードを書かなくても、この“設計図”をAIに渡せば、AIが組み立ててくれます。

📱 あなた(LINE)
▲ 通知を受け取る / 写真・文字を送る ▼
🤖 ぽけっと手帳(GAS)=司令塔
doPost が「写真」か「文字」かで仕分け
📷 写真
→ AIが読取 → 📊 シート
💬 文字
→ 予定に変換 → 📊 シート
⏰ 毎朝・前夜
→ 📱 LINEに自動通知
📊 シート
→ 📅 Googleカレンダー

① AIに渡す“設計図”(統合プロンプト)

コードを書かない人は、これをそのままAI(ChatGPTやGemini)に渡すだけ。AIが下のようなコードを書いてくれます。

Google Apps Scriptで、自分専用のLINEボット「ぽけっと手帳」を作ってください。
①LINEに送った予定表の写真をAI(Gemini)で読み取り、日付・予定・時刻をスプレッドシートに自動追記(読めない行は推測せず見送り、登録後に一覧で確認・修正・削除できる)
②自然文(例「明日15時に歯医者」)を送ると予定として解釈して追加
③毎朝に「今日の予定」、前夜に「明日の予定」を自動でLINE通知
④シートの予定をGoogleカレンダーへ一方向ミラー(15分ごとに同期)
⑤送った写真の原本をGoogleドライブとGoogleフォトに保存
※APIキーやトークンはスクリプトプロパティにまとめ、コードに直書きしない。AIが一時的に混雑(503)したら自動でリトライする設計に。

② 全体のコード(書き下ろし・約140行・コピペOK)

「中身も見たい」という人向け。実物のボットそのものではなく、同じ仕組みをゼロから書き起こした学習用の全体版です。トークンやキーはコードに書かず、スクリプトプロパティ(設定欄)から読む安全な作りにしてあります。

▶ 全体のコードを開く(約140行)
/**************************************************************************
 * ぽけっと手帳(学習用クリーン版・全体リファレンス)
 *   自分専用の LINE 秘書ボット。1つのスクリプトに「部品」を組み合わせています。
 *
 *   できること
 *     ① 予定表の写真を送る → AIが読み取り → スプレッドシートに追記(+確認返信)
 *     ② 自然文(例「明日15時に歯医者」)を送る → 予定として追記
 *     ③ 毎朝「今日の予定」/ 前夜「明日の予定」を自動でLINE通知(時間トリガー)
 *     ④ シートの予定を Google カレンダーへミラー(時間トリガーで同期)
 *
 *   ★秘密情報(LINEトークン・Geminiキー)はコードに直接書きません。
 *     エディタ左の[⚙ プロジェクトの設定]→「スクリプト プロパティ」に
 *     LINE_TOKEN と GEMINI_KEY を登録してください。
 **************************************************************************/

// ===== 設定:秘密はスクリプトプロパティから読む(コードに直書きしない)=====
const PROP         = PropertiesService.getScriptProperties();
const LINE_TOKEN   = PROP.getProperty('LINE_TOKEN');   // LINE チャネルアクセストークン
const GEMINI_KEY   = PROP.getProperty('GEMINI_KEY');   // Gemini API キー
const GEMINI_MODEL = 'gemini-2.5-flash';
const SS    = SpreadsheetApp.getActiveSpreadsheet();
const SHEET = SS.getSheetByName('予定') || SS.getActiveSheet();   // 列=[日付, 予定, 時刻]
const MY_CAL = CalendarApp.getDefaultCalendar();

/* ============ ① LINE からの入口(Webアプリ:doPost)============ */
function doPost(e) {
  const event = JSON.parse(e.postData.contents).events[0];
  if (event && event.type === 'message') {
    const m = event.message;
    if (m.type === 'image') handleImage(m.id, event.replyToken);       // 写真
    else if (m.type === 'text') handleText(m.text, event.replyToken);  // 文字
  }
  return ContentService.createTextOutput('OK');   // LINEへ即200を返す
}

/* ============ ② 写真 → AIで読み取り → シートに追記 ============ */
function handleImage(messageId, replyToken) {
  const blob = UrlFetchApp.fetch(
    'https://api-data.line.me/v2/bot/message/' + messageId + '/content',
    { headers: { Authorization: 'Bearer ' + LINE_TOKEN } }
  ).getBlob();

  const rows = readScheduleFromImage(blob);
  if (rows.length === 0) {
    reply(replyToken, '写真をうまく読み取れませんでした。少し時間をおいて、もう一度お試しください。');
    return;
  }
  rows.forEach(function (r) { SHEET.appendRow([r.date, r.title, r.time]); });
  reply(replyToken, '📸 写真から予定を' + rows.length + '件取り込みました。\n'
    + rows.map(function (r) { return r.date + ' ' + r.title; }).join('\n'));
}

// 画像をGeminiに渡し、日付・予定・時刻の配列で受け取る
function readScheduleFromImage(blob) {
  const prompt = 'この画像は予定表です。日付・予定・時刻を読み取り、'
    + 'JSON配列 [{"date":"YYYY-MM-DD","title":"予定名","time":"HH:mm"}] だけで返してください。'
    + '読めない項目は空文字に。説明文は不要です。';
  const text = callGemini(JSON.stringify({ contents: [{ parts: [
    { text: prompt },
    { inline_data: { mime_type: blob.getContentType(), data: Utilities.base64Encode(blob.getBytes()) } }
  ] }] }));
  return text ? JSON.parse(text.replace(/```json|```/g, '').trim()) : [];
}

/* ============ ③ 文字メッセージ → 予定追加 / 一覧 ============ */
function handleText(text, replyToken) {
  if (text === '今日の予定') { reply(replyToken, listText(0)); return; }
  if (text === '明日の予定') { reply(replyToken, listText(1)); return; }

  // 自然文(例「明日15時に歯医者」)を1件の予定に変換
  const prompt = '次の文を予定に変換し、JSON {"date":"YYYY-MM-DD","title":"...","time":"HH:mm"} '
    + 'だけで返してください。今日は ' + ymd(new Date()) + ' です。文:' + text;
  const ans = callGemini(JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] }));
  if (!ans) { reply(replyToken, 'AIが混み合っています。少し後でお試しください。'); return; }
  const r = JSON.parse(ans.replace(/```json|```/g, '').trim());
  SHEET.appendRow([r.date, r.title, r.time]);
  reply(replyToken, '✅ 追加しました:' + r.date + ' ' + r.title + ' ' + r.time);
}

/* ============ ④ 毎朝/前夜の自動通知(時間トリガーで実行)============ */
function notifyToday()    { push(listText(0)); }   // 毎朝に
function notifyTomorrow() { push(listText(1)); }   // 前夜に

// 指定日(0=今日,1=明日)の予定をテキスト化
function listText(offset) {
  const target = ymd(new Date(Date.now() + offset * 86400000));
  const items = SHEET.getDataRange().getValues().filter(function (row) {
    return row[0] && ymd(new Date(row[0])) === target;
  });
  if (items.length === 0) return (offset ? '明日' : '今日') + 'の予定はありません。';
  return (offset ? '明日' : '今日') + 'の予定\n'
    + items.map(function (row) { return '・' + (row[2] || '') + ' ' + row[1]; }).join('\n');
}

/* ============ ⑤ シート → Google カレンダーへミラー(時間トリガー)======= */
function syncToCalendar() {
  SHEET.getDataRange().getValues().forEach(function (row) {
    const date = row[0], title = row[1], time = row[2];
    if (!date || !title) return;
    const start = new Date(date);
    const hm = String(time || '09:00').split(':');
    start.setHours(Number(hm[0]) || 9, Number(hm[1]) || 0, 0, 0);
    // 同じ日に同じ予定が無ければ作成(重複防止の簡易版)
    const dup = MY_CAL.getEventsForDay(start).some(function (ev) { return ev.getTitle() === title; });
    if (!dup) MY_CAL.createEvent(title, start, new Date(start.getTime() + 3600000));
  });
}

/* ============ 共通:LINE返信/通知・日付ユーティリティ ============ */
function reply(replyToken, text) {
  lineApi('reply', { replyToken: replyToken, messages: [{ type: 'text', text: text }] });
}
function push(text) {   // 自分(友だち)へ配信
  lineApi('broadcast', { messages: [{ type: 'text', text: text }] });
}
function lineApi(path, body) {
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/' + path, {
    method: 'post', contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify(body)
  });
}
// Geminiを呼ぶ。一時的な混雑(HTTP 503)は最大3回まで自動リトライ
function callGemini(payload) {
  for (let i = 0; i < 3; i++) {
    const res = UrlFetchApp.fetch(
      'https://generativelanguage.googleapis.com/v1beta/models/' + GEMINI_MODEL + ':generateContent',
      { method: 'post', contentType: 'application/json',
        headers: { 'x-goog-api-key': GEMINI_KEY }, muteHttpExceptions: true, payload: payload });
    if (res.getResponseCode() < 500) {
      return JSON.parse(res.getContentText()).candidates[0].content.parts[0].text;
    }
    Utilities.sleep(1000 * (i + 1));   // 少し待って再挑戦
  }
  return '';
}
function ymd(d) { return Utilities.formatDate(d, Session.getScriptTimeZone(), 'yyyy-MM-dd'); }

③ 「どの部分が、どの機能」対応表

コードの部分機能シリーズ
doPostLINEの入口(写真/文字で仕分け)第2回
handleImage / readScheduleFromImage写真→表(OCR)第1回=記事06
handleText自然文を予定に変換/一覧第2回
notifyToday / listText毎朝・前夜の自動通知第4回
syncToCalendarカレンダーへミラー第5回
reply / push / callGemini共通部品(通知・AI・503リトライ)全回共通

このシリーズのこれまで:創刊号(入口)記事01:フォーム回答をメールで受け取る記事02:フォーム回答をLINEに即時通知記事03:フォーム回答を毎朝自動集計してLINEに通知記事04:名簿から差し込みメールを一斉送信記事05:長いGmailをAIで3行+やることリストに記事06:毎朝、未読メールを自動で要約して1通に記事07:たまった自由記述を、AIでテーマ別に分類・集計記事08:登録キーワードのニュースを、毎朝LINEに応用編その1:AI秘書「ぽけっと手帳」(全体像・この記事)応用編その2:リサーチ秘書(追いたい分野のニュースが毎朝1通)育てる編①:ためた見出しに"質問"できる(台帳Q&A)育てる編②:「医療も追って」で集める分野を増減(設定変更)育てる編③:「今週どうだった?」でためた台帳を週次まとめ育てる編④:「地図にして」でためた見出しを放射状の地図に(見出しマップ)育てる編⑤:「再エネ政策を追いたい」で検索語まで含む設定をAIが設計(設定アシスタント・完結)応用編その3:受発注・請求 秘書(受注が来たら、選ぶだけで見積書・請求書のPDFに)受発注 育てる編①:入金消込・督促・領収書PDF受発注 育てる編②:受注した瞬間に在庫を押さえる受発注 育てる編③:発注点アラートと発注書PDF受発注 育てる編④:月次集計・売上グラフ・会計CSV(最終回)基礎編:URLひとつで“自分の申込ページ”を作る(Webアプリ)基礎編:申し込まれた瞬間に、確認メールが自動で返る基礎編:申し込んだら、控えのPDFが自動で届く基礎編:申込の控えが、勝手にドライブへ積み上がっていく基礎編:申込のひとことを、届いた瞬間にAIが仕分ける基礎編:誰も画面を開いていないのに、毎朝まとめが届く基礎編:申込が来た瞬間、ポケットのLINEが鳴る(最終回)応用編その4:申込・予約 秘書(満席の瞬間、ページが自分から閉まる)申込・予約 育てる編①:キャンセル待ちと自動繰り上げ申込・予約 育てる編②:承認制とお断り下書き申込・予約 育てる編③:前日リマインダーと毎朝まとめ申込・予約 育てる編④:管理ページ(最終回)

#AI秘書 #ぽけっと手帳

コメント

タイトルとURLをコピーしました