メインコンテンツへスキップ

Langfuseにおける個人情報(PII)マスキング:Guardrails for Amazon Bedrockの活用

著者
Hiromi Kuwa

LangfuseにおけるPIIマスキング手法の検討
#

前回の記事 では、llm-guardを用いたPIIマスキングについて検証しました。llm-guardは柔軟なモデル選択が可能である一方、日本語のPII検出にはまだ課題が残ることが分かりました。

そこで、今回はその代替手段として、Guardrails for Amazon Bedrockの機密情報フィルター(Maskモード)の利用を検討します。

Guardrails for Amazon Bedrock とは?
#

Guardrails for Amazon Bedrock は、Amazon Bedrock 上に構築される生成 AI アプリケーションに対して、以下の主要なガードレール(保護機能)を提供します。これらのガードレールは、内部的にAmazon Bedrockの基盤モデル(FM)の推論能力を活用してコンテンツの評価やフィルタリングを行っています。ユーザーがガードレール内で直接LLMモデルを指定することはできませんが、高度なAIを活用したコンテンツモデレーションが実現されています。

  • コンテンツフィルター: 憎悪、侮辱、性的、暴力、不法行為、プロンプト攻撃などのカテゴリに対してフィルタリング強度を調整できま
  • 禁止トピック: 特定の話題に関するプロンプトや応答をブロックできます
  • ワードフィルター: 指定した単語が含まれる入出力をブロックまたはマスキングできます
  • 機密情報フィルター (Sensitive Information Filters): 氏名、メールアドレス、電話番号、クレジットカード番号などの PII を含む入出力を検出・ブロック・マスキングする機能です

特に「機密情報フィルター」は、事前に定義された PII タイプ(31種類以上)に対応しており、正規表現(RegEx)を使用してカスタムの機密情報を定義することも可能です。

また、PII の処理方法として、以下の2つのモードを選択できます。

  • BLOCK: 機密情報が検出された場合、コンテンツ全体をブロックし、カスタムメッセージを返します
  • MASK: コンテンツに含まれる機密情報をマスキング(編集)し、[NAME]、[EMAIL]のような識別子タグに置き換えます

なぜGuardrails for Amazon Bedrockなのか?
#

Guardrails for Amazon Bedrockには、基盤モデルを呼び出すことなく、ガードレールのルールのみを適用してコンテンツを評価・マスキングできる独立したAPI(bedrock_runtime.apply_guardrail)が存在します。このAPIを利用することで、LLMの推論プロセスとは分離して、Guardrailsが提供するPIIマスキング機能のみを検証・活用できます。

このapply_guardrailメソッドを活用し、Langfuseのトレースにおけるマスキング処理を実現します。

PIIマスキングのテスト方法
#

今回は、以下の手順でGuardrails for Amazon Bedrockの機密情報フィルター(Maskモード)の動作を検証します。

事前準備
#

AWS上に有効なGuardrailsを作成する必要があります。今回は正規表現によるカスタム制約は検証せず、標準設定で利用可能な機密情報フィルターに焦点を当てます。

事前定義されているPIIフィルターはこちら に記載があります。

上記のページを参考に、コンソールまたはAPIから利用したい機密情報フィルターを設定していきます。今回は PII のマスキングを行うため、フィルターの動作は「マスク」を選択します。

参考

前回マスキング対象として期待した項目と、llm-guardのAnonymize機能が対応しているとされる項目、Guardrails form Amazon Bedrockの機密情報フィルターで相応すると思われる項目を並べてみました。

llm-guard と比較すると、Guardrails for Amazon Bedrock は標準で設定可能なPIIフィルターが充実していることが分かります。

PIIllm-guardGuardrail for Amazon Bedrock
氏名人名NAME
生年月日--
住所-ADDRESS
電話番号電話番号PHONE
メールアドレスメールアドレスEMAIL
運転免許証番号-DRIVER_ID
パスポート番号-US_PASSPORT_NUMBER
社会保障番号米国社会保障番号(SSN)US_SOCIAL_SECURITY_NUMBER
(クレジットカード情報) カード番号クレジットカードCREDIT_DEBIT_CARD_NUMBER
(クレジットカード情報) 有効期限-CREDIT_DEBIT_CARD_EXPIRY
(クレジットカード情報) セキュリティコード-CREDIT_DEBIT_CARD_CVV
(銀行口座情報)銀行名--
(銀行口座情報)支店名--
(銀行口座情報) 口座番号-US_BANK_ROUTING_NUMBER

Guardrailsを利用すれば、生年月日と銀行口座情報の銀行名、支店名を除き期待される項目は網羅できそうです。

今回の検証では、誤検知が起こらないかを確認する意味合いも含め、上記に含まれない項目を含めた31種をフィルターとして設定しました。

アプリケーションコード
#

前回同様、簡単なコードを用いて、プロンプトの入力とLLMの回答を模擬的に生成し、それぞれをLangfuseに登録する形式でテストを行いました。

前回のコードに対し、マスキング処理部分と初期化部分に手を加えます。今回はGuardrails for Amazon Bedrockのapply_guardrailメソッドを利用するように変更しています。

bedrockRuntime = boto3.client(
    'bedrock-runtime',
    region_name="*********",
    aws_access_key_id="**********************",
    aws_secret_access_key="**********************"
)
guardrail_id = "[あなたのガードレールのID]"
guardrail_version = "DRAFT" # またはバージョン番号

def masking_function(data: any, **kwargs) -> any
    if isinstance(data, str):
        responce = bedrockRuntime.apply_guardrail(
            guardrailIdentifier=guardrail_id,
            guardrailVersion=guardrail_version,
            source="INPUT", # 今回はシンプルにするため、常に"INPUT"として評価する
            content=[
                {
                    "text": {
                        "text": data
                    }
                }
            ]
        )

        # outputsからマスキング済みテキストを取得
        if 'outputs' in responce and len(responce['outputs']) > 0:
            sanitized_data = responce['outputs'][0].get('text', data)
            return sanitized_data
        else:
            # outputsがない場合は元のデータを返す
            return data
    return data

テストデータ
#

前回利用した以下のダミーデータを、今回もそのまま利用します。

私の個人情報は以下の通りです:

氏名:山田 太郎 Name: Taro Yamada

生年月日:1985年3月15日 Birthday: March 15, 198

住所:東京都新宿区西新宿2-8-1 Address: 2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo, Japan

電話番号:03-1234-5678 / 090-9999-8888 Phone: +81-3-1234-5678 / +81-90-9999-8888

メールアドレス:taro.yamada@example.com Email: taro.yamada@example.com

運転免許証番号:123456789012 Driver’s License: DL-123456789012

パスポート番号:TK1234567 Passport Number: TK1234567

社会保障番号:987-65-4321 Social Security Number: 987-65-4321

クレジットカード情報:

  • カード番号:4111-2222-3333-4444
  • 有効期限:12/25
  • セキュリティコード:123

Credit Card Information:

  • Card Number: 4111-2222-3333-4444
  • Expiry Date: 12/25
  • Security Code: 123

銀行口座情報:

  • 銀行名:みずほ銀行
  • 支店名:新宿支店
  • 口座番号:1234567

Bank Account Information:

  • Bank Name: Mizuho Bank
  • Branch: Shinjuku Branch
  • Account Number: 1234567

検証結果
#

以下に、llm-guard(デフォルト設定)と Guardrails for Amazon Bedrock(apply_guardrail)それぞれで処理した際のPII項目ごとの置換結果を比較します。

llm-guard(デフォルト設定)Guardrail for Amazon Bedrock
氏名(日本語)[REDACTED_PERSON_1][REDACTED_PERSON_2][REDACTED_PERSON_3]{NAME}
氏名(英語)[REDACTED_PERSON_4]{NAME}
生年月日--
住所-{ADDRESS}
電話番号(日本)03-1234-[REDACTED_PHONE_NUMBER_1]{PHONE}
電話番号(海外)[REDACTED_PHONE_NUMBER_2]{PHONE}
メールアドレス[REDACTED_EMAIL_ADDRESS_1]{EMAIL}
運転免許証番号-{DRIVER_ID}
パスポート番号-{US_PASSPORT_NUMBER}
社会保障番号[REDACTED_US_SSN_RE_1]987-65-4321
{US_SOCIAL_SECURITY_NUMBER}
クレジットカード(カード番号)[REDACTED_CREDIT_CARD_RE_1]{CREDIT_DEBIT_CARD_NUMBER}
クレジットカード(カード番号以外)-有効期限:{CREDIT_DEBIT_CARD_EXPIRY}
セキュリティコード:{CREDIT_DEBIT_CARD_CVV}
銀行口座情報-銀行名:みずほ銀行
支店名:新宿支店
口座番号:{US_BANK_ACCOUNT_NUMBER}

Bank Name: Mizuho Bank
Branch: {ADDRESS} Branch
Account Number: {US_BANK_ACCOUNT_NUMBER}

llm-guardのマスキング結果は、前回記事のデフォルト設定時のものです。

一部誤検出や期待通りに検出されていないものもありますが、概ね期待通りの結果が得られました。

結果詳細(誤検出・検出漏れ項目の抜粋)
#

社会保障番号:987-65-4321 Social Security Number: {US_SOCIAL_SECURITY_NUMBER}

英語版は適切にマスキングされましたが、日本語版においては英語版と同様のフォーマットを渡していますが、マスキングされませんでした。

本項目については、米国向けのフィルターとして準備されているため、日本語の文脈では対応できない可能性があります。

銀行口座情報:

銀行名:みずほ銀行 支店名:新宿支店 口座番号:{US_BANK_ACCOUNT_NUMBER} Bank Account Information:

Bank Name: Mizuho Bank Branch: {ADDRESS} Branch Account Number: {US_BANK_ACCOUNT_NUMBER}

英語版の支店名が住所として誤検出されていました。

今回は文脈を含まない情報でテストしましたが、実際の自然な文脈中で銀行支店名が出現した場合の挙動は別途検証が必要です。

今回の検証では、口座番号は正常に検出されていますが、利用したフィルターは米国の銀行口座番号として準備されています。実際に利用する際は、日本の形式に適合するか、もう少し検証してみる必要はありそうです。

まとめ
#

今回はGuardrails for Amazon BedrockのPIIマスキング機能について検証しました。

テストの結果、概ね Guardrails for Amazon Bedrock で PII の除去が出来ました。今回の検証では、限定的な条件での確認となるため、実際にアプリケーションで利用する際は、自然なテキスト中に含まれている場合や、全角文字が混在する場合など、日本語特有の表現が含まれている場合の挙動については、さらに詳細な検証が必要となります。

一方、現状ではカバーしきれていない情報もいくつかは残っています。

しかし、フィルターが準備されていない生年月日や、検知が上手くいかなかった社会保障番号(類似する情報であるマイナンバー)においては、ある程度フォーマットが定まっている情報になります。これらに対しては、正規表現フィルターを併用することで、PII除去の確実性を高められるでしょう。

検討課題
#

今回の検証では Langfuse の mask オプションを使用して実装しましたが、運用を考慮すると、今回の実装が適切とは限りません。

mask オプションは Langfuse にデータが渡されるたびに実行されるため、APIコール数が想定以上に増加し、コストに影響する可能性があります。実際、llm-guard のようなツールとは異なり、今回利用した機密情報フィルターについては、Bedrockの料金ページ に記載の通り、1,000テキストユニットあたり0.10 USD発生します。(2025年7月時点)

また、mask オプションを利用することで、エラーハンドリングが複雑になってしまう側面もあります。API エラーが発生した場合に Langfuse のトレース処理全体に影響を及ぼす懸念も無視できません。

そのため、Langfuse のトレース保存前に、アプリケーションコード内で明示的に apply_guardrail を呼び出し、その結果を input や output として Langfuse に渡す方法など、様々な実装アプローチを比較・検討することをおすすめします。