【保存版】GASでAzure OpenAI APIを使いこなす!基本から並列処理まで

【保存版】GASでAzure OpenAI APIを使いこなす!基本から並列処理まで

挨拶

最近は、「Dify入門シリーズ」の検証+執筆にいそしんでいる龍ちゃんです。応用的ステップとして、GASで本格運用する業務アプリの開発などもやっていました。こちらの業務アプリでは、DifyのAPIを叩くわけでなく、Azure OpenAI Service(AOAI)のREST APIで生成AIを実行しています。

今回は、参照用のサンプルコードとして「プロパティサービスを活用してアクセスキーなどを保護しつつ、APIのリクエストを作成するスクリプト」を書いておきます。

検証に使用したモデルのバージョンとAPIのバージョンは以下になります。

項目バージョン
モデルGPT-4o-mini
AOAI APIバージョン2024-10-21

サンプルコード:スクリプトプロパティでアクセスキーを取得

AOAIのモデルのデプロイ等は、終わっていると仮定して進めていきます。REST APIのアクセスには、以下の情報が必要になります。必要な情報はこちらを参考に取得してください。

コンテンツ説明
エンドポイントAzure Portal上で確認するエンドポイント
キーAzure Portal上で確認するアクセスキー
モデル名モデルをデプロイした際に設定したデプロイ名
APIバージョンMS Learn上で参照して最新のGAリリースの情報を取得

こちらの情報を取得して、スクリプトプロパティとして保存しておきます。スクリプトプロパティの登録などはこちらを参考にしてください。以下のパラメータで保存しています。

コンテンツスクリプトプロパティ
エンドポイントAOAI_API_URL
キーAOAI_API_KEY
モデル名AOAI_API_MODEL
APIバージョンAOAI_API_VERSION

上記のパラメータで保存することで、以下のスクリプトがそのまま使うことができます。

const apiEndpoint = PropertiesService.getScriptProperties().getProperty("AOAI_API_URL");
const modelName = PropertiesService.getScriptProperties().getProperty("AOAI_API_MODEL");
const apiVersion = PropertiesService.getScriptProperties().getProperty("AOAI_API_VERSION");
const apiKey = PropertiesService.getScriptProperties().getProperty("AOAI_API_KEY");
const apiUrl = `${apiEndpoint}/openai/deployments/${modelName}/chat/completions?api-version=${apiVersion}`;

サンプルコード:AOAIへの単発リクエスト(fetch)

AOAIへの単発のチャットリクエストを送信するサンプルとなります。fetch:公式リファレンス情報はこちらです。

// AOAIへの単発リクエスト
function testRequestToAOAI() {
  const apiEndpoint = PropertiesService.getScriptProperties().getProperty("AOAI_API_URL");
  const modelName = PropertiesService.getScriptProperties().getProperty("AOAI_API_MODEL");
  const apiVersion = PropertiesService.getScriptProperties().getProperty("AOAI_API_VERSION");
  const apiKey = PropertiesService.getScriptProperties().getProperty("AOAI_API_KEY");
  const apiUrl = `${apiEndpoint}/openai/deployments/${modelName}/chat/completions?api-version=${apiVersion}`;

  const payload = {
    messages: [
      {
        role: "system",
        content: `あなたは先生でユーザーは小学生です。優しく教えてください。` //システムプロンプト
      }, {
        role: "user",
        content: "先生!トイレ~" //ユーザープロンプト
      }
    ]
  }
  const option = {
    method: 'post',
    headers: {
      "Content-Type": "application/json",
      "api-key": apiKey
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  }

  const response = UrlFetchApp.fetch(apiUrl, option)
  const responsJson = JSON.parse(response.getContentText());
  try {
    const res = responsJson.choices[0].message.content.trim();
    Logger.log(res)
  } catch (e) {
    if(responsJson.error) Logger.log(responsJson.error.message) // リクエストでエラーレスポンスが返答されている可能性
    Logger.log(e.message)
  }

}

注目してほしい点としては、muteHttpExceptionsです。こちらは、リクエスト時にエラーが発生した場合は、responseにエラーオブジェクトを詰めて処理を継続させます。もしエラーの場合では、生成AIのレスポンス取得部分が落ちてしまうので、try~catchで捕捉しています。

catchで捕捉されるエラーは、jsonオブジェクトへアクセスできない等のエラーになるため、responseJsonを検証してエラーを取得しています。

龍ちゃん
龍ちゃん

Errorを継承したカスタムエラーを作成する方法もありますが、厳密なエラーハンドリングが求められないのであればこれぐらいの軽量でよいと思います。

サンプルコード:AOAIへの並列リクエスト(fetchAll)

fetchAllを活用して並列でAOAIへのリクエストを送信するサンプルとなります。fetchAll:公式リファレンスの情報はこちらです。

// AOAIへのリクエスト作成
function createRequestToAOAI(text) {
  const apiEndpoint = PropertiesService.getScriptProperties().getProperty("AOAI_API_URL");
  const modelName = PropertiesService.getScriptProperties().getProperty("AOAI_API_MODEL");
  const apiVersion = PropertiesService.getScriptProperties().getProperty("AOAI_API_VERSION");
  const apiKey = PropertiesService.getScriptProperties().getProperty("AOAI_API_KEY");
  const apiUrl = `${apiEndpoint}/openai/deployments/${modelName}/chat/completions?api-version=${apiVersion}`;

  const payload = {
    messages: [
      {
        role: "system",
        content: `あなたは先生でユーザーは小学生です。優しく教えてください。` //システムプロンプト
      }, {
        role: "user",
        content: text
      }
    ]
  }
  const request = {
    url: apiUrl,
    method: 'post',
    headers: {
      "Content-Type": "application/json",
      "api-key": apiKey
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  }
  return request
}

function testRequestsToAOAI(){
  const children = ["先生トイレ!","`右`の漢字の書き順がわかりません", "1+1は?"]
  const requests = children.map((value) => createRequestToAOAI(value))
  const reponses = UrlFetchApp.fetchAll(requests)
  reponses.forEach((response, index) => {
    const responsJson = JSON.parse(response.getContentText());
    try {
      const res = responsJson.choices[0].message.content.trim();
      Logger.log(`${children[index]}:${res}`)
    } catch (e) {
      Logger.log(responsJson)
      Logger.log(e)
    }
  })
}

こちらは、事前にリクエストを作成する関数を使用してリクエスト配列を生成し、そちらの内容をfetchAllで送信しています。単発のリクエストとは異なり、requestオブジェクト内にurlが含まれているのが特徴です。

終わり

今回は、GASでAOAIのREST APIを実行するサンプルコードを紹介しました。並列実行を含めたリクエストの作成方法と、エラーハンドリングの実装について解説しています。参考にしていただければ幸いです。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です