AI技術は大きな進歩を遂げ、私たちのワークフローを自動化してくれる貴重なツールになりました。今回のブログ記事では、OpenAIで取得したデータをスプレッドシートに入力するマクロをご紹介します。
OpenAI APIの利用
前回は、RapidAPIプラットフォームを通じて、OpenAI APIに接続しました。フェッチリクエストを仲介するのに手っ取り早くて便利な方法です。しかし、ベーシックプランでは月に100リクエストしかできません。そこで今回は、さらにレベルアップして、独自のNode.js Expressサーバを作成します。これは、OpenAIライブラリを利用して、APIリクエストを適切な言語モデルに直接提供するものです。また、ブラウザからフェッチ・リクエストを行う際に、CORSエラーを回避することができるようになります。
ご注意!OpenAIへの直接のリクエストは、OpenAIのアカウントを登録し、個人のAPIキーを取得することで可能となります。
サーバーのセットアップ
まず、ターミナルで以下のコマンドを実行して、サーバ用の新しいフォルダを作成し、新しい Node.js プロジェクトをセットアップします。
1 |
$ npm init -y |
次に、以下の依存関係をインストールします。
- express パッケージ – サーバー機能を促進する重要なフレームワークです。
- cors パッケージ – CORS を有効にするためのミドルウェアを提供します。
- openaiパッケージ – Node.jsからOpenAI APIにアクセスするための便利な機能を提供します。
- dotenv パッケージ – .env ファイルから process.env に環境変数をロードします。
これらをインストールするには、ターミナルで npm instal コマンドを実行します。
1 2 3 4 |
$ npm install express $ npm install openai $ npm install dotenv --save $ npm install cors |
その後、.env ファイルを作成します。そこにAPIキーを格納します。
1 2 |
OPEN_AI_API_KEY="<YourOpenAIkey" SECOND_API_KEY="YourAPIkey" |
OPEN_AI_API_KEYパラメータにはOpenAIから取得したAPIキーが、SECOND_API_KEYパラメータには弊社サーバーのAPIキーが入ります。セキュリティ対策のため、受信したフェッチリクエストに組み込んでおきます。次に、index.js ファイルでインストールしたパッケージを初期化します。
1 2 3 4 5 6 7 8 9 |
const express = require('express'); const cors = require('cors'); const { Configuration, OpenAIApi } = require('openai'); require('dotenv').config(); const app = express(); const openai = new OpenAIApi(configuration); app.use(cors()); app.use(express.json()); |
OpenAIへのリクエストが成功するには、OpenAIのAPIキーを含める必要があります。そのために、このAPIキーを.envファイルから抽出して、設定変数に格納します。
1 2 3 |
const configuration = new Configuration({ apiKey: process.env.OPEN_AI_API_KEY, }); |
次に、OpenAIにリクエストを送るためのpostルートを追加します。
1 2 |
app.post('/completion', async (req, res) => { } |
また、セキュリティ対策として、APIキーも実装したいと思います。この2つ目のAPIキーは、フェッチ・リクエストに含まれることが想定されています。このため、サーバーは受信したフェッチ・リクエストをチェックし、含まれるAPIキーを検証する必要があります。そのために、以下のコードをルートに追加します。
1 2 3 4 5 6 7 8 9 10 11 |
app.post('/completion', async (req, res) => { const requestApiKey = req.body.apiKey; if (!requestApiKey) { res.status(400).send({ error: "Error: API Key is missing." }); return; } if (requestApiKey !== process.env.SECOND_API_KEY) { res.status(401).send({ error: "Error: API Key is incorrect." }); return; } |
APIキーが正しければ、OpenAIにリクエストを送ります。
1 2 3 4 5 6 7 |
const completion = await openai.createCompletion({ model: 'text-davinci-003', prompt: req.body.prompt, temperature: 0, max_tokens: 300, }); res.json(completion.data.choices[0].text); |
- modelパラメータはテキスト生成に使用する言語モデルの名前を指定します.この場合はテキスト生成に最も適したモデルであるtext-davinci-003を指定します。
- promptパラメータは,言語モデルがテキストを生成するためのテキストまたはコンテキストを指定します.この値はフェッチリクエストのボディから取得します。
- temperatureパラメータは生成されるテキストのランダム性を制御します.0の値はテキストが完全に決定論的であることを意味し、高い値はより多様で予期しないテキストになることを意味します。
- max-tokensパラメータは、生成されるテキストの最大長をトークン(単語やサブワードなど)で示します。
最後に、ポート3000をリッスンするようにサーバーをセットアップする。
1 2 3 |
app.listen(3000, () => { console.log('Server running on port 3000'); }); |
index.jsのコード全体は以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const express = require('express'); const cors = require('cors'); const { Configuration, OpenAIApi } = require('openai'); require('dotenv').config(); const app = express(); const configuration = new Configuration({ apiKey: process.env.OPEN_AI_API_KEY, }); const openai = new OpenAIApi(configuration); app.use(cors()); app.use(express.json()); app.post('/completion', async (req, res) => { const requestApiKey = req.body.apiKey; if (!requestApiKey) { res.status(400).send({ error: "Error: API Key is missing." }); return; } if (requestApiKey !== process.env.SECOND_API_KEY) { res.status(401).send({ error: "Error: API Key is incorrect." }); return; } const completion = await openai.createCompletion({ model: 'text-davinci-003', prompt: req.body.prompt, temperature: 0, max_tokens: 300, }); res.json(completion.data.choices[0].text); }); app.listen(3000, () => { console.log('Server running on port 3000'); }); |
マクロの構築
まず、サーバーにフェッチリクエストを行います。
1 2 3 4 5 6 7 8 9 10 |
fetch('http://<your_server_address>/completion', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'List the 10 wealthiest countries in the world. Provide GPT figures', apiKey: '<YourAPIkey>' }) }) |
リクエストボディにpromptとapiKeyの値を指定しました。これらのパラメータは、受け取りたいデータとサーバーのAPIキーによって異なることに注意してください。次に、レスポンスをJSONデータとしてパースします。
1 |
.then(response => response.json()) |
応答テキストを改行で文字列の配列に分割し、アクティブなスプレッドシートを対象とします。
1 2 3 |
.then(data => { var arrAllRows = data.split(/\r?\n|\r/); var wSheet = Api.GetActiveSheet() |
その後、スプレッドシートの行と列を反復するネストされたループを作成し、split() 関数を使用します。SetValue メソッドを使用して、各セルに応答からのデータを入力し、空白を切り詰めます。
1 2 3 4 5 6 7 8 9 10 11 |
var i = 0; var j = 0; for (var singleRow = 0; singleRow < arrAllRows.length; singleRow++) { var rowCells = arrAllRows[singleRow].split('-'); for (var rowCell = 0; rowCell < rowCells.length; rowCell++) { wSheet.GetCells(i,j).SetValue(rowCells[rowCell].trim()); j = j + 1; } i = i + 1; j = 1; } |
マクロのコード全体は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
(function() { fetch('http://<your_server_address>/completion', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'List the 10 wealthiest countries in the world. Provide GPT figures', apiKey: 'dfhgsd63456efgdfhjikhfzf435332fdhd' }) }) .then(response => response.json()) .then(data => { var arrAllRows = data.split(/\r?\n|\r/); var wSheet = Api.GetActiveSheet(); var i = 0; var j = 0; for (var singleRow = 0; singleRow < arrAllRows.length; singleRow++) { var rowCells = arrAllRows[singleRow].split('-'); for (var rowCell = 0; rowCell < rowCells.length; rowCell++) { wSheet.GetCells(i,j).SetValue(rowCells[rowCell].trim()); j = j + 1; } i = i + 1; j = 1; } }); })(); |
では、スプレッドシートのエディタを開いて、マクロを実行してみましょう!
このブログで紹介したコンセプトを活用し、お客様のワークフローをより効率的にしていただければと思います。また、APIを活用し、日々の業務に取り入れていただければ幸いです。
ご質問やご意見がありましたら、お気軽にコメントやお問い合わせください。