1今回のゴール ── 資料づくりを5段階で自動化する
「毎月おなじ形式の報告資料を作っている」── これは自動化の効果がいちばん出る仕事です。 形式が決まっているものは、機械に組み立てさせられるからです。 しかし資料づくりには、集計作業には無かった悩みがあります。「見た目をどうするか」です。
文字の大きさ、色、余白、どこに何を置くか。これを毎回考えていては自動化になりません。 かといって全部を生成AIに丸投げすると、実行するたびに違うデザインのものが出てきます。 今回学ぶ5つのやり方は、突き詰めると「デザインを誰が決めるか」と「中身を誰が書くか」の組み合わせの違いです。
| レベル | やり方 | デザインを決めるのは | 中身を書くのは | 向いている場面 |
|---|---|---|---|---|
| 1 | GASを自分で書く | 自分(コードの定数) | 自分(コードに直書き) | 仕組みを理解する・型を作る |
| 2 | シートのデータから生成 | 自分(コードの定数) | スプレッドシート | 毎月の定型報告 |
| 3 | AIにGASを設計させる | AIと相談して決める | AIと相談して決める | ゼロから作る・一度きりの資料 |
| 4 | まじん式(テンプレート方式) | 固定(触らせない) | AI | 中身が毎回ちがう資料を量産する |
| 5 | ChatGPT × PowerPoint | ルールを渡して守らせる | AI | 提出先がOffice環境 |
今回は「どれか1つを覚える回」ではありません。この5つは上位互換の関係ではなく、使い分けるものです。毎月同じ形式で出す報告書にレベル4を使うのは遠回りですし、一度きりの提案資料にレベル2の仕組みを作るのは無駄です。手元の仕事がどれに当たるかを判断できることが、今回いちばん持ち帰ってほしいものです。
2Googleスライドの構造と SlidesApp
第4回でスプレッドシートを操作したときは、SpreadsheetApp という窓口を使いました。
スライドの窓口は SlidesApp です。考え方は同じで、大きい入れ物から順に降りていくだけです。
| 階層 | スプレッドシート(第4回) | スライド(今回) | 何にあたるか |
|---|---|---|---|
| いちばん外側 | Spreadsheet | Presentation | ファイルそのもの |
| 中の1単位 | Sheet(シート) | Slide(1枚のページ) | タブ/ページ |
| いちばん細かい単位 | Range(セル) | PageElement(要素) | 置いてある物ひとつ |
違いは、スライドには「セル」に相当する決まった枠が無いことです。
スプレッドシートは A1 と言えば場所が決まりますが、スライドは白紙の上に自分で座標を指定して物を置いていく世界です。
置ける物は、おおまかに次の4種類です。
テキストボックス
insertTextBox()。文字を置く。見出しも本文も、ほぼこれ
図形
insertShape()。四角・丸・矢印。罫線や色帯もこれで作る
表
insertTable()。行数・列数を決めてから、セルに書き込む
画像
insertImage()。公開されているURLか、Drive上のファイルを指定する
座標の考え方 ── 左上が原点、単位はポイント
物を置くときは 「左からの距離・上からの距離・幅・高さ」 の4つを指定します。 原点はスライドの左上で、単位はポイント(pt)です。 標準の16:9スライドは 横720 × 縦405ポイント。この数字は今回ずっと出てくるので覚えておいてください。
// スライドを1枚作り、文字と図形を1つずつ置くだけの例
function helloSlide() {
const pres = SlidesApp.create('はじめてのスライド'); // 新しいスライドファイルを作る
const slide = pres.getSlides()[0]; // 最初の1枚を取り出す
// 左から40pt・上から150ptの位置に、幅600pt・高さ60ptのテキストボックスを置く
const box = slide.insertTextBox('はじめてのスライド', 40, 150, 600, 60);
box.getText().getTextStyle()
.setFontSize(32)
.setBold(true)
.setForegroundColor('#0d1b4c');
// その下に、幅120pt・高さ4ptの青い帯を置く(見出しの下線のかわり)
const bar = slide.insertShape(SlidesApp.ShapeType.RECTANGLE, 40, 220, 120, 4);
bar.getFill().setSolidFill('#2563eb');
bar.getBorder().setTransparent();
Logger.log(pres.getUrl()); // 完成したスライドのURLをログに出す
}
座標は「決め打ち」でよい、というのが今回の肝です。デザインが崩れる原因のほとんどは、毎回ちがう場所に物を置いてしまうことです。逆に言えば、置く場所さえ固定してしまえば、中身が変わってもデザインは崩れません。この後のコードは、すべて「座標を先に決めておく」という方針で書かれています。
まずはGASで、自分の手で作ってみる
最初に、コピペするだけで動くGASを1本お渡しします。これを動かしたうえで、 デザインの規定を自分の会社仕様に書き換える、スプレッドシートのデータから一括生成する、 AIにGASごと設計させる、と順に広げていきます。
3【レベル1】コピペで動かす ── デザイン規定つきGAS
まずは動かします。手順は4つだけです。まっさらなスプレッドシートから始めてください。
スプレッドシートを新規作成
名前は「スライド自動生成」など。中身は空のままでOK
拡張機能 → Apps Script
スクリプトエディタが開く。コード.gs の中身は全部消す
下のコードを全文貼り付け
コピーボタンを押して、そのまま貼り付けて保存(Ctrl/⌘ + S)
createDeck を実行
関数の選択欄で createDeck を選び、[実行]を押す
初回だけ「承認」を求められます。「このアプリはGoogleで確認されていません」と出たら、[詳細]→[(プロジェクト名)に移動(安全ではないページ)]と進んでください。自分で書いたコードを、自分のアカウントで動かすための確認です。第4回・第5回と同じ流れなので、迷ったらそのときの手順を思い出してください。
/**
* 第6回 レベル1 ── デザイン規定つきスライド自動生成
*
* 使い方:
* 1. 新しいスプレッドシートを1つ作る(名前は「スライド自動生成」など)
* 2. [拡張機能]→[Apps Script]を開く
* 3. コード.gs の中身を消して、この全文を貼り付けて保存
* 4. 関数の選択欄で createDeck を選び、[実行]を押す
* 5. 実行ログに出てきたURLを開くと、スライドが完成している
*/
// ===== 1. デザイン規定(ここを変えると、全スライドの見た目が一度に変わる) =====
const DESIGN = {
font: 'Arial', // 日本語を明朝やゴシックにしたいときは 'Noto Sans JP' などに変更
colorMain: '#0d1b4c', // メインカラー(濃紺)
colorAccent: '#2563eb', // アクセントカラー(青)
colorText: '#1a1a2e', // 本文の文字色
colorMuted: '#6b7280', // 補足の文字色
colorLine: '#c8ccd4', // 罫線
colorBg: '#ffffff', // スライドの背景
footerText: '株式会社 日本能率協会総合研究所'
};
// ===== 2. レイアウト(単位はポイント。16:9のスライドは 720 × 405) =====
const LAYOUT = {
pageW: 720,
pageH: 405,
marginX: 42,
titleTop: 150,
dateTop: 232,
headTop: 34,
ruleTop: 74,
subheadTop: 84,
bodyTop: 118,
lineHeight: 34,
footerTop: 378
};
// ページ番号を数えるための変数
let pageCounter = 0;
// ===== 3. 実行する関数(ここに「どんなスライドを何枚作るか」を書く) =====
function createDeck() {
const pres = SlidesApp.create('自動生成スライド_' + todayString_());
removeFirstBlankSlide_(pres);
pageCounter = 0;
addTitleSlide_(pres, '調査レポート作成の自動化', todayString_());
addSectionSlide_(pres, 1, '取り組みの背景');
addContentSlide_(pres, '現在の課題', '毎月40時間が転記作業に消えている', [
'集計結果をスライドへ転記する作業に、担当者1名で月40時間かかっている',
'転記ミスが見つかるのが、発表の直前になりやすい',
'担当者ごとに書式が違うため、レビューに時間がかかる'
]);
addSectionSlide_(pres, 2, '自動化してわかったこと');
addContentSlide_(pres, '導入の効果', '書式が決まっている資料は、機械に任せられる', [
'転記作業は月40時間から3時間に短縮した',
'書式が統一され、レビューでの差し戻しがほぼ無くなった',
'人が考えるのは「中身」だけになった'
]);
addClosingSlide_(pres, 'ご清聴ありがとうございました');
Logger.log('スライドを作成しました:' + pres.getUrl());
}
// ===== 4. スライドの種類ごとの組み立て =====
/** 表紙 */
function addTitleSlide_(pres, title, dateText) {
const slide = newSlide_(pres, DESIGN.colorBg);
putBar_(slide, LAYOUT.marginX, LAYOUT.titleTop - 24, 60, 4, DESIGN.colorAccent);
putText_(slide, title, LAYOUT.marginX, LAYOUT.titleTop, 620, 60,
{ size: 30, bold: true, color: DESIGN.colorMain });
putText_(slide, dateText + ' / ' + DESIGN.footerText, LAYOUT.marginX, LAYOUT.dateTop, 560, 24,
{ size: 12, color: DESIGN.colorMuted });
return slide;
}
/** 章扉(背景を濃紺にして、大きな章番号を置く) */
function addSectionSlide_(pres, no, title) {
const slide = newSlide_(pres, DESIGN.colorMain);
putText_(slide, ('0' + no).slice(-2), LAYOUT.marginX, 118, 220, 100,
{ size: 64, bold: true, color: '#3d4d7d' });
putText_(slide, title, LAYOUT.marginX, 214, 600, 50,
{ size: 28, bold: true, color: '#ffffff' });
return slide;
}
/** 中面(見出し+小見出し+箇条書き) */
function addContentSlide_(pres, title, subhead, points) {
const slide = newSlide_(pres, DESIGN.colorBg);
putText_(slide, title, LAYOUT.marginX, LAYOUT.headTop, 620, 32,
{ size: 22, bold: true, color: DESIGN.colorMain });
putBar_(slide, LAYOUT.marginX, LAYOUT.ruleTop, 130, 3, DESIGN.colorAccent);
if (subhead) {
putText_(slide, subhead, LAYOUT.marginX, LAYOUT.subheadTop, 620, 22,
{ size: 12, color: DESIGN.colorMuted });
}
for (let i = 0; i < points.length; i++) {
const top = LAYOUT.bodyTop + i * LAYOUT.lineHeight;
putBar_(slide, LAYOUT.marginX, top + 9, 6, 6, DESIGN.colorAccent);
putText_(slide, points[i], LAYOUT.marginX + 18, top, 600, 28,
{ size: 13, color: DESIGN.colorText });
}
putFooter_(slide);
return slide;
}
/** 結び */
function addClosingSlide_(pres, message) {
const slide = newSlide_(pres, DESIGN.colorMain);
putText_(slide, message, 0, 180, LAYOUT.pageW, 40,
{ size: 24, bold: true, color: '#ffffff', align: SlidesApp.ParagraphAlignment.CENTER });
return slide;
}
// ===== 5. 部品(ここから下は、慣れるまで触らなくてよい) =====
/** まっさらなスライドを1枚足して、背景色を塗る */
function newSlide_(pres, bgColor) {
const slide = pres.appendSlide(SlidesApp.PredefinedLayout.BLANK);
slide.getBackground().setSolidFill(bgColor);
return slide;
}
/** テキストボックスを1つ置く */
function putText_(slide, text, left, top, width, height, options) {
const opt = options || {};
const box = slide.insertTextBox(String(text), left, top, width, height);
const style = box.getText().getTextStyle();
style.setFontFamily(DESIGN.font);
style.setFontSize(opt.size || 13);
style.setBold(opt.bold === true);
style.setForegroundColor(opt.color || DESIGN.colorText);
if (opt.align) {
box.getText().getParagraphStyle().setParagraphAlignment(opt.align);
}
return box;
}
/** 塗りつぶした四角を1つ置く(罫線・アクセントバー・箇条書きの点に使う) */
function putBar_(slide, left, top, width, height, color) {
const bar = slide.insertShape(SlidesApp.ShapeType.RECTANGLE, left, top, width, height);
bar.getFill().setSolidFill(color);
bar.getBorder().setTransparent();
return bar;
}
/** 全ページ共通のフッター(会社名とページ番号) */
function putFooter_(slide) {
pageCounter++;
putBar_(slide, 0, LAYOUT.footerTop - 10, LAYOUT.pageW, 1, DESIGN.colorLine);
putText_(slide, DESIGN.footerText, LAYOUT.marginX, LAYOUT.footerTop, 400, 16,
{ size: 8, color: DESIGN.colorMuted });
putText_(slide, String(pageCounter), LAYOUT.pageW - LAYOUT.marginX - 40, LAYOUT.footerTop, 40, 16,
{ size: 8, color: DESIGN.colorMuted, align: SlidesApp.ParagraphAlignment.END });
}
/** SlidesApp.create で最初にできる空のスライドを消す */
function removeFirstBlankSlide_(pres) {
const slides = pres.getSlides();
for (let i = slides.length - 1; i >= 0; i--) {
slides[i].remove();
}
}
/** 今日の日付を 2026.09.01 の形にする */
function todayString_() {
return Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy.MM.dd');
}
実行すると、下部の実行ログに スライドを作成しました:https://docs.google.com/presentation/d/... というURLが出ます。
クリックすると、表紙・章扉・本文・結びの6枚が出来上がっているはずです。
このコードは何をしているのか
長く見えますが、やっていることは3層に分かれているだけです。この構造が今回いちばん大事なところです。
| ブロック | 役割 | 変更するか |
|---|---|---|
1. デザイン規定(DESIGN)2. レイアウト( LAYOUT) | 色・フォント・余白・座標を1か所にまとめた設定表 | ここだけ変える。会社の色にするならここ |
3. 実行する関数(createDeck) | 「どんなスライドを、何枚、どの順で作るか」を書く台本 | 資料の中身に応じて書き換える |
| 4. スライドの種類ごとの組み立て 5. 部品 | 表紙・章扉・本文をどう描くかの実装 | 基本は触らない |
注目してほしいのは、createDeck の中身です。
addContentSlide_(pres, '現在の課題', '毎月40時間が…', ['…', '…']) のように、
「タイトル・小見出し・箇条書き」を渡すだけで1枚できています。
フォントの指定も、余白の計算も、フッターの配置も、呼び出す側には出てきません。
これが「デザインを固定した」状態です。
末尾のアンダースコアには意味があります。addTitleSlide_ のように名前の最後に _ を付けた関数は、スクリプトエディタの実行メニューに出てこなくなります。「これは部品なので、直接実行するものではない」という印です。実行してよい関数(createDeck)だけがメニューに並ぶので、押し間違いが減ります。
4デザイン規定を自分の会社仕様に変える
いま出来上がったスライドは、濃紺と青の配色になっています。これを自分の部署の資料の色に変えてみましょう。
変えるのは DESIGN の中だけで、他の行は1文字も触りません。
// ===== 1. デザイン規定(ここだけ書き換える。他の行は触らない) =====
const DESIGN = {
font: 'Noto Sans JP', // ← 日本語ゴシックに変更
colorMain: '#1f3864', // ← メインカラーを自社の紺に
colorAccent: '#c00000', // ← アクセントを自社の赤に
colorText: '#333333',
colorMuted: '#767171',
colorLine: '#d9d9d9',
colorBg: '#ffffff',
footerText: '調査事業本部 第1調査部' // ← 部署名に変更
};
保存して createDeck をもう一度実行すると、全ページの配色が一度に入れ替わります。
この「1か所を変えれば全部変わる」構造が、資料を量産するときに効いてきます。
色は3系統までに抑えてください。メインカラー(濃い色)・アクセントカラー(1色だけ効かせる色)・グレー系(補足の文字と罫線)の3つです。色を増やすほど、資料は素人っぽく見えます。「強調したい箇所が3つあるから3色使う」ではなく、アクセント1色を、いちばん見てほしい1か所にだけ使うのがコツです。
| 変えたいこと | 触る場所 | 補足 |
|---|---|---|
| 配色 | DESIGN.colorMain / colorAccent | カラーコードは # から始まる6桁で書く |
| フォント | DESIGN.font | 'Noto Sans JP' や 'M PLUS 1p' など、Googleスライドのフォント一覧にある名前を正確に |
| フッターの会社名 | DESIGN.footerText | 部署名を入れてもよい |
| 本文の位置・行間 | LAYOUT.bodyTop / lineHeight | 行が下にはみ出すときは lineHeight を小さくする |
| 左右の余白 | LAYOUT.marginX | 大きくすると余白が広がり、落ち着いた印象になる |
5【レベル2】スプレッドシートのデータから一括生成
ここからが実務の本番です。レベル1では、スライドの中身をコードに直接書いていました。 これでは、来月また同じ作業をするときにコードを開いて文章を書き換えることになります。それでは自動化とは言えません。
そこで、中身をスプレッドシートに出します。表を更新してスクリプトを実行すれば、 行の数だけスライドが自動で増える。これがレベル2です。
手順1:シートを用意する
レベル1で使ったスプレッドシートに、「報告データ」という名前のシートを追加してください (下のタブを右クリック →[名前を変更])。 データは手で入力しなくて構いません。下の表の右上[TSVでコピー]を押して、 シートの A1 セルを選んでそのまま貼り付けると、見出し行を含めて一度に入ります。 1行目の見出しは必ずこの順番にしてください。コードが列の位置で読んでいます。
| A列:部署 | B列:対象月 | C列:主な実績 | D列:課題 | E列:次月の打ち手 | F列:達成率 |
|---|---|---|---|---|---|
| 調査1部 | 2026年8月 | 受託件数18件(前月比+3) | 報告書の作成が納期ぎりぎり | 集計テンプレートの共通化 | 112 |
| 調査2部 | 2026年8月 | 受託件数12件(前月比-1) | 自由記述の分類に工数がかかる | 分類作業の一部を生成AIへ | 88 |
| 企画部 | 2026年8月 | 新規提案7件・受注2件 | 提案書の作成に時間がかかる | 提案書の型を3種類に絞る | 95 |
| 営業推進部 | 2026年8月 | 商談化24件(前月比+5) | 商談後のフォローが属人化 | フォローの自動リマインド | 104 |
貼り付けたあとに1つだけ確認してください。F列の達成率が数値として入っているか(セルの右側に寄っていれば数値)。文字列として入ってしまった場合は、その列を選んで[表示形式]→[数値]に直してください。この列だけは、コードが計算に使っています。
手順2:関数を2つ追加する
スクリプトエディタを開き、レベル1のコードの一番下に、次の2つの関数を貼り足してください。
上のコードは消しません。DESIGN も addContentSlide_ も、そのまま部品として使い回します。
/**
* 第6回 レベル2 ── スプレッドシートのデータからスライドを一括生成
*
* レベル1のコードの「一番下」に、この2つの関数を貼り足してください。
* DESIGN / LAYOUT / addTitleSlide_ などの部品は、レベル1のものをそのまま使います。
*
* 実行前に、同じスプレッドシートに「報告データ」という名前のシートを作り、
* 1行目に見出し、2行目から部署ごとのデータを入れておきます。
*/
/** シートを読んで、部署の数だけスライドを作る */
function createReportFromSheet() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('報告データ');
if (!sheet) {
throw new Error('「報告データ」という名前のシートが見つかりません。シート名を確認してください。');
}
const values = sheet.getDataRange().getValues();
const rows = values.slice(1); // 1行目は見出しなので使わない
const pres = SlidesApp.create('月次報告_' + todayString_());
removeFirstBlankSlide_(pres);
pageCounter = 0;
addTitleSlide_(pres, '月次報告', todayString_());
addSectionSlide_(pres, 1, '部署別の報告');
rows.forEach(function (row) {
const dept = row[0];
if (!dept) return; // 空行は飛ばす
addContentSlide_(pres, dept + ' ' + row[1], '達成率 ' + row[5] + '%', [
'実績:' + row[2],
'課題:' + row[3],
'打ち手:' + row[4]
]);
});
addSectionSlide_(pres, 2, '全体サマリ');
addSummarySlide_(pres, rows);
addClosingSlide_(pres, 'ご清聴ありがとうございました');
Logger.log('スライドを作成しました:' + pres.getUrl());
}
/** 達成率を横棒グラフのように並べた1枚を作る */
function addSummarySlide_(pres, rows) {
const slide = newSlide_(pres, DESIGN.colorBg);
putText_(slide, '達成率の一覧', LAYOUT.marginX, LAYOUT.headTop, 620, 32,
{ size: 22, bold: true, color: DESIGN.colorMain });
putBar_(slide, LAYOUT.marginX, LAYOUT.ruleTop, 130, 3, DESIGN.colorAccent);
putText_(slide, '棒の長さは達成率。100%を満たすと右端まで伸びる', LAYOUT.marginX, LAYOUT.subheadTop, 620, 22,
{ size: 12, color: DESIGN.colorMuted });
const barLeft = LAYOUT.marginX + 120;
const barMaxW = 420;
let top = LAYOUT.bodyTop;
rows.forEach(function (row) {
if (!row[0]) return;
const rate = Number(row[5]) || 0;
const filled = Math.max(2, barMaxW * Math.min(rate, 100) / 100); // 幅0の図形は作れないので最低2
putText_(slide, row[0], LAYOUT.marginX, top, 110, 22,
{ size: 12, color: DESIGN.colorText });
putBar_(slide, barLeft, top + 6, barMaxW, 10, '#eef1f6'); // 目盛りの下地
putBar_(slide, barLeft, top + 6, filled, 10, DESIGN.colorAccent); // 実績ぶん
putText_(slide, rate + '%', barLeft + barMaxW + 12, top, 60, 22,
{ size: 12, color: DESIGN.colorMuted });
top += 30;
});
putFooter_(slide);
return slide;
}
保存して createReportFromSheet を実行すると、
表紙 → 章扉 → 部署ごとに1枚(4枚)→ 章扉 → 達成率の一覧 → 結び の全8枚が出来上がります。
シートに5行目を足してもう一度実行すれば、9枚になります。コードは1文字も変えずに、です。
ここが「自動化した」と言える状態です。来月やることは、シートの数字を新しくして、実行ボタンを1回押すだけ。転記も、書式合わせも、ページ番号の振り直しも発生しません。第4回で作った集計スクリプトの出力先をこのシートにすれば、集計から資料作成までが1本につながります。
グラフのように見せている部分
addSummarySlide_ では、達成率を横棒で表しています。
やっていることは単純で、灰色の四角を目盛りとして置き、その上に達成率のぶんだけ青い四角を重ねているだけです。
barMaxW * Math.min(rate, 100) / 100 が、その「ぶん」を計算している式です。
幅や高さに 0 を指定するとエラーになります。達成率が 0% の行があると、青い四角の幅が 0 になって GAS が止まります。コード中の Math.max(2, ...) はそれを防ぐための保険です。データ側に想定外の値が来ても止まらないようにする ── これは自動化を長く運用するうえで欠かせない考え方です。
6【レベル3】AIに「最適なGAS」を設計させる
ここまでは、私たちが用意した型の上で作ってきました。 では、まったく新しい種類の資料を作りたいときはどうするか。ここで生成AIの出番です。
ただし、「スライドを作るGASを書いて」と頼むだけでは、まず失敗します。 スライドのGASは、スプレッドシートのGASと比べて格段に難しいからです。 理由は3つあります。
- 座標をすべて自分で決める必要がある ── セルのような「決まった置き場所」が無い
- 正解が1つに決まらない ── 「見やすい」は数値で書かれていないので、AIが勝手に判断してしまう
- メソッド名が独特で長い ──
getText().getTextStyle().setForegroundColor()のような連結が多く、AIが途中で間違えやすい
そこで、いきなりコードを書かせないプロンプトを使います。 先に目的と聞き手を聞かせ、構成案とデザイン方針を出させ、こちらが承認してからコードを書かせる。 このやり取りを最初から指示に埋め込んでおくのがコツです。
このプロンプトの使い方
そのまま送る
ChatGPTに貼り付けて送信。AIから5つの質問が返ってくる
質問に答える
目的・聞き手・時間・結論・色を答える。ここが資料の質を決める
構成案を直す
出てきた構成表に注文を付ける。「3枚目と4枚目を入れ替えて」など
承認してコードを受け取る
「これで進めて」と伝えると、GAS全文が出てくる
一発で完璧なものを狙わないでください。スライドのGASは、初回はどこかが必ずずれます(文字がはみ出す、図形が重なる、色が想定と違う)。そこで諦めずに、「実際に実行したらこうなった」を具体的に伝えて直させるのが正しい進め方です。第3回で学んだ「段階的に改善させる」やり方が、そのまま効きます。
スクリーンショットを貼るのがいちばん速いです。出来上がったスライドの画面を撮って、そのまま ChatGPT に貼り付け、「この赤枠のところが重なっている。直して」と伝えてください。文章で「3枚目の左下の図形が」と説明するより、はるかに正確に伝わります。ChatGPTは画像を読めます。
デザインを固定して、中身だけを生成AIに書かせる
ここからが今回の本命です。「AIにコードを書かせない」という、一見すると後ろ向きに見える発想が、 実はいちばん安定して、いちばん品質が揃うという話をします。 世の中で「まじん式」と呼ばれている手法と、その考え方をそのまま使った教材オリジナルの小さいテンプレートの両方を扱います。
7【レベル4】まじん式 ── コード固定 × データ可変
最近は、文章を渡すだけでスライドを作ってくれるサービスがいくつもあります。 しかし業務で使おうとすると、「そのサービスを社内で使ってよいか」という壁にぶつかります。 情報システム部門の承認が下りない、社外サービスに社内資料を渡せない ── よくある話です。
この壁を、すでに会社が使っているツール(GoogleとChatGPT/Gemini)だけで越えるために考えられたのが、 「まじん式」と呼ばれる手法です。2025年8月に公開され、広く使われるようになりました。
出典:まじん氏「【神回】Googleスライドが一瞬で完成する“奇跡”のプロンプト教えます」(note、2025年8月公開)。プロンプト全文とGASテンプレートは無料で公開されています。本ページでも、受講者がすぐ使えるよう第8章にマスタープロンプトの全文を引用して掲載しています(コピーボタンで全文を取得できます)。まじん式は改良が続いているため、最新版は必ず上記の記事でご確認ください。また、この後で扱う教材オリジナルの小さいテンプレートは、同じ設計思想をこの研修用に書き起こしたものです。
なぜ「AIにGASを書かせる」と失敗するのか
第6章でも触れましたが、これは実験でもはっきり出ています。 「この文字起こしをもとにGoogleスライドを作るGASを書いて」 という短い依頼を主要なAIに投げると、 多くの場合構文エラーで動かないか、動いても見るに堪えないデザインになります。
原因は、依頼の規模が大きすぎることです。この頼み方は、たとえるなら 「自動車工場をゼロから設計・建設して、ついでに新型車も1台つくってくれ」と言っているのと同じです。 設計図も部品の規格も無い状態では、まともなものが安定して出てくるはずがありません。
逆転の発想 ── 工場は人間が建てる
まじん式のやり方は、この順番をひっくり返します。
この作りが生む3つのメリット
エラーが激減する
AIが触るのはデータだけ。コードの構文を壊しようがない
AIが構成に集中できる
コード生成という雑務から解放され「どう見せれば伝わるか」に力を使える
品質が毎回そろう
デザインが固定なので、誰がいつ作っても同じ体裁になる
この考え方は、スライド以外にも効きます。「毎回ちがう結果になって困る」という自動化は、たいていAIに任せる範囲が広すぎることが原因です。変わらない部分をプログラムで固定し、変わる部分だけをAIに書かせる。この切り分けは、報告書・メール文面・議事録・分析レポートなど、あらゆる場面で使えます。第14回の統合演習でも、この発想が中心になります。
8まじん式を実際に動かす
原典のまじん式を使う手順です。ChatGPT でも Gemini でも動きますが、 推論能力の高いモデルを選んでください(考える時間が長いモードを使う、と考えれば十分です)。
プロンプトをコピーする
下のマスタープロンプトの[コピー]を押して、全文をクリップボードへ
AIに設定する
ChatGPTのプロジェクト(またはGeminiのGem)に貼り、常設の指示にする
素材を渡す
議事録・レポート・企画書などを、そのまま貼り付けるか添付する
出力を全文コピー
AIが .gs ファイルの中身をまるごと出してくる。それを全部コピーする
スライドに貼る
空のGoogleスライドで[拡張機能]→[Apps Script]を開き、貼り付けて保存
実行する
generatePresentation を実行。承認して、スライドに戻る
マスタープロンプト全文
約4万字あります。画面では枠内をスクロールして確認でき、[コピー]を押せば全文がクリップボードに入ります。 中身は①AIへの指示(1.0〜6.0)と②GASテンプレートの設計図(7.0)の2部構成です。 AIはこれを読んで、7.0のコードはそのままに、slideData の中身だけを差し替えたものを出力してきます。
掲載にあたっての注記:上記はまじん氏のnote記事で無料公開されているプロンプトを、受講者がそのまま使えるように引用したものです。原文に対して行った変更は、記事の表示上まぎれこんでいた記号(Markdownのエスケープ記号とコードフェンス)の除去だけで、指示内容とコードには手を加えていません。研修外での再配布はお控えください。また、まじん式は改良が続いているため、最新版はnote記事でご確認ください。
入り口が今までと違います。レベル1・2はスプレッドシートから Apps Script を開きましたが、まじん式は空のGoogleスライドから開きます。作ったスライドの上に直接描いていく作りだからです。大事なスライドの上で実行しないでください。初期設定では既存のページをすべて消してから描き直します。
つまずきやすい点
| 症状 | 原因 | 対処 |
|---|---|---|
| 出力が途中で止まる | コードが長く、1回の返信に収まらない | 「続きを出力して」と送る。それでも切れる場合は新しいチャットでやり直すのが早い |
| 貼り付けたら赤い波線だらけ | コピーの範囲が足りない/途中が欠けた | 先頭の /** から最後の } まで、全部入っているか確認する |
| 実行しても何も起きない | 実行する関数の選択が違う | 関数の選択欄が generatePresentation になっているか確認する |
| ロゴが表示されない | 画像URLが公開されていない | Web上で誰でも開けるURLが必要。社内サーバーやDriveの限定公開URLは使えない |
| 文字がはみ出す | AIが長い文章を入れた | 該当箇所の文章を短くする。または「各項目を40文字以内にして」と指示し直す |
自社仕様に変える
まじん式のテンプレートは初期状態がGoogle風のデザイン(青系、Googleロゴ、ダミーのフッター)になっています。 これを自社仕様にするには、AIに頼むのがいちばん簡単です。
変えてよいのは「設定」だけです。テンプレートの中で自由に変えてよいのは、色・フォント・ロゴ・フッター文言といった設定値の部分です。描画の処理そのもの(関数の中身)に手を入れると、たいてい壊れます。「もっと大きく変えたい」と思ったときは、次の章で扱う短いテンプレートのほうを改造してください。中身が読める分量なので、安全に手を入れられます。
9自作テンプレートで体験する ── ミニ版の全文
原典のまじん式は700行を超える大作です。実務ではそのまま使えばよいのですが、 中身を読んで仕組みを理解するには大きすぎます。 そこで、まったく同じ設計思想で、この研修用に短く書き直したテンプレートを用意しました。
扱える型は8種類(表紙・章扉・箇条書き・比較・手順・カード・表・結び)。 スピーカーノートの自動設定にも対応しています。 こちらは全部読める分量なので、自分で型を追加するところまで踏み込めます。
貼り付ける場所に注意してください。このコードは空のGoogleスライドの Apps Script に貼ります(レベル1のスプレッドシートとは別のプロジェクトです)。同じプロジェクトに両方貼ると、関数名が重なってエラーになります。
/**
* 第6回 ── テンプレート方式(コード固定 × データ可変)
*
* 使い方:
* 1. 空のGoogleスライドを新規作成する
* 2. [拡張機能]→[Apps Script]を開く
* 3. コード.gs の中身を消して、この全文を貼り付けて保存
* 4. generatePresentation を実行する
*
* 以降、変えるのは「2. スライドデータ」の const slideData だけです。
* ここを生成AIに書かせれば、中身の違うスライドが何度でも作れます。
*
* ※ レベル1のコードとは別のプロジェクトに貼ってください(関数名が重なります)
*/
// ===== 1. デザイン規定(会社のトンマナに合わせて、ここだけ書き換える) =====
const CONFIG = {
font: 'Arial',
colors: {
main: '#0d1b4c', // メインカラー(濃紺)
accent: '#2563eb', // アクセント(青)
text: '#1a1a2e', // 本文
muted: '#6b7280', // 補足
line: '#c8ccd4', // 罫線・カード枠
soft: '#f5f6f8', // 薄いグレーの塗り
white: '#ffffff'
},
sizes: {
coverTitle: 30, coverDate: 12,
sectionNo: 64, sectionTitle: 28,
head: 22, subhead: 12, body: 13, small: 11, footer: 8
},
page: { w: 720, h: 405 },
pos: {
marginX: 42,
headTop: 34, ruleTop: 74, subheadTop: 84,
bodyTop: 118, bodyH: 240,
footerTop: 378
},
footerText: '株式会社 日本能率協会総合研究所',
clearBeforeGenerate: true // 実行のたびに既存のスライドを消してから作り直す
};
// ===== 2. スライドデータ(生成AIに書かせるのは、ここだけ) =====
const slideData = [
{
type: 'title',
title: '調査レポート作成の自動化',
date: '2026.09.01',
notes: '本日は、調査レポートの作成工程を自動化した取り組みについてご報告します。'
},
{
type: 'section',
title: '取り組みの背景',
notes: 'まず、なぜこの取り組みを始めたのかをご説明します。'
},
{
type: 'content',
title: '現在の課題',
subhead: '毎月40時間が転記作業に消えている',
points: [
'集計結果をスライドへ転記する作業に、担当者1名で月40時間',
'転記ミスが見つかるのが発表の直前になりやすい',
'担当者ごとに書式が違い、レビューに時間がかかる'
],
notes: '課題は3つあります。特に1つ目の40時間という数字が、今回の出発点になりました。'
},
{
type: 'compare',
title: '導入前後の比較',
subhead: '同じ作業を、同じ品質で、10分の1の時間で終える',
leftTitle: '導入前',
rightTitle: '導入後',
leftItems: ['転記に月40時間', '書式が担当者ごとに違う', '差し戻しが平均3回'],
rightItems: ['転記は月3時間', '全社共通の書式', '差し戻しは0〜1回'],
notes: '数字の比較でご覧いただくと、削減幅がはっきりします。'
},
{
type: 'process',
title: '自動化の流れ',
subhead: '人が手を動かすのは、集計シートの更新だけ',
steps: ['集計シートを更新', 'スクリプトを実行', 'スライドが生成', '中身だけ確認'],
notes: '4つの工程のうち、人が担当するのは1つ目と4つ目だけです。'
},
{
type: 'cards',
title: '得られた効果',
columns: 3,
items: [
{ title: '時間', desc: '月40時間 → 月3時間' },
{ title: '品質', desc: '転記ミスがゼロに' },
{ title: '横展開', desc: '3部署へ展開済み' }
],
notes: '効果は3つの観点で整理しました。'
},
{
type: 'table',
title: '部署別の削減時間',
headers: ['部署', '導入前', '導入後', '削減'],
rows: [
['調査1部', '40時間', '3時間', '37時間'],
['調査2部', '28時間', '4時間', '24時間'],
['企画部', '15時間', '2時間', '13時間']
],
notes: '部署ごとの内訳です。合計で月74時間の削減になりました。'
},
{
type: 'closing',
message: 'ご清聴ありがとうございました',
notes: '以上でご報告を終わります。ご質問をお受けします。'
}
];
// ===== 3. 実行(触らない) =====
let pageNo = 0;
let sectionNo = 0;
function generatePresentation() {
const pres = SlidesApp.getActivePresentation();
if (CONFIG.clearBeforeGenerate) {
const slides = pres.getSlides();
for (let i = slides.length - 1; i >= 0; i--) {
slides[i].remove();
}
}
pageNo = 0;
sectionNo = 0;
slideData.forEach(function (data, index) {
const build = builders[data.type];
if (!build) {
Logger.log((index + 1) + '枚目:未対応の type です → ' + data.type);
return;
}
const slide = pres.appendSlide(SlidesApp.PredefinedLayout.BLANK);
build(slide, data);
if (data.notes) {
setNotes_(slide, data.notes);
}
});
Logger.log('スライドを ' + pres.getSlides().length + ' 枚作成しました。');
}
// type と描画関数の対応表。ここに1行足せば、自分のパターンを追加できる
const builders = {
title: buildTitle_,
section: buildSection_,
content: buildContent_,
compare: buildCompare_,
process: buildProcess_,
cards: buildCards_,
table: buildTable_,
closing: buildClosing_
};
// ===== 4. パターンごとの描画(触らない) =====
function buildTitle_(slide, d) {
bg_(slide, CONFIG.colors.white);
bar_(slide, CONFIG.pos.marginX, 126, 60, 4, CONFIG.colors.accent);
text_(slide, d.title, CONFIG.pos.marginX, 150, 620, 60,
{ size: CONFIG.sizes.coverTitle, bold: true, color: CONFIG.colors.main });
text_(slide, (d.date || '') + ' / ' + CONFIG.footerText, CONFIG.pos.marginX, 232, 560, 24,
{ size: CONFIG.sizes.coverDate, color: CONFIG.colors.muted });
}
function buildSection_(slide, d) {
sectionNo++;
bg_(slide, CONFIG.colors.main);
text_(slide, ('0' + (d.sectionNo || sectionNo)).slice(-2), CONFIG.pos.marginX, 118, 220, 100,
{ size: CONFIG.sizes.sectionNo, bold: true, color: '#3d4d7d' });
text_(slide, d.title, CONFIG.pos.marginX, 214, 600, 50,
{ size: CONFIG.sizes.sectionTitle, bold: true, color: CONFIG.colors.white });
}
function buildContent_(slide, d) {
head_(slide, d);
const points = d.points || [];
for (let i = 0; i < points.length; i++) {
const top = CONFIG.pos.bodyTop + i * 34;
bar_(slide, CONFIG.pos.marginX, top + 9, 6, 6, CONFIG.colors.accent);
text_(slide, points[i], CONFIG.pos.marginX + 18, top, 600, 28,
{ size: CONFIG.sizes.body, color: CONFIG.colors.text });
}
footer_(slide);
}
function buildCompare_(slide, d) {
head_(slide, d);
const w = 300;
const gap = 36;
const h = 200;
panel_(slide, d.leftTitle || '現状', d.leftItems || [],
CONFIG.pos.marginX, CONFIG.pos.bodyTop, w, h, CONFIG.colors.muted);
panel_(slide, d.rightTitle || '改善後', d.rightItems || [],
CONFIG.pos.marginX + w + gap, CONFIG.pos.bodyTop, w, h, CONFIG.colors.accent);
footer_(slide);
}
function buildProcess_(slide, d) {
head_(slide, d);
const steps = d.steps || [];
const n = Math.max(steps.length, 1);
const areaW = CONFIG.page.w - CONFIG.pos.marginX * 2;
const gap = 22;
const boxW = (areaW - gap * (n - 1)) / n;
const top = CONFIG.pos.bodyTop + 30;
for (let i = 0; i < steps.length; i++) {
const x = CONFIG.pos.marginX + i * (boxW + gap);
const box = slide.insertShape(SlidesApp.ShapeType.ROUND_RECTANGLE, x, top, boxW, 96);
box.getFill().setSolidFill(CONFIG.colors.soft);
box.getBorder().getLineFill().setSolidFill(CONFIG.colors.line);
text_(slide, 'STEP ' + (i + 1), x + 12, top + 12, boxW - 24, 18,
{ size: 9, bold: true, color: CONFIG.colors.accent });
text_(slide, steps[i], x + 12, top + 34, boxW - 24, 50,
{ size: CONFIG.sizes.body, color: CONFIG.colors.text });
if (i < steps.length - 1) {
const arrow = slide.insertShape(SlidesApp.ShapeType.RIGHT_ARROW,
x + boxW + 4, top + 42, gap - 8, 12);
arrow.getFill().setSolidFill(CONFIG.colors.line);
arrow.getBorder().setTransparent();
}
}
footer_(slide);
}
function buildCards_(slide, d) {
head_(slide, d);
const items = d.items || [];
const cols = d.columns || 3;
const rows = Math.ceil(items.length / cols);
const areaW = CONFIG.page.w - CONFIG.pos.marginX * 2;
const gap = 16;
const cardW = (areaW - gap * (cols - 1)) / cols;
const cardH = Math.min(110, (CONFIG.pos.bodyH - gap * (rows - 1)) / rows);
for (let i = 0; i < items.length; i++) {
const item = items[i];
const r = Math.floor(i / cols);
const c = i % cols;
const x = CONFIG.pos.marginX + c * (cardW + gap);
const y = CONFIG.pos.bodyTop + r * (cardH + gap);
const card = slide.insertShape(SlidesApp.ShapeType.ROUND_RECTANGLE, x, y, cardW, cardH);
card.getFill().setSolidFill(CONFIG.colors.white);
card.getBorder().getLineFill().setSolidFill(CONFIG.colors.line);
const title = (typeof item === 'string') ? item : item.title;
const desc = (typeof item === 'string') ? '' : (item.desc || '');
text_(slide, title, x + 14, y + 14, cardW - 28, 22,
{ size: CONFIG.sizes.small, bold: true, color: CONFIG.colors.main });
if (desc) {
text_(slide, desc, x + 14, y + 42, cardW - 28, cardH - 56,
{ size: CONFIG.sizes.body, color: CONFIG.colors.text });
}
}
footer_(slide);
}
function buildTable_(slide, d) {
head_(slide, d);
const headers = d.headers || [];
const rows = d.rows || [];
if (headers.length === 0) {
footer_(slide);
return;
}
const table = slide.insertTable(
rows.length + 1, headers.length,
CONFIG.pos.marginX, CONFIG.pos.bodyTop,
CONFIG.page.w - CONFIG.pos.marginX * 2,
Math.min(CONFIG.pos.bodyH, 30 * (rows.length + 1))
);
for (let c = 0; c < headers.length; c++) {
const cell = table.getCell(0, c);
cell.getFill().setSolidFill(CONFIG.colors.main);
const t = cell.getText();
t.setText(String(headers[c]));
t.getTextStyle()
.setFontFamily(CONFIG.font)
.setFontSize(CONFIG.sizes.small)
.setBold(true)
.setForegroundColor(CONFIG.colors.white);
}
for (let r = 0; r < rows.length; r++) {
for (let c = 0; c < headers.length; c++) {
const cell = table.getCell(r + 1, c);
const t = cell.getText();
t.setText(String(rows[r][c] === undefined ? '' : rows[r][c]));
t.getTextStyle()
.setFontFamily(CONFIG.font)
.setFontSize(CONFIG.sizes.small)
.setBold(false)
.setForegroundColor(CONFIG.colors.text);
}
}
footer_(slide);
}
function buildClosing_(slide, d) {
bg_(slide, CONFIG.colors.main);
text_(slide, d.message || 'ご清聴ありがとうございました', 0, 178, CONFIG.page.w, 40,
{ size: 24, bold: true, color: CONFIG.colors.white, align: SlidesApp.ParagraphAlignment.CENTER });
}
// ===== 5. 共通部品(触らない) =====
function bg_(slide, color) {
slide.getBackground().setSolidFill(color);
}
/** 見出し+アクセント罫線+小見出し。中面はすべてこれで始まる */
function head_(slide, d) {
bg_(slide, CONFIG.colors.white);
text_(slide, d.title, CONFIG.pos.marginX, CONFIG.pos.headTop, 620, 32,
{ size: CONFIG.sizes.head, bold: true, color: CONFIG.colors.main });
bar_(slide, CONFIG.pos.marginX, CONFIG.pos.ruleTop, 130, 3, CONFIG.colors.accent);
if (d.subhead) {
text_(slide, d.subhead, CONFIG.pos.marginX, CONFIG.pos.subheadTop, 620, 22,
{ size: CONFIG.sizes.subhead, color: CONFIG.colors.muted });
}
}
/** 比較スライドの左右パネル */
function panel_(slide, title, items, x, y, w, h, headColor) {
const box = slide.insertShape(SlidesApp.ShapeType.RECTANGLE, x, y, w, h);
box.getFill().setSolidFill(CONFIG.colors.white);
box.getBorder().getLineFill().setSolidFill(CONFIG.colors.line);
bar_(slide, x, y, w, 28, headColor);
text_(slide, title, x + 12, y + 4, w - 24, 20,
{ size: CONFIG.sizes.small, bold: true, color: CONFIG.colors.white });
for (let i = 0; i < items.length; i++) {
text_(slide, '・' + items[i], x + 12, y + 42 + i * 30, w - 24, 26,
{ size: CONFIG.sizes.body, color: CONFIG.colors.text });
}
}
function text_(slide, str, x, y, w, h, options) {
const opt = options || {};
const box = slide.insertTextBox(String(str), x, y, w, h);
const style = box.getText().getTextStyle();
style.setFontFamily(CONFIG.font);
style.setFontSize(opt.size || CONFIG.sizes.body);
style.setBold(opt.bold === true);
style.setForegroundColor(opt.color || CONFIG.colors.text);
if (opt.align) {
box.getText().getParagraphStyle().setParagraphAlignment(opt.align);
}
return box;
}
function bar_(slide, x, y, w, h, color) {
const shape = slide.insertShape(SlidesApp.ShapeType.RECTANGLE, x, y, w, h);
shape.getFill().setSolidFill(color);
shape.getBorder().setTransparent();
return shape;
}
function footer_(slide) {
pageNo++;
bar_(slide, 0, CONFIG.pos.footerTop - 10, CONFIG.page.w, 1, CONFIG.colors.line);
text_(slide, CONFIG.footerText, CONFIG.pos.marginX, CONFIG.pos.footerTop, 400, 16,
{ size: CONFIG.sizes.footer, color: CONFIG.colors.muted });
text_(slide, String(pageNo), CONFIG.page.w - CONFIG.pos.marginX - 40, CONFIG.pos.footerTop, 40, 16,
{ size: CONFIG.sizes.footer, color: CONFIG.colors.muted, align: SlidesApp.ParagraphAlignment.END });
}
function setNotes_(slide, notes) {
try {
const shape = slide.getNotesPage().getSpeakerNotesShape();
if (shape) {
shape.getText().setText(notes);
}
} catch (e) {
Logger.log('スピーカーノートを設定できませんでした:' + e.message);
}
}
generatePresentation を実行すると、8枚のスライドが出来上がります。
表紙・章扉・箇条書き・比較・手順・カード・表・結びが1枚ずつ入っているので、
「どの型がどう見えるか」の見本帳としても使えます。
構造は、レベル1とまったく同じ
| ブロック | 中身 | 誰が触るか |
|---|---|---|
1. デザイン規定(CONFIG) | 色・フォント・文字サイズ・座標 | 最初に1回だけ自分で決める |
2. スライドデータ(slideData) | 資料の中身そのもの | 毎回ここだけを生成AIが書く |
3. 実行(generatePresentation) | データを1件ずつ読んで、型に応じた描画を呼ぶ | 触らない |
4. 型ごとの描画(build◯◯_) | 8種類それぞれの描き方 | 型を増やすときだけ |
| 5. 共通部品 | テキスト・図形・フッター・ノート | 触らない |
仕掛けの中心は builders という対応表です。
{ type: 'compare', ... } というデータが来たら buildCompare_ を呼ぶ、
という紐づけの一覧がここに書いてあります。
データの中の type という文字が、そのまま「どの描き方を使うか」の指示になっているわけです。
この形にしておくと、型を1つ増やすのが簡単になります。やることは2つだけ ── ①描画する関数を1つ書く、②builders に1行足す。既存の型のコードには一切触りません。機能を足すのに、動いているものを壊さなくて済む。これはプログラムの設計として、とても大事な性質です。
10slideData の書き方 ── AIへの生成計画書
テンプレートが手元にあれば、あとは slideData を差し替えるだけで別の資料になります。
そして、この slideData を書くのが生成AIの仕事です。
使える8つの型
| type | 何を表すか | 必要な項目 |
|---|---|---|
title | 表紙 | title / date |
section | 章扉(章番号は自動) | title |
content | 箇条書き(最も基本) | title / subhead / points |
compare | 左右の比較 | leftTitle / rightTitle / leftItems / rightItems |
process | 手順・工程(左から右へ) | steps |
cards | 並列に並ぶ要素 | columns / items |
table | 数値の比較・内訳 | headers / rows |
closing | 結び | message |
どの型にも notes を付けられます。ここに書いた文章は、
スライドのスピーカーノート(発表者用メモ)に自動で入ります。
発表原稿の下書きまでAIに書かせられる、ということです。
AIに渡すプロンプト
次のプロンプトを ChatGPT に貼り、いちばん下の【素材】に議事録やレポートを貼り付けてください。
返ってくるのは slideData の部分だけです。
差し替えの手順
出力をコピー
AIが出した const slideData = [ ... ]; を全文コピーする
置き換える範囲を選ぶ
スクリプトエディタで const slideData = [ の行から、対応する ]; の行までを選択
貼り付けて保存
選択した範囲に貼り付ける。CONFIG や関数は絶対に消さない
実行する
generatePresentation を実行。新しい中身で描き直される
いちばん多い失敗は「貼りすぎ」です。置き換えるのは // ===== 2. スライドデータ ===== から // ===== 3. 実行 ===== の手前までだけ。誤って CONFIG や描画関数まで消してしまうと動かなくなります。作業前にコード全文をどこかにコピーしておくと安心です。
自分の型を追加する
「うちの資料には、必ずKPIの数字を3つ大きく並べるページがある」といった場合は、 型そのものを足してしまうのが早いです。やることは2つだけです。
// ① 描画する関数を1つ書く(他の build◯◯_ の並びに追加する)
function buildKpi_(slide, d) {
head_(slide, d);
const items = d.items || [];
const n = Math.max(items.length, 1);
const areaW = CONFIG.page.w - CONFIG.pos.marginX * 2;
const gap = 20;
const boxW = (areaW - gap * (n - 1)) / n;
for (let i = 0; i < items.length; i++) {
const x = CONFIG.pos.marginX + i * (boxW + gap);
text_(slide, items[i].value, x, CONFIG.pos.bodyTop + 24, boxW, 62,
{ size: 40, bold: true, color: CONFIG.colors.accent,
align: SlidesApp.ParagraphAlignment.CENTER });
text_(slide, items[i].label, x, CONFIG.pos.bodyTop + 96, boxW, 24,
{ size: CONFIG.sizes.body, color: CONFIG.colors.muted,
align: SlidesApp.ParagraphAlignment.CENTER });
}
footer_(slide);
}
// ② 対応表に1行足す(既存の行はそのまま)
const builders = {
title: buildTitle_,
section: buildSection_,
content: buildContent_,
compare: buildCompare_,
process: buildProcess_,
cards: buildCards_,
table: buildTable_,
kpi: buildKpi_, // ← この1行を追加するだけ
closing: buildClosing_
};
// ③ これで、slideData に次の型が書けるようになる
// { type: 'kpi', title: '今期の実績', subhead: '3つの数字で見る',
// items: [
// { value: '74h', label: '月あたりの削減時間' },
// { value: '0件', label: '転記ミスの発生' },
// { value: '3部署', label: '展開済み' }
// ] }
これで { type: 'kpi', title: '...', items: [...] } というデータが使えるようになります。
あとは、第10章のプロンプトの「使えるスライドの型」に kpi を書き足せば、AIもその型を選んでくれるようになります。
11GAS系3つの使い分け
ここまでで、GASを使うやり方を3つ(レベル1〜2、レベル3、レベル4)扱いました。整理しておきます。
| レベル1・2 自分で書く/シート連携 | レベル3 AIにGASを設計させる | レベル4 テンプレート方式 | |
|---|---|---|---|
| デザイン | 自分で決めて固定 | 毎回AIと相談 | 最初に決めて固定 |
| 中身 | コードかシート | AIと相談 | 毎回AIが書く |
| 準備の手間 | 中(コードを書く) | 小(プロンプトを送るだけ) | 中(テンプレートを1回作る) |
| 2回目以降の手間 | ほぼゼロ | 毎回やり直し | 小(素材を渡すだけ) |
| 仕上がりの安定度 | 完全に一定 | 毎回ちがう | ほぼ一定 |
| 向いている資料 | 月次報告・定例会議資料 | 一度きりの提案・新しい形式 | 議事録要約・調査報告・研修資料 |
| 向かない場面 | 毎回構成が変わる資料 | 同じものを繰り返し作る | 1枚だけ作りたいとき |
迷ったら「同じ形式で何回作るか」で決めてください。年に12回作るならレベル2(シートを更新するだけにする)。形式は同じだが中身が毎回大きく変わるならレベル4。一度きりならレベル3で十分です。一度きりの資料のために仕組みを作るのは、いちばんよくある失敗です。
ChatGPTに、PowerPointを直接つくらせる
ここまではGoogleスライドの話でした。しかし提出先がPowerPointのことも多いはずです。 ここからは、ChatGPTに .pptx ファイルそのものを作らせる方法を扱います。 さらにテンプレートファイルを渡して体裁を引き継がせる方法、 プロジェクト機能で自社のデザインルールを常設する方法まで進みます。
12【レベル5】ChatGPTでPowerPointを直接つくる
ChatGPTは、ファイルを作って渡してくれます。Excel も Word も PowerPoint も、です。 しかもここで起きていることは、「文章を返す」のとは根本的に違います。
ChatGPTは、頭の中で何度も考え直している
「PowerPointを作って」と頼んだとき、ChatGPTの中では1回の思考で終わっていません。 構成を考える → コードを書く → 実行する → 出来上がりを自分で確認する → 直すという 複数回のやり取りを、内部で自動的に繰り返しています。これをマルチターン推論と呼びます。
だから指示の書き方が変わります。AIが自分で確認して直せるということは、私たちが細かい作り方を指示する必要は無いということです。代わりに伝えるべきなのは「どうなっていたら合格か」── 文字がはみ出していないこと、位置が揃っていること、色がルールどおりであること。作り方ではなく、合格条件を渡す。これが今のAIへの頼み方です。
実際に頼んでみる
フォントの扱いだけ注意してください。ChatGPTが動いている環境には、日本語フォントが十分に入っていないことがあります。生成されたファイルをPowerPointで開けば游ゴシックで表示されますが、AIが自己確認のために作る画像では、日本語が四角(□□□)になることがあります。その状態でAIが「文字化けしている」と誤解して直そうとしたら、「日本語フォントが無い環境なので、□□□は無視してよい。位置とサイズだけ確認して」と伝えてください。
13テンプレートファイルを添付して体裁を引き継ぐ
前章のやり方には弱点があります。会社の書式にならないことです。 いくら「白背景で、青をアクセントに」と伝えても、いつも使っている資料と並べると、明らかに別物になります。
いちばん確実な解決策は、いつも使っているファイルそのものを渡すことです。 ChatGPTは.pptx ファイルを読み込んで、中の色・フォント・座標を読み取れます。
手本を1つ選ぶ
過去に作った資料のうちいちばん体裁が整っているものを選ぶ
中身を消す
社外秘の数字・個人名を消し、体裁だけの状態にして保存する
添付して読ませる
ChatGPTに添付し、読み取った内容を報告させて認識を合わせる
その体裁で作らせる
新しい素材を渡し、読み取った体裁のまま資料を作らせる
手本にするファイルから、社外秘の情報を必ず消してください。実績の数字、取引先名、個人名、単価 ── これらが残ったまま添付すると、その情報ごとAIに渡すことになります。体裁を伝えるのに中身は要りません。表紙・見出し・本文・表・グラフが1枚ずつ入った「見本帳」を1つ作っておくと、以後ずっと使い回せます。
「読み取った内容を報告させる」工程を飛ばさないでください。AIが読み違えていることは普通にあります(メインカラーを別の色だと思っている、など)。先に報告させて確認しておけば、資料が全部できてから作り直す事態を防げます。手戻りを防ぐいちばん安いコストです。
14プロジェクト機能で自社のルールを常設する
前章までのやり方には、まだ面倒が残っています。毎回おなじ説明をしなければならないことです。 新しいチャットを開くたびに、色を伝え、フォントを伝え、テンプレートを添付し直す。 3回目くらいで嫌になります。
ChatGPTのプロジェクト機能は、これを解決します。 指示とファイルをプロジェクトに置いておくと、その中で始めた会話すべてに自動で適用されます。
何を入れるか
ここが今回のいちばん実務的な話です。入れるものは4つあります。 この4つを揃えると、AIは「自社の資料の作り方を知っている状態」になります。
| 入れるもの | 置き場所 | 役割 |
|---|---|---|
| プロジェクトの指示 | プロジェクトの「指示」欄 | 進め方の手順を決める。いきなり作らせず、要件確認 → 構成案 → 承認 → 生成 → 自己チェック、の順に進ませる |
| デザイン定義 | ファイルとしてアップロード | 色・フォント・文字サイズ・余白・図解のルールを、数値で書いたもの |
| レイアウトパターン集 | ファイルとしてアップロード | 「比較の話ならこの型」「手順ならこの型」という型の選び方 |
| 生成スクリプトの雛形 | ファイルとしてアップロード | 実際にファイルを組み立てるコードの土台。AIはこれを使い回すので、毎回同じ品質になる |
これは第7章の「まじん式」とまったく同じ発想です。まじん式ではGASのテンプレートを固定し、AIにはデータだけ書かせました。ここではデザイン定義とスクリプトの雛形を固定し、AIには中身だけ書かせています。変わらない部分を固定して、変わる部分だけをAIに任せる。道具が変わっても、うまくいく作り方は同じです。
作り方の手順
プロジェクトを作る
ChatGPTの左メニューから[プロジェクト]→ 新規作成。名前は「社内資料づくり」など
指示を貼る
プロジェクトの「指示」欄に、進め方と守ることを書いたテキストを貼る
ファイルを入れる
デザイン定義・パターン集・スクリプト雛形の3つをアップロードする
その中で会話する
以後は「◯◯の資料を作って」だけでよい。毎回の説明が要らなくなる
いちばん効くのは「指示」欄です。ファイルは資料ですが、指示欄は行動の決まりです。とくに「いきなり作り始めず、まず要件を4点確認すること」と「生成後にチェックリストを自分で確認して報告すること」の2つを書いておくと、仕上がりが目に見えて変わります。AIに考える手順を与えるのが、プロンプトのいちばん強い使い方です。
15配布ファイルを持ち帰って自分用に改造する
前章の4つを、ゼロから書くのは大変です。そこで、この研修を実施しているスニフアウトが実際に使っている デザイン定義一式を、そのまま持ち帰れる形でお渡しします。 色とフォントを自社のものに書き換えるだけで、皆さんの会社のトンマナで資料が出てくるようになります。
自分のデザインに変える手順
書き換えるのは3か所だけです。デザインガイドを直したら、雛形スクリプトの数値も同じ値に揃えるのを忘れないでください。
| 変えるもの | デザインガイドの場所 | 雛形スクリプトの場所 |
|---|---|---|
| 配色 | §1 カラーパレット | COLORS と GRADIENT |
| フォント | §2 タイポグラフィ | FONT_JA / FONT_EN / SIZE |
| フッターの会社名 | §8 フッター | FOOTER_TEXT |
そのまま残すことをおすすめする部分もあります。デザインガイドの§3(セーフエリア)と§7(図解のコネクタ規則)、そして§12(生成後のチェックリスト)です。ここはどこの会社でも通用する「崩れないための決まり」で、色やフォントとは性質が違います。会社の個性は §1・§2・§8 に出て、資料の破綻を防ぐのは §3・§7・§12 が担っています。
この配布物は、株式会社スニフアウトのデザイン定義をもとにしています。皆さまの社内でご自由に改変してお使いいただけますが、社外への配布・転載はご遠慮ください。また、自社仕様に書き換えたあとは、ファイル内の会社名表記(© SNIFFOUT Inc. など)も忘れずに変更してください。
16つまずきポイントと対処
「このアプリはGoogleで確認されていません」と出る
初回だけ出ます。[詳細]→[(プロジェクト名)に移動(安全ではないページ)]と進んでください。 自分が書いたコードを、自分のアカウントで動かすための確認です。スライドを作る権限が要求されるのは正常です。
TypeError: Cannot read properties of null
シート名が見つかっていないときの典型です。レベル2で getSheetByName('報告データ') と書いているのに、
実際のシート名が「報告データ 」(末尾に空白)や「報告データ2」になっていると起きます。
下のタブの名前を、1文字ずつ確認してください。
Exception: The parameters (…) don't match the method signature
関数に渡す値の数や種類が合っていないときのエラーです。
スライド系でとくに多いのが座標に文字列を渡してしまうケース
('40' ではなく 40 と書く)と、
幅や高さに 0 やマイナスを渡すケースです。
シートから読んだ数値は Number() で数値に直してから使ってください。
フォントを指定したのに変わらない
setFontFamily() に渡す名前は、Googleスライドのフォント一覧に出てくる表記と完全に一致している必要があります。
'Noto Sans Japanese' ではなく 'Noto Sans JP'、というレベルで厳密です。
間違った名前を渡してもエラーにはならず、黙って既定のフォントのままになりますので気づきにくい点に注意してください。
大事なスライドが消えてしまった
テンプレート方式のコードは、初期設定で既存のページをすべて削除してから描き直します
(clearBeforeGenerate: true)。必ず空のスライドで実行してください。
万一消してしまった場合は、スライドの[ファイル]→[変更履歴]→[変更履歴を表示]から実行前の状態に戻せます。
AIの出力が途中で切れる
コードが長いと、1回の返信に収まらないことがあります。「続きを出力して」と送れば続きが出ますが、
つなぎ目でずれることが多いので、新しいチャットでやり直すほうが結果的に速いことがよくあります。
テンプレート方式なら、AIに出させるのは slideData だけなので、この問題はほぼ起きません。
PowerPointを開いたら、フォントが変わっていた
指定したフォントが開いた側のPCに入っていないと、自動的に別のフォントに置き換わります。 社内で配る資料は、游ゴシック・メイリオ・MS ゴシックなど、Windowsに標準で入っているものを選んでおくと安全です。
どうしても解決しないとき
エラー文をそのまま貼って、AIに聞くのがいちばん速いです。「エラーが出ました」だけでは直せません。 次の型で、状況・エラー文・コードの3点セットを渡してください。
スライド系のデバッグは、実行ログとスクリーンショットの両方を渡すのがコツです。「エラーは出ないが、見た目がおかしい」という症状が多いのがスライド自動化の特徴です。エラーが出ていないなら、ログには何も残りません。そのときは出来上がったスライドの画面を撮って貼るのがいちばん確実に伝わります。
第6回の宿題
レベル1を動かし、配色を自分仕様に変える
第3章のコードを貼り付けて createDeck を実行し、6枚のスライドができることを確認してください。そのうえで DESIGN の colorMain と colorAccent を自分の部署の資料の色に変え、もう一度実行して全ページの配色が入れ替わることを確かめましょう。フッターの表記もご自身の部署名に変えてみてください。
スプレッドシートから一括生成する
第5章の表のとおりに「報告データ」シートを作り、createReportFromSheet を実行してください。次に5行目に自分の部署の行を1つ追加して再実行し、コードを1文字も変えずにスライドが1枚増えることを確認します。余力があれば、列を1つ増やして(例:担当者名)、その内容がスライドに出るようにコードを直してみてください。
テンプレート方式で、AIに中身を書かせる
第9章のコードを空のGoogleスライドに貼り付けて実行し、8枚の見本が出ることを確認します。次に第10章のプロンプトを使い、自分の手元にある実際の資料(過去の報告書・議事録・企画メモなど)を素材にして slideData を作らせ、差し替えて実行してください。スピーカーノートに発表原稿が入っていることまで確認できたら合格です。
ChatGPTにPowerPointを作らせる
第12章のプロンプトで、.pptx ファイルを1本作らせてください。そのうえで第13章のやり方で、社外秘の情報を消した手持ちの資料をテンプレートとして添付し、同じ内容をもう一度作らせて2つを見比べてください。「テンプレートを渡すと何がどう変わるか」を、ご自身の目で確認するのがこの課題の目的です。
プロジェクトを作り、自社仕様に書き換える
第15章のスライド作成キット(ZIP)をダウンロードし、ChatGPTにプロジェクトを1つ作って、指示欄とファイル3点を設定してください。そのうえでデザインガイドの §1(配色)・§2(フォント)・§8(フッター)を自社の値に書き換え、雛形スクリプトの数値も同じ値に揃えます。最後に、そのプロジェクトの中で資料を1本作らせて、自社のトンマナで出てくることを確認してください。
宿題を進めるときのお願い:テンプレートとして添付する資料からは、取引先名・個人名・実績の数字・単価を必ず削除してください。体裁を伝えるのに中身は必要ありません。また、テンプレート方式のコードは必ず空のスライドで実行し、業務で使っている資料の上では動かさないでください。
提出方法:①レベル1で配色を変えたスライドのURL、②シートから一括生成したスライドのURL、③テンプレート方式で作ったスライドのURLと、AIに書かせた slideData 全文、④ChatGPTに作らせた PowerPoint ファイル(テンプレートあり/なしの2本)、⑤自社仕様に書き換えたデザインガイドと、それで作った資料の5点を、研修事務局へメールでご提出ください。提出期限:次回研修の前日17:00まで。