1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生成AIで遊ぼAdvent Calendar 2024

Day 14

GAS(Google Apps Script)で生成AIWebアプリ

Last updated at Posted at 2024-12-13

概要

ごくごく小規模な生成AIWebアプリを開発するのに、GASを使ってみた記録です。

生成AIをWebで活用するにはサーバーサイドスクリプトが欲しくなると思います。APIキーを見えなくしたいからです。Python Flaskとかでも良いけどデプロイ先を考えるのが面倒くさいと思っていたところ、GASでWebアプリが作れることを思い出したので試してみました。

できたもの

Words Avenue

スクリーンショット 2024-12-12 22.12.11.png

送信されたワード(文章でもいいです。140字まで)を25カ国語くらいに翻訳して一気に表示するサイトです。

ローディング画面を作るのをサボったため、送信後の間がちょっと不安になります…。

あと phonics という名前で発音を表示してもらおうと思ったのですが、生成具合が不安定です。

使ったもの

Clasp

ClaspはGAS上のファイルとかをローカルに pull したり、逆にサーバー上に push したりするライブラリです。

GASの画面上のエディターで編集するのは大変なので導入したほうが良いです。

Rollup / Babel

RollupとBabelを用いて ts ファイルからデプロイ用の js ファイルをビルドしました。

これにより node のモジュールを一緒にデプロイできます。

ただし、今回インストールしたモジュールは @google/generative-ai のみで、それも SchemaType しか使わなかったので、別にバンドルしなくても良かったかもしれません。

Gemini

Googleで完結しようと思ってAIモデルはGeminiを選択しました。

無料枠があります。すごい!

gemini-2.0-flash-exp を使用しました。

Almond.css

HTMLファイルのスタイリングに使用しました。クラスレスなタイプのCSSで、HTMLのネイティブなエレメントにスタイルが当たります。

詰まったところ

doPost編

GASではPOSTを受けるのにdoPost()という関数を使います。

function doPost(e) {
  console.log("event", e);
  const sentence = e.sentence !== undefined ? e.sentence : "hello, world!";
}

こんな感じで始めましたが、サーバー上でのテストでリクエスト(eの中身)を設定する方法がいまいち分からなかったです。

そこでローカルからcurlでPOSTリクエストをしようと思いましたが、 CORS エラーが出てきて失敗…

結局上記 "hello, world" を決め打ちでテストしました。

fetch編

@google/generative-ai は内部的にfetch APIを使っているみたいなのですが、これがGAS上では動かず。

UrlFetchApp.fetch を使って自力でリクエストするように書き直しました。

なので @google/generative-ai で使っているのは型情報のみです。

doGet編

GASで静的なHTMLを配置しようと思ったら、ブラウザからのGETリクエストを受けることになるので doGet() を使います。

function doGet() {
  return HtmlService.createHtmlOutputFromFile("get.html");
}

get.html には簡素なフォームを置き、サブミットイベントが発火したら doPost() へのリクエストを自力で投げます(e.preventDefault()使用)

const sendForm = document.getElementById("sendForm");
sendForm.addEventListener("submit", async (event) => {
    event.preventDefault();
    // フォーム送信処理
    try {
          google.script.run
            .withSuccessHandler((data) => {
              console.log(data);
            }).doPost({sentence: sentenceVal})
        }catch(e){
            console.log(e)
        }
        
}

HTMLネイティブのフォーム送信処理ではなく、 google.script.run.withSuccessHandler を使うことが必要だったみたいでけっこう時間を取られました。

一応できた

もう一度載せますが、 Words Avenue です。

カスタムドメインが使えたらな、というのと、

静的HTML(get.html)は本当にただのHTMLなので 久々に Vanilla な JS を書いてDOM操作をしました。

そのあたりで大規模なアプリにはなりようがない気がしますが、小規模な実験的アプリならGASでやるのもありじゃないかなと思いました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?