Flutter + NCMB + ChatGPTでAIチャットアプリを作る（その2: OpenAI APIの実行）

NCMBでは公式SDKとしてSwift/Objective-C/Kotlin/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回はコミュニティSDKの一つ、Flutter SDKを使ってChatGPTを用いたチャットアプリを作ってみます。[前回は画面の仕様とSDKの初期化について解説しました](https://qiita.com/goofmint/items/dcd08057ad4c37314517)ので、今回はスクリプトでの処理について解説します。

## コード

今回のコードは [NCMBMania/flutter-gpt-chat](https://github.com/NCMBMania/flutter-gpt-chat) にアップロードしてあります。実装時の参考にしてください。

## スクリプトについて

スクリプトというのはNCMBの一機能になります。サーバー側にプログラミングコードを置いて、それをクライアントから呼び出せます。クライアントでは結果だけを受け取れるので、スクリプトのプログラミングコードは非公開にできます。

今回のようにAPIキーを隠蔽したいケース、ロジックを隠蔽したりiOSとAndroidで共通化したいケースなどで利用されます。

## OpenAI APIについて

[OpenAIではNode.js向けにライブラリを提供しています](https://www.npmjs.com/package/openai)が、NCMBのスクリプトでは自由にライブラリが使える訳ではありません。そこで今回はREST APIを直接実行します。

APIドキュメントは[API Reference - OpenAI API](https://platform.openai.com/docs/api-reference/introduction)にあります。今回はユーザーとボット（アシスタント）で対話するので、 `/v1/chat/completions` を使っています。

## スクリプトの実装

### APIキー・URLの定義

まずOpenAIのAPIキーやURLを定義します。APIキーは自分のものと書き換えてください。

```jsx
const url = 'https://api.openai.com/v1/chat/completions';
const apiKey = 'YOUR_OPENAI_API_KEY';

// 使用する AI モデルの名前を定義する
const model = 'gpt-3.5-turbo';
```

### スクリプト処理の呼び出し関数を定義

スクリプト処理では関数を定義し、これをエクスポートします。HTTPアクセスがあると、この関数が呼ばれる仕組みです。

```jsx
// 関数をエクスポートする
module.exports = async function(req, res) {
　　// この中に処理を実装
}
```

以下はこの関数内に記述していきます。

### リクエスト内容を作成

クライアントから何からの文字列と、過去のやり取りを受け取ります。それらを使って、OpenAI APIにアクセスするリクエストデータを作成します。

```jsx
// リクエストボディからコンテンツを取得する
const { content } = req.body;
// リクエストボディから messages を取得するか、空の配列を設定する
const messages = req.body.messages || [];
// ユーザーのメッセージを messages 配列に追加する
messages.push({content, role: 'user'});
// OpenAI API に送信するパラメータを設定する
const params = {
  model,
  messages,
};
```

見ての通り、質問文を `content` というキー、過去のやり取りを `messages` というキーで受け取っています。

### OpenAI APIにリクエスト

作成したリクエストデータを送信します。

```jsx
// OpenAI API に POST リクエストを送信し、レスポンスを受け取る
const response = await request.post(url)
  .set('Content-Type', 'application/json') // ヘッダーに Content-Type を設定する
  .set('Authorization', `Bearer ${apiKey}`) // ヘッダーに API キーを設定する
  .send(JSON.stringify(params)); // JSON 形式のパラメータを送信する
```

### レスポンスの返却

最後にスクリプトの実行結果として、OpenAI APIの結果を返却します。

```jsx
// レスポンスから AI の返答を取得し、クライアントに JSON 形式で返す
res.json({text: response.body.choices[0].message.content});
```

### スクリプト全体のコード

全体のコードは以下の通りです。

```jsx
// `superagent` ライブラリをインポートする
const request = require('superagent');

// OpenAI API のエンドポイントと API キーを定義する
const url = 'https://api.openai.com/v1/chat/completions';
const apiKey = 'YOUR_OPENAI_API_KEY';

// 使用する AI モデルの名前を定義する
const model = 'gpt-3.5-turbo';

// 関数をエクスポートする
module.exports = async function(req, res) {
  // リクエストボディからコンテンツを取得する
  const { content } = req.body;
  // リクエストボディから messages を取得するか、空の配列を設定する
  const messages = req.body.messages || [];
  // ユーザーのメッセージを messages 配列に追加する
  messages.push({content, role: 'user'});
  // OpenAI API に送信するパラメータを設定する
  const params = {
    model,
    messages,
  };
  // OpenAI API に POST リクエストを送信し、レスポンスを受け取る
  const response = await request.post(url)
    .set('Content-Type', 'application/json') // ヘッダーに Content-Type を設定する
    .set('Authorization', `Bearer ${apiKey}`) // ヘッダーに API キーを設定する
    .send(JSON.stringify(params)); // JSON 形式のパラメータを送信する

  // レスポンスから AI の返答を取得し、クライアントに JSON 形式で返す
  res.json({text: response.body.choices[0].message.content});
}
```

## 管理画面にアップロード

できあがったスクリプトをNCMBの管理画面でアップロードします。管理画面でテスト実行もできるので確認してください。今回はスクリプトを `text.js` として、POSTメソッドで動作するようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/3018a777-9560-e449-d2ae-1a5b137b673a.png)


## 開発時の注意

スクリプトを開発している際にはテスト実行したくなると思います。そのための環境を[NCMBMania/node-script-dev-env](https://github.com/NCMBMania/node-script-dev-env) に用意していますのでご利用ください。 `npm start` した後で以下のようなcurlコマンドで実行結果を確認できます。

```sh
$ curl -XPOST -H "Content-Type: application/json" \
-d '{"content": "彼の身長は？", "messages": [{"role": "user", "content": "大谷翔平は誰ですか？"}, {"role": "assistant", "content": "大谷翔平は、日本のプロ野球選手で、現在は アメリカのロサンゼルス・エンゼルスに 所属している投手兼打者です。彼は 、2013年に日本の北海道日本ハムファイターズに入団し、2016年には日本プロ野球の最優秀選手に選ばれました。2017年には、アメリカのメジャーリーグベースボール（MLB）の ロサンゼルス ・エンゼルスに移籍し、同年にはアメリカンリーグの新人王に選ばれまし た。彼は、投手としても打者と しても優れた能力を持ち、世界的に注目されています。"}]}' \
http://localhost:3000/

{"text":"大谷翔平の身長は、190cmです。"}
```

## まとめ

今回はチャットアプリにおけるスクリプト機能について解説しました。スクリプト機能を使えばAPIキーを隠蔽化して安全にコードをクラウドで実行できます。ぜひ利用してください。

[次回はチャット画面を作成](https://qiita.com/goofmint/items/6ddd6cd3a6149306eaf4)し、チャットアプリを完成させます。