When users interact with AI systems, sensitive data flows through multiple points:
User Input (may contain PII)
|
v
Your Application Server (logs?)
|
v
API Provider (OpenAI, Anthropic, etc.)
| - May use data for training?
| - Stored in logs?
| - Accessible to provider employees?
v
Model Response (may reflect PII)
|
v
Your Storage (conversation history, analytics)
importrefromtypingimportDict,List,Tupledefdetect_pii(text:str)->List[Dict]:"""Detect common PII patterns in text."""patterns={"email":r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',"phone_us":r'\b(?:\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b',"ssn":r'\b\d{3}-\d{2}-\d{4}\b',"credit_card":r'\b(?:\d{4}[-\s]?){3}\d{4}\b',"ip_address":r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b',"date_of_birth":r'\b(?:DOB|born|birthday)[:\s]+\d{1,2}[/\-]\d{1,2}[/\-]\d{2,4}\b',}found_pii=[]forpii_type,patterninpatterns.items():matches=re.finditer(pattern,text,re.IGNORECASE)formatchinmatches:found_pii.append({"type":pii_type,"value":match.group(),"start":match.start(),"end":match.end()})returnfound_piidefredact_pii(text:str)->Tuple[str,List[Dict]]:"""Detect and redact PII from text."""pii_items=detect_pii(text)# Sort by position (reverse) to maintain correct indices during replacementpii_items.sort(key=lambdax:x["start"],reverse=True)redacted=textforiteminpii_items:placeholder=f"[{item['type'].upper()}_REDACTED]"redacted=redacted[:item["start"]]+placeholder+redacted[item["end"]:]returnredacted,pii_items# Example usagetext="Please help John Smith at john.smith@email.com, phone 555-123-4567, SSN 123-45-6789"redacted,found=redact_pii(text)print(f"Redacted: {redacted}")print(f"Found {len(found)} PII items")
fromopenaiimportOpenAIimportjsonclient=OpenAI()defdetect_pii_with_llm(text,model="gpt-4.1-mini"):"""Use an LLM to detect PII that regex might miss."""response=client.chat.completions.create(model=model,messages=[{"role":"system","content":"""Identify all personally identifiable information (PII) in the text.Include: names, emails, phone numbers, addresses, SSNs, credit cards, dates of birth, medical info, financial info, and any other identifying data.Return JSON: {"pii_items": [{"type": "...", "value": "...", "risk": "high|medium|low"}]}"""},{"role":"user","content":text}],temperature=0,response_format={"type":"json_object"})returnjson.loads(response.choices[0].message.content)
Send only the data the model needs — nothing more.
defminimize_data_for_llm(user_message,task_type):"""Strip unnecessary PII before sending to LLM."""# Step 1: Detect PIIredacted_text,pii_items=redact_pii(user_message)# Step 2: Determine which PII is needed for the taskneeded_pii_types={"general_question":[],# No PII needed"account_lookup":["email"],# Only email needed"shipping_update":["name"],# Only name needed"billing_issue":["name","email"],# Name and email}allowed=needed_pii_types.get(task_type,[])# Step 3: Only restore PII that is neededminimized=redacted_textforiteminpii_items:ifitem["type"]inallowed:minimized=minimized.replace(f"[{item['type'].upper()}_REDACTED]",item["value"])returnminimized
classPrivacyAwareAIClient:"""Wrapper that enforces privacy controls before sending data to an LLM."""def__init__(self,client,log_pii_detections=True):self.client=clientself.log_pii_detections=log_pii_detectionsdefchat(self,messages,model="gpt-4.1-mini",redact=True,**kwargs):"""Send a chat request with automatic PII handling."""processed_messages=[]formsginmessages:ifmsg["role"]=="user"andredact:redacted_content,pii_found=redact_pii(msg["content"])ifpii_foundandself.log_pii_detections:print(f"WARNING: Redacted {len(pii_found)} PII items from user message")processed_messages.append({"role":msg["role"],"content":redacted_content})else:processed_messages.append(msg)returnself.client.chat.completions.create(model=model,messages=processed_messages,**kwargs)# Usageprivacy_client=PrivacyAwareAIClient(OpenAI())response=privacy_client.chat([{"role":"user","content":"Help me with my account, email is john@example.com"}])