人々は、OpenAI、Gemini、Claude などの人気のある LLM に関してプライバシーの懸念を抱いています。オープンソース モデルでない限り、画面の裏側で何が起こっているのかはわかりません。したがって、私たち側も注意しなければなりません。
最初に行うことは、LLM に渡す情報の処理です。専門家は、プロンプトに機密情報や個人識別情報を含めないようにすることを推奨しています。簡単そうに聞こえますが、LLM のコンテキスト サイズが増加しているため、大きなテキストをモデルに渡すことができます。したがって、ハードレビューになり、すべての識別子がマスクされる可能性があります。
そこで、識別子と機密情報を検出してマスクする Python スクリプトを作成しようとしました。 Regex は魔法のようなもので、さまざまな機密情報を認識し、それをマスクに置き換えるために実装されています。また、名前、場所などの一般的な識別子を検出するために spacy ライブラリを使用しました。
注: 現時点では、これはインドのコンテキストに適していますが、共通の識別子は引き続き検出できます。
それでは、実装を見てみましょう (実装には LLM の助けを借りました)
説明をスキップしたい場合は。
コードベースへのリンクは次のとおりです: aditykris/prompt-masker-Indian-context
必要なモジュール/ライブラリをインポートする
import re from typing import Dict, List, Tuple import spacy nlp = spacy.load("en_core_web_sm")
以下のスニペットを使用して「en_core_web_sm」を手動でインストールする必要があります
python -m spacy ダウンロード en_core_web_sm
インドの共通機密情報を設定します。
class IndianIdentifier: '''Regex for common Indian identifiers''' PAN = r'[A-Z]{5}[0-9]{4}[A-Z]{1}' AADHAR = r'[2-9]{1}[0-9]{3}\s[0-9]{4}\s[0-9]{4}' INDIAN_PASSPORT = r'[A-PR-WYa-pr-wy][1-9]\d\s?\d{4}[1-9]' DRIVING_LICENSE = r'(([A-Z]{2}[0-9]{2})( )|([A-Z]{2}-[0-9]{2}))((19|20)[0-9][0-9])[0-9]{7}' UPI_ID = r'[\.\-a-z0-9]+@[a-z]+' INDIAN_BANK_ACCOUNT = r'\d{9,18}' IFSC_CODE = r'[A-Z]{4}0[A-Z0-9]{6}' INDIAN_PHONE_NUMBER = r'(\+91|\+91\-|0)?[789]\d{9}' EMAIL = r'[\w\.-]+@[\w\.-]+\.\w+' @classmethod def get_all_patterns(cls) -> Dict[str, str]: """Returns all regex patterns defined in the class""" return { name: pattern for name, pattern in vars(cls).items() if isinstance(pattern, str) and not name.startswith('_') }
そこで、Python のクラスとメソッドを修正し、ここで実装することにしました。
DebugPointer からこれらの識別子の正規表現を見つけました。とても役に立ちました。
次に検出機能について説明します。単純な re.finditer() を使用して、さまざまなパターンをループして一致を見つけました。一致したものはリストに保存されます。
def find_matches(text: str, pattern: str) -> List[Tuple[int, int, str]]: """ Find all matches of a pattern in text and return their positions and matched text """ matches = [] for match in re.finditer(pattern, text): matches.append((match.start(), match.end(), match.group())) return matches
単純な辞書を使用して置換テキストを保存しました。置換テキストを返す関数にまとめました。
def get_replacement_text(identifier_type: str) -> str: """ Returns appropriate replacement text based on the type of identifier """ replacements = { 'PAN': '[PAN_NUMBER]', 'AADHAR': '[AADHAR_NUMBER]', 'INDIAN_PASSPORT': '[PASSPORT_NUMBER]', 'DRIVING_LICENSE': '[DL_NUMBER]', 'UPI_ID': '[UPI_ID]', 'INDIAN_BANK_ACCOUNT': '[BANK_ACCOUNT]', 'IFSC_CODE': '[IFSC_CODE]', 'INDIAN_PHONE_NUMBER': '[PHONE_NUMBER]', 'EMAIL': '[EMAIL_ADDRESS]', 'PERSON': '[PERSON_NAME]', 'ORG': '[ORGANIZATION]', 'GPE': '[LOCATION]' } return replacements.get(identifier_type, '[MASKED]')
あ!本編始まります
def analyze_identifiers(text: str) -> Tuple[str, Dict[str, List[str]]]: """ Function to identify and hide sensitive information. Returns: - masked_text: Text with all sensitive information masked - found_identifiers: Dictionary containing all identified sensitive information """ # Initialize variables masked_text = text found_identifiers = {} positions_to_mask = [] # First, find all regex matches for identifier_name, pattern in IndianIdentifier.get_all_patterns().items(): matches = find_matches(text, pattern) if matches: found_identifiers[identifier_name] = [match[2] for match in matches] positions_to_mask.extend( (start, end, identifier_name) for start, end, _ in matches ) # Then, process named entities using spaCy doc = nlp(text) for ent in doc.ents: if ent.label_ in ["PERSON", "ORG", "GPE"]: positions_to_mask.append((ent.start_char, ent.end_char, ent.label_)) if ent.label_ not in found_identifiers: found_identifiers[ent.label_] = [] found_identifiers[ent.label_].append(ent.text) # Sort positions by start index in reverse order to handle overlapping matches positions_to_mask.sort(key=lambda x: x[0], reverse=True) # Apply masking for start, end, identifier_type in positions_to_mask: replacement = get_replacement_text(identifier_type) masked_text = masked_text[:start] + replacement + masked_text[end:] return masked_text, found_identifiers
この関数はプロンプトを入力として受け取り、マスクされたプロンプトと識別された要素を辞書として返します。
一つずつ説明していきます。
次のループでは、さまざまな識別子の正規表現をループして、プロンプト内で一致するものを見つけます。見つかった場合は、次のようになります:
1. 識別された情報を、識別子タイプをキーとして使用して辞書に保存し、追跡します。
2. 位置をメモし、positions_to_mask に保存して、後でマスキングを適用できるようにします。
import re from typing import Dict, List, Tuple import spacy nlp = spacy.load("en_core_web_sm")
さあ、スペーシーな時間です。これは、自然言語処理 (nlp) タスクに最適なライブラリです。 nlp モジュールを使用してテキストから識別子を抽出できます。
現在、名前、組織、場所の検出には慣れています。
これは、場所を特定して保存するための上記のループと同じように機能します。
class IndianIdentifier: '''Regex for common Indian identifiers''' PAN = r'[A-Z]{5}[0-9]{4}[A-Z]{1}' AADHAR = r'[2-9]{1}[0-9]{3}\s[0-9]{4}\s[0-9]{4}' INDIAN_PASSPORT = r'[A-PR-WYa-pr-wy][1-9]\d\s?\d{4}[1-9]' DRIVING_LICENSE = r'(([A-Z]{2}[0-9]{2})( )|([A-Z]{2}-[0-9]{2}))((19|20)[0-9][0-9])[0-9]{7}' UPI_ID = r'[\.\-a-z0-9]+@[a-z]+' INDIAN_BANK_ACCOUNT = r'\d{9,18}' IFSC_CODE = r'[A-Z]{4}0[A-Z0-9]{6}' INDIAN_PHONE_NUMBER = r'(\+91|\+91\-|0)?[789]\d{9}' EMAIL = r'[\w\.-]+@[\w\.-]+\.\w+' @classmethod def get_all_patterns(cls) -> Dict[str, str]: """Returns all regex patterns defined in the class""" return { name: pattern for name, pattern in vars(cls).items() if isinstance(pattern, str) and not name.startswith('_') }
一部のテストケースでは、一部のマスクが欠落していることに気付きましたが、これは主に識別子の重複が原因でした。したがって、逆の順序で並べ替えることが問題を解決するのに役立ちました。
def find_matches(text: str, pattern: str) -> List[Tuple[int, int, str]]: """ Find all matches of a pattern in text and return their positions and matched text """ matches = [] for match in re.finditer(pattern, text): matches.append((match.start(), match.end(), match.group())) return matches
最後に、found_identifiers と Position_to_mask のデータを使用してマスク処理を行います。
def get_replacement_text(identifier_type: str) -> str: """ Returns appropriate replacement text based on the type of identifier """ replacements = { 'PAN': '[PAN_NUMBER]', 'AADHAR': '[AADHAR_NUMBER]', 'INDIAN_PASSPORT': '[PASSPORT_NUMBER]', 'DRIVING_LICENSE': '[DL_NUMBER]', 'UPI_ID': '[UPI_ID]', 'INDIAN_BANK_ACCOUNT': '[BANK_ACCOUNT]', 'IFSC_CODE': '[IFSC_CODE]', 'INDIAN_PHONE_NUMBER': '[PHONE_NUMBER]', 'EMAIL': '[EMAIL_ADDRESS]', 'PERSON': '[PERSON_NAME]', 'ORG': '[ORGANIZATION]', 'GPE': '[LOCATION]' } return replacements.get(identifier_type, '[MASKED]')
このプログラムのサンプル入力は次のようになります:
入力:
def analyze_identifiers(text: str) -> Tuple[str, Dict[str, List[str]]]: """ Function to identify and hide sensitive information. Returns: - masked_text: Text with all sensitive information masked - found_identifiers: Dictionary containing all identified sensitive information """ # Initialize variables masked_text = text found_identifiers = {} positions_to_mask = [] # First, find all regex matches for identifier_name, pattern in IndianIdentifier.get_all_patterns().items(): matches = find_matches(text, pattern) if matches: found_identifiers[identifier_name] = [match[2] for match in matches] positions_to_mask.extend( (start, end, identifier_name) for start, end, _ in matches ) # Then, process named entities using spaCy doc = nlp(text) for ent in doc.ents: if ent.label_ in ["PERSON", "ORG", "GPE"]: positions_to_mask.append((ent.start_char, ent.end_char, ent.label_)) if ent.label_ not in found_identifiers: found_identifiers[ent.label_] = [] found_identifiers[ent.label_].append(ent.text) # Sort positions by start index in reverse order to handle overlapping matches positions_to_mask.sort(key=lambda x: x[0], reverse=True) # Apply masking for start, end, identifier_type in positions_to_mask: replacement = get_replacement_text(identifier_type) masked_text = masked_text[:start] + replacement + masked_text[end:] return masked_text, found_identifiers
出力:
マスクされたテキスト:
for identifier_name, pattern in IndianIdentifier.get_all_patterns().items(): matches = find_matches(text, pattern) if matches: found_identifiers[identifier_name] = [match[2] for match in matches] positions_to_mask.extend( (start, end, identifier_name) for start, end, _ in matches )
以上がRegex と spaCy を使用してプロンプト内の機密データをマスクするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。