GPTsとGASとGemini Proで作るLLM Bot「幻(げん)」

Gemini

近年、人工知能技術の進化に伴い、多くの人々が日常生活やビジネスシーンでAIを活用するようになりました。今回は、GPTs(Generative Pre-trained Transformers)、GAS(Google Apps Script)、そしてGemini Proを組み合わせて、LINE上で動作するLLM(Large Language Model)Bot「幻(げん)」を作成する方法を紹介します。

デモ動画

GPTsとGASのオウム返しBotを進化させる

初めに、GPTsとGASを使用したシンプルなオウム返しBotの基本的な構造から始めます。このBotは、ユーザーからのメッセージを受け取り、そのメッセージをそのまま返すという単純な機能を持っています。しかし、この基本的な機能をGemini Proの力を借りて、より高度な対話能力を持つBotへと進化させることが可能です。

GPTsとGASで作るオウム返しBot
オウム返しBotは、送られてきたメッセージをそのまま返すシンプルなBotです。この記事では、Google Apps Script(GAS)とGPTsを使用して、簡単なオウム返しBotを作成する方法について説明します。初心者の方でも理解しやす...

GPTsとGASのコード解説

GASを用いた開発では、主にGoogle Apps Scriptを使用しています。以下に示すコードブロックは、LINE Botの基本的な設定や、スプレッドシートへのログ記録、Gemini APIへのリクエストを行うための関数などを含んでいます。

// LINE Bot設定
const LINE_CONFIG = {
  token: PropertiesService.getScriptProperties().getProperty("LINE_TOKEN"),
  url: 'https://api.line.me/v2/bot/message/reply'
};

// スプレッドシート設定
const SPREADSHEET_CONFIG = {
  id: 'YYYYYYYYYYYYYYYYYYYYYYYYYYY',
  systemSheetName: "SYSTEM",
  messageSheetName: "message"
};

// メッセージを処理
function processMessage(message) {
  if (!message) {
    return { error: "Invalid request. Please provide a message." };
  }

  const responseMessage = getGeminiApiResponse(message);
  logMessageToSheet(new Date(), message, responseMessage);
  return { echo: responseMessage };
}

このコードは、LINEから受け取ったメッセージをGemini APIに送信し、得られたレスポンスをユーザーに返信する処理を行います。また、通信の履歴をスプレッドシートに保存する機能も備えています。

LINE Bot設定

// LINE Bot設定
const LINE_CONFIG = {
  token: PropertiesService.getScriptProperties().getProperty("LINE_TOKEN"),
  url: 'https://api.line.me/v2/bot/message/reply'
};

この部分では、LINE BotのアクセストークンとAPIのエンドポイントURLを設定しています。PropertiesService.getScriptProperties().getProperty("LINE_TOKEN")は、スクリプトのプロパティに保存されたLINE_TOKENを取得しています。これにより、LINE Platformとの認証を可能にします。

スプレッドシート設定

// スプレッドシート設定
const SPREADSHEET_CONFIG = {
  id: 'YYYYYYYYYYYYYYYYYYYYYYYYYYY',
  systemSheetName: "SYSTEM",
  messageSheetName: "message"
};

ここでは、使用するスプレッドシートのIDと、システム情報およびメッセージログを保存するためのシート名を設定しています。

システムプロンプトを取得

file

// システムプロンプトを取得
function getSystemPrompt() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SPREADSHEET_CONFIG.systemSheetName);
  return sheet.getRange("A2").getValue();
}

getSystemPrompt関数では、設定したスプレッドシートのSYSTEMシートから、特定のセル(A2)の値を取得します。この値は、後続の処理でGemini APIへのリクエストに使用されるプロンプトとして機能します。

メッセージを処理

// メッセージを処理
function processMessage(message) {
  if (!message) {
    return { error: "Invalid request. Please provide a message." };
  }

  const responseMessage = getGeminiApiResponse(message);
  logMessageToSheet(new Date(), message, responseMessage);
  return { echo: responseMessage };
}

processMessage関数は、受け取ったメッセージを処理し、それに対する応答を生成します。メッセージが無効な場合はエラーを返し、有効な場合はGemini APIを通じて応答を取得し、それをログに記録した後、応答を返します。

スプレッドシートにメッセージを記録

file

// スプレッドシートにメッセージを記録
function logMessageToSheet(timestamp, message, response) {
  const ss = SpreadsheetApp.openById(SPREADSHEET_CONFIG.id);
  const sheet = ss.getSheetByName(SPREADSHEET_CONFIG.messageSheetName);
  sheet.appendRow([timestamp, message, response]);
}

この関数は、処理されたメッセージとその応答をスプレッドシートに記録します。これにより、Botの対話履歴を追跡できます。

doGet関数の改善版

// doGet関数の改善版
function doGet(e) {
  const response = processMessage(e.parameter.message);
  return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}

doGet関数は、HTTP GETリクエストを処理し、クエリパラメータとして受け取ったメッセージをprocessMessage関数に渡して処理します。その結果をJSON形式で返します。

Gemini APIへのリクエストを行い、レスポンスを返す関数

// Gemini APIへのリクエストを行い、レスポンスを返す関数
function getGeminiApiResponse(prompt = 'あなたができることは何ですか?') {
  // 省略...
}

getGeminiApiResponse関数では、Gemini APIに対してHTTP POSTリクエストを行い、受け取ったプロンプトに基づいた応答を取得します。この関数の内部で、APIキーの取得、APIエンドポイントの設定、リクエストの送信、およびレスポンスの処理が行われます。

このコードは、Google Apps Scriptを使ってLINE Botを構築し、Gemini APIを介して動的な応答を生成するプロセスを示しています。LINEからのメッセージを受け取り、それに基づいてGemini APIにリクエストを送信し、得られた応答をユーザーに返す仕組みを構築しています。

全体コード

// LINE Bot設定
const LINE_CONFIG = {
  token: PropertiesService.getScriptProperties().getProperty("LINE_TOKEN"),
  url: 'https://api.line.me/v2/bot/message/reply'
};

// スプレッドシート設定
const SPREADSHEET_CONFIG = {
  id: 'YYYYYYYYYYYYYYYYYYYYYYYYYYY',
  systemSheetName: "SYSTEM",
  messageSheetName: "message"
};

// システムプロンプトを取得
function getSystemPrompt() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SPREADSHEET_CONFIG.systemSheetName);
  return sheet.getRange("A2").getValue();
}

// メッセージを処理
function processMessage(message) {
  if (!message) {
    return { error: "Invalid request. Please provide a message." };
  }

  const responseMessage = getGeminiApiResponse(message);
  logMessageToSheet(new Date(), message, responseMessage);
  return { echo: responseMessage };
}

// スプレッドシートにメッセージを記録
function logMessageToSheet(timestamp, message, response) {
  const ss = SpreadsheetApp.openById(SPREADSHEET_CONFIG.id);
  const sheet = ss.getSheetByName(SPREADSHEET_CONFIG.messageSheetName);
  sheet.appendRow([timestamp, message, response]);
}

// doGet関数の改善版
function doGet(e) {
  const response = processMessage(e.parameter.message);
  return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}

// Gemini APIへのリクエストを行い、レスポンスを返す関数
function getGeminiApiResponse(prompt = 'あなたができることは何ですか?') {
  // スクリプトプロパティからAPIキーを取得
  const apiKey = PropertiesService.getScriptProperties().getProperty('APIKEY');
  // Gemini APIのエンドポイントURL
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`;

  const systemPrompt = getSystemPrompt();  // 修正: getSystemPrompt関数を呼び出してSYSTEM_PROMPTの値を動的に取得
  prompt = `${systemPrompt}\n${prompt}`;

  // Gemini APIリクエストに必要なペイロード
  const payload = {
    "contents": [
      {"role": "user","parts": { "text": prompt }}
    ],
  };

  // HTTPリクエストのオプション設定
  const options = {
    'payload': JSON.stringify(payload),
    'method' : 'POST',
    'muteHttpExceptions': true,
    'contentType': 'application/json'
  };

  // Gemini APIにリクエストを送信し、レスポンスを取得
  try {
    const responseText = UrlFetchApp.fetch(apiUrl, options).getContentText();
    const response = JSON.parse(responseText); 

    // レスポンスからテキストを抽出
    const { content: { parts: [{ text }] } } = response.candidates[0];
    console.log(text);
    return text;
  } catch (error) {
    console.error('APIリクエストでエラーが発生しました: ', error);
    return '少々お待ちください。';
  }
}

このシステムの利点

このBotシステムの最大の利点は、GPTs の Instructionsがほぼ空であるため、プロンプトインジェクション攻撃に対して強いという点です。プロンプトインジェクション攻撃は、不正な入力を介してシステムに意図しない命令を実行させる攻撃手法の一つですが、このBotはそのような攻撃に対して耐性を持っています。

GPTs のSchema

GPTs を活用したAPIの構築には、OpenAPIスペックが使用されます。以下は、LINE Botが受け取ったメッセージをエコーバックする機能を持つAPIのOpenAPI 3.0仕様の一部です。

openapi: 3.0.0
info:
  title: Echo Message API
  description: Echoes back the received message
  version: v1.0.0
servers:
  - url: https://script.google.com
paths:
  /macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/exec:
    get:
      operationId: echoMessage
      summary: Echoes back the received message
      description: Receives a message as a query parameter and echoes it back
      parameters:
        - name: message
          in: query
          description: The message to echo back
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  echo:
                    type: string

OpenAPIバージョン

openapi: 3.0.0

この行では、使用しているOpenAPIスペックのバージョンを宣言しています。ここでは3.0.0が使用されています。

API情報

info:
  title: Echo Message API
  description: Echoes back the received message
  version: v1.0.0
  • title: APIの名前を指定します。
  • description: APIの簡単な説明を記述します。
  • version: APIのバージョンを指定します。

サーバ情報

servers:
  - url: https://script.google.com

APIがホストされているサーバのURLを指定します。この例では、Google Apps ScriptでデプロイされたウェブアプリケーションのURLが使用されています。

パスと操作

paths:
  /macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/exec:
    get:
      operationId: echoMessage
      summary: Echoes back the received message
      description: Receives a message as a query parameter and echoes it back
  • paths: APIが提供するエンドポイントのパスを定義します。
  • get: HTTPメソッドを指定します。この場合はGETメソッドです。
  • operationId: この操作を一意に識別するためのIDです。これは、コード生成ツールで使用されることがあります。
  • summary: 操作の簡単な説明を提供します。
  • description: 操作の詳細な説明を提供します。

パラメータ

parameters:
  - name: message
    in: query
    description: The message to echo back
    required: true
    schema:
      type: string
  • parameters: エンドポイントが受け取るパラメータを定義します。
  • name: パラメータの名前です。
  • in: パラメータがどこにあるかを指定します。この場合、クエリパラメータとして指定されています。
  • description: パラメータの説明です。
  • required: このパラメータが必須かどうかを指定します。
  • schema: パラメータの型を定義します。ここではstring型が使用されています。

レスポンス

responses:
  "200":
    description: Success
    content:
      application/json:
        schema:
          type: object
          properties:
            echo:
              type: string
  "400":
    description: Invalid request. Please provide a message.
  • responses: APIが返す可能性のあるレスポンスを定義します。
  • "200": HTTPステータスコードを指定します。ここでは、成功時に200 OKを返します。
  • description: レスポンスの説明です。
  • content: レスポンスボディの内容を定義します。この例では、application/jsonタイプのオブジェクトを返し、そのオブジェクトにはechoプロパティが含まれています。
  • "400": エラーレスポンスを定義します。無効なリクエストが行われた場合、400 Bad Requestを返します。

まとめ

GPTs、GAS、Gemini Proを組み合わせることで、高度な対話機能を持つBotを簡単に作成できます。この記事で紹介したBot「幻(げん)」は、その一例に過ぎません。皆さんもこの仕組みを応用して、様々な用途に合わせたBotを作成してみてはいかがでしょうか。

参考サイト

https://gptstore.ai/gpts/fOysSRAMK-actionsgpt
https://platform.openai.com/docs/actions/introduction

コメント

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