【Dify×GAS】PDFマニュアルを一瞬で「ラジオ台本&自動クイズ」に変換!JSONエラーを乗り越えたAI教材工場の作り方

DifyとGoogle Apps Script(GAS)を連携させ、PDFのマニュアルを一瞬で「ラジオ台本」と「自動生成クイズ」へ変換する「AI教材工場」の作り方を解説したブログ第114話のアイキャッチ画像。笑顔でガッツポーズする49歳の男性(Yasu)の前で、ノートパソコンに「SUCCESS」の文字とチェックマークが表示され、隣の自動ドキュメント生成機から画像付きの「Video Script」が次々と出力されている、業務効率化の成功を象徴するイメージ図。 Uncategorized

皆さんこんにちは、横浜で清掃業を営む49歳、ヤスです。

前回は論破王と口喧嘩!論理的思考を鍛える「AIディベートボット

のアプリを作りましたね。

同世代の皆さん、現場の機材マニュアルや業務用洗剤の成分表、分厚い専門書……読まなきゃいけないと分かっていても、活字ばかりで睡魔に襲われませんか?

私は思いつきました。「この退屈なPDFを、運転中に聴けるラジオ番組と、スマホで解けるクイズに全自動で変えてしまえばいいんだ!」と。 そこで今回は、DifyとGASを連携させ、**「PDFを投げ込むだけで、Googleドキュメント(ラジオ台本)とGoogleフォーム(自動採点クイズ)を同時に作り出す魔法のアプリ」**を開発しました。

実は今回、途中で「JSONの文字化け」というAPI連携最大の壁にぶち当たりました。しかし、荷造りの方法(form-data)を変えることで見事突破!今回はその「失敗と解決のリアルな軌跡」も含めて、中学生でも絶対に真似できるレベルで丁寧に解説します。49歳の等身大の挑戦、一緒にやってみましょう!


【結論】 このアプリの正体は、「Dify(脳)」と「GAS(手)」の最強タッグです。

DifyがPDFの文章を読み込み、「無知なDJと専門家の分かりやすい対話」と「巧妙なダミーを含んだ3択クイズ」を作成します。そして、そのデータをGASへ送信し、GASがGoogleドキュメントとGoogleフォームを自動で組み立てます。

開発の最大のキモは、DifyからGASへデータを送る際の**「form-data」**という通信方式です。AIが作る文章には「改行」や「記号」がたくさん含まれるため、通常のJSON形式で送るとエラー(箱の中身が空っぽ現象)が起きます。これをform-dataに切り替えることで、どんなに複雑な台本でも安全にGASへ届けることができるようになりました!


【手順:専門書変換マシンの作り方】

ステップ1:GASで「ドキュメント&フォーム自動作成工場」を作る

まずは受け皿となるGASの設定です。Googleドライブから「Google Apps Script」を開き、以下のコードを貼り付けます。前回のエラーを教訓に、form-dataで受け取るための最新仕様になっています!

JavaScript

// Difyからデータが送られてきた時に自動で実行されるメインの関数です
function doPost(e) {
  // エラーが起きてもシステム全体が止まらないように安全装置(try-catch)をつけます
  try {
    // Difyの「form-data」から、送られてきたタイトル(theme)を受け取ります
    const title = e.parameter.title;
    // Difyの「form-data」から、AIが書いたラジオ台本を受け取ります
    const script = e.parameter.script;
    // クイズデータは文字の塊で届くので、プログラムで扱える配列(JSON)に変換します
    const quizzes = JSON.parse(e.parameter.quizzes);

    // 【1. Googleドキュメント(ラジオ台本)の作成】
    // ファイル名に「【ラジオ台本】」と付けて、新しいドキュメントを作成します
    const doc = DocumentApp.create("【ラジオ台本】" + title);
    // 作成したドキュメントの中身(本文)を編集する準備をします
    const body = doc.getBody();
    
    // ドキュメントの一番上に、一番大きな文字(見出し1)でタイトルを書き込みます
    body.insertParagraph(0, "■ " + title + " - 解説ラジオ台本").setHeading(DocumentApp.ParagraphHeading.HEADING1);
    // その下に、AIが作ってくれたラジオ台本の文章をそのまま貼り付けます
    body.appendParagraph(script);
    
    // 完成したドキュメントのURL(リンク)を取得して、後でDifyに返せるように保存します
    const docUrl = doc.getUrl();

    // 【2. Googleフォーム(確認クイズ)の作成】
    // ファイル名に「【確認クイズ】」と付けて、新しいGoogleフォームを作成します
    const form = FormApp.create("【確認クイズ】" + title);
    // フォームの説明文に、回答者を励ますメッセージを設定します
    form.setDescription("ラジオ台本を読んだ後の確認テストです。全問正解を目指しましょう!");
    
    // アンケートではなく、正解・不正解の採点ができる「テストモード」をONにします
    form.setIsQuiz(true); 

    // 【3. クイズの問題を順番に作っていくループ処理】
    // AIが作った問題の数(今回は3問)だけ、以下の作業を繰り返します
    for (let i = 0; i < quizzes.length; i++) {
      // 今から処理する1問分のデータを変数 q に取り出します
      const q = quizzes[i];
      
      // フォームに「ラジオボタン形式(1つだけ選ぶ形式)」の質問を新しく追加します
      const item = form.addMultipleChoiceItem();
      // 質問のタイトルとして、AIが考えた問題文を設定します
      item.setTitle(q.question);
      // この問題に正解した時にもらえる点数を「10点」に設定します
      item.setPoints(10); 
      
      // 選択肢を入れるための空っぽのリスト(配列)を準備します
      const choiceList = [];
      // AIが考えた選択肢の数(今回は3つ)だけ、さらに作業を繰り返します
      for (let j = 0; j < q.choices.length; j++) {
        // 今処理している選択肢が、AIが指定した「正解」と同じかどうかを判定します(同じならtrue)
        const isCorrect = (q.choices[j] === q.answer); 
        // 選択肢の文字と、それが正解かどうか(true/false)をセットにして、リストに追加します
        choiceList.push(item.createChoice(q.choices[j], isCorrect));
      }
      // 完成した3つの選択肢のリストを、質問アイテムに一気に設定します
      item.setChoices(choiceList);
    }
    
    // 完成したGoogleフォームの編集用URL(リンク)を取得して保存します
    const formUrl = form.getEditUrl();

    // 【4. Difyへ大成功の報告をする】
    // ドキュメントとフォーム、2つのURLをセットにしてDifyに返信するデータを作ります
    const result = { status: "success", docUrl: docUrl, formUrl: formUrl };
    // 返信データをJSONという形式に変換して、Difyへ送り返します
    return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);

  // エラーが起きた場合の処理です
  } catch (error) {
    // 万が一エラーが起きた場合は、エラーの詳しい原因をDifyに送り返します
    const errorResult = { status: "error", message: "GASエラー: " + error.toString() };
    // エラーデータも同じようにJSON形式にしてDifyへ送り返します
    return ContentService.createTextOutput(JSON.stringify(errorResult)).setMimeType(ContentService.MimeType.JSON);
  }
}

⚠️超重要ポイント(私がハマった罠) コードを貼り付けたら「デプロイ」を行いますが、コードを修正した際は必ず「デプロイを管理」から「新バージョン」を選んでデプロイしてください!これをやらないと、いつまでも古いコードが動いてエラー地獄から抜け出せません!

ステップ2:Difyで「AIの脳みそ」を組み立てる

Difyの「ワークフロー」で以下の順番にブロックを繋ぎます。

  1. 開始ブロック: theme(文字)と document(ファイル)を入力。
  2. 文書抽出: アップロードしたファイルから文字を読み取る。
  3. LLM(ラジオ用): 抽出した文字を元に、DJと専門家の対話台本を作成。
    • プロンプトのコツ: 「DJは無知な設定にする」「必ず例え話を強制する」ことで劇的に面白くなります。
  4. LLM(クイズ用): 重要なポイントを3択クイズ(JSON形式)にする。
    • プロンプトのコツ: 「ダミーの選択肢はもっともらしい間違いにする」「余計な挨拶は一切出力させない」のがプロの技です。

ステップ3:最大の難関「HTTPリクエスト」の設定

ここが今回一番苦労したところです!⑤番目に「HTTPリクエスト」ブロックを置きますが、ボディの設定を間違えると undefined is not valid JSON という恐怖のエラーが出ます。

  • ダメな例(JSON): AIが改行や記号をたくさん使うと、送信用の箱が内側から破裂してGASに届きません。
  • ⭕️ 大正解(form-data): ボディを form-data に切り替えましょう!
    • キー:title / 値:{x}theme
    • キー:script / 値:{x}text (ラジオLLMの出力)
    • キー:quizzes / 値:{x}text (クイズLLMの出力)

これで、どんなに長い台本でも頑丈な箱に入れて安全に届けられます!最後に終了ブロックを繋いで、GASから返ってきたURLを表示させれば完成です。

Difyワークフローの全体図とテスト実行結果画面。PDFドキュメントの入力から「テキスト抽出」ノード、2つのLLMノード(gpt-4o)による「ラジオ台本生成」と「クイズ生成」、そしてGASへデータを送信する「HTTPリクエスト」ノードまでが連携されている。右側のコンソールには、Googleドキュメント(docUrl)とGoogleフォーム(formUrl)の両方の発行URLを含むJSONレスポンスが表示され、教材作成の全自動化が成功した様子。

【まとめ】 テスト実行の画面で {"status":"success", "docUrl":"...", "formUrl":"..."} という文字が出た瞬間、思わず「ヨッシャ!!」とガッツポーズをしてしまいました。

「undefined(箱が空っぽだぞ!)」とGASに怒られ続け、何度も心が折れそうになりましたが、form-dataへの切り替えと、GASのバージョン更新という「プログラミングの基本ルール」を学べたことは、私にとって星5つ以上の大きな収穫です。

DifyとGASの連携により、PDFマニュアルから自動生成されたGoogleフォーム形式の「【確認クイズ】ゼロから学ぶ『情報セキュリティ』基礎知識」。ラジオ台本の内容に基づき、「情報セキュリティの基本要素」や「多要素認証」に関する選択肢形式の設問が、適切なタイトル・説明文とともに自動で作成されている様子。

年齢を理由に諦める必要なんてありません。エラーはAIからの「ここを直せば動くよ」というただのラブレターです。自分で作った魔法のドキュメントとクイズで、今日も現場の知識をアップデートしてきます。今回作ったラジオ台本をラジオにするにはどうすればいいか

調べていたらノートブックLMで作れる事が分かりました。

その結果そもそもPDFをノートブックLMにアップすればこのアプリを使わなくても

ラジオが作れるし、クイズも作れる!

まあ、今回も練習と思って良しとします。

xもやっているので遊びに来てください。

私のエックスはこちら

次回のアプリも是非お楽しみに!

コメント

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