バックエンドクラウドサービスHexabaseで、ChatGPTと連携する

この記事では、HexabaseのActionScriptで外部Webサービスと連携する方法を解説します。題材として、OpenAIの[**ChatGPT API**](https://platform.openai.com/docs/introduction/overview)を取り上げます。

ChatGPTは、現在人気の人工知能チャットボットプログラムですね。

[**Hexabase**](https://www.hexabase.com/)は、Webサービスや企業システムの開発に利用できる国産バックエンドクラウドサービスです。このHexabaseを利用することで、ChatGPTを利用したWebサービスや企業システムを高速で開発できます。

この記事で解説しているのは、2つのサービスの基本的な連携方法です。ChatGPTのAPIをHexabaseから1回だけ呼び出して、その結果をフィールドに書き込みます。

ChatGPTの回答には数10秒程度の時間がかかることがあるため、質問してすぐに回答が取得されません。このデモプログラムでは、数10秒程度が過ぎてからアイテムを確認することで回答を表示できます。

## 作成するもの

今回作成するのは、単純はQ&Aツールです。質問に投稿するとChatGPTが回答してくれます。

フロントエンドは持っておらず、管理画面で直接操作して回答を得ています。

また、ひとつひとつの質問は独立しており、前の質問と回答を考慮しません。

![chatgpt_answer_list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/2f39e320-b5c7-e1f1-7652-be39555bc661.png)

### 利用するAPI

- OpenAI Chat API
  - endpoint: 'https://api.openai.com/v1/chat/completions'
  - model: 'gpt-3.5-turbo',

- UpdateItem | Hexabase API ガイド
  - https://apidoc.hexabase.com/docs/v0/item-actions/UpdateItem


## 準備

### ChatGPT API

1. OpenAI のアカウントを取得する
   - https://platform.openai.com/account/
2. OpenAI APIのAPI Keyを取得する
   - https://platform.openai.com/account/api-keys


次のCurlコマンドで動作確認できます。

```bash
$ curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "観光客向けに、ソメイヨシノについて150文字程度で説明して"}
  ]
}'
```

### Hexabase

- Hexabaseのアカウントを取得する
- ワークスペース・アプリケーション・データベースを用意する
- データベースに次のフィールドを用意する
  - 質問(Display ID = "title")
  - 回答(Display ID = "answer_ai")

- [アプリケーション設定 > プログラム拡張 > 定数の登録](https://devdoc.hexabase.com/docs/features/application_script)に、OpenAI APIのAPI Keyを設定する

![chatgpt_apikey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/a32bd03a-48f3-07cc-6652-eee096a391f4.png)


## アクションスクリプト

新規作成アクションのActionScript - Postに、次のスクリプトを設定する。

### ソースコード

```js
async function main(data) {

    logger.info("Main Process starting...");

    const ENDPOINT = 'https://api.openai.com/v1/chat/completions';
    const API_KEY = '{gpt_token}';
    const result_ai = await callChatGPT(data.title, ENDPOINT, API_KEY);
    
    logger.info("Result AI：" + result_ai);
    
    const appId = "APP-AI-posts";
    const datastoreId = "Db-Article";
    const comment_fieldId = "answer_ai"; // コメントを書き込むフィールドのID
    return await callHexaAPI(data, appId, datastoreId, comment_fieldId, result_ai);
}

async function callChatGPT(prompt, ENDPOINT, API_KEY) {

    return await axios.post(
            ENDPOINT,
            {
                model: 'gpt-3.5-turbo',
                messages: [{
                    role: "user",
                    content: prompt
                }],
                max_tokens: 200
            },
            {
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${API_KEY}`,
                },
            }
        ).then(response => {
            console.log('API呼び出し成功:', response.data.choices[0]);
            return response.data.choices[0].message.content;
        })
        .catch (error => {
            if (error.response) {
                console.log('API呼び出しエラー:', error.response.data);
            } else if (error.request) {
                console.log('APIへのリクエストエラー:', error.request);
            } else {
                console.log('エラー:', error.message);
            }
            throw new Error(error);
    });
}

async function callHexaAPI(data, appId, datastoreId, comment_fieldId, comment_ai) {
    return new Promise((resolve, reject) => {
    
        logger.info("処理を開始します...");
    
        // Hexabase APIを呼び出し
        const url = "api/v0/applications/" + appId + "/datastores/" + datastoreId + "/items/edit/" + data.i_id;
        const payload = {
            "item": {
                [comment_fieldId]: comment_ai
            },
            "use_display_id": true,
            "return_item_result": true,
            "is_force_update": true,
            "access_key_updates": {	
                "ignore_action_settings": true,
                "overwrite": false,
                "apply_related_ds": true,
                "groups_to_publish": []
            }
        } // APIのリクエストパラメータ
        return callAPIAsync('POST', url, payload).then(res => {
            logger.log(res.data);
            logger.info("処理が完了しました：" + data.title + "：" + comment_ai);
        }).catch(error => {
            logger.error("エラーが発生しました：" + error)
            reject(error);  
        });    
    
    });
}
```

### 解説

![chatgpt_action_script.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/7c45913f-71d2-628f-54b8-e348b6004969.png)

Hexabaseでは、データベースに対するCURDなどの基本的な操作をアクションと呼んでいます。ActionScriptは、このアクションをトリガーにして呼び出せるJavaScriptのコードです。

アクションを実行すると、ActionScriptのmain関数を呼び出します。

main関数からは、次の2つの関数を呼び出しています。

1つ目のcallChatGPT関数は、ChatGPTのAPIを呼出し、質問から回答を習得しています。

2つ目のcallHexaAPI関数は、HexabaseのAPIを呼出し、回答をフィールドに書き込みます。


## 動作

1. 管理画面のアプリケーションで、新規投稿ボタンを押す
2. アイテムの新規作成が表示されたら、質問を入力する
![chatgpt_question.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/8c0f3665-887a-e23e-2a62-f2805f6f2e35.png)

3. アイテムが作成されたら、アイテム詳細を閉じる(この段階では、回答は取得できていない)
4. 数10秒程度して、作成した質問アイテムをクリックすると、回答が書き込まれている
![chatgpt_answer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/7d11d36c-55cd-fee6-07e1-1de1bf67bfef.png)


## 関連情報

- 株式会社 Hexabaseー法人向けクラウドシステム『Hexabase』ー
  https://www.hexabase.com/

- OpenAI API
  https://platform.openai.com/

- Introducing ChatGPT
  https://openai.com/blog/chatgpt

- ChatGPT API利用方法の簡単解説 - Qiita
  https://qiita.com/mikito/items/b69f38c54b362c20e9e6

- ChatGPT APIを使ってみよう（PowerShellで） - Qiita
  https://qiita.com/mkht/items/b4d5fa42af490886d981