ざつタブ
Google の AI サービス(Gemini チャットアプリの Gem や NotebookLM など)でファイルを利用するとき、Google ドキュメント形式で追加するといくつかの利点があります。
-
更新されたドキュメントファイルを同期できる
- Gem は自動で同期される
- NotebookLM は手動で同期させる
-
ドキュメント内の画像を考慮してくれる(例外はある)
また、Google ドキュメント形式で追加した場合は、ドキュメント内のタブ構造も考慮されるので「スクリプトでドキュメント内の特定箇所(タブ)だけを更新しやすい」というありがたい状況になります。
そこで、Google の AI サービス用にドキュメントファイルのタブへ「ざつに情報を詰め込むスクリプト」を試作してみました。
基本の形
今回試作したスクリプトは「タブにリンクを記述しておくと、リンクの内容をタブへ書き込む」「ただし書き込まれた内容は人間が編集することを考慮していない」といった感じになります。
利用する方法としてはいくつかありますが、Google ドキュメントのファイルにスクリプトを追加して、上記ファイルを貼り付けるのが楽かと思います。
リスト 1-1 スクリプト
貼り付けたら、サービスに Drive API v3 を追加しmyFunction
をリストのようにします。
図 1-1 スクリプトエディターで Drive API v3 を追加
リスト 1-2 myFunction
の例
function myFunction() {
const doc = DocumentApp.getActiveDocument()
try {
const now = new Date()
refreshTabsByLinks(doc, now)
} finally {
doc.saveAndClose()
}
}
その後、以下のように取り込みたい対象(RSS フィードなど)をリスト形式で指定します。
図 1-2 リンクをリスト形式で指定
myFunction
を実行すると、リストの下に各 URL から受信したテキストが配置されます。
図 1-3 スクリプトで更新された状態
試しにドキュメントファイルを Notebook LM へ追加してみます。「設定のリストは残ったまま」「フィードのソースをベタ貼り」の状態ですが、わりと普通に扱ってくれます。
図 1-4 NotebookLMへソースとして追加しチャット
あとは、スケジュール実行などで myFunction
を指定しておくと、定期的にタブの内容が更新されます。
なお、スクリプトの更新対象にならないタブはそのまま残ります。これを利用し、以下のようにカバーページなど作成しておくと、ドキュメントファイルの概要(作成された目的など)を AI サービスへ伝えやすくなるかと思います。
図 1-5 カバーページ用のタブに概要を記述
この後は、リストに指定できる対象などについて説明していきます。
各種 URL
上記の RSS フィード以外に、ウェブページ(HTML)や JSON を指定した場合でも、NotebookLM ではわりと普通に扱ってくれます。
私は以下のようなドキュメントを作成し、NotebookLM の音声概要でニュース番組風ポッドキャストを作ることに利用しています。
図 2-1 各種情報を取得するドキュメント
タブの構成は以下のようになっています。
-
ニュース: 以前に作成した「Feed リーダーの直近の更新を Google ドキュメント化したもの」を取り込む(Google ドキュメント取り込みは後述します)
-
天気予報: 気象庁から以下の JSON ファイルを取り込む
-
路線情報: 鉄道会社の路線情報などのページを取り込む
調子よいときは天気情報を話しているときに、「暑さが続く中で、自動販売機の設定温度を 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 テキストの表として転記される
Gemini チャットアプリでは、このようなことをしなくても Google スプレッドシートを直接追加できます。 (試してみたところ、2025/08 時点では Google AI プランを利用していなくても追加はできました)
しかし、残念なことに NotebookLM ではいまのところ(少なくとも無課金ユーザーは)対応していないようなので、Google ドキュメントへ転記するとある程度は対応できるようになります。また、転記するときに Google フォームと AppSheet 形式の画像をとりあえず取り込むようにしているので、一部制約はありますが画像を含むシートでも対応できます。
図 4-2 Google フォームから保存しているスプレッドシート
図 4-3 転記するとき画像も取り込まれる
なお、Gemini チャットアプリの表内画像を認識しない点については、部分的に回避する方法も後述しています。
フォルダーのファイルをタブへマッピング(ドキュメント、スプレッドシート、画像)
「最新」タブにフォルダーのリンクを記述しておくと、フォルダー内のファイルの内容を後続の「n 個前」タブへ転記します。 (対象にするタブ名はスクリプトで指定できます)
図 5-1 サンプルコード
function myFunction() {
const doc = DocumentApp.getActiveDocument()
try {
const now = new Date()
refreshTabsByFolderFiles(doc, now)
} finally {
doc.saveAndClose()
}
}
図 5-2 マッピング用タブのサンプル
いまのところ、以下のフォーマットに対応しています。
- 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 として分割される)
図 6-3 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 個前」へ転記される
考慮点
先頭の方の記述が優先される(ように思える)
NotebookLM での音声概要や要約のときに個人的に感じているだけのことですが、先頭(1 行目)に近い記述が優先されるように思えます。たとえば、複数の RSS フィードを 1 つのタブにまとめたとき、音声概要を作成すると先頭に近いフィードについての説明が増えてきます(ように思える)。
カバーページなどを設けて「どのようにドキュメントを扱ってほしいか」を記述するとある程度回避できましたましたが、扱ってほしい優先度などから保存する位置を考慮した方がよいのかなとは思いました。
人間用に情報を保存しないデメリット
今回は更新されたタブの内容は「人間が編集(参照)することを考慮しない」方針にしています。これにより、従来のような「受信した HTML などをパースして必要な部分を抜き出して整形する」といった地味に手間のかかる処理が必要なくなります。
しかし、以下のような問題も出てきます。
- AI サービスへ送信するテキスト量が増える傾向になる
- AI サービスからの返答の正確性に影響する
- AI サービスが参照元を提示してきたとき確認が難しい
HTML については、hast-util-select
などを使ってセレクターで必要な部分を抜き出せるようにしてもよかったかなと少し後悔しています。
おわりに
Google ドキュメントのタブへ各種情報を動的に集約させ、Google の AI サービスから利用してみました。
軽いノリで作ったスクリプトなので作りはいつも以上にグダグダですが、ウェブや Google ドライブ上に分散している情報を 1 つにまとめやすくなったかなと思っています。
たとえば、上の方に挙げた「天気予報とウォーキングメモを組み合わせたドキュメントファイル」から Gem を簡単に作れるようになったのは結構重宝しています。
Discussion