今回は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による添削・改善案
エラーハンドリング
エラーハンドリング: 現在の実装では、
ChatGPT GPT4post
関数内でエラーが発生した場合、ログにエラーを出力していますが、それ以上のエラーハンドリングがありません。これを改善するためには、例外をキャッチした際に適切なエラーメッセージを返すようにすると良いでしょう。
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のほうが蛇足なのかな
デフォルト値の設定
デフォルト値の設定:
ChatGPT GPT4post
関数のtemperature
パラメータにデフォルト値を設定すると、ライブラリの利用者がこのパラメータを省略できるようになります。
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で学んだ気がする。今回は面倒だからいいや。次回から頑張る
関数の分割
関数の分割:
ChatGPT GPT4post
関数はAPIリクエストを送信するだけでなく、レスポンスをパースしています。これらの機能を分割して、それぞれの関数が一つの責任を持つようにすると、コードの可読性と再利用性が向上します。
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;
}
感想
元のコードはほとんどChatGPTに頼らずに書いたコードだったが、GPT4の添削はそれほど大きな変更は要求されなかったように思う。多くの添削を却下したが、目的にそぐわないと判断したため許してほしい。
今後の目標
このライブラリを利用し、スプレッドシート専用の新たなライブラリを作成したい。
最終的にはアドオンとして公開したり、クラウドソーシング向けのサービスとすることを目標とする。
コメント