Guardrails for Amazon Bedrock で機微情報フィルターを試す

## Guardrails for Amazon Bedrock の 3 機能目

今までコンテンツフィルター、禁止トピックとやってきましたが、今回は機微情報フィルターです。

https://qiita.com/kazuneet/items/89aeca6d8884c543daec

https://qiita.com/kazuneet/items/753f63c89739660be500

* コンテンツフィルター
* 禁止トピック
* 機密情報フィルター <- 今ココ
* 単語フィルター
* 文脈的根拠チェック

## 機微情報フィルターとは？

詳細は[ここを読め](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-sensitive-filters.html)、ですがざっくり言うと、
* モデルの入出力に対して個人を特定できる情報（PII）などの機密情報を検出できる
* 正規表現（regex）を使用して、ユースケースや組織に特有の機密情報を定義して設定できる

といった機能を提供します。
また、検出した時の挙動も 2 種類用意しており、ブロックとマスクがあります。ブロックは文字通りリクエストやレスポンスをブロックするのに対して、マスクは該当箇所をマスクすることができます。

PII も予め[定義](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-sensitive-filters.html)されているので、何を PII と判定するかは明確です。

ここでは全 PII を対象にマスクしてみましょう。ただし注意があります。

>Mask — Sensitive information filter policies can mask or redact information from model responses. For example, guardrails will mask PIIs while generating summaries of conversations between users and customer service agents. 
>(日本語訳) マスク - 機密情報フィルターポリシーは、モデルの応答から情報をマスクまたは編集することができます。例えば、ガードレールは、ユーザーとカスタマーサービス担当者の会話の要約を生成する際に、個人を特定できる情報をマスクします。

「**モデルの応答から情報をマスクまたは編集**」ここ試験（なんの?）に出るから要注意です。
マスクはモデルの出力にしか適用されません。
入力については人間がマスクをしろ、あるいはそもそも入れさせない、という感じです。

## 機微情報フィルターを試す

さて、早速やってみましょう。機微情報フィルター全部入りのガードレールを作ります。

### ガードレール作成

例によって CDK で。

```typescript:lib/guardrails-japanese-test-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { aws_bedrock as bedrock } from 'aws-cdk-lib';

export class GuardrailsJapaneseTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const cfnGuardrail = new bedrock.CfnGuardrail(this,`contentGuardrail`,{
      blockedInputMessaging: 'そんなの入れちゃダメぇぇぇ！！',
      blockedOutputsMessaging: '変なの出ちゃぅぅ！！',
      name : `PII-Guardrail`,
      sensitiveInformationPolicyConfig: {
        piiEntitiesConfig: [
          {'action': 'ANONYMIZE', 'type': 'AGE'},
          {'action': 'ANONYMIZE', 'type': 'AWS_ACCESS_KEY'},
          {'action': 'ANONYMIZE', 'type': 'AWS_SECRET_KEY'},
          {'action': 'ANONYMIZE', 'type': 'CA_HEALTH_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'CA_SOCIAL_INSURANCE_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'CREDIT_DEBIT_CARD_CVV'},
          {'action': 'ANONYMIZE', 'type': 'CREDIT_DEBIT_CARD_EXPIRY'},
          {'action': 'ANONYMIZE', 'type': 'CREDIT_DEBIT_CARD_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'DRIVER_ID'},
          {'action': 'ANONYMIZE', 'type': 'EMAIL'},
          {'action': 'ANONYMIZE', 'type': 'INTERNATIONAL_BANK_ACCOUNT_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'IP_ADDRESS'},
          {'action': 'ANONYMIZE', 'type': 'LICENSE_PLATE'},
          {'action': 'ANONYMIZE', 'type': 'MAC_ADDRESS'},
          {'action': 'ANONYMIZE', 'type': 'NAME'},
          {'action': 'ANONYMIZE', 'type': 'PASSWORD'},
          {'action': 'ANONYMIZE', 'type': 'PHONE'},
          {'action': 'ANONYMIZE', 'type': 'PIN'},
          {'action': 'ANONYMIZE', 'type': 'SWIFT_CODE'},
          {'action': 'ANONYMIZE', 'type': 'UK_NATIONAL_HEALTH_SERVICE_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'UK_NATIONAL_INSURANCE_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'UK_UNIQUE_TAXPAYER_REFERENCE_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'URL'},
          {'action': 'ANONYMIZE', 'type': 'USERNAME'},
          {'action': 'ANONYMIZE', 'type': 'US_BANK_ACCOUNT_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'US_BANK_ROUTING_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'US_INDIVIDUAL_TAX_IDENTIFICATION_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'US_PASSPORT_NUMBER'},
          {'action': 'ANONYMIZE', 'type': 'US_SOCIAL_SECURITY_NUMBER'}
        ],
      },
    })
    new bedrock.CfnGuardrailVersion(this, `contentGuardrailVersion`, {
      guardrailIdentifier:cfnGuardrail.attrGuardrailId,
      description: `initial version`,
    })
  }
}
```

これをデプロイしました。

### ガードレールを試す。

今回は日英 2 種類のプロンプトを用意しました。

```python:test.ipynb
prompt_list = [
    '''私の名前は呉和仁です。
38歳です。
使用しているアクセスキーは AKIAIOSFODNN7EXAMPLE で、シークレットアクセスキーは wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY です。
カナダの医療保険番号と社会保険番号を持っており、それぞれ ABCD 1234 5678, 123-456-789 です。
使用しているクレジットカードの番号は 0123-4567-8901-2345, 有効期限は 24/08 で、CVV は 123 です。
免許証の番号は300001234510でメールアドレスは kazuhitogo@example.jp です。
IBAN は FR14 2004 1010 0505 0001 3M02 606 で、使用している IP アドレスは、127.0.0.1 です。
車のナンバーは、品川 330 あ 12-34 です。
MACアドレスは 00-1B-8B-12-34-56 です。
パスワードは password を使いまわしており、電話番号は 03-1234-5678 です。
PIN も 1234 を使いまわしています。
SWIFT CODE は BOTKJPJTXXX です。
イギリスの National Health Number は 485 777 3456 で、National Insurance Number は AB 12 34 56 C です。
Unique Taxpayer Refference NUmber は 623 12345 67890 です。
自分のウェブサイトは https://example.jp/ です。
USERNAME は kazuhitogo でログインできます。
US Bank Account Number は 01234567890 で、Routing Number は 123456789 です。
US Individual Tax Identification Number は 123-45-6789 で、パスポート番号は 31195855 です。
US Social Security Number は 123-45-6789 です。''',
    '''My name is Kazuhito Go.
I am 38 years old.
The access key I use is AKIAIOSFODNN7EXAMPLE and the secret access key is wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY.
I have a Canadian health insurance number and social insurance number, which are ABCD 1234 5678 and 123-456-789 respectively.
The credit card I use has the number 0123-4567-8901-2345, an expiration date of 08/24, and a CVV of 123.
My driver's license number is 300001234510 and my email address is kazuhitogo@example.jp.
My IBAN is FR14 2004 1010 0505 0001 3M02 606 and the IP address I use is 127.0.0.1.
My vehicle license plate is Shinagawa 330 a 12-34.
My MAC address is 00-1B-8B-12-34-56.
I reuse the password "password" and my phone number is 03-1234-5678.
I also reuse the PIN 1234.
My SWIFT CODE is BOTKJPJTXXX.
My UK National Health Number is 485 777 3456 and my National Insurance Number is AB 12 34 56 C.
My Unique Taxpayer Reference Number is 623 12345 67890.
My website is https://example.jp/.
My USERNAME is kazuhitogo and I can log in with that.
My US Bank Account Number is 01234567890 and the Routing Number is 123456789.
My US Individual Tax Identification Number is 123-45-6789 and my passport number is 31195855.
My US Social Security Number is 123-45-6789.
'''
]
```

すべてにひっかかるようなダミーのデータを用意してみました。
早速ガードレールにかけてみます。
再掲ですが mask は output のみにかけられるので、`source='OUTPUT'` になっているところにご注意ください。

```python:test.ipynb
for prompt in prompt_list:
    print(prompt)
    response = brt.apply_guardrail(
        guardrailIdentifier='xxxxx', # さっき作ったガードレール
        guardrailVersion='DRAFT',
        source='OUTPUT',
        content=[
            {
                'text': {
                    'text': prompt,
                    'qualifiers': [
                        'guard_content',
                    ]
                }
            },
        ]
    )
    pprint(response['outputs'])
```

```text:出力
私の名前は呉和仁です。
38歳です。
使用しているアクセスキーは AKIAIOSFODNN7EXAMPLE で、シークレットアクセスキーは wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY です。
カナダの医療保険番号と社会保険番号を持っており、それぞれ ABCD 1234 5678, 123-456-789 です。
使用しているクレジットカードの番号は 0123-4567-8901-2345, 有効期限は 24/08 で、CVV は 123 です。
免許証の番号は300001234510でメールアドレスは kazuhitogo@example.jp です。
IBAN は FR14 2004 1010 0505 0001 3M02 606 で、使用している IP アドレスは、127.0.0.1 です。
車のナンバーは、品川 330 あ 12-34 です。
MACアドレスは 00-1B-8B-12-34-56 です。
パスワードは password を使いまわしており、電話番号は 03-1234-5678 です。
PIN も 1234 を使いまわしています。
SWIFT CODE は BOTKJPJTXXX です。
イギリスの National Health Number は 485 777 3456 で、National Insurance Number は AB 12 34 56 C です。
Unique Taxpayer Refference NUmber は 623 12345 67890 です。
自分のウェブサイトは https://example.jp/ です。
USERNAME は kazuhitogo でログインできます。
US Bank Account Number は 01234567890 で、Routing Number は 123456789 です。
US Individual Tax Identification Number は 123-45-6789 で、パスポート番号は 31195855 です。
US Social Security Number は 123-45-6789 です。
[{'text': '私の名前は呉和仁です。\n'
          '{AGE}。\n'
          '使用しているアクセスキーは {AWS_ACCESS_KEY} で、シークレットアクセスキーは {AWS_SECRET_KEY} '
          'です。\n'
          'カナダの医療保険番号と社会保険番号を持っており、それぞれ {UK_NATIONAL_INSURANCE_NUMBER} '
          '{CA_HEALTH_NUMBER}, {UK_NATIONAL_INSURANCE_NUMBER} です。\n'
          '使用しているクレジットカードの番号は {CREDIT_DEBIT_CARD_NUMBER}, 有効期限は '
          '{CREDIT_DEBIT_CARD_EXPIRY} で、CVV は {CREDIT_DEBIT_CARD_CVV} です。\n'
          '免許証の番号は300001234510でメールアドレスは {EMAIL} です。\n'
          'IBAN は {INTERNATIONAL_BANK_ACCOUNT_NUMBER} で、使用している IP '
          'アドレスは、{IP_ADDRESS} です。\n'
          '車のナンバーは、{LICENSE_PLATE} です。\n'
          'MACアドレスは {MAC_ADDRESS} です。\n'
          'パスワードは password を使いまわしており、電話番号は {PHONE} です。\n'
          'PIN も {PIN} を使いまわしています。\n'
          'SWIFT CODE は {SWIFT_CODE} です。\n'
          'イギリスの National Health Number は {UK_NATIONAL_HEALTH_SERVICE_NUMBER} '
          'で、National Insurance Number は {UK_NATIONAL_INSURANCE_NUMBER} です。\n'
          'Unique Taxpayer Refference NUmber は '
          '{UK_UNIQUE_TAXPAYER_REFERENCE_NUMBER} です。\n'
          '自分のウェブサイトは {URL} です。\n'
          'USERNAME は {USERNAME} でログインできます。\n'
          'US Bank Account Number は {US_BANK_ACCOUNT_NUMBER} で、Routing Number '
          'は {US_BANK_ROUTING_NUMBER} です。\n'
          'US Individual Tax Identification Number は '
          '{US_INDIVIDUAL_TAX_IDENTIFICATION_NUMBER} で、パスポート番号は '
          '{US_PASSPORT_NUMBER} です。\n'
          'US Social Security Number は {US_SOCIAL_SECURITY_NUMBER} です。\n'}]
My name is Kazuhito Go.
I am 38 years old.
The access key I use is AKIAIOSFODNN7EXAMPLE and the secret access key is wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY.
I have a Canadian health insurance number and social insurance number, which are ABCD 1234 5678 and 123-456-789 respectively.
The credit card I use has the number 0123-4567-8901-2345, an expiration date of 08/24, and a CVV of 123.
My driver's license number is 300001234510 and my email address is kazuhitogo@example.jp.
My IBAN is FR14 2004 1010 0505 0001 3M02 606 and the IP address I use is 127.0.0.1.
My vehicle license plate is Shinagawa 330 a 12-34.
My MAC address is 00-1B-8B-12-34-56.
I reuse the password "password" and my phone number is 03-1234-5678.
I also reuse the PIN 1234.
My SWIFT CODE is BOTKJPJTXXX.
My UK National Health Number is 485 777 3456 and my National Insurance Number is AB 12 34 56 C.
My Unique Taxpayer Reference Number is 623 12345 67890.
My website is https://example.jp/.
My USERNAME is kazuhitogo and I can log in with that.
My US Bank Account Number is 01234567890 and the Routing Number is 123456789.
My US Individual Tax Identification Number is 123-45-6789 and my passport number is 31195855.
My US Social Security Number is 123-45-6789.

[{'text': 'My name is {NAME}.\n'
          'I am {AGE} old.\n'
          'The access key I use is {AWS_ACCESS_KEY} and the secret access key '
          'is {AWS_SECRET_KEY}.\n'
          'I have a Canadian health insurance number and social insurance '
          'number, which are {CA_HEALTH_NUMBER} and 123-456-789 respectively.\n'
          'The credit card I use has the number {CREDIT_DEBIT_CARD_NUMBER}, an '
          'expiration date of {CREDIT_DEBIT_CARD_EXPIRY}, and a CVV of '
          '{CREDIT_DEBIT_CARD_CVV}.\n'
          "My driver's license number is {DRIVER_ID} and my email address is "
          '{EMAIL}.\n'
          'My IBAN is {INTERNATIONAL_BANK_ACCOUNT_NUMBER} and the IP address I '
          'use is {IP_ADDRESS}.\n'
          'My vehicle license plate is {LICENSE_PLATE} {LICENSE_PLATE} '
          '{UK_NATIONAL_INSURANCE_NUMBER}.\n'
          'My MAC address is {MAC_ADDRESS}.\n'
          'I reuse the password "password" and my phone number is {PHONE}.\n'
          'I also reuse the PIN {PIN}.\n'
          'My SWIFT CODE is {SWIFT_CODE}.\n'
          'My UK National Health Number is {UK_NATIONAL_HEALTH_SERVICE_NUMBER} '
          'and my National Insurance Number is {UK_NATIONAL_INSURANCE_NUMBER}\n'
          'My Unique Taxpayer Reference Number is '
          '{UK_UNIQUE_TAXPAYER_REFERENCE_NUMBER}.\n'
          'My website is {URL}.\n'
          'My USERNAME is {USERNAME} and I can log in with that.\n'
          'My US Bank Account Number is {US_BANK_ACCOUNT_NUMBER} and the '
          'Routing Number is {US_BANK_ROUTING_NUMBER}.\n'
          'My US Individual Tax Identification Number is '
          '{US_INDIVIDUAL_TAX_IDENTIFICATION_NUMBER} and my passport number is '
          '{US_PASSPORT_NUMBER}.\n'
          'My US Social Security Number is {US_SOCIAL_SECURITY_NUMBER}.\n'
          '\n'}]
```

結果を見てみると、

日本語の場合以下が検出漏れ
* 名前
* パスワード
* 免許証番号

英語の場合以下が検出漏れ
* カナダの社会保険番号がひっかからない

日本語の名前はさておき、パスワードや免許証番号は本物じゃない適当な数値を入れてしまっていたので、パスワードは bitwarden で生成したパスワード、免許証番号は私の日本の番号を試してみます。

(見せられません)

結果、パスワードはちゃんと検出しましたが、免許証番号は検出できませんでした。また、日本語の名前をいくつか試したのですが、やはり検出できませんでした。
そして、`mask(ANONYMIZE)` にするとフィルターごとにタグというかプレースホルダーというかが出てくるので、これを置換してやれば好きにカスタマイズできますね。

## カスタムフィルター（正規表現フィルター）を試す
もう一つ正規表現をためしてみましょう。
日本の免許証番号は12桁の数字なので、それを使って取ってみます。
(実際にはチェックデジットや、発行場所、発行年、発行回数等があるので単純な１２桁じゃダメですがここでは簡単のため１２桁の数字とします）

### 正規表現フィルターの作成

CDK で。
ちなみに**正規表現で使うバックスラッシュのエスケープに気をつけてください**。
１時間くらい溶かしました。

```typescript:guardrails-japanese-test-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { aws_bedrock as bedrock } from 'aws-cdk-lib';

export class GuardrailsJapaneseTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const cfnGuardrail = new bedrock.CfnGuardrail(this,`contentGuardrail`,{
      blockedInputMessaging: 'そんなの入れちゃダメぇぇぇ！！',
      blockedOutputsMessaging: '変なの出ちゃぅぅ！！',
      name : `PII-Guardrail`,
      sensitiveInformationPolicyConfig: {
        regexesConfig: [{
          action: 'ANONYMIZE',
          name: 'license-number',
          pattern: '\\d{12}',
          description: '免許証番号',
        }],
      },
    })
    new bedrock.CfnGuardrailVersion(this, `contentGuardrailVersion`, {
      guardrailIdentifier:cfnGuardrail.attrGuardrailId,
      description: `initial version`,
    })
  }
}
```

### 正規表現フィルターを試す

さて実行してみましょう。
```python:test.ipynb
prompt_list = [
    '''あなたの免許証番号は 012345678901 です。'''
]
for prompt in prompt_list:
    print(prompt)
    response = brt.apply_guardrail(
        guardrailIdentifier='xxxxx',
        guardrailVersion=guardrail['version'],
        source='OUTPUT',
        content=[
            {
                'text': {
                    'text': prompt,
                    'qualifiers': [
                        'guard_content',
                    ]
                }
            },
        ]
    )
    pprint(response['outputs'])
```

```text:出力
あなたの免許証番号は 012345678901 です。
[{'text': 'あなたの免許証番号は {license-number} です。\n'}]
```

完璧ですね！

## まとめ
機微情報フィルターは日本語の名前と免許証番号とカナダの社会保険番号以外は機能した。
ただし、カナダの社会保険番号は、実際の番号ではなくダミーで試したので本物でやたら機能する可能性があるので、持っている方はぜひためしてみてください。
また、正規表現は機能しますが、バックスラッシュのエスケープにはご注意ください。