📑

ざつタブ

に公開

Google の AI サービス(Gemini チャットアプリの Gem や NotebookLM など)でファイルを利用するとき、Google ドキュメント形式で追加するといくつかの利点があります。

  • 更新されたドキュメントファイルを同期できる

    • Gem は自動で同期される
    • NotebookLM は手動で同期させる
  • ドキュメント内の画像を考慮してくれる(例外はある)

また、Google ドキュメント形式で追加した場合は、ドキュメント内のタブ構造も考慮されるので「スクリプトでドキュメント内の特定箇所(タブ)だけを更新しやすい」というありがたい状況になります。

そこで、Google の AI サービス用にドキュメントファイルのタブへ「ざつに情報を詰め込むスクリプト」を試作してみました。

基本の形

今回試作したスクリプトは「タブにリンクを記述しておくと、リンクの内容をタブへ書き込む」「ただし書き込まれた内容は人間が編集することを考慮していない」といった感じになります。

利用する方法としてはいくつかありますが、Google ドキュメントのファイルにスクリプトを追加して、上記ファイルを貼り付けるのが楽かと思います。

リスト 1-1 スクリプト

https://github.com/hankei6km/test-gas-gdocs-za2tab/blob/main/code.js

貼り付けたら、サービスに Drive API v3 を追加しmyFunction をリストのようにします。

図 1-1 スクリプトエディターで Drive API v3 を追加

スクリプトエディターで「サービス」の下に「Drive」が追加されているスクリーンショット

リスト 1-2 myFunction の例

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    refreshTabsByLinks(doc, now)
  } finally {
    doc.saveAndClose()
  }
}

その後、以下のように取り込みたい対象(RSS フィードなど)をリスト形式で指定します。

図 1-2 リンクをリスト形式で指定

Google ドキュメントの編集画面で、リンクをリスト形式で記述しているスクリーンショット

myFunction を実行すると、リストの下に各 URL から受信したテキストが配置されます。

図 1-3 スクリプトで更新された状態

リンクの下に取得したフィードのソースコードが挿入されているスクリーンショット

試しにドキュメントファイルを Notebook LM へ追加してみます。「設定のリストは残ったまま」「フィードのソースをベタ貼り」の状態ですが、わりと普通に扱ってくれます。

図 1-4 NotebookLMへソースとして追加しチャット

NotebookLMでチャットしているスクリーンショット。質問に対してフィード内の記事が返答されている。

あとは、スケジュール実行などで myFunction を指定しておくと、定期的にタブの内容が更新されます。

なお、スクリプトの更新対象にならないタブはそのまま残ります。これを利用し、以下のようにカバーページなど作成しておくと、ドキュメントファイルの概要(作成された目的など)を AI サービスへ伝えやすくなるかと思います。

図 1-5 カバーページ用のタブに概要を記述

「カパーページ」タブの中にドキュメントの概要が記述されているスクリーンショット

この後は、リストに指定できる対象などについて説明していきます。

各種 URL

上記の RSS フィード以外に、ウェブページ(HTML)や JSON を指定した場合でも、NotebookLM ではわりと普通に扱ってくれます。

私は以下のようなドキュメントを作成し、NotebookLM の音声概要でニュース番組風ポッドキャストを作ることに利用しています。

図 2-1 各種情報を取得するドキュメント

「ニュース」「天気予報」「路線情報」を作成してあるドキュメントファイルのスクリーンショット

タブの構成は以下のようになっています。

調子よいときは天気情報を話しているときに、「暑さが続く中で、自動販売機の設定温度を 2 度下げるという情報もありますね」のようにニュース番組風になります(ならないことも多いです)。なお、路線情報は鉄道会社の情報ページをそのままだからか(サイズが大きくなりがち)、音声概要だと「概ね平常なようです」みたいにされてしまいます。

図 2-2 チャットで路線について確認(この状態でも音声概要は「平常」となる)

路線の状況を確認すると、遅延している路線を表示しているスクリーンショット

また、ソースとして各記事の概要しか渡していないと NotebookLM は話を盛ってきます。

音声概要でのニュース要約は以下のような利点もあり便利に使っていますが、だらだら聞きながら「なんか抹茶の価格が高騰してるらしいよ」みたいな話のネタを仕入れるくらいのノリがよいのかなと思っています。 (念の為に書いておくと、この状況は「ソースにニュース本文が含まれない」ことが原因だと思われるので、NotebookLM の要約が悪いというわけではないとは思います)

  • 日本語以外のニュースフィードを混在させても日本語で要約してくれる
  • 移動中などでも利用しやすい

Google ドキュメント

Google ドキュメントをブラウザーで開いているときの URL を指定することで、他の Google ドキュメントの内容を転記するようになっています。ただし、制限は結構あります。

  • 書式はほとんどコピーされません

  • 以下のいずれかのタブが対象となる

    • URL にタブが含まれる: 含まれているタブ
    • URL にタブが含まれない: アクティブタブ(通常は最初のタブ)

書式をできるだけ保持する方法としては、Markdown 形式でエクスポートしたものを貼り付ける指定もあります。こちらの場合は、Markdown で表現できる書式もソースとして取り込まれます。ただし、画像についてはデータ URL の参照リンクとして表現されるため「テキストが大きくなりがち」「AI サービスが画像として認識できない」状態となります。

以下は Markdown 形式で指定する URL の書式です。

リスト 3-1 ドキュメントの ID だけで指定する場合(アクティブタブが対象となります)

https://docs.google.com/feeds/download/documents/export/Export?exportFormat=markdown&id={DOCUMNET_ID}

リスト 3-2 タブを指定する場合

https://docs.google.com/feeds/download/documents/export/Export?exportFormat=markdown&id={DOCUMNET_ID}&tab={TAB_ID}

Google ドキュメントを転記する場合のスクリプトは以下のようになります。 (Google ドキュメントとして転記、Markdown 形式でエクスポートしたものを貼り付けのどちらにも対応します)

リスト 3-3 サンプルコード

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    refreshTabsByDocuments(doc, now)    
  } finally {
    doc.saveAndClose()
  }
}

図 3-1 通常の転記と Markdown での貼り付け

通常の転記で取り込まれたドキュメントとマークダウンで転記されたドキュメントが表示されているスクリーンショット、マークダウンの方はソースとして書式が表現されている

「Google ドキュメントとして転記」「Markdown 形式で貼り付け」のどちらを使うかは悩むところですが、画像無しでテキストが多い場合は Markdown 形式の方が処理は軽くなるようです。 (Markdown のテキストを 1 つのパラグラフとして挿入しているためだと思われる)

Google スプレッドシート

Google ドキュメントと同じように Google スプレッドシートの URL を指定した場合です。シートの各セルをテキストにしたものを転記するだけですが、Google フォームや AppSheet で更新されるシートなどには使えるかなと思います。

リスト 4-1 サンプルコード

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    refreshTabsBySpreadsheets(doc, now)
  } finally {
    doc.saveAndClose()
  }
}

図 4-1 テキストの表として転記される

Google ドキュメントのタブの中に表(テーブル)が表示されているスクリーンショット

Gemini チャットアプリでは、このようなことをしなくても Google スプレッドシートを直接追加できます。 (試してみたところ、2025/08 時点では Google AI プランを利用していなくても追加はできました)

しかし、残念なことに NotebookLM ではいまのところ(少なくとも無課金ユーザーは)対応していないようなので、Google ドキュメントへ転記するとある程度は対応できるようになります。また、転記するときに Google フォームと AppSheet 形式の画像をとりあえず取り込むようにしているので、一部制約はありますが画像を含むシートでも対応できます。

図 4-2 Google フォームから保存しているスプレッドシート

セルの中にドライブ上の画像ファイルへのリンクが挿入されているスプレッドシートのスクリーンショット

図 4-3 転記するとき画像も取り込まれる

Google ドキュメントへ転記された表では画像が埋め込まれているスクリーンショット

なお、Gemini チャットアプリの表内画像を認識しない点については、部分的に回避する方法も後述しています。

フォルダーのファイルをタブへマッピング(ドキュメント、スプレッドシート、画像)

「最新」タブにフォルダーのリンクを記述しておくと、フォルダー内のファイルの内容を後続の「n 個前」タブへ転記します。 (対象にするタブ名はスクリプトで指定できます)

図 5-1 サンプルコード

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    refreshTabsByFolderFiles(doc, now)
  } finally {
    doc.saveAndClose()
  }
}

図 5-2 マッピング用タブのサンプル

「最新」タブと「1 個前」から「4 個前」タブが表示されているスクリーンショット

いまのところ、以下のフォーマットに対応しています。

  • Google ドキュメント - アクティブタブ(通常は最初のタブ)をドキュメントとして転記
  • Google スプレッドシート - アクティブシート(通常は最初のシート)の表をテキストとして転記
  • 画像 - 画像の内容を転記
  • PDF - ファイルのサムネイル画像をコピー

リンクテキストの中にフィルターソートも指定できるようにしたので、特定の用途向けに作成した直近のメモを 1 ドキュメントへまとめるために利用しています。

リスト 5-1 リンクテキストの書式

 ラベル : フィルター : ソート

リスト 5-2 URL リンクとファイルマッピングを行うコード

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    refreshTabsByLinks(doc, now)
    refreshTabsByFolderFiles(doc, now)
  } finally {
    doc.saveAndClose()
  }
}

以下は天気予報とウォーキング(時々ジョギング)のメモをまとめた例です。このファイルを追加した Gem を作成しておき、ウォーキング前に「この後ウォーキングする予定です、注意点は?」みたいな確認に使っています。

図 5-3 ウォーキングメモ用を作成日順に転記

「最新」タブにフォルダー指定のリンクが記述され、その下に転記されたメモファイルが表示されているスクリーンショット

上記メモはフリーフォマットで「暑い」「腰に来た」とかをざつに入力しているだけですが、知識ある人が書式をきちんと作れば「ワークアウト用の有益な Gem を作れるのかな」という気はしています。

スプレッドシートの行をタブへマッピング

上記のファイルと同じような感じで、スプレッドシートの行をタブへマッピングします。マッピングする順序は最後尾の行を最新として、上の行を n 個前として扱います。フォームや AppSheet などで最新の数レコードだけを扱うような場合を想定しています。

図 6-1 サンプルコード

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    refreshTabsBySheetRows_(doc, now)
  } finally {
    doc.saveAndClose()
  }
}

図 6-2 行が 1 つのタブへ転記される(列は見出し 2 として分割される)

タブの中で行が表現されているスクリーンショット。Google フォームや AppSheet の画像が取り込まれている

図 6-3 1 つ上の行

「1 個前」タブでも行が表現されているスクリーンショット

この方法の場合、表を使わずに行を表現しているので、表内の画像を Gemini が認識しないという制限を回避できます。

ローテーション

「最新」タブ以降の内容を 1 つ後ろのタブに転記させる方法です。更新履歴を扱いたいときを想定しています。 (対象にするタブ名はスクリプトで指定できます)

図 7-1 サンプルコード

function myFunction() {
  const doc = DocumentApp.getActiveDocument()
  try {
    const now = new Date()
    rotateTabs(doc)
    refreshFirstTabByLinks(doc, now)
  } finally {
    doc.saveAndClose()
  }
}

図 7-2 「最新」タブで URL リンクの内容を取得しておく

「最新」タブにフィードのソースが保存されているスクリーンショット

図 7-3 スクリプトを実行すると「1 個前」へ転記される

「1  個前」タブに「最新」タブの内容が転記されているスクリーンショット

考慮点

先頭の方の記述が優先される(ように思える)

NotebookLM での音声概要や要約のときに個人的に感じているだけのことですが、先頭(1 行目)に近い記述が優先されるように思えます。たとえば、複数の RSS フィードを 1 つのタブにまとめたとき、音声概要を作成すると先頭に近いフィードについての説明が増えてきます(ように思える)。

カバーページなどを設けて「どのようにドキュメントを扱ってほしいか」を記述するとある程度回避できましたましたが、扱ってほしい優先度などから保存する位置を考慮した方がよいのかなとは思いました。

人間用に情報を保存しないデメリット

今回は更新されたタブの内容は「人間が編集(参照)することを考慮しない」方針にしています。これにより、従来のような「受信した HTML などをパースして必要な部分を抜き出して整形する」といった地味に手間のかかる処理が必要なくなります。

しかし、以下のような問題も出てきます。

  • AI サービスへ送信するテキスト量が増える傾向になる
  • AI サービスからの返答の正確性に影響する
  • AI サービスが参照元を提示してきたとき確認が難しい

HTML については、hast-util-select などを使ってセレクターで必要な部分を抜き出せるようにしてもよかったかなと少し後悔しています。

おわりに

Google ドキュメントのタブへ各種情報を動的に集約させ、Google の AI サービスから利用してみました。

軽いノリで作ったスクリプトなので作りはいつも以上にグダグダですが、ウェブや Google ドライブ上に分散している情報を 1 つにまとめやすくなったかなと思っています。

たとえば、上の方に挙げた「天気予報とウォーキングメモを組み合わせたドキュメントファイル」から Gem を簡単に作れるようになったのは結構重宝しています。

GitHubで編集を提案

Discussion