Closed10

Supabase Edge Functionsで Gemini API を使ってみる

masa5714masa5714

Supabase Edge Functionsとは

Denoが動くFaaS。
ちょっとした関数をサーバーサイドで処理してくれるサービス。
月500,000回のリクエストまでは無料枠。
このスクラップではローカル環境の Supabase Edge Functionsで動かす。

Gemini APIとは

生成AIのGeminiをNode.js等で動かすことができるAPI。
現在(2025年1月18日)時点では、Gemini 2.0 Flashを使うのが良いらしい。

masa5714masa5714

Supabase Edge Functionsの準備をする

supabase functions new gemini

このコマンドを叩くと、Supabase CLIによって gemini という Edge Functions の実行ファイルが生成される。


ちなみに生成されたファイルは

supabase functions serve

を叩けば動く状態になる。

詳細な動かし方は生成したフォルダの index.ts の最下部にコメントアウトで書かれているので、そこを参考にすると良いだろう。

そのコメントアウトされている記述の例
/* To invoke locally:

  1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start)
  2. Make an HTTP request:

  curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/gemini' \
    --header 'Authorization: Bearer hogehogehogehogeohoge' \
    --header 'Content-Type: application/json' \
    --data '{"name":"Functions"}'

*/

CURLでもいいですし、 http://127.0.0.1:54321/functions/v1/gemini にブラウザでアクセスしても良し。

masa5714masa5714

Supabase Edge FunctionsにGeminiが動くように記述をしてみよう

functions/gemini/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { GoogleGenerativeAI } from "npm:@google/generative-ai";

const genAI = new GoogleGenerativeAI("【ここにAPIキー】");
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash-exp" });

Deno.serve(async (req) => {
  const prompt = `Geminiさん、こんにちは!`;

  const result = await model.generateContent(prompt);
  console.log(result.response.text());

  const data = {};

  return new Response(JSON.stringify(data), { headers: { "Content-Type": "application/json" } });
});

とりあえず上記のコードとしておく。
プロンプトは「Geminiさん、こんにちは!」としており、これに対する返答を見てみることにする。

では、 supabase functions serve を実行して、http://127.0.0.1:54321/functions/v1/gemini にアクセスしてみると、コンソールに

と出てきました。
これでGeminiと疎通できていることが確認できた。
あとは prompt を整えたり、 .getGenerativeModel() のオプション周りを指定して欲しいデータを受け取れるようにするだけ。


ちなみに、 const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash-exp" }); は「Gemini 2.0 Flash」を使うことを宣言している。他のモデル(例えば Gemini 1.5 Proなど)を使いたい場合は gemini-1.5-pro とすることで変更できる。

モデル一覧は https://ai.google.dev/gemini-api/docs/models/gemini?hl=ja#model-variations こちらの、「モデルバリエーション」という欄の小文字の文字列として掲載されており、これをコードに入れれば自由に指定できる。

masa5714masa5714

Geminiの回答を必ずJSON形式にする方法

アプリケーションとして処理した結果を受け取る場合、必ずJSON形式で返ってきてくれた方が都合が良い。Gemini APIにはJSON形式で返すように指示できる機能が備わっている。

https://ai.google.dev/gemini-api/docs/structured-output?hl=ja&lang=node

公式ドキュメントに書かれている通り。

import { GoogleGenerativeAI, SchemaType } from "npm:@google/generative-ai";

const model = genAI.getGenerativeModel({
  model: "gemini-2.0-flash-exp",
  generationConfig: {
    responseMimeType: "application/json",
    responseSchema: {
      type: SchemaType.ARRAY,
      items: {
        type: SchemaType.OBJECT,
        properties: {
          "aiueo": {
            type: SchemaType.STRING,
            nullable: false
          }
        },
        required: ["aiueo"]
      }
    }
  }
});

この場合、

JSON
[
  {
    "aiueo": "ここに何らかの値が入ってくれる"
  }
]

という形式で返してくれることになる。

https://cloud.google.com/vertex-ai/docs/reference/rest/v1/Schema

他にどんな指定ができるかは上記をチェックしよう。

ちなみに、 type: SchemaType.ARRAY の場合は、itemsが必要。配列の型を指定しないと動かないので注意。 string[] っぽい感じでデータがほしければ、

items: {
  type: SchemaType.OBJECT,
  properties: {
    hoge: {
      type: SchemaType.ARRAY,
      items: {
        type: SchemaType.STRING
      }
    }
  }
}

という形になる。上記の場合は hogeというキーに対して stirng[] なデータが入ることになる。


required: ["aiueo"] では、aiueoというキーは必ず入れておいてくれっていう指示になる(はず)

masa5714masa5714

独り言

「LLMは言葉のOS」とも表現できると思った。
コンピュータも機械語を使ってプログラミングすることは99.9%の人はしない。それは難しい上に生産的ではないから。難しいものを簡単に使えるようにしてくれたものがOSである。

言葉を分析するAIも一般人には作るのが難しい。難しいものを使えるようにしてくれたものがGeminiなどの生成AIである。

そういう意味では基本的な基盤を提供してくれる訳だから、LLMはOSと同じ位置づけだと表現できる。

kazukikazuki

日本語を入力してその回答をもらおうとしたとき文字化けしてうまく返答がもらえないのですがどうすればいいか教えてほしいです
curlで
--data '{"query":"こんにちわ"}'
みたいにして
Deno.serve(async (req) => {
try {
const { query } = await req.json() ...

これをconsole.logで表示すると文字化けしていてプロンプトとして認識されずHelloと英語でgeminiに返されてしまうのですが

masa5714masa5714

curlから叩いていないので正確なことは分かりませんが、
console.log(query); でまずはデータが正しく渡っているか確認しておいた方が良いかもしれませんね。あとは、いきなりデータを渡さずに

僕の環境では、

const formData = await req.formData();
const myText = formData.get("my-test") as string;

という形でデータを受け取っています。
formから <input type="text" name="my-test" /> の中身を受け取っているようなイメージです。


あとはモデル名の指定でしょうか。
このスクラップを書いていた時点では gemini-2.0-flash-exp がモデル名でしたが、現在は gemini-2.0-flash になっています。

const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });

もしかするとコイツが原因だったりするのかもしれませんね。

kazukikazuki

curl検証で時間を費やしましたが、curlで動かしてないとのことで自分も本来使用するDartの方で動かした所正常に動作しました。以前curlはよくわかりませんが無駄な時間から救われました、ありがとうございます!

このスクラップは2025/01/18にクローズされました