KongのAIプラグインを使ってAPIリクエストの機密情報を簡単にマスキングする

APIで受け取るリクエストを加工したい時がある。
例えば問い合わせフォームから連絡を貰う場合、個人情報に繋がるデータは保管しない、というルールになっていると格納前にマスキングなどして加工する必要が出てくる。
こういったケースの場合、大昔だと個々に加工することが一般的だった気がする。
![20240626103919.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/47196fec-98d5-75e1-7bbd-781c90e6cf7c.png)

最近だと転送前に加工したり、中継サーバやAPI Gatewayなどで加工することも増えている。
![20240626104407.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5ca923d8-e497-ca98-f07c-597935e9b855.png)

ただ、この場合でも加工処理をコーディングする必要があり、作り込むのが結構面倒だったりする。
これをKong Gatewayの[AI Request Transformer Plugin](https://docs.konghq.com/hub/kong-inc/ai-request-transformer/)を使うと加工処理・マスキング処理をAI側にまかせて、開発側ではコーディングなどして実装する必要がなくなる。
![20240626105020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f3f0675e-fd0d-a32f-6aa5-87ba8ad737f6.png)

今回はこの使い方を確認する。

## AI Request Transformer Pluginとは
AI Request Transformer PluginとはKong Gatewayのプラグインであり、バックエンドのサービスにリクエストを投げる前に設定されたLLMのサービスにリクエストを渡してリクエストを加工することが出来るものである。
[公式ページ](https://docs.konghq.com/hub/kong-inc/ai-request-transformer/)のフローがAI Response Transformer Pluginの挙動も混じってて分かりづらかったので、以下公式のフローからAI Request Transformer Pluginのフローだけ抜き出したものを記載する。
```mermaid
sequenceDiagram
    participant Client
    participant KongGateway as Kong Gateway
    participant AIService as AI LLM service
    participant Backend as Backend service
    
    Client->>KongGateway: Sends a request
    KongGateway->>AIService: Sends client's request for transformation
    AIService->>KongGateway: Transforms request
    KongGateway->>Backend: Sends transformed request to backend
    Backend->>KongGateway: Returns response to Kong Gateway
    KongGateway->>Client: Returns transformed response to client
```
ここのAI LLM Serviceは6/26現在ではOpenAI, Azure, Anthropic, Cohere, Mistral, Llama2が選べるようになっている。

今回は機密情報をマスキングするようなユースケースで検証するが、使い方次第では以下のような事も出来ると思われる。
- 特定の会社名が入っていたら電話番号のフィールドに自動で値を追加する
- AIに問い合わせる際に補足情報を足す

コーディングせずにささっとリクエストの内容を加工したい場合に使えるものと思うと良さそう。

## 検証の前提
検証を行うにあたり、ここでは既に以下があるものとして進める。
- Kong Gateway
- LLMサービスのAPIトークン
- [deckコマンド](https://docs.konghq.com/deck/latest/)

Kong GatewayのAdmin APIは公開されており、deckコマンドで利用可能になっているものとする。
LLMサービスのAPIトークンはここでは[OpenAI](https://platform.openai.com/api-keys)のものを利用する。
お金をかけずに試したい人は[Cohere](https://dashboard.cohere.com/welcome/login)かローカルにLLMサービスを立てて使うとよい。

## 設定
検証では[httpbin.org](https://httpbin.org)を使う。
[httpbin.org](https://httpbin.org)は`/anything`にアクセスすると、リクエストの内容をそのまま表示してくれる。
```json
$ curl -X POST https://httpbin.org/anything -d "key=value"
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "key": "value"
  },
  "headers": {
    "Accept": "*/*",
：（省略）
```
これを使ってリクエストが書き換わっているかを確認する。
構成としては以下のようになる。
![20240626114704.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/3fd81295-9f44-3527-22de-2865a8f71bb4.png)

最初にKong Gatewayの設定ファイルを作成する。
以下のdeck用YAMLが設定となる。

```yaml
LLM_APITOKEN="sk-proj-xxxx"
cat <<EOF > ./ai-transformer.yaml
services: 
- name: public-httpbin
  host: httpbin.org
  path: /
  protocol: https
  port: 443
  routes:
  - name: httpbin-transformer
    paths:
    - /transformer
    plugins:
    - name: ai-request-transformer
      config:
        prompt: JSON内のクレジットカードの番号をマスクして
        llm:
          route_type: llm/v1/chat
          auth:
            header_name: Authorization
            header_value: Bearer $LLM_APITOKEN
          model:
            provider: openai
            name: gpt-4

EOF
```
`services`と`routes`については説明は省略する。
`plugins`の部分は設定は非常にシンプルで、`prompt`のところに加工したい内容を記載し、`llm`のところに使うLLMのサービス、モデル名、認証方法を記載するだけである。
AI側のAPIのエンドポイントはプラグインで隠蔽されており、ユーザが意識する必要はない。
(自前で用意したLLMサービスを使う場合は設定する必要あり）
なおCohereやその他のLLMサービスを使う場合は`model`部分を以下のような感じで修正し、トークンをCohereのものに変えるだけである。
```yaml
          model:
            provider: cohere
            name: command-r
```
YAMLファイルの作成が終わったらdeckコマンドで設定を反映する。
一度今の設定を取り出す。
```sh
deck gateway dump -o /tmp/dump.yaml
```
今の設定に追記する形で作成したYAMLを適用する。
```sh
deck gateway sync /tmp/dump.yaml ./ai-transformer.yaml
```
特に問題なければKong ManagerからService/Routeが作成され、RouteにAI Request Transformer Pluginが適用されていることが確認できる。
![20240626142107.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/0d0860f2-ad50-ac01-f5ff-a88e11e24c1b.png)

## 検証
Kong Gateway経由でhttpbin.orgにリクエストを送ってみる。
先程プロンプトには以下のように設定した。
```yaml
prompt: JSON内のクレジットカードの番号をマスクして
```
リクエスト内にクレジットカードの番号があればマスクされるはずだ。
以下のようにクレジットカードの番号を含むリクエストを投げてみる。
```json
PROXY=https://192.168.64.202
curl -s -k --http1.1 -X POST ${PROXY}/transformer/anything \
  --data-raw \
  '{"Question": "DMMでVISAのクレジットカードは使えますか？私のクレジットカード番号は1234-5678-9012-3456です。ユーザIDは08642157です。"}' | jq .
```
出力結果は以下となった。
```json
：（省略）
  "files": {},
  "form": {
    "{\"Question\": \"DMMでVISAのクレジットカードは使えますか？私のクレジットカード番号は****-****-****-3456です。ユーザIDは08642157です。\"}": ""
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "172",
：（省略）
```
クレジットカード番号が`****-****-****-3456`と末尾の番号を除いてマスキングされ、ユーザIDはマスキングされず期待通りの結果となった。
末尾の番号を含めてマスキングしたいとか、細かな要望はプロンプトを調整することで実現出来る。
`prompt`で以下のように「全て」という言葉を追加して再度試してみる。
```yaml
prompt: JSON内のクレジットカードの番号を全てマスクして
```
結果は以下。
```json
"{\"Question\": \"DMMでVISAのクレジットカードは使えますか？私のクレジットカード番号はxxxx-xxxx-xxxx-xxxxです。ユーザIDは08642157です。\"}"
```
上手く行った。
ということで、マスキングのためのコーディングなどを行うことなくリクエストにマスクを掛けることが出来た。
プロンプトエンジニアリングが必要にはなるが、実装コストが不要になる点は大きいので、リクエストの加工が必要なシーンでは利用を検討してみてもよいかと思う。
ただし、有料のLLMサービスを利用する場合は当然コストが掛かるのでその点は注意。