Using AI at work

Mask names, emails, and phone numbers before pasting text into an AI tool

Remove or replace obvious personal data before sending text to an AI chat assistant, then review both the masked text and a detection report for anything the script missed. This tutorial uses a small synthetic example and keeps the original file unchanged.

Show contents

Who this is forFor people who want a practical pre-check before sending work text containing possible names, email addresses, or phone numbers to an AI assistant.

What you need
  • Python 3.12
  • A text editor and an AI chat assistant

01Start with a small synthetic message

This example is entirely synthetic. The names, email addresses, phone numbers, company details, and message content are invented for the tutorial. The purpose is to practice removing obvious personal data before text leaves your local workspace.

Save the following text as message.txt. It contains two names, two email addresses, two phone numbers, and one deliberately awkward reference that a simple masking rule will miss.

text
Project review notes

Alice Kim asked us to send the revised table to alice.kim@example.com.
If the file is delayed, call Alice at 010-1234-5678.

Bob Lee will review the second draft. His email is bob.lee@example.com and his office number is 02-345-6789.

The final approval should also be checked with A. Kim before Friday.

02Ask AI for a masking plan, not for the sensitive text

You can ask an AI assistant how to design masking rules without sending it the actual sensitive document. Describe the categories and use invented examples instead.

A useful answer should distinguish structured identifiers from unstructured language. Email addresses and phone numbers often have recognizable patterns. Human names are harder: ordinary words can be names, names can contain initials or multiple scripts, and the same person may be referenced in several forms.

03Convert the AI answer into explicit masking rules

For this synthetic example, use a small approved list for names and regular expressions for email addresses and phone numbers. This is intentionally narrower than a general personal-data detector.

Data typeRulePlaceholder
Full nameReplace only exact approved names Alice Kim and Bob Lee[NAME_1], [NAME_2]
EmailMatch common address-like patterns containing a local part, @, and domain[EMAIL_1], [EMAIL_2]
PhoneMatch the two phone formats present in this example[PHONE_1], [PHONE_2]
Other textLeave unchanged for manual reviewNo automatic replacement

Consistent placeholders are useful because repeated references can remain understandable without retaining the original identifier. The script below gives the same placeholder to repeated occurrences of the same detected value.

04Apply the rules to a local copy

The following Python script reads message.txt, applies the approved rules, and writes results into a new outputs folder. It also writes a replacements.csv report containing every detected original value and its placeholder. It stops if outputs already exists and never overwrites message.txt.

python
import csv
import re
import sys
from pathlib import Path

INPUT = Path("message.txt")
OUTPUT_DIR = Path("outputs")
MASKED_FILE = OUTPUT_DIR / "message_masked.txt"
REPORT_FILE = OUTPUT_DIR / "replacements.csv"

KNOWN_NAMES = ["Alice Kim", "Bob Lee"]

EMAIL_RE = re.compile(
    r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"
)
PHONE_RE = re.compile(
    r"(?<!\d)(?:010-\d{4}-\d{4}|02-\d{3}-\d{4})(?!\d)"
)


def make_replacer(kind, mapping, rows):
    def replace(match):
        original = match.group(0)
        if original not in mapping:
            placeholder = f"[{kind}_{len(mapping) + 1}]"
            mapping[original] = placeholder
            rows.append(
                {
                    "type": kind,
                    "original": original,
                    "replacement": placeholder,
                }
            )
        return mapping[original]

    return replace


def mask_known_names(text, mapping, rows):
    for name in KNOWN_NAMES:
        if name not in text:
            continue
        if name not in mapping:
            placeholder = f"[NAME_{len(mapping) + 1}]"
            mapping[name] = placeholder
            rows.append(
                {
                    "type": "NAME",
                    "original": name,
                    "replacement": placeholder,
                }
            )
        text = text.replace(name, mapping[name])
    return text


def main():
    if not INPUT.is_file():
        sys.exit(f"Input file not found: {INPUT}")

    if OUTPUT_DIR.exists():
        sys.exit(f"Stop: {OUTPUT_DIR} already exists. Review or rename it first.")

    original = INPUT.read_text(encoding="utf-8")
    rows = []

    name_map = {}
    email_map = {}
    phone_map = {}

    masked = mask_known_names(original, name_map, rows)
    masked = EMAIL_RE.sub(make_replacer("EMAIL", email_map, rows), masked)
    masked = PHONE_RE.sub(make_replacer("PHONE", phone_map, rows), masked)

    OUTPUT_DIR.mkdir()
    MASKED_FILE.write_text(masked, encoding="utf-8")

    with REPORT_FILE.open("x", encoding="utf-8", newline="") as target:
        writer = csv.DictWriter(
            target,
            fieldnames=["type", "original", "replacement"],
        )
        writer.writeheader()
        writer.writerows(rows)

    print(f"Unique names masked: {len(name_map)}")
    print(f"Unique emails masked: {len(email_map)}")
    print(f"Unique phones masked: {len(phone_map)}")
    print(f"Masked copy: {MASKED_FILE}")
    print(f"Replacement report: {REPORT_FILE}")


if __name__ == "__main__":
    main()

Place mask_personal_data.py beside message.txt and run python mask_personal_data.py. For this exact example, the expected summary is 2 unique names, 2 unique emails, and 2 unique phone numbers masked.

05Check the replacements against the source

Work out the expected replacements before relying on the result. The script should record six unique detected values.

TypeOriginalReplacement
NAMEAlice Kim[NAME_1]
NAMEBob Lee[NAME_2]
EMAILalice.kim@example.com[EMAIL_1]
EMAILbob.lee@example.com[EMAIL_2]
PHONE010-1234-5678[PHONE_1]
PHONE02-345-6789[PHONE_2]

The exact full name Alice Kim appears once in the source and is replaced with [NAME_1]. The later shorter reference Alice is not an exact match for the approved full-name list, so it remains unmasked and should be caught during manual review. The report contains unique detected values rather than one row for every occurrence.

06Review what the automatic rules missed

Automatic replacement is only the first pass. Open outputs/message_masked.txt and read the entire result before pasting it anywhere. In this example, the last paragraph still contains A. Kim. The script cannot know from its approved list that this may refer to Alice Kim.

  1. Search the masked file for @ and confirm that no email address remains.
  2. Search for 010- and 02- and confirm that the two expected phone formats are gone.
  3. Search for Alice Kim and Bob Lee and confirm that the approved full names are gone.
  4. Read every line manually for initials, nicknames, signatures, addresses, employee IDs, account numbers, project-specific identifiers, or other identifying context.
  5. Notice that A. Kim remains. Decide manually whether it identifies a person in the real document and mask it if necessary.
  6. Compare replacements.csv with the source and confirm that each listed replacement corresponds to the intended text.

07Common mistakes and limits

  • Do not paste the original sensitive text into an AI tool merely to ask the AI what should be masked.
  • Do not assume a regular expression can reliably identify arbitrary human names.
  • Do not rely on one phone-number pattern if your source may contain spaces, country codes, extensions, parentheses, or other formats.
  • Do not delete the mapping report until you have finished checking the masked text, but protect that report because it contains the original identifiers.
  • Do not overwrite the source file. Keep the original separate from the masked copy.
  • Do not assume masking three categories removes confidential business information, credentials, financial data, health information, or other sensitive content.

For real workflows, define what must be removed based on the document, applicable policy, and purpose of the AI task. When possible, provide only the minimum text required for the task instead of masking a large document and sending the whole thing.

Execution and verification record

ChatGPT Python tool · Python 3.x · standard library only

  • The first execution completed successfully.
  • The console reported 2 unique names, 2 unique emails, and 2 unique phone numbers masked.
  • replacements.csv contained exactly 6 rows: Alice Kim -> [NAME_1], Bob Lee -> [NAME_2], two email replacements, and two phone replacements.
  • outputs/message_masked.txt replaced both approved full names, both emails, and both phone numbers.
  • The shorter reference 'Alice' remained unmasked because the code replaces only the exact approved name 'Alice Kim'.
  • The deliberately abbreviated reference 'A. Kim' also remained unmasked as expected.
  • message.txt was not modified.
  • A second execution stopped because outputs already existed, confirming the no-overwrite behavior.
  • Local recheck on 2026-09-21 with Python 3.12.14: the corrected example passed; source preservation and overwrite protection also passed.
Verification limits
  • The full-name logic is intentionally an exact two-name allowlist and is not a general personal-data detector.
  • An unrelated spreadsheet-runtime warmup warning was emitted by the Python environment; it did not affect the script's execution or generated files.

References

The explanations and examples were written for this site. See the official sources below for the related behavior and concepts.