連載: AIに仕事を奪われる不安から始めるハーネス作成入門
| 回 | タイトル | 状態 |
|---|---|---|
| 第1回〜第19回 | (既刊) | ✅ |
| 第20回 | プロンプト投入前のマスキング・復元フローを設計する | 📖 |
| 第21回 | ハーネス作成をポートフォリオ化する方法 | ─ |
| 第22回 | Qiita連載とGitHubリポジトリを連動させる運用 | ─ |
| 第23回 | AIハーネスを副業・受託・社内改善に転用する前に考えること | ─ |
| 第24回 | 12週間のハーネス連載を振り返り、次に作るAI基盤を決める | ─ |
はじめに ── LLMに渡してはいけないデータとは
AIハーネスを作っていると、いつか必ずぶつかる問題があります。「このデータ、LLMに渡して大丈夫だろうか?」という問いです。
SE経験のある方なら、本番環境のデータ取り扱いに気を遣ってきた経験があるかもしれません。その感覚はAI活用でも変わりません。むしろ、外部APIにデータを送るという行為は、従来の社内システム運用以上に慎重さが求められる場面があります。
今回は、LLMにプロンプトを投入する前にデータをマスキングし、結果を受け取った後に復元する「マスキング・復元フロー」の設計パターンを考えます。
なぜマスキングが必要になるのか
外部API利用時のリスクを整理する
LLMのAPIを利用する際、送信したデータがどう扱われるかは提供元のポリシーに依存します。判断に迷ったときのために、リスクの観点を整理しておきましょう。
| 観点 | 確認すべきこと | 判断基準の例 |
|---|---|---|
| データ保持 | 送信データが学習に使われるか | 利用規約・オプトアウト設定の有無 |
| 通信経路 | TLS暗号化されているか | API仕様書で確認 |
| 法規制 | 個人情報保護法等の対象か | データの種別で判断 |
| 社内規定 | 社外へのデータ送信ルール | 情報セキュリティポリシー |
ポイントは「すべてを禁止する」のではなく、何を守るべきかを明確にして、守るべきものだけをマスキングするという考え方です。
マスキング対象の判断フレームワーク
以下のような分類で考えると、対象を絞りやすくなります。
- 必須マスキング: 個人名、メールアドレス、電話番号、クレジットカード番号など
- 推奨マスキング: 社内プロジェクト名、顧客企業名、内部APIキーなど
- 不要: 一般的な技術用語、公開情報、サンプルデータ
マスキング・復元フローの全体設計
3段パイプラインの概要
以下のMermaidフロー図は、マスキング・復元パイプラインの全体像を示しています。
この設計のポイントは3つあります。
- マスキング処理: 機密データをプレースホルダに置換し、マッピングテーブルを生成
- LLM API呼び出し: マスク済みデータだけが外部に送信される
- 復元処理: マッピングテーブルを使い、プレースホルダを元データに戻す
マスキング処理の詳細フロー
Python実装サンプル
基本的なマスキングクラス
import re
from dataclasses import dataclass, field
@dataclass
class MaskMapping:
"""マスキングのマッピング情報を保持する"""
original: str
placeholder: str
category: str
class DataMasker:
"""プロンプト投入前のデータマスキングを行うクラス"""
def __init__(self):
self.mappings: list[MaskMapping] = []
self._counters: dict[str, int] = {}
self._patterns = {
"EMAIL": r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
"PHONE": r'0\d{1,4}-\d{1,4}-\d{4}',
"CREDIT_CARD": r'\d{4}-\d{4}-\d{4}-\d{4}',
}
def _get_placeholder(self, category: str) -> str:
count = self._counters.get(category, 0) + 1
self._counters[category] = count
return f"[{category}_{count}]"
def mask(self, text: str) -> str:
"""テキスト内の機密データをプレースホルダに置換する"""
masked = text
for category, pattern in self._patterns.items():
for match in re.finditer(pattern, masked):
original = match.group()
placeholder = self._get_placeholder(category)
self.mappings.append(
MaskMapping(original=original,
placeholder=placeholder,
category=category)
)
masked = masked.replace(original, placeholder, 1)
return masked
def unmask(self, text: str) -> str:
"""プレースホルダを元データに復元する"""
restored = text
for mapping in reversed(self.mappings):
restored = restored.replace(
mapping.placeholder, mapping.original
)
return restored
使い方の例
# マスキング→LLM投入→復元の流れ
masker = DataMasker()
original_prompt = """
田中太郎さん(tanaka@example.com)に
請求書を送付してください。
電話番号は 03-1234-5678 です。
"""
# ステップ1: マスキング
masked_prompt = masker.mask(original_prompt)
print(masked_prompt)
# [PERSON_1]さん([EMAIL_1])に
# 請求書を送付してください。
# 電話番号は [PHONE_1] です。
# ステップ2: LLM API呼び出し(マスク済みデータを送信)
# llm_response = call_llm_api(masked_prompt)
# ステップ3: 復元
# restored = masker.unmask(llm_response)
注意: 上記の人名検出は正規表現だけでは難しい場合があります。実際の運用では、辞書ベースや形態素解析ライブラリの併用を検討してください。
カスタムパターンの追加方法
プロジェクトごとに守るべきデータは異なります。カスタムパターンを追加できる設計にしておくと、ハーネスの汎用性が高まります。
# カスタムパターンの追加例
masker = DataMasker()
# 社内プロジェクトコードをマスキング対象に追加
masker._patterns["PROJECT"] = r'PRJ-\d{4}-[A-Z]{3}'
# APIキーのパターンを追加
masker._patterns["API_KEY"] = r'sk-[a-zA-Z0-9]{32,}'
ハーネスへの組み込み判断基準
マスキング・復元フローをハーネスに組み込むかどうかは、以下の判断基準で考えてみてください。
| 判断基準 | 組み込むべきケース | 不要なケース |
|---|---|---|
| データの種別 | 実データを扱う可能性がある | サンプルデータのみ |
| API提供元 | 外部クラウドサービス | 完全ローカルLLM |
| 利用シーン | 業務利用・受託開発 | 個人学習のみ |
| 将来の拡張 | 他者にハーネスを共有する予定 | 自分専用 |
「今すぐ必要ない」と判断した場合でも、将来の拡張を見据えてマスキング処理の挿入ポイントだけは確保しておくという選択もあります。
運用上の注意点
マッピングテーブルの管理
マッピングテーブル自体が機密情報を含むため、以下の点に注意してください。
- メモリ上で管理: 可能な限りファイルに書き出さない
- 処理完了後に破棄: 復元が終わったらマッピングを削除
- ログに出力しない: デバッグ時も元データの露出に注意
マスキング漏れへの対策
正規表現だけですべての機密データを検出するのは困難です。多層防御の考え方で対策しましょう。
- 正規表現: メールアドレスや電話番号など定型パターン
- 辞書マッチ: 社名リストや人名リストとの突合
- 目視確認: 初回運用時はマスク済みプロンプトを人が確認
SE経験を活かすポイント
ここまで読んで「これ、データベースのマスキングと考え方が同じだ」と感じた方もいるかもしれません。その感覚は正しいです。
- テスト環境のデータマスキング → LLM投入前のマスキング
- 暗号化と復号 → マスキングと復元
- アクセス制御 → マスキング対象の分類
SE時代に培ったセキュリティの感覚は、AI活用でもそのまま活きます。新しい技術だからといって、ゼロから学び直す必要はありません。
まとめ
今回のポイントを振り返ります。
- LLMにデータを送る前に、何を守るべきかを明確にすることが出発点
- マスキング→API呼び出し→復元の3段パイプラインで安全性を確保
- 正規表現+辞書+目視の多層防御で漏れを防ぐ
- SE経験のセキュリティ感覚はそのまま転用できる
次回予告
次回 第21回「ハーネス作成をポートフォリオ化する方法」では、これまで作ってきたハーネスをQiitaとGitHubに蓄積し、キャリア資産として見せられる形に整える方法を紹介します。技術力を「見える化」するREADMEの構成テンプレートもお届けします。
この記事は連載『AIに仕事を奪われる不安から始めるハーネス作成入門』第20回です。
連載についてのフィードバックやご質問は、コメント欄またはQiitaのDMでお気軽にどうぞ。