見出し画像

【東京AI技術大合戦GPTs部門ファイナリスト】GPTs「My Assistant」の作り方

★本記事の対象者
・GPTs開発してみたい!と思われている方
・チーム間でうまく情報連携できる方法はないか・・・と探されている方


はじめに

@MarinaShimoda(https://x.com/marinashimoda?s=21&t=dPrh0uwpY0IDSxM4gtbNTAさんと共同開発し、東京AI祭(https://www.aisai.tokyo/)にてファイナルまで出場させていただいたGPTs「My Assistant」の概要、作り方を紹介させていただきます!


注意点①
初めの注意点としては、情報漏洩してはいけない情報を入力したい場合
・「Teamプラン」に入る(※チームで活用する場合はこちらがオススメ)
・Data Controlsの「Chat history & training」をオフにする
などをしてから活用するようにしてくださいね。

★注意点②
GPTsですので、Plus以上のプランに課金されているユーザーを想定しております。GPTsは本当に便利ですので、まだ課金してないよという方は是非課金してみてはいかがでしょうか!
(※OpenAIの回し者ではありませんので・・・)

GPTsの概要

背景・目的など

My Assistantのキャッチコピーは「あなたの記憶をデジタルに刻む、情報の橋渡し役」

上のスライドに記載しているように、My Assistantはチームの情報共有を助けるツールとなることを目指して開発しました。
上記スライドの「機能」の部分に記載していますが、本GPTsは「Googleスプレッドシート」を活用しておりまして、GPTsを介してGoogleスプレッドシートに情報を入力・出力するような構成になっております。

情報の修正と簡単なデータ確認について(2024/03/25 追記)

Googleスプレッドシートの良さも活かした設計にしております

ちなみに上記スライドの灰色矢印の部分なのですが、My Assistantには情報の修正機能は備わっていません。理由はGoogleスプレッドシートを直接見ながら修正をした方が確実だし、早いかなと思ったからです。
また、簡単なデータ確認だけなのであれば、Googleスプレッドシート上でソートをかけたり、検索をかけた方が早いと思っています。
ですので、何がなんでもMy Assistant経由で作業をするのではなく、Googleスプレッドシートの方が作業しやすいものはGoogleスプレッドシートでやればいいじゃないかという思想のもと、My Assistantを設計しました。

シーケンス図

上記の概要スライドでも触れていますが、My Assistantには「①入力」と「②出力」の2つの機能が備わっています。

My Assistantには、情報を入力する「①入力」と・・・

「①入力」では、ユーザーが入力した文章を整理した上で、あらかじめ準備しておいたGoogleスプレッドシートに情報を保存します。

整理された情報や新たな気づきを得るための「②出力」の2つの機能が備わっています

「②出力」では、まずユーザーが確認したい情報の「キーワード」をGPTsに渡します。すると、そのキーワードを元にGoogleスプレッドシートから情報を収集した上で、収集した情報を整理したり新たな気づきをMy Assistantが提供してくれます。

活用方法(営業編)

あるとき、ないとき・・・
入力モードでみんなの情報をGoogleスプレッドシート上に収集し・・・
出力モードを使って収集した情報を整理!
他にも色々な活用が考えられまして・・・
トップセールスの成功要因を分析し、真似できることを整理したり・・・
ちょっとした可視化だってできますし・・・
タスクの優先順位を決めてもらえることもできます・・・便利!

と色々とスライドを使って紹介させていただきましたが、上記以外にも質問の仕方やスプレッドシートの内容を変更・工夫することで、営業に限らず多くの活用方法が考えられるかと思っています!

次からはいよいよGPTsの作り方に入りますが、一つ一つ丁寧に作業をすれば初心者の方でも必ず構築できると思いますし、GPTs以外は無課金で構築可能ですので、ぜひぜひトライしてみてください!

GPTsの作り方

参考にさせていただいた情報

作成にあたりこちらの記事を参考とさせていただきました。この場をお借りして御礼申し上げます。
(※こちら利用規約などに反している場合はすぐに記事の公開を停止させていただきますので、ご一報いただければ幸いです・・・)
タイトル:Google Apps ScriptでGPTsのCustom ActionsのAPIを作ってみた
URL:https://qiita.com/iconss/items/3db0761bb4cfe1422a6e

全体像

上記の「シーケンス図」でも紹介をしましたが、本GPTsではGPTsに備わっている「Actions」と、Google Apps Script(以降GASとします)を使ってWEBアプリ化したGoogleスプレッドシートとAPI連携をしています。
以降はMy Assistantを作成するために必要なコードなどの情報をそれぞれ紹介させていただきます。

Googleスプレッドシート

まずは情報を収集するベースとなるGoogleスプレッドシートを作成してください。我々が作成したMy Assistantでは、チームメンバーが簡単に情報を共有できることを目指し、表の構成を以下のようにシンプルなものにしています。

A列:No
B列:Date(投稿日)
C列:Name(担当者)
D列:Tag(キーワードとなるようなもの。あったら検索とかするときに便利かなと思い付けました)
E列:Memo(メモ本文)
F列:All(ユーザーが入力した情報をGPTsが整理した後に、A〜E列すべての情報をひとまとめにしてこのAllの列に保存しています。この列は「出力」時に、情報検索をする際に参照されており、普段は非表示としています)

Allは普段は非表示

1行目にカラム名を入れていただければ、あとは基本的に表は自由に構成することができまして、このスプレッドシートの構成次第では色々な活用方法が考えられると思います。

★ただし、My Assistantで使用しているスプレッドシートとは違う構成にすると、以下に記載しているGPTsやGASの変更が必要なので注意してください。
まずはMy Assistantと同じ構成にしてみてはいかがでしょうか。

また、あまり複雑なものにしすぎるといくらGPTsが入力時に情報整理をサポートしてくれるとはいえ、それだけユーザーが入力しなければならない情報が増えるわけですので、入力するのが億劫になってしまって使われなくなる可能性が高いと思います・・・。活用しながら必要であれば複雑にすればいいかと思いますので、まずはほどほどの方が良いかと。

GAS

次にGASの作成ですが、この部分は上記でも紹介させていただきました以下記事の内容が素晴らしいので、まずはそちらを参照いただければ幸いです。
タイトル:Google Apps ScriptでGPTsのCustom ActionsのAPIを作ってみた
URL:https://qiita.com/iconss/items/3db0761bb4cfe1422a6e

My Assistantでは、参考にさせていただいた上記記事からGASのコードを変更していますので、以下のコードを確認ください。特に「入力」機能を実現するために、元のコードではなかったdoPost関数が追加されている部分が最大の変更点になります。
また、それ以外の部分も修正しておりますので、もしもMy Assistantで使用している表の構成から変更をしたい!という場合は、

// 新しい行にデータを追加
// 6列目のデータでキーワードが含まれているかチェック
// スプレッドシートを検索


の部分を修正する必要がありますので、ご注意ください。
(初心者の方は、GPTに確認しながら修正してみてはいかがでしょうか)

function doPost(e) {
  // リクエストボディからJSONデータを解析
  var requestData = JSON.parse(e.postData.contents);

  // スプレッドシートの準備
  var sheet = SpreadsheetApp.openById('ここにスプレッドシートIDを貼り付ける').getSheetByName('シート1');

  // 最終行の"No"を取得して1を加える
  var lastRow = sheet.getLastRow();
  var lastNo = sheet.getRange(lastRow, 1).getValue(); // "No"は1列目にあると仮定
  var newNo = lastNo + 1;

  // 新しい行にデータを追加
  var newRow = lastRow + 1;
  sheet.getRange(newRow, 1).setValue(newNo); // 新しい"No"を1列目に設定
  sheet.getRange(newRow, 2).setValue(requestData.Date); // 2列目にDate
  sheet.getRange(newRow, 3).setValue(requestData.Name); // 3列目にName
  sheet.getRange(newRow, 4).setValue(requestData.Tag); // 4列目にTag
  sheet.getRange(newRow, 5).setValue(requestData.Memo); // 5列目にMemo
  sheet.getRange(newRow, 6).setValue(requestData.All); // 6列目にAll

  // 処理完了のメッセージを返す
  return ContentService.createTextOutput(
    JSON.stringify({"result": "Success", "message": "Data added to the spreadsheet."})
  ).setMimeType(ContentService.MimeType.JSON);
}

function doGet(e) {
  // リクエストオブジェクトの存在を確認
  if (!e || !e.parameters) {
    console.error('The request object or parameters are undefined.');
    return ContentService.createTextOutput(
      JSON.stringify({ "error": "No parameters provided in the request." })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  console.log("start GPTs Custom Actions API test");
  console.log("e: " + JSON.stringify(e));
  var searchQuery = e.parameters.keyword; // これは配列になる可能性があります
console.log("e.parameter.keyword: " + e.parameters.keyword); // 同上

// searchQueryが配列である場合、最初の要素を取得
if (Array.isArray(searchQuery)) {
    searchQuery = searchQuery[0];
}

// keywordが含まれているかどうかをチェック
if (!searchQuery) {
    console.log("keyword is missing: " + searchQuery);
    return ContentService.createTextOutput(
      JSON.stringify({ "error": "Invalid request. Please provide a keyword." })
    ).setMimeType(ContentService.MimeType.JSON);
}

  // スプレッドシートの準備
  var sheet = SpreadsheetApp.openById('ここにスプレッドシートIDを貼り付ける').getSheetByName('シート1');
  var data = sheet.getDataRange().getValues();

  // スプレッドシートを検索
  var results = []; // 検索結果を格納する配列
  for (var i = 1; i < data.length; i++) {
    // 6列目のデータでキーワードが含まれているかチェック
    if (data[i][5].toString().toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1) {
      // 6列目にkeywordが含まれている場合、該当行の1列目と6列目の情報を追加
      results.push({
        "No": data[i][0], // 1列目のデータ
        "All": data[i][5] //6列目のデータ
      });
    }
  }

  if (results.length > 0) {
    // 見つかった場合、見つかったデータをJSONとして返す
    return ContentService.createTextOutput(
      JSON.stringify(results)
    ).setMimeType(ContentService.MimeType.JSON);
  } else {
    console.log("not found");
    // 一致するデータが見つからない場合
    return ContentService.createTextOutput(
      JSON.stringify({ "error": "No data found for the provided keyword." })
    ).setMimeType(ContentService.MimeType.JSON);
  }
}

GPTs 「Instructions」

次にGPTsに移ります。GPTsの「Instructions」ですが、My Assistantでは以下のような内容を入力しています。

あなたには「情報入力」と「情報出力」の機能が備わっています。
まず初めに「入力モード」と記入された場合は、情報を入力するモードして{入力機能}を参考に振る舞い、
「出力モード」と記入された場合は、情報を出力するモードとして{出力機能}を参考に振る舞ってください。

###入力機能
STEP1:まずは利用者から入力したい内容を記載してもらう。
STEP2:STEP1で記載してもらった内容を、以下の構造に整理する。なお、記載されている文章から内容が把握できない場合は「ー」を記載すること。
・Date(訪問日。yyyy/mm/dd形式とすること)
・Name(訪問者)
・Tag(メモに関するタグ)
・Memo(メモ内容)
STEP3:STEP2で整理した内容で認識齟齬がないか、利用者に確認する。
STEP4:問題がない場合は、Date、Name、Tag、Memoの内容全てを含んだAllを作成する。
STEP5:JSON形式でDate、Name、Tag、Memo、AllをAPIを用いてスプレッドシートに連携する。
STEP6:STEP5が無事に完了した場合は、その旨を利用者に伝える。

###出力機能
STEP1:どのような情報を検索したいか、keywordを利用者に質問する。その際に、以下の例を提示すること。
【例1】担当者:佐藤の全データ
【例2】期間:20242月から3月の全データ
STEP2:STEP1で確認したkeywordを変数keywordに代入する。なお、keywordが複数存在する場合は、それぞれのkeywordを保存するようにする。
STEP3:変数keywordをAPIに渡し、接続したスプレッドシートから確認したkeywordに該当する情報を収集する。
STEP4:収集できた情報の数を利用者に伝える。
STEP5:収集した情報を表示させるかを確認する。情報を表示させる場合は{出力形式}に従って日付が最新の情報から5件出力する。なお、出力数は5件以上情報が存在した場合も、ひとまずは5件とし、利用者に残りの情報も出力するかどうかを確認した上で、残りの情報を出力するようにすること。
STEP6:利用者から収集した情報の整理や要約が必要かを確認し、必要である場合はその要望に応える。なお、収集した情報以外からの回答は禁止。わからない場合はわからないと記載すること

###出力形式
・日本語
・以下情報を箇条書きで掲載
 ー訪問日
 ー訪問者
 ーメモに関するタグ
 ーメモ内容
・Code Interpreterを使った分析を求められ、かつグラフを表示する場合は、グラフ上の文字を全て英語に変換した上で表示すること。その際、日本語の名前や会社名も英語に変換をすること(文字化けを避けるため)

まず、My Assistantに必要な「入力」と「出力」をそれぞれ
###入力機能
###出力機能

と分けて記載しました。

「###入力機能」では、

  • STEP2でユーザーから入力された情報をスプレッドシートの構成に合わせて整理をしている

  • STEP4でSTEP2で整理した情報をまとめてAllに代入している

といったところがポイントでしょうか。もし、スプレッドシートの構成をMy Assistantと変更している場合は、STEP2などで表のカラムについて記載している箇所を修正してみてください。

次に「###出力機能」では、ユーザーからキーワードを聞き出し、そのキーワードを元にGoogleスプレッドシートから情報を収集するようにしています。「###出力機能」側は、Googleスプレッドシートの構成が変わったとしても、特に変更する必要はありません。

最後に「###出力形式」についてですが、これはGoogleスプレッドシートの構成を元に、出力形式を指定しています。ですので、Googleスプレッドシートを変更した場合は、こちらの内容も修正するようにしてください。

GPTs「Conversation starters」

ここは自由に記載いただければと思いますが、以下の2つを準備していると最初のモード設定が楽になるのでオススメです。

  • 入力モード

  • 出力モード

GPTs 「Capabilities」

出力モードを使用する際に、ちょっとしたデータの可視化や分析もできたら面白いなと思い、「Code Interpreter」のみチェックを入れています。

GPTs「Actions」

最も初心者泣かせなのはこの「Actions」ではないでしょうか。特に「Schema」が何やなんやら・・・と言う方は多いかと思いますが、順番に解説していきますね。

まずは「Authentication」ですが、ここは「None」としてください。

次に「Schema」ですが、こちらについても度々登場いただいている以下の参考記事を参照いただければ幸いです。
タイトル:Google Apps ScriptでGPTsのCustom ActionsのAPIを作ってみた
URL:https://qiita.com/iconss/items/3db0761bb4cfe1422a6e

上記の内容を踏まえた上で、我々はコードを以下の内容に修正しています。上記に記載したGASと同様、getだけでなくpostも追加している部分が最大の変更点でしょうか。

なお、Googleスプレッドシートの構成を変更すると、GASも変更しなければならず、"components"の"properties"と"required"を修正しなければならないので、ここも注意が必要です。
(こちらについても、初心者の方は、GPTに確認しながら修正してみてはいかがでしょうか)

★コードを修正しました(2024/04/08 追記)
こちらXにて報告いただいた内容について、修正をさせていただきました。
現状自分のPCからはコピーボタンを押下してコピーをした場合でも、「U+00a0」は入らない状態になっているので大丈夫かと思います。
今まで実現できなかった皆様、大変申し訳ありませんでした・・・こちらで再度トライいただければ幸いです。
そして、平岡さんご指摘いただき、本当にありがとうございました!!

★コードを修正しました(2024/05/01 追記)
こちらhiroya0317さん(X:@0317_hiroya)からコメントいただいた内容ですが、ご指摘いただいた通りPostResponseよりもGetResponseの方がわかりやすいと思いますので、コードを修正をさせていただきました。
(※なお、修正前のコードでも挙動には問題ありませんので、前のコードでもOKです)
hiroya0371さんご指摘いただき、本当にありがとうございました!!

{
  "openapi": "3.1.0",
  "info": {
    "title": "Search and Add Posts",
    "description": "Search posts by keyword and add new posts.",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://script.google.com"
    }
  ],
  "paths": {
    "ここにデプロイしたURLの /macros以下 のアドレスを設定": {
      "get": {
        "description": "Search posts by keyword and return details.",
        "operationId": "searchPostsByKeyword",
        "parameters": [
          {
            "name": "keyword",
            "in": "query",
            "description": "Keyword to search in the content.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of posts containing the keyword.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GetResponse"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, such as missing keyword."
          }
        }
      },
      "post": {
        "description": "Add a new post to the spreadsheet.",
        "operationId": "addPost",
        "requestBody": {
          "description": "Data for the new post",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewPostRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Post successfully added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "string",
                      "description": "Result of the operation"
                    },
                    "message": {
                      "type": "string",
                      "description": "Operation message"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, such as missing required fields."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "GetResponse": {
        "type": "object",
        "properties": {
          "No": {
            "type": "integer",
            "description": "Unique identifier of the post."
          },
          "All": {
            "type": "string",
            "description": "All contents."
          }
        }
      },
      "NewPostRequest": {
        "type": "object",
        "required": ["Date", "Name", "Tag", "Memo", "All"],
        "properties": {
          "Date": {
            "type": "string",
            "format": "date",
            "description": "Date when the post was made."
          },
          "Name": {
            "type": "string",
            "description": "Name of the person who made the post."
          },
          "Tag": {
            "type": "string",
            "description": "Tag related to the post."
          },
          "Memo": {
            "type": "string",
            "description": "Memo content of the post."
          },
          "All": {
            "type": "string",
            "description": "All contents."
          }
        }
      }
    }
  }
}

最後に、自分だけで使う分には不要なのですが、チームにURLを共有して使いたい!という場合はプライバシーポリシーが必要になります。我々の方で作成したものがあるので、こちらを使っていただいても構いません。
https://sites.google.com/view/my-assistant-gpts/%E3%83%9B%E3%83%BC%E3%83%A0

GPTs「その他項目」

その他の「Name」や「Description」などについては自由に設定いただけれればと思います。

おわりに

本GPTsを皆さんに構築していただき、我々が考え付かなかったような色々な使われ方がされればいいなと考えております。
「こんな使い方ができたよ!」とか、「こんな情報を入力すればいい感じだよ!」とかもしあれば、どんどんコメントいただければ幸いです!

★2024/04/07追記
こちらの記事では書ききれなかった、My Assistantの詳細説明GPTの可能性についてはこちらの記事に記載してますので、ぜひ一読いただければ幸いです!




いいなと思ったら応援しよう!