Twilio と OpenAI を Azure Functions でつないだ ChatGPT っぽいもの

# はじめに
Twilio と OpenAI 使って、お手軽な SMS チャットボットを作ってみます。1時間くらいでできちゃいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/b48a766e-c003-002a-73d9-ece705a0b706.png)

上図で右から順に準備していきます。

# OpenAI の準備

今回は OpenAI の Create completion API を使います。

https://beta.openai.com/docs/api-reference/completions/create

OpenAI にサインアップして、https://beta.openai.com/account/api-keys から API Key を取得しておきます。

# Azure Functions の準備

今回は Node ランタイムを選択しました。
アプリケーション設定に、`OPENAI_API_KEY` として、先程取得したキーを保存しておきます。

今回は、送受信両方[^1]を行うので[HTTP Trigger 関数](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-http-webhook?tabs=in-process%2Cfunctionsv2&pivots=programming-language-javascript) を作ります。

やることはとてもシンプルです。
1. `OPENAI_API_KEY` を使って OpenAI の Configのセットアップ。
   これは export する関数の外側で実施します。
1. HTTP リクエストフォームから入力の取得
   Twilio からの [WebHook](https://www.twilio.com/docs/messaging/guides/webhook-request)は、`application/x-www-form-urlencoded` で届き、`Body` という要素に SMS の本文が含まれています。本来は[シグネチャの検証](https://www.twilio.com/docs/usage/security)などをするべきですが、デモなので省略します。
1. OpenAI の Create Completion API を呼び出す
   [`text-davinci-003`](https://beta.openai.com/docs/models/gpt-3) モデルを使ってみます。各種パラメータの詳細は[こちら](https://beta.openai.com/docs/api-reference/completions/create)
1. SMS 返却用のデータ作成
  [TwiML](https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-node-js#generating-twiml-in-your-web-application)というフォーマットに整形します。今回はメッセージ本文のみの利用です。
1. Azure Functions としてのレスポンス整形
  Azure Functions として　HTTP トリガー関数の[レスポンス](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-reference-node?tabs=v2-v3-v4-export%2Cv2-v3-v4-done%2Cv2%2Cv2-log-custom-telemetry%2Cv2-accessing-request-and-response%2Cwindows-setting-the-node-version#response-object)を生成します。

```javascript
const { MessagingResponse } = require('twilio').twiml;
const { Configuration, OpenAIApi } = require("openai");

// 1. OpenAI の Configのセットアップ
const configuration = new Configuration({
    apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
module.exports = async function (context, req) {
    // 2. HTTP リクエストフォームから入力の取得
    // SMS incoming message Webhook fornated as "application/x-www-form-urlencoded"
    // https://www.twilio.com/docs/messaging/guides/webhook-request
    // How to parse formdata in Azure Functions
    // https://github.com/Azure/azure-functions-nodejs-worker/issues/347#issuecomment-984036447
    const form = req.parseFormBody();
    const input = form.get("Body").value.toString() || "Say Hello";

    // 3. OpenAI の Create Completion API を呼び出す
    // CreateCompletion API
    // https://beta.openai.com/docs/api-reference/completions/create
    const response = await openai.createCompletion({
        model: "text-davinci-003",
        prompt: input,
        max_tokens: 1000,
        temperature: 0.9,
        n: 1,
    });
    const resBody = response.data.choices[0].text || "Sorry";

    // context.log({In: input, Out: resBody})

    // 4.SMS 返却用のデータ作成
    // Generate TwiML response
    // https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-node-js#generating-twiml-in-your-web-application
    const twiml = new MessagingResponse();
    twiml.message(resBody);

    // 5. Azure Functions としてのレスポンス整形
    context.res = {
        headers: {
            "Content-Type": "text/xml"
        },
        body: twiml.toString()
    };
}
```

# Twilio の準備

無料トライアル用のアカウントを作成し、番号を取得、[Messaging の WebHook](https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-node-js#configure-your-webhook-url)に、Azure Functions の URL を [関数レベルのキー](https://learn.microsoft.com/ja-jp/azure/azure-functions/security-concepts?tabs=v4#authorization-scopes-function-level)とともに設定します。

https://www.twilio.com/blog/how-to-create-twilio-account-jp

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/0f855c80-cb60-8886-d0bb-dbb1e5326c0a.png)


# 動かしてみた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/a0caa3fc-1605-c7ad-a80f-11830505b69a.png)

トライアルアカウントなので、`Sent from your Twilio trial account -` が入ってしまいますが、無事に小麦粉入りの炒飯ができました。やったぜ。
※長文だと Azure Functions が完了しても、スマホ側に SMS が届くまでに結構時間がかかることがあるようです。


[^1]: Azure Functions には [Twilio バインディング](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-twilio?tabs=in-process%2Cfunctionsv2&pivots=programming-language-javascript) が用意されていますが、これは Output バインディングのみとなるため受信ができません。SMS 受信の Twilio からの WebHook を HTTP トリガー関数で受付、レスポンスを WebHook に返すことで SMS の送信も行います。