Галоўная / Артыкулы

Працэўнечыя даныя аб адзіночных асобах і даныя карточак плацікавых карт пры выкарыстоўванні ў запітах, контэксце і адпаведзях у графе RAG

Усунення персональных даных і даных крэдытных карт у трохіба точках графа RAG: перад пошуком, перад запітом і перад інтэрфейсом, з алгорытмамі перэвіркі для віпадків чыстых даных, даных карт і номераў соціального страхування.

Выдавец: REACTAPP.TOP8 хв чытання1908 слоў

Крыніцы: Adding PII/PCI Guardrails to the RAG Pipeline

Захаванне як запіту, так і адказу — выдаленне персональных даных

Пайплайны RAG, які індэксуюць табелі з тарыфамі, документы або транскрыпціі падтрымкі, зрэшты будуць бачыць номеры соцыяльнага страхавання, даныя карт PAN і імена як у запытэ корыстніка, так і ў отрыманам кантэксце. Меры захавання павінны дзейсніцца у обох напрамках: выдалваць чутлівыя даныя з уваходзячага запыту, очышваць отрыманы фрагменты перад тым, як яны патрапляюць у запрос, і адчышваць адказ мадэлі перад тым, як ён дасягне інтэрфейсу.

Хорашыя новіны

Azure AI Language (і адпаведныя функцыі для захавання персональных даных) можа выявляць распашчытныя тыпы элементаў з прыемнай точнасцю, што служыць першым рубежам захавання. Спалучэнне гэтага з правіламі для поль у кодзе прыкладнай програмы пакрывае большасць дэмовых і багато рэальных сцэнарыёў — особліва калі меры захавання з Foundry дадаюць ўторы слой абароны.

Змены ў кодзе па супэрніцтву з пакойшняй рэалізацыяй

Расшырыце граф ад трох пунктав перакантролі:

  1. Захоўнік запыту — выявляе/маскуе PII/PCI ў тэксце карыстальніка пры вбіранні/пошуку
  2. Захоўнік кантэксту — маскуе элементы ў атрыбутаваных фрагментах
  3. Захоўнік адпаведзі — пасляпэльная перакантроль генераванай адпаведзі
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..

Зберагаеце структураваныя логі пра тое, што было маскуе (тып элемента + пазіцыя), але ніколі самы секрэтны тэкст, для аўдыту.

Выконанне: чы рэальна ўсё працуе

Просты запыт без дадзэнняў PII/PCI

Можна спакуяцца, што тэкст праходзіць праз усе трох пункты перакантролі без змян і адпаведзь будзе стандартная.

Запыт, які містыць фальшывыя дадзеныя PCI

Засадзіце тэставы PAN; паказваце, чы рэгулярныя выразы для захавання даных маскуюць яго пры пошуку, і чы логі паказваюць выкрычку CreditCard без зберагання цыфр.

Запіт, які містыць фальшывыя персональныя даны (SSN)

Паказваце, чы значэння, якія нагадваюць SSN, таксама маскуюцца.

Выявленне імені власніка рахунку з атрыбутаў, якія былі выявлены

Нават калі запіт карыстальніка чысты, тэкст PDF, які быў выявлены, можа містыць імены. Рэгулярныя выразы для захавання даных павінны маскаваць іх прытаму, калі мадэль чату бачыць этап даных; рэгулярныя выразы для адпаведных адказаў павінны запобегаць перадзначэнню імені мадэллю.

Урачыстковыя прыметкі

  • Зберагаце набор „золатых“ прамптов з чыстымі, PCI-, PII-данымі і данымі разных фарматаў
  • Вядомае захаванне даных прыорытэтная ад змены, каб структура рэчэй застаўалася чытаемай
  • Ніколі не лічуце первосныя PAN/SSN „для дыбаггінгу"
  • Спалучайце з правіламі портала Foundry для катэгорыяў, якія не контролюецца Python-шаром
  • Пераглянуце кейсы хыбных пазытков для бізнес-ідэнтыфікатораў, якія выглядаюць як секрэты (внутранія коды абліков)
  • Заканчэнне

    Нормы захавання PII/PCI не ўжо толькі дапаможны элементы пад час дэманстрацыі RAG — яны ўскладневаюць сам прынцып выкарыстоўвання дадзеных. Кожны запіт, кантэкст і адпаведнае адказа патрабуюць чыткіх контрольных пунктаў, тэстаў і логаў, якія падтвердзяюць, што дадзеныя былі засланы без захавання секрэтных інформацый.

    Пад час інтэграцыі неабходна блокаваць работу пры збоях дэтэктараў для маршрутаў высокага рызыку (працаваць трэба з безпечнымі памылкамі, а не з неконтрольваным кантэкстам). Для менш рызыковых внутраніх інструментаў блокаванне дазволяецца толькі у разы перасягнуць певных прагаў. Неабходна задокументаваць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна застосаваць прынцып замкнутага абрабатвання кэсоў (працаваць трэба з безпечным адказам, а не трансляваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам прынцып абрабатвання кэсоў застосаваць толькі з выражаным показнікам і прагамай апавешчэння. Запісаць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна застосаваць прынцып замкнутага абрабатвання кэсоў (працаваць трэба з безпечным адказам, а не трансляваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам прынцып абрабатвання кэсоў застосаваць толькі з выражаным показнікам і прагамай апавешчэння. Запісаць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна застосаваць прынцып замкнутага абрабатвання кэсоў (працаваць трэба з безпечным адказам, а не трансляваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам прынцып абрабатвання кэсоў застосаваць толькі з выражаным показнікам і прагамай апавешчэння. Запісаць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна адхіліць запуск (працяваць толькі з безпечным адказам, а не струяваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам неяўна адхіліць запуск толькі за наявнасці выражанага показніка і прагу для апавешчэння. Запісаць, які рэжым вікорыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна адхіліць запуск (працяваць толькі з безпечным адказам, а не струяваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам неяўна адхіліць запуск толькі за наявнасцю выражанага показніка і прагу для апавешчэння. Запісаць, які рэжым вікорыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна адхіліць запуск (працяваць толькі з безпечным адказам, а не струяваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам неяўна адхіліць запуск толькі за наявнасцю выражанага показніка і прагу для апавешчэння. Запісаць, які рэжым вікорыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна застосаваць прынцып замкнутага абрабатвання кэсоў (працягваць аддачу безпечнай памылкі заместа тэстравання неконтрольванага кантэксту). Для внутраніх інструментоў з ніжшым рызыкам прынцып абрабатвання кэсоў застосаваць толькі з выражаным показнікам і прагамай апавешчэння. Запісаць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна застосаваць прынцып замкнутага абрабатвання кэсоў (працягваць аддачу безпечнай памылкі заместа тэстравання неконтрольванага кантэксту). Для внутраніх інструментоў з ніжшым рызыкам прынцып абрабатвання кэсоў застосаваць толькі з выражаным показнікам і прагамай апавешчэння. Запісаць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна застосаваць прынцып замкнутага абрабатвання кэсоў (працягваць аддачу безпечнай памылкі заместа тэстравання неконтрольванага кантэксту). Для внутраніх інструментоў з ніжшым рызыкам прынцып абрабатвання кэсоў застосаваць толькі з выражаным показнікам і прагамай апавешчэння. Запісаць, які рэжым выкарыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна павінна быць адмова (працяваць трэба з безпечным адказам, а не струяваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам неяўна адмова дазволена толькі за наявнасці выражанага показніка і певнага порога. Неабходна задокументаваць, які рэжым вікорыстоўвае кожна асяроддзе.

    Падчас інтэграцыі, у разе збою датчыка на маршрутах з высокім рызыкам, неяўна павінна быць адмова (працяваць трэба з безпечным адказам, а не струяваць неконтрольваны кантэкст). Для внутраніх інструментаў з ніжшым рызыкам неяўна адмова дазволена толькі за наявнасці выражанага показніка і певнага порога. Неабходна задокументаваць, які рэжым вікорыстоўвае кожна асяроддзе.