Presidio PII Masking with LiteLLM - 완전한 튜토리얼

Presidio PII Masking with LiteLLM - 완전한 튜토리얼

이 튜토리얼은 Microsoft Presidio와 LiteLLM Gateway로 PII(개인 식별 정보) 마스킹을 설정하는 방법을 안내합니다. 끝나면 LLM 요청에서 민감한 정보를 자동으로 감지하고 마스킹하는 프로덕션 준비 완료된 설정을 갖게 됩니다.

배울 내용

  • PII 감지를 위한 Presidio 컨테이너 배포
  • 민감한 데이터를 자동으로 마스킹하도록 LiteLLM 구성
  • 실제 예시로 PII 마스킹 테스트
  • guardrail 실행 모니터링 및 추적
  • 출력 파싱과 언어 지원 같은 고급 기능 구성

PII 마스킹을 쓰는 이유

LLM을 사용할 때 사용자는 다음 같은 민감한 정보를 부지불식간에 공유할 수 있어요:

  • 신용카드 번호
  • 이메일 주소
  • 전화번호
  • 사회보장번호(SSN)
  • 의료 정보(PHI)
  • 개인 이름과 주소

PII 마스킹은 이 정보가 LLM에 도달하기 전에 자동으로 감지하고 편집(redact)하여 사용자 프라이버시를 보호하고 GDPR, HIPAA, CCPA 같은 규정 준수를 돕습니다.

전제 조건

이 튜토리얼을 시작하기 전에 다음을 확인하세요:

  • Docker가 머신에 설치됨
  • 테스트용 LiteLLM API 키 또는 OpenAI API 키
  • YAML 구성에 대한 기본 지식
  • 테스트용 curl 또는 유사한 HTTP 클라이언트

1부: Presidio 컨테이너 배포

Presidio는 두 가지 주요 서비스로 구성됩니다:

  • Presidio Analyzer: 텍스트에서 PII 감지
  • Presidio Anonymizer: 감지된 PII 마스킹 또는 편집

1.1단계: Docker로 배포

Presidio용 docker-compose.yml 파일을 만드세요:

version: '3.8'services:  presidio-analyzer:    image: mcr.microsoft.com/presidio-analyzer:latest    ports:      - "5002:3000"    environment:      - GRPC_PORT=5001    networks:      - presidio-network  presidio-anonymizer:    image: mcr.microsoft.com/presidio-anonymizer:latest    ports:      - "5001:3000"    networks:      - presidio-networknetworks:  presidio-network:    driver: bridge

1.2단계: 컨테이너 시작

docker-compose up -d

1.3단계: Presidio 실행 확인

analyzer 엔드포인트를 테스트하세요:

curl -X POST http://localhost:5002/analyze \
  -H "Content-Type: application/json" \
  -d '{
    "text": "My email is [email protected]",
    "language": "en"
  }'

다음 같은 응답을 볼 수 있어요:

[
  {
    "entity_type": "EMAIL_ADDRESS",
    "start": 12,
    "end": 33,
    "score": 1.0
  }
]

Checkpoint: Presidio 컨테이너가 이제 실행 중이고 준비됐어요!

2부: LiteLLM Gateway 구성

이제 LiteLLM이 자동 PII 마스킹에 Presidio를 사용하도록 구성해 봅시다.

2.1단계: LiteLLM 구성 생성

config.yaml 파일을 만드세요:

model_list:
  - model_name: gpt-5.6-luna
    litellm_params:
      model: openai/gpt-5.6-luna
      api_key: os.environ/OPENAI_API_KEYguardrails:
  - guardrail_name: "presidio-pii-guard"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"  # Run before LLM call
      presidio_score_thresholds:  # optional confidence score thresholds for detections
        CREDIT_CARD: 0.8
        EMAIL_ADDRESS: 0.6
      pii_entities_config:
        CREDIT_CARD: "MASK"
        EMAIL_ADDRESS: "MASK"
        PHONE_NUMBER: "MASK"
        PERSON: "MASK"
        US_SSN: "MASK"

2.2단계: 환경 변수 설정

export OPENAI_API_KEY="your-openai-key"export PRESIDIO_ANALYZER_API_BASE="http://localhost:5002"export PRESIDIO_ANONYMIZER_API_BASE="http://localhost:5001"

2.3단계: LiteLLM Gateway 시작

litellm --config config.yaml --port 4000 --detailed_debug

guardrails가 로드됐다는 출력을 볼 수 있어요:

Loaded guardrails: ['presidio-pii-guard']

Checkpoint: LiteLLM Gateway가 PII 마스킹을 활성화한 채 실행 중입니다!

3부: PII 마스킹 테스트

다양한 유형의 민감한 데이터로 PII 마스킹을 테스트해 보세요.

테스트 1: 기본 PII 감지

  • Request with PII
  • What LLM Receives
  • Response
curl -X POST http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "gpt-5.6-luna",
    "messages": [
      {
        "role": "user",
        "content": "My name is John Smith, my email is [email protected], and my credit card is 4111-1111-1111-1111"
      }
    ],
    "guardrails": ["presidio-pii-guard"]
  }'

LLM은 마스킹된 버전을 받습니다:

My name is , my email is , and my credit card is
{
  "id": "chatcmpl-123abc",
  "choices": [
    {
      "message": {
        "content": "I can see you've provided some information. However, I noticed some sensitive data placeholders. For security reasons, I recommend not sharing actual personal information like credit card numbers.",
        "role": "assistant"
      },
      "finish_reason": "stop"
    }
  ],
  "model": "gpt-5.6-luna"
}

테스트 2: 의료 정보(PHI)

curl -X POST http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "gpt-5.6-luna",
    "messages": [
      {
        "role": "user",
        "content": "Patient Jane Doe, DOB 01/15/1980, MRN 123456, presents with symptoms of fever."
      }
    ],
    "guardrails": ["presidio-pii-guard"]
  }'

환자 이름과 의료 기록 번호가 자동으로 마스킹됩니다.

테스트 3: PII 없음(일반 요청)

curl -X POST http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "gpt-5.6-luna",
    "messages": [
      {
        "role": "user",
        "content": "What is the capital of France?"
      }
    ],
    "guardrails": ["presidio-pii-guard"]
  }'

PII가 감지되지 않으므로 이 요청은 변경 없이 통과합니다.

Checkpoint: PII 마스킹 테스트를 성공적으로 마쳤어요!

4부: 고급 구성

민감한 엔티티 차단

마스킹 대신 특정 PII 유형이 포함된 요청을 완전히 차단할 수 있어요:

guardrails:
  - guardrail_name: "presidio-block-guard"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      pii_entities_config:
        US_SSN: "BLOCK"  # Block any request with SSN
        CREDIT_CARD: "BLOCK"  # Block credit card numbers
        MEDICAL_LICENSE: "BLOCK"

차단 동작을 테스트하세요:

curl -X POST http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "gpt-5.6-luna",
    "messages": [
      {"role": "user", "content": "My SSN is 123-45-6789"}
    ],
    "guardrails": ["presidio-block-guard"]
  }'

기대되는 응답:

{
  "error": {
    "message": "Blocked PII entity detected: US_SSN by Guardrail: presidio-block-guard."
  }
}

출력 파싱(언마스킹)

출력 파싱을 활성화해 LLM 응답의 마스킹된 토큰을 원래 값으로 자동 대체하세요:

guardrails:
  - guardrail_name: "presidio-output-parse"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      output_parse_pii: true  # Enable output parsing
      pii_entities_config:
        PERSON: "MASK"
        PHONE_NUMBER: "MASK"

동작 방식:

  • 사용자 입력: "Hello, my name is Jane Doe. My number is 555-1234"
  • LLM이 받는 것: "Hello, my name is <PERSON>. My number is <PHONE_NUMBER>"
  • LLM 응답: "Nice to meet you, <PERSON>!"
  • 사용자가 받는 것: "Nice to meet you, Jane Doe!" ✨

다중 언어 지원

다른 언어에 대한 PII 감지를 구성하세요:

guardrails:
  - guardrail_name: "presidio-spanish"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      presidio_language: "es"  # Spanish
      pii_entities_config:
        CREDIT_CARD: "MASK"
        PERSON: "MASK"
  - guardrail_name: "presidio-german"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      presidio_language: "de"  # German
      pii_entities_config:
        CREDIT_CARD: "MASK"
        PERSON: "MASK"

요청별로 언어를 재정의할 수도 있어요:

curl -X POST http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "gpt-5.6-luna",
    "messages": [
      {"role": "user", "content": "Mi tarjeta de crédito es 4111-1111-1111-1111"}
    ],
    "guardrails": ["presidio-spanish"],
    "guardrail_config": {"language": "fr"}
  }'

로깅 전용 모드

PII 마스킹을 실제 LLM 요청이 아니라 로그에만 적용하세요:

guardrails:
  - guardrail_name: "presidio-logging"
    litellm_params:
      guardrail: presidio
      mode: "logging_only"  # Only mask in logs
      pii_entities_config:
        CREDIT_CARD: "MASK"
        EMAIL_ADDRESS: "MASK"

이것은 다음 경우에 유용해요:

  • 프로덕션 요청에서 PII를 허용하고 싶을 때
  • 하지만 로깅 규정을 준수해야 할 때
  • Langfuse, Datadog 등과 통합할 때

5부: 모니터링과 추적

LiteLLM UI에서 Guardrail 실행 보기

LiteLLM Admin UI를 사용 중이라면 상세 guardrail 추적을 볼 수 있어요:

  • Logs 페이지로 이동
  • guardrail을 사용한 요청 아무거나 클릭
  • 자세한 정보 보기:
    • 감지된 엔티티
    • 각 감지의 신뢰도 점수
    • Guardrail 실행 시간
    • 원본 대 마스킹된 콘텐츠

Langfuse와 통합

Langfuse에 로깅한다면 guardrail 정보가 자동으로 포함됩니다:

litellm_settings:
  success_callback: ["langfuse"]environment_variables:
  LANGFUSE_PUBLIC_KEY: "your-public-key"
  LANGFUSE_SECRET_KEY: "your-secret-key"

Guardrail 메타데이터에 대한 프로그래매틱 접근

커스텀 콜백에서 guardrail 메타데이터에 접근할 수 있어요:

import litellmdef custom_callback(kwargs, result, **callback_kwargs):    # Access guardrail metadata
    metadata = kwargs.get("metadata", {})
    guardrail_results = metadata.get("guardrails", {})
        print(f"Masked entities: {guardrail_results}")
    litellm.callbacks = [custom_callback]

6부: 프로덕션 모범 사례

1. 성능 최적화

pre-call guardrails에 병렬 실행 사용:

guardrails:
  - guardrail_name: "presidio-guard"
    litellm_params:
      guardrail: presidio
      mode: "during_call"  # Runs in parallel with LLM call

2. 사용 사례별 엔티티 유형 구성

의료(Healthcare) 애플리케이션:

pii_entities_config:
  PERSON: "MASK"
  MEDICAL_LICENSE: "BLOCK"
  US_SSN: "BLOCK"
  PHONE_NUMBER: "MASK"
  EMAIL_ADDRESS: "MASK"
  DATE_TIME: "MASK"  # May contain appointment dates

금융(Financial) 애플리케이션:

pii_entities_config:
  CREDIT_CARD: "BLOCK"
  US_BANK_NUMBER: "BLOCK"
  US_SSN: "BLOCK"
  PHONE_NUMBER: "MASK"
  EMAIL_ADDRESS: "MASK"
  PERSON: "MASK"

고객 지원 애플리케이션:

pii_entities_config:
  EMAIL_ADDRESS: "MASK"
  PHONE_NUMBER: "MASK"
  PERSON: "MASK"
  CREDIT_CARD: "BLOCK"  # Should never be shared

3. 고가용성 설정

프로덕션 배포에서는 여러 Presidio 인스턴스를 실행하세요:

version: '3.8'services:  presidio-analyzer-1:    image: mcr.microsoft.com/presidio-analyzer:latest    ports:      - "5002:3000"    deploy:      replicas: 3
  presidio-anonymizer-1:    image: mcr.microsoft.com/presidio-anonymizer:latest    ports:      - "5001:3000"    deploy:      replicas: 3

nginx, HAProxy 같은 로드 밸런서를 사용해 요청을 분산하세요.

4. 커스텀 엔티티 인식

도메인 특화 PII(예: 내부 직원 ID)에 대해 커스텀 인식기를 만드세요:

custom_recognizers.json 생성:

[
  {
    "supported_language": "en",
    "supported_entity": "EMPLOYEE_ID",
    "patterns": [
      {
        "name": "employee_id_pattern",
        "regex": "EMP-[0-9]{6}",
        "score": 0.9
      }
    ]
  }
]

LiteLLM에서 구성:

guardrails:
  - guardrail_name: "presidio-custom"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      presidio_ad_hoc_recognizers: "./custom_recognizers.json"
      pii_entities_config:
        EMPLOYEE_ID: "MASK"

5. 테스트 전략

PII 마스킹 테스트 케이스를 만드세요:

import pytestfrom litellm import completiondef test_pii_masking_credit_card():    """Test that credit cards are properly masked"""
    response = completion(
        model="gpt-5.6-luna",
        messages=[{
            "role": "user",
            "content": "My card is 4111-1111-1111-1111"
        }],
        api_base="http://localhost:4000",
        metadata={
            "guardrails": ["presidio-pii-guard"]
        }
    )
        # Verify the card number was masked
    metadata = response.get("_hidden_params", {}).get("metadata", {})
    assert "CREDIT_CARD" in str(metadata.get("guardrails", {}))def test_pii_masking_allows_normal_text():    """Test that normal text passes through"""
    response = completion(
        model="gpt-5.6-luna",
        messages=[{
            "role": "user",
            "content": "What is the weather today?"
        }],
        api_base="http://localhost:4000",
        metadata={
            "guardrails": ["presidio-pii-guard"]
        }
    )
        assert response.choices[0].message.content is not None

7부: 트러블슈팅

문제: Guardrail failure: non-JSON response from Presidio

증상: expected application/json Content-Type but received text/html 같은 오류를 받습니다.

근본 원인: 인그레스 컨트롤러나 리버스 프록시가 /analyze 또는 /anonymize POST 요청을 JSON 대신 일반 텍스트를 반환하는 헬스 엔드포인트(예: /health 또는 /presidio-analyzer/health)로 라우팅하고 있을 수 있어요.

해결: PRESIDIO_ANALYZER_API_BASEPRESIDIO_ANONYMIZER_API_BASE가 Presidio API 엔드포인트를 직접 올바르게 가리키는지, 또는 인그레스가 경로를 제거하거나 실수로 일반 텍스트 헬스 체크 엔드포인트로 전달하지 않도록 경로를 올바르게 라우팅하는지 확인하세요.

검증: curl로 엔드포인트를 검증할 수 있어요. text/html이 아니라 JSON 배열을 반환해야 합니다:

curl -sv -X POST http://your-analyzer-endpoint/analyze \
  -H "Content-Type: application/json" \
  -d '{"text":"test","language":"en"}'

문제: Presidio가 PII를 감지하지 못함

확인 1: 언어 구성

# Verify language is set correctlycurl -X POST http://localhost:5002/analyze \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Meine E-Mail ist [email protected]",
    "language": "de"
  }'

확인 2: 엔티티 유형

찾는 엔티티 유형이 설정에 있는지 확인하세요:

pii_entities_config:
  CREDIT_CARD: "MASK"  # Add all entity types you need

지원되는 모든 엔티티 유형 보기

문제: Presidio 컨테이너가 시작되지 않음

로그 확인:

docker-compose logs presidio-analyzerdocker-compose logs presidio-anonymizer

흔한 문제:

  • 포트 충돌(5001, 5002가 이미 사용 중)
  • 메모리 할당 부족
  • Docker 네트워크 문제

문제: 높은 지연 시간

해결 1: during_call 모드 사용

mode: "during_call"  # Runs in parallel

해결 2: Presidio 컨테이너 스케일링

deploy:
  replicas: 3

해결 3: 캐싱 활성화

litellm_settings:
  cache: true
  cache_params:
    type: "redis"

결론

축하합니다! 🎉 Presidio와 LiteLLM으로 PII 마스킹을 성공적으로 설정했어요. 이제 다음을 갖췄습니다:

✅ 프로덕션 준비 완료된 PII 마스킹 솔루션 ✅ 민감한 정보의 자동 감지 ✅ 여러 구성 옵션(마스킹 vs 차단) ✅ 모니터링 및 추적 기능 ✅ 다중 언어 지원 ✅ 프로덕션 배포를 위한 모범 사례

다음 단계

  • 지원되는 모든 PII 엔티티 유형 보기
  • 다른 LiteLLM guardrails 탐색
  • 여러 guardrails 설정
  • 키별 guardrails 구성
  • 커스텀 guardrails 알아보기

추가 리소스

  • Presidio Documentation
  • LiteLLM Guardrails Reference
  • LiteLLM GitHub Repository
  • Report Issues

도움이 필요하세요? Discord 커뮤니티에 참여하거나 GitHub에 이슈를 여세요!

오탐(False Positive) 억제

Presidio는 때때로 오탐 감지를 트리거할 수 있어요. 예를 들어 짧은 영숫자 문자열이 US_DRIVER_LICENSE로 잘못 플래그될 수 있습니다.

presidio_score_thresholds 또는 presidio_entities_deny_list로 이런 오탐을 억제할 수 있어요.

guardrails:
  - guardrail_name: presidio-pii
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      presidio_analyzer_api_base: "http://localhost:5002/"
      presidio_anonymizer_api_base: "http://localhost:5001/"
          # Use high score thresholds to reduce false positives
      presidio_score_thresholds:
        US_DRIVER_LICENSE: 0.85
        ALL: 0.5
          # Or exclude certain entity types entirely from detection
      presidio_entities_deny_list:
        - US_DRIVER_LICENSE

더 알아보기 (Learn more)