kintoneにChatGPTを取り入れるなら隠したいトコ

こんにちは、キン担ラボの本橋です。

ChatGPTの認証情報をユーザーから隠蔽する、という目的で kintone plugin APIのsetProxyConfg() の使い方を書きました。

https://www.monosus.co.jp/service/kintanlab/


# これまでのChatGPTプラグイン（キン担ラボ製）の大問題

先日、kintoneで使えるChatGPTプラグインを公開しました。

しかしこのバージョンにはひとつ、大きな問題がありました。

https://qiita.com/motohasystem/items/409461782d1aee6b8b0e

ChatGPTのAPI呼び出しに使うAPI Tokenがブラウザから丸見えになってしまう問題です。

![何も考えずにOpenAI APIを呼び出したとき](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13915/5f9dc3b6-c474-e6db-3f19-6ad7e4990cef.png)

開発者ツールを使うと一目瞭然ですね。何も考えないで作るからこういう下手を踏むのです。

リクエスト先のホストは api.openai.com となっていて、さらにリクエストヘッダのAuthorizationにはAPI Tokenがそのまま乗っています。

# 大問題を図解

図で表すとこう。

```mermaid
graph TD
    User((ユーザー)) --> Browser
    Browser(一覧画面) --認証情報付きリクエスト--> ChatGPT_API[ChatGPT API]
```


認証情報がユーザーの通信経路上に乗りブラウザから丸見え。

こうなるとユーザーはこの丸見えのAPI Tokenを悪用し放題。生殺与奪の権利を性善説に握らせた状態です。

こんなときに使いたいAPIが kintoneプラグインで使用できるkintone.plugin.app.setProxyConfig() と kintone.plugin.app.proxy() の両APIです。

https://cybozu.dev/ja/kintone/docs/js-api/plugins/set-config-for-proxy/

https://cybozu.dev/ja/kintone/docs/js-api/plugins/kintone-plug-in-proxy/

それぞれこんな使い方をします。

### setProxyConfig()の使用例

```javascript: kintone.plugin.app.setProxyConfig()の使用例
const url = this.api_endpoint + '/completions'
const method = "POST"
const headers = {
    'Authorization': 'Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
const data = {}
const result = kintone.plugin.app.setProxyConfig(URL, method, headers, data, () => {
    // 画面遷移させないためにsuccess callbackを定義する
    console.log({ result })
})
```


setProxyConfig()は呼び出し後のsuccess callbackを省略すると強制的に設定画面に画面遷移します。

今回は複数回のsetProxyConfig()呼び出しが想定される設計でしたので、画面遷移を避けるためconsole.log()だけのcallbackを指定しました。


### proxy()の使用例

```javascript: kintone.plugin.app.proxy()の使用例
const pluginId = PLUGINID
const url = this.api_endpoint + '/completions'
const method = "POST"
const headers = {
    'Content-Type': 'application/json',
    // 'Authorization': 'Bearer ' + API_KEY
}

const data = {
    "model": 'text-davinci-003',
    "prompt": prompt,
    "max_tokens": 256,
    "temperature": 0.7
}
return kintone.plugin.app.proxy(pluginId, url, method, headers, data)

```

headersの定義でコメントアウトしているAuthorizationヘッダーは、事前にsetProxyConfig()を設定してあればkintone proxy側で付与してくれます。dataについても同じように扱えるようです。

ドキュメントによると下記条件で付与を判定しているとのことでした。

> - この API を使ったリクエストでは、次の条件をすべて満たすときに、プラグインへ保存した情報がリクエストに追加されます。
>
>    - アプリが同一
>    - プラグインが同一
>    - HTTP メソッドが同一
>    - 実行する API の URL が前方一致する *1
>    - URL の大文字と小文字は区別されます。 

https://cybozu.dev/ja/kintone/docs/js-api/plugins/kintone-plug-in-proxy/


# 対策後の図解

この２つのAPIを使うことでプラグインからのAPI呼び出し安全な通信経路にできました。

```mermaid
graph TD
    Admin((管理者))--> config
    User((ユーザー)) --> desktop
    config(設定画面からkintone.plugin.app.setProxyConfig) -- 認証情報を保存 --> kintoneProxy[kintoneサーバー]
    kintoneProxy[kintone proxy] --認証情報付きリクエスト--> ChatGPT_API[ChatGPT API]
    desktop(一覧画面からkintone.plugin.app.proxy) -- ChatGPT APIをリクエスト --> kintoneProxy[kintone proxy]
```

一覧画面からはChatGPTの呼び出しに使うプロンプトのリクエストを投げてあげるだけで、自動的にkintone proxyが認証情報を追加してChatGPT APIにリクエストを投げてくれます。

この経路なら心配はご無用です。ユーザーから認証情報は見えません。

確認のため、対策後の通信を開発者ツールで覗いてみましょう。

![対策済み](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13915/1483fd0b-6821-435b-b769-767dbf5ed7ae.png)

リクエスト先のホストが monosus-dev.cybozu.com となっています。弊社の開発用kintoneサーバーです。

リクエストヘッダはトークンを含んでおらず、ユーザーからトークンを隠蔽できました。

もちろんChatGPTのレスポンスも受け取れています。

びろーん問題は解決！

# ちょっとまって設定画面の再表示は？

そうでした。プラグインを作るなら設定画面を開いた際に過去の保存内容を復元する必要がありますね。

復元ではこちらのkintone.plugin.app.getProxyConfig() APIを使ってください。

https://cybozu.dev/ja/kintone/docs/js-api/plugins/get-config-for-proxy/

設定画面を構築する初期化の時点で呼び出して、フォームに復元してあげましょう。


```javascript: kintone.plugin.app.getProxyConfig()の使用例
const proxy_config = kintone.plugin.app.getProxyConfig(input_field.URL, input_field.method)
if (proxy_config == null) { // 初回起動時
    return ""
}
```

保存情報がなければnullが返ってくるので適宜初期化します。

### 開発中の設定画面ビルダーをチラ見せ

kintoneプラグインを開発していると設定値の取り回しが結構面倒なことに気づきます。

- 入力を受け付けるフォームの構築
- プラグインの設定を保存
- 保存された設定を読み込み
- 必須項目の管理
- 認証情報の管理

設定値の保存や読み込みは項目が増えるたびに記述量が増えていきます。ここはプラグインを作っていてなかなか取り回しが面倒くさい部分です。

面倒なので共通コードをまとめてライブラリ化しています。いま開発中の設定画面ビルダーでは、以下のコードを加えれば自動でproxy周りも処理してくれるようにしました。

```javascript
    {
        'label': 'API Key'
        , 'desc': 'ChatGPT APIのAPI Tokenを指定してください。'
        , 'code': CONSTANTS.API_KEY
        , 'type': FieldType.Text
        , 'required': true
        , 'secret': true
        , 'URL': 'https://api.openai.com/v1/completions'
        , 'method': 'POST'
        , 'header': 'Authorization Bearer'
    }
```

https://github.com/motohasystem/KintoneChatGPTPlugin/blob/main/src/ts/config/settings.ts

このtsファイル単体さえ書けば設定画面のコーディングは完了です。

まだまだ開発中のため利用できない設定がありますが、いずれ設定画面ビルダーとして公開したいと思っています。もう少々お待ちください。


# ここまで反映済みのプラグインを公開中！

プラグイン自体もすぐにご利用いただけるzip形式でGithubにアップしています。ぜひお試しください。

https://github.com/motohasystem/KintoneChatGPTPlugin

ChatGPTをkintoneに導入することで、いわゆる自然言語処理的なロジックを簡単にkintoneに乗せられるようになりました。それどころか、ユーザーにプロンプトを入力させてよければどんな処理だって可能です。

前回の記事ではkintone上での自然言語処理的な使い道について実験してみました。あわせてこちらもお読みいただけると嬉しいです。

https://qiita.com/motohasystem/items/409461782d1aee6b8b0e

ChatGPTの導入をご検討中の方はぜひ、キン担ラボで作成したプラグインを試してみてください。kintoneの可能性がぐんと広がったことを理解していただけること請け合いです。


https://www.monosus.co.jp/service/kintanlab/