Startseite / Artikel

Redaktion von PII und PCI in Abfragen, Kontexten und Antworten in einem RAG-Graphen

Redaktion von PII und PCI an drei Stellen in einem RAG-Graphen: vor der Suche, vor dem Prompt und vor der Benutzeroberfläche, mit Gold-Tests für saubere Fälle, Kartenfälle und SSN-Fälle.

Verlag: REACTAPP.TOP8 Min. Lesezeit1908 Wörter

Quellen: Adding PII/PCI Guardrails to the RAG Pipeline

Schutz von Abfragen und Antworten – Redaktion von PII & PCI

RAG-Pipelines, die Gebührenpläne, Kontodokumente oder Support-Transkripte indizieren, werden letztendlich Sozialversicherungsnummern, Karten-PANs sowie Namen entweder in der Benutzerabfrage oder im abgerufenen Kontext finden. Schutzmaßnahmen müssen in beide Richtungen wirken: sensible Abschnitte in der eingehenden Frage entfernen, die abgerufenen Daten vor deren Eintragung in den Prompt bereinigen sowie die Antwort des Modells vor ihrer Anzeige in der Benutzeroberfläche sanitisieren.

Gute Nachrichten

Azure AI Language (sowie damit verbundene PII-Funktionen) kann gängige Entity-Typen mit ausreichender Genauigkeit erkennen, um eine erste Verteidigungslinie zu bilden. In Kombination mit in der Anwendungscode definierten Feldrichtlinien, die nur erlaubte Werte zulassen, werden die meisten Demo-Fälle sowie viele Produktionsfälle abgedeckt – insbesondere, wenn zusätzlich die Schutzmechanismen von Portal Foundry eine zweite Schicht bieten.

Code-Änderungen gegenüber der vorherigen Implementierung

Erweitern Sie das Diagramm um drei Kontrollpunkte:

  1. Abfrageschutz – Erkennen/Verbergen von PII/PCI im Benutzertext vor Einbetten/Suchen
  2. Kontextschutz – Verbergen von Entitäten in den abgerufenen Passagen
  3. Antwortschutz – Letzte Überprüfung der generierten Antwort
mkdir AISearchGRManualVectorization && cd AISearchGRManualVectorization && \
  uv init .
uv add langgraph langchain-core langchain-openai langchain-text-splitters \
       azure-ai-documentintelligence azure-core \
       langchain-azure-ai azure-search-documents \
       azure-ai-textanalytics \
       python-dotenv
# Add all files from git repo or clone repository
@dataclass
class RedactionResult:
    text: str
    pii_found: bool = False
    pci_found: bool = False
    categories: List[str] = field(default_factory=list)
    error: Optional[str] = None


def redact_text(text: str) -> RedactionResult:
    if not text or not text.strip():
        return RedactionResult(text=text)

    client = _get_client()
    results = client.recognize_pii_entities([text], language="en")
    result = results[0]

    if result.is_error:
        return RedactionResult(text=text, error=result.error.message)

    categories = [e.category for e in result.entities]
    return RedactionResult(
        text=result.redacted_text,
        pii_found=len(categories) > 0,
        pci_found=any(c in PCI_CATEGORIES for c in categories),
        categories=categories,
    )
def redact_query_node(state: ChatFileSearchState) -> ChatFileSearchState:
    if not state.get("query"):
        return state
    result = guardrails.redact_text(state["query"])
    if result.pii_found:
        kind = "PCI" if result.pci_found else "PII"
        print(f"[guardrail] {kind} detected and redacted in query")
    state["query"] = result.text
    return state


def redact_response_node(state: ChatFileSearchState) -> ChatFileSearchState:
    if not state.get("answer"):
        return state
    result = guardrails.redact_text(state["answer"])
    if result.pii_found:
        kind = "PCI" if result.pci_found else "PII"
        print(f"[guardrail] {kind} detected and redacted in response")
    state["answer"] = result.text
    return state
def generate_node(state: ChatFileSearchState) -> ChatFileSearchState:
    if not state.get("query"):
        state["answer"] = None
        return state

    if not state["retrieved"]:
        context = "No relevant content was found in the uploaded files."
    else:
        context = "\n\n---\n\n".join(
            f"[{doc.metadata.get('file_name', 'unknown')}] {doc.page_content}"
            for doc in state["retrieved"]
        )
        context = guardrails.redact_text(context).text  # <-- checkpoint 2

    prompt = (
        "Answer the question using only the context below, which comes from "
        "files the user uploaded to this chat. If the answer isn't in the "
        "context, say you don't know - don't make anything up.\n\n"
        "Format the answer in markdown: use a short bolded summary line "
        "first, then bullet points or a table for any figures, dates, or "
        "line items. Keep prose minimal - prefer structure over paragraphs.\n\n"
        f"Context:\n{context}\n\n"
        f"Question: {state['query']}"
    )
    response = llm.invoke(prompt)
    state["answer"] = response.content
    return state
graph.add_edge("ingest", "redact_query")
graph.add_edge("redact_query", "retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", "redact_response")
graph.add_edge("redact_response", END)
┌──────────┐      ┌──────────────┐      ┌──────────┐      ┌──────────┐      ┌─────────────────┐      ┌─────┐
│  ingest  ├─────►│ redact_query ├─────►│ retrieve ├─────►│ generate ├─────►│ redact_response ├─────►│ END │
└──────────┘      └──────────────┘      └──────────┘      └──────────┘      └─────────────────┘      └─────┘
uv run python -m aisearchgrmanualvectorization.main \
  --session demo1 \
  --query "What's the overdraft protection fee?"

Q: What's the overdraft protection fee?

A: **Overdraft protection transfer fee: $10.00 per occurrence**

- Overdraft protection transfers from a linked savings account are subject to a $10.00 fee per occurrence (as stated in the Overdraft Protection disclosure).
- If no linked savings account, insufficient funds fee: $34.00 per item (up to four items per day).

Sources:
  - sample_bank_statement.pdf (chunk 7): #### Important Account Disclosures   ##### Overdraft Protection  If a transaction is presented that ...
  - sample_bank_statement.pdf (chunk 8): ##### Interest Rate & Annual Percentage Yield  This account earns interest at a variable rate, curre...
  - sample_bank_statement.pdf (chunk 9): ##### Monthly Maintenance Fee  A $12.00 monthly maintenance fee applies unless one of the following ...
  - sample_bank_statement.pdf (chunk 3): <td>-$10.44</td> <td>$8,096.52</td> </tr> <tr> <td>02/07/2026</td> <td>Greenfield Grocers</td> <td>D...
uv run python -m aisearchgrmanualvectorization.main \
  --session demo1 \
  --query "My card number is 4111 1111 1111 1111, what's the late payment fee on my loan?"
[guardrail] PCI detected and redacted in query

Q: My card number is 4111 1111 1111 1111, what's the late payment fee on my loan?

A: **Short answer: Late payment fee is $35.00 or 5% of the payment, whichever is less.**

- Late Payment Fee: $35.00 or 5% of the payment amount, whichever is less
- Delinquent threshold: payment not received within 15 calendar days of its Scheduled Payment Date
- Default threshold: payment unpaid 30 calendar days past Scheduled Payment Date

Sources:
  - sample_loan_agreement.pdf (chunk 7): ### 6. Late Payments and Default  A payment not received within 15 calendar days of its Scheduled Pa...
  - sample_loan_agreement.pdf (chunk 1): ## 2. Key Loan Terms   <table> <tr> <th>Principal Amount</th> <th>$18,000.00</th> <th>Loan Term</th>...
  - sample_loan_agreement.pdf (chunk 6): A complete 48-month amortization schedule is available from the Lender upon request. Figures above a...
  - sample_bank_statement.pdf (chunk 9): ##### Monthly Maintenance Fee  A $12.00 monthly maintenance fee applies unless one of the following ...
uv run python -m aisearchgrmanualvectorization.main \
  --session demo1 \
  --query "My SSN is 123-45-6789 - can you also tell me the monthly payment on the loan?"
[guardrail] PII detected and redacted in query

Q: My SSN is 123-45-6789 - can you also tell me the monthly payment on the loan?

A: **Monthly payment is $454.37**

- Monthly Payment: $454.37

Sources:
  - sample_loan_agreement.pdf (chunk 1): ## 2. Key Loan Terms   <table> <tr> <th>Principal Amount</th> <th>$18,000.00</th> <th>Loan Term</th>...
  - sample_loan_agreement.pdf (chunk 3): "Scheduled Payment Date" means the 15th calendar day of each month during the Loan Term, or the next...
  - sample_loan_agreement.pdf (chunk 6): A complete 48-month amortization schedule is available from the Lender upon request. Figures above a...
  - sample_loan_agreement.pdf (chunk 4): <table> <tr> <th>Payment #</th> <th>Payment Amount</th> <th>Interest</th> <th>Principal</th> <th>Rem...
uv run python -m aisearchgrmanualvectorization.main \
  --session demo1 \
  --query "Who is the account holder on the bank statement?"
[guardrail] PII detected and redacted in query

Q: Who is the account holder on the bank statement?

A: **I don't know — the statement redacts that name.**

- The document shows "**************" in place of the name (redacted).
- No unredacted name or identifying information for that field appears in the provided context.

Sources:
  - sample_bank_statement.pdf (chunk 0): # SAMPLE STATEMENT - SYNTHETIC TEST DATA - NOT A REAL FINANCIAL DOCUMENT   # Meridian Trust Bank (fi...
  - sample_bank_statement.pdf (chunk 6): <!-- PageBreak -->   <table> <tr> <th>Date</th> <th>Description</th> <th>Type</th> <th>Amount</th> <...
  - sample_bank_statement.pdf (chunk 10): ##### Error Resolution & Your Rights  In case of errors or questions about electronic transfers, tel...
  - sample_bank_statement.pdf (chunk 8): ##### Interest Rate & Annual Percentage Yield  This account earns interest at a variable rate, curre..

Führen Sie strukturierte Protokolle darüber auf, was verdeckt wurde (Entitätenart + Offset), niemals die Rohdaten selbst, zur Prüfungszwecken.

Ausführung: Funktioniert es tatsächlich?

Eine einfache Abfrage ohne PII/PCI-Daten

Es sollte unveränderter Text durch alle drei Schutzmechanismen sowie eine normale Abfrageantwort entstehen.

Abfrage mit gefälschten PCI-Daten

Planen Sie einen Test-PAN; überprüfen Sie, ob der Abfrageschutz ihn vor der Suche maskiert und ob die Protokolle eine Erkennung der CreditCard-Klasse ohne Speicherung der Ziffern anzeigen.

Abfrage mit gefälschten PII-Daten (SSN)

Stellen Sie sicher, dass wertebasierte Angaben in SSN-Form ähnlich maskiert werden.

Aus dem abgerufenen Kontext den Namen des Kontoinhabers extrahieren

Auch wenn die Anfrage des Benutzers sauber ist, kann der abgerufte PDF-Text Namen enthalten. Der Kontextschutz muss diese vor dem Zugriff des Chat-Modells maskieren; der Antwortenschutz verhindert, dass das Modell den Namen erneut generiert.

Betriebshinweise

  • Führen Sie ein „Golden Prompt“-Paket mit sauberen, PCI-, PII-Daten sowie Mischformen auf
  • Ziehen Sie es vor, Daten zu maskieren anstatt zu löschen, damit die Satzstruktur lesbar bleibt
  • Protokollieren Sie niemals Rohdaten wie PANs/SSNs „zur Fehlerbehebung“
  • Kombinieren Sie dies mit den Sicherheitsregeln des Foundry-Portals für Kategorien wie Jailbreak oder Schäden, die nicht vom Python-Layer abgedeckt werden
  • Überprüfen Sie falsche Positive bei Geschäftsidentifikatoren, die wie Geheimnisse aussehen (interne Kontonummern).
  • Fazit

    Die Schutzmaßnahmen für PII/PCI sind keine optionalen Ergänzungen zu einer RAG-Demo – sie gehören zum Rückgabevertrag. Sowohl Abfragen, Kontexte als auch Antworten benötigen explizite Überprüfungspunkte, Tests sowie Protokolle, die nachweisen, dass eine Verschleierung stattgefunden hat, ohne das Geheimnis zu bewahren.

    Bei der Integration sollte bei Ausfällen der Detektoren für hochriskante Routen ein Fehler ausgelöst werden (ein sicherer Fehler anstelle des Streamens unüberprüften Kontexts). Für weniger risikoreiche interne Tools soll nur bei starken Metriken und einem definierten Schwellenwert ein Fehler ausgelöst werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicherer Fehler zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicheres Fehlermeldung zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.

    Bei der Integration soll bei Ausfällen der Detektoren auf hochriskanten Routen ein fehlerbehaftetes Verhalten eingeleitet werden (es soll ein sicheres Fehlermeldung zurückgegeben werden anstelle des Streamens unüberprüften Kontexts). Für interne Tools mit geringerem Risiko soll nur bei einem deutlichen Metrikwert und einem bestimmten Paging-Threshold ein fehlerbehaftetes Verhalten eingeleitet werden. Dokumentieren Sie, welchen Modus jede Umgebung verwendet.