OpenAI API×Googleスプレッドシートを使いやすくするライブラリを作る

Web制作

今回はGASでOpenAI APIを叩きやすくするライブラリを作成していきます。

ライブラリに必要な機能

  • OpenAI APIを叩く
  • プロンプトの入力に応じて、レスポンスを出力する関数(gpt3.5, gpt4を使い分ける)

作ってみたコード

/*
このライブラリの使い方
ID:1XfXAZJYp7VGEMgLWgdPbkxy21Jt_v_oDHxops2nQCIBe86Twd_zigZtG
あらかじめAPIキーを用意して下さい

基本
- post(model, apiKey, temperature, messages)
  - model:gpt-4/gpt-3.5-turboなどのモデルを指定する
  - apiKey:OpenAI APIのAPIキーを指定する
  - temperature:回答のばらつきを0~2で設定する。0に近いほど確定的な返答になる(通常、0~0.7で良い)
  - messages:messageオブジェクトを指定する。content()もしくはmessages()で作成できる

- content(role, prompt)
  - role:user/system/assisstantなどの発言者を指定する。通常userで良い
  - prompt:ChatGPTに尋ねる文章の全文を指定する

- messages(postMessages,newContent):連続したチャットをする場合に利用する
  - postMessages:前回までの会話(messagesオブジェクト)
  - newContent:新たに追加する会話(content)

- gpt4(api, prompt)/gptTurbo(api,prompt)
  - api:OpenAI APIキー
  - prompt:ChatGPTに投げるプロンプト
 */
function headers(apiKey) {
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + apiKey
  };
  return headers;
}
//httpレスポンスをOpenAI APIに投げる
function post(model, apiKey, temperature, messages) {
  var payload = {
    "model": model,
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers(apiKey),
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    Logger.log(response)
  } catch (e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
  }
  var data = JSON.parse(response.getContentText());
  return data;
}
// contentを作成する
function makeContent(role, prompt) {
  let content = { "role": role, "content": prompt };
  return content;
}
// contentをあつめてメッセージを作成する。(関数なんか使わないほうが早い)
function makeConversation(postMessages,newContent) {// 
  let messages = postMessages.push(newContent);
  return messages
}
// chat of GPT4
function gpt4(api, prompt) {
  let messages = [makeContent("user", prompt)];
  let data = post("gpt-4", api, 0, messages)
  return data;
}
// chat of gpt-3.5-turbo
function gptTurbo(api, prompt) {
  let messages = [makeContent("user", prompt)];
  let data = post("gpt-3.5-turbo", api, 0, messages)
  return data;
}

ChatGPTによる添削・改善案

リンク

エラーハンドリング

エラーハンドリング: 現在の実装では、post関数内でエラーが発生した場合、ログにエラーを出力していますが、それ以上のエラーハンドリングがありません。これを改善するためには、例外をキャッチした際に適切なエラーメッセージを返すようにすると良いでしょう。

ChatGPT GPT4
try {
  var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
  Logger.log(response)
} catch (e) {
  Logger.log('Error:')
  Logger.log(e)
  throw new Error('API request failed: ' + e);
}
ぶっくま
ぶっくま

throw new Error()ってメソッドは初めて知った。正直蛇足感ある。

むしろLogger.logのほうが蛇足なのかな

デフォルト値の設定

デフォルト値の設定: post関数のtemperatureパラメータにデフォルト値を設定すると、ライブラリの利用者がこのパラメータを省略できるようになります。

ChatGPT GPT4
function post(model, apiKey, messages, temperature = 0.5) {
  // ...
}
ぶっくま
ぶっくま

デフォルト値を設定するのはともかくとして、デフォルト値が設定された関数を利用するときにエラーを出さないようにする方法がいまいちわかってないんですよね。設定するデフォルト値が一個だけならなんとかなるんだけども

ドキュメンテーションの充実

ドキュメンテーションの充実: コード内のコメントは基本的な使い方を説明していますが、各関数の詳細な説明やパラメータの説明、戻り値の説明などを追加すると良いでしょう。

ChatGPT GPT4
/**
 * Send a POST request to the OpenAI API.
 *
 * @param {string} model - The model to use, such as 'gpt-4' or 'gpt-3.5-turbo'.
 * @param {string} apiKey - The API key for the OpenAI API.
 * @param {Array} messages - The messages to send to the API.
 * @param {number} [temperature=0.5] - The temperature to use for the response.
 * @return {Object} The response from the API.
 */
function post(model, apiKey, messages, temperature = 0.5) {
  // ...
}
ぶっくま
ぶっくま

そういえば、変数を説明するときは「@param」みたいに記述する的なことを以前CHatGPTで学んだ気がする。今回は面倒だからいいや。次回から頑張る

関数の分割

関数の分割: post関数はAPIリクエストを送信するだけでなく、レスポンスをパースしています。これらの機能を分割して、それぞれの関数が一つの責任を持つようにすると、コードの可読性と再利用性が向上します。

ChatGPT GPT4
function sendRequest(model, apiKey, messages, temperature) {
  var payload = {
    "model": model,
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers(apiKey),
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    return response;
  } catch (e) {
    Logger.log('Error:')
    Logger.log(e)
    throw new Error('API request failed: ' + e);
  }
}

function parseResponse(response) {
  var data = JSON.parse(response.getContentText());
  return data;
}

function post(model, apiKey, temperature, messages) {
  var response = sendRequest(model, apiKey, messages, temperature);
  var data = parseResponse(response);
  return data;
}
ぶっくま
ぶっくま

リクエストを送る部分とレスポンスをJSONからパースする部分に分けるってことか。

この2つの機能は別々に実行することってまず無いと思うんだけど、分ける必要あるかな…?

改善案

/*
このライブラリの使い方
ID:1XfXAZJYp7VGEMgLWgdPbkxy21Jt_v_oDHxops2nQCIBe86Twd_zigZtG
あらかじめAPIキーを用意して下さい

基本
- post(model, apiKey, temperature, messages)
  - model:gpt-4/gpt-3.5-turboなどのモデルを指定する
  - apiKey:OpenAI APIのAPIキーを指定する
  - temperature:回答のばらつきを0~2で設定する。0に近いほど確定的な返答になる(通常、0~0.7で良い)
  - messages:messageオブジェクトを指定する。content()もしくはmessages()で作成できる

- content(role, prompt)
  - role:user/system/assisstantなどの発言者を指定する。通常userで良い
  - prompt:ChatGPTに尋ねる文章の全文を指定する

- messages(postMessages,newContent):連続したチャットをする場合に利用する
  - postMessages:前回までの会話(messagesオブジェクト)
  - newContent:新たに追加する会話(content)

- gpt4(api, prompt)/gptTurbo(api,prompt)
  - api:OpenAI APIキー
  - prompt:ChatGPTに投げるプロンプト
 */
function headers(apiKey) {
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + apiKey
  };
  return headers;
}
//httpレスポンスをOpenAI APIに投げる
function post(model, apiKey, temperature=0.7, messages) {
  var payload = {
    "model": model,
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers(apiKey),
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    Logger.log(response)
  } catch (e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
    throw new Error('API request failed: ' + e);
  }
  var data = JSON.parse(response.getContentText());
  return data;
}
// contentを作成する
function makeContent(role, prompt) {
  let content = { "role": role, "content": prompt };
  return content;
}
// contentをあつめてメッセージを作成する。(関数なんか使わないほうが早い)
function makeConversation(postMessages,newContent) {// 
  let messages = postMessages.push(newContent);
  return messages
}
// chat of GPT4
function gpt4(api, prompt) {
  let messages = [makeContent("user", prompt)];
  let data = post("gpt-4", api, 0, messages)
  return data;
}
// chat of gpt-3.5-turbo
function gptTurbo(api, prompt) {
  let messages = [makeContent("user", prompt)];
  let data = post("gpt-3.5-turbo", api, 0, messages)
  return data;
}
改善点
  • post()関数のtemperatureにデフォルト値を設定した。
  • 例外エラー処理にthrow new Error()を追加してみた。

感想

元のコードはほとんどChatGPTに頼らずに書いたコードだったが、GPT4の添削はそれほど大きな変更は要求されなかったように思う。多くの添削を却下したが、目的にそぐわないと判断したため許してほしい。

今後の目標

このライブラリを利用し、スプレッドシート専用の新たなライブラリを作成したい。

最終的にはアドオンとして公開したり、クラウドソーシング向けのサービスとすることを目標とする。

コメント

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