最近處理到PII(個人識別資訊 / 機敏資料),想了解其他公司是怎麼進行處理。
資料平台的公司,例如:Snowflake、Databricks 與 Microsoft,有的先產生遮罩後的資料供分析,有的在模型請求送出前檢查,也有的直接限制機密文件進入 AI。對準備讓 Agent 讀取公司文件的團隊,這些公開方案能幫助我們判斷:個資應在哪個階段處理,以及現成平台已經做到哪裡。
例如,航空客服想分析一批改票申請,找出最常見的改票原因。資料可能來自 Excel、PDF、Word 或客服對話,包含旅客姓名、聯絡方式與申請內容。分析原因需要保留敘述,通常不需要真實姓名;若任務改成追蹤同一位旅客的多次申請,又需要保留人物之間的關聯。
因此,遮罩不只是把文字換成星號,而是決定哪些資訊保留、以什麼形式保留,以及後續系統能讀到哪份資料。本文以 2025–2026 年官方文章、教學與大會分享為主,整理四種具體做法,再討論它們對文件處理的限制。
Snowflake 是雲端資料平台,可以在資料表上執行 SQL 與 AI 函式。它在 2025 年 12 月 8 日宣布 AI_REDACT 正式推出,用模型找出非結構化文字中的個資並替換。這裡的非結構化文字,可以是資料表某一欄保存的客服備註,不一定是整份 PDF。
可以這樣理解:
| 階段 | 資料示意 |
|---|---|
| 原始備註 | 王小明希望改到下午,請寄信到 ming@example.com,因上午需陪家人就醫。 |
| 遮罩後備註 | [NAME] 希望改到下午,請寄信到 [EMAIL],因上午需陪家人就醫。 |
| 後續分析 | 改票原因:家庭/就醫安排;時段偏好:下午。 |
創建遮罩資料的table的SQL:
CREATE TABLE feedback_redacted AS
SELECT
request_id,
SNOWFLAKE.CORTEX.AI_REDACT(feedback) AS redacted_feedback
FROM feedback_raw;
在執行這類 SQL 後,才會建立處理後的資料表;原始表不會因此消失。後續工作必須明確讀取遮罩欄位,才能達成預期。
AI_REDACT 被歸類為封閉式的「Cortex AI Functions」,Snowflake 將其封裝成一個簡單的 SQL 函數。可指定要處理的類型,如姓名、信箱,並以類別標籤替換命中的內容。它的主要用途是減少識別資訊;不能因為兩個人都被換成 [NAME],就假設系統仍保有每個人的唯一身分對照。
更關鍵的是,辨識與遮罩本身就會使用模型。這條路徑是在平台允許的處理環境中,先用模型清理資料,再供後續分析;但官方目前沒有公布使用什麼模型。
官方目前也提醒,英文文字表現較佳,支援的個資類型主要涵蓋美國及部分英國、加拿大資料,而且可能漏掉個資。
Microsoft 的 Azure Language 提供個資偵測服務,應用程式送入內容後,可取得辨識出的類型、位置、信心分數與遮罩結果。這能讓開發者使用既有服務,省去自行維護整套辨識模型的部分工作。
另一個是 PII Redaction,也提供 Playground 與 MCP 入口。文章以醫療助理說明:先處理訊息中的識別資訊,再將處理後內容交給下游系統。
Azure 的 PII 服務會依輸入類型走不同流程。像訊息、Prompt 這類純文字,可以直接送進文字 API;PDF、DOCX、TXT 這類文件,則會走文件處理流程,以非同步方式處理檔案,並盡量保留原本的文件結構。
兩種方式適合的情境也不同。假設只是要把旅客的改票原因送給 LLM 分析,先把文字中的姓名、電話遮掉就夠了;但如果需求是產生一份可以直接分享給其他人的遮罩版 PDF 或 Word,就不能只處理抽出的文字,還要保留文件本身的格式與版面。
實際串接時,可以把 PII 服務放在 LLM 前面:先完成文字或文件的遮罩,再把處理後的內容送給 Azure OpenAI 或其他下游服務。也就是說,它提供的是一個可加入既有流程的遮罩步驟,而不是接上服務後,所有送往模型的資料就會自動受到保護。
Microsoft 在 2025 年的文章中展示了幾種不同做法,包括把敏感內容遮掉、改成實體標籤,或直接用合成資料取代原本的值。
例如:王小明,可以改成:[PERSON_1]
也可以換成另一個虛構姓名。後者的好處是句子讀起來比較自然,適合拿來做測試、展示或後續分析。這些方式可以先在 Foundry Playground 測試,再依使用的 API 版本選擇對應的處理方式。
不過,用虛構資料取代原值時要特別注意用途。假設改票紀錄中的旅客姓名被換成另一個名字,這份資料可以拿來做摘要或分析,但不能再拿這個名字去查訂位、改票或執行其他實際交易。如果文件中多次出現同一位旅客,也要確認替換後是否還能維持前後一致,否則可能影響後續分析結果。
同篇文章也引用 Nationwide Building Society 的合作結果,提到個資遮罩的準確率從 81% 提升到超過 90%。這可以看出企業確實會在意 PII 辨識品質,不過文章沒有完整公開評測資料與計算方式,因此不適合直接解讀成台灣文件或所有語言都能達到相同效果。
如果公司原本就是用 Azure OpenAI API 開發 Agent,不需要因此更換模型;比較直覺的做法,是在呼叫 Azure OpenAI 前多一道 PII Redaction。
資料流會變成:
User
↓
Agent Backend
↓
Azure AI Language PII
↓
取得 redactedText
↓
Azure OpenAI
↓
Agent Response
例如旅客輸入:
我是王小明,電話 0912-345-678,
因為工作改期,想把明天的班機延到星期五。
後端可以先呼叫 Azure AI Language:
import os
import requests
from openai import OpenAI
LANGUAGE_ENDPOINT = os.environ["AZURE_LANGUAGE_ENDPOINT"]
LANGUAGE_KEY = os.environ["AZURE_LANGUAGE_KEY"]
def redact_pii(text: str) -> str:
url = (
f"{LANGUAGE_ENDPOINT}/language/:analyze-text"
"?api-version=2026-05-01"
)
response = requests.post(
url,
headers={
"Ocp-Apim-Subscription-Key": LANGUAGE_KEY,
"Content-Type": "application/json",
},
json={
"kind": "PiiEntityRecognition",
"parameters": {
"modelVersion": "latest",
"redactionPolicies": [
{
"policyKind": "entityMask"
}
],
},
"analysisInput": {
"documents": [
{
"id": "1",
"text": text,
}
]
},
},
)
response.raise_for_status()
return response.json()["results"]["documents"][0]["redactedText"]
PII 服務可能把內容轉成類似:
我是 [PERSON_1],電話 [PHONENUMBER_1],
因為工作改期,想把明天的班機延到星期五。
接著才把這段內容送進原本的 Azure OpenAI Agent:
client = OpenAI(
base_url=(
os.environ["AZURE_OPENAI_ENDPOINT"].rstrip("/")
+ "/openai/v1/"
),
api_key=os.environ["AZURE_OPENAI_API_KEY"],
)
user_text = """
我是王小明,電話 0912-345-678,
因為工作改期,想把明天的班機延到星期五。
"""
safe_text = redact_pii(user_text)
response = client.chat.completions.create(
model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
messages=[
{
"role": "system",
"content": "你是航空改票助理,整理旅客的改票需求。",
},
{
"role": "user",
"content": safe_text,
},
],
)
print(response.choices[0].message.content)
這裡也要注意一個界線:**原始文字仍然先送到了 Azure AI Language,只有後續 Azure OpenAI 收到的是遮罩版本。**如果公司的要求是「姓名、電話連 Azure 都不能看到」,那麼這個架構仍不符合需求,必須改成在內網或應用程式端先使用 Presidio 等方案處理,再呼叫 Azure OpenAI。
Databricks 是整合資料工程、分析與 AI 的平台。它的公開分享呈現兩個處理位置:一個在資料集準備階段,另一個在模型 API 呼叫時。
ai_query 是 Databricks 在 SQL 或 Python 中呼叫模型的通用介面。開發者可以指定模型與 Prompt,例如把客服紀錄或臨床筆記交給模型,要求找出姓名、電話、地址等資訊,再產生去識別化版本。
例如:
SELECT ai_query(
'system.ai.claude-sonnet-4-5',
'請移除以下文字中的個資:' || note
)
FROM medical_notes;
實際使用哪個模型並不固定,可以是 Databricks 提供的 Foundation Model、自建或 fine-tuned model,也可以接 External Model endpoint。
因此,ai_query 和 Presidio、Azure PII 的定位其實不太一樣。Presidio 與 Azure PII 是專門找出 PII 的工具或服務;ai_query 則比較像是叫一個模型幫忙產生去識別化資料。
這也帶來一個很重要的限制:負責遮罩的模型本身,仍然會先看到原始資料。
如果真正的需求是「不要讓原始個資直接送進後方模型」,檢查位置就要放在模型前面。
Databricks 在 2025 年 8 月 7 日的 AI Gateway 文章中,介紹統一模型存取與 PII guardrails。這裡的 Gateway 是應用程式呼叫模型時的共用入口,可以集中套用安全政策。來源:Build with GPT-5 on Databricks with AI Gateway
對應的模型服務端點文件提供 Block、Mask 與 None 等設定,可以檢查支援的模型 request 與 response。這和 ai_query 最大的差別,是 Gateway 可以在內容真正進入後方模型前先處理。
ai_query:
原始資料
↓
模型先看到原文
↓
產生去識別化資料
↓
後續分析
Gateway:
使用者輸入
↓
PII 檢查
↓
Block / Mask
↓
模型
模型輸入與輸出也要分開看。若只對 response 做遮罩,只代表使用者看不到模型輸出的敏感內容,並不能改變「模型已經收到原始輸入」這件事;若目標是保護送進模型的資料,就必須在 request 階段先檢查或遮罩。來源:端點設定文件
這份 Model Serving AI Gateway 文件目前已標示為 legacy,當中的 AI Guardrails 也屬於預覽階段能力,因此本文用它來說明 Databricks 曾公開提供的處理方式,不把舊端點介面視為所有 Databricks 環境都固定具備的功能,實際導入仍要確認目前使用版本與 Gateway 能力。
對改票系統而言,這兩個位置解的是不同問題。ai_query 可以先整理歷史改票申請,產生一份適合分析或測試的去識別化資料集;模型入口則負責處理當下的新訊息,例如旅客突然在備註中貼入電話、Email 或其他敏感資料。
即使歷史資料已經清理過,新輸入仍可能再次帶入個資,因此「資料集去識別化」不能取代「模型入口檢查」。簡單來說,ai_query 比較像是在整理既有資料,而 Gateway 才是在保護這一次即將送給模型的內容。
Agent 每次查資料、取得工具結果與呼叫模型,都在決定哪些資訊會進入模型的上下文。因此,前面的遮罩方案,也可以從 Agent 的資料流來理解。下表是依公開案例整理的設計對應,不代表各家 Agent 預設都會自動套用。
| 業界做法 | 對應到 Agent 設計 | 改票 Agent 的例子 |
|---|---|---|
| Snowflake:先建立遮罩後的資料 | 資料來源:讓查詢工具只讀取處理後的資料 | 分析歷史改票原因時,工具只回傳原因與必要欄位,不帶姓名、信箱 |
| Microsoft Azure Language:文字與文件遮罩服務 | 工具結果處理:內容回到模型前,先由後端呼叫遮罩服務 | 讀取申請文件後,先處理個資,再讓模型整理需求 |
| Databricks AI Gateway:檢查模型請求 | 模型呼叫入口:集中檢查即將送出的內容 | 客服臨時貼入電話,也在模型呼叫前依政策遮罩或阻擋 |
| Microsoft Purview:限制敏感文件使用 | 資料存取政策:決定 Agent 所在應用能否使用該份文件 | 在支援的情境中,禁止將標為機密的企業包機報價交給 AI |
對 Agent 而言,關鍵是由程式安排遮罩發生的順序。若原文已先進入模型,再讓模型決定是否呼叫遮罩工具,就無法達成「該模型不接觸原始個資」的目的;需要這項保護時,應在資料進入它的上下文之前處理。遮罩服務本身能否接觸原文,則是另一個需要確認的處理範圍。
如果公司原本就使用 Databricks、Snowflake、Azure 這類平台,個資遮罩不一定要從零開始做。可以直接透過 SQL、受管理服務或模型入口的政策,把辨識、遮罩或阻擋加進既有流程。不過平台只負責提供能力,團隊還是要先決定哪些資料要遮、哪些可以保留,以及辨識失敗時要阻擋、告警,還是繼續往下處理。
遮罩方式也會影響資料之後怎麼用。例如,把所有姓名都改成 [PERSON],雖然可以保留句子意思,但可能看不出兩段文字是不是在講同一個人;改成虛構姓名會比較好閱讀,卻不能再拿來查詢真實訂位。如果後續還需要把分析結果對回原本的旅客,就要另外設計穩定的代碼、對照方式與存取權限,不能只因為「名字被換掉了」就把資料視為完全匿名。
如果是自己開發的小型 Agent,也有相對簡單的開源做法。最直接的方式,是在後端呼叫模型之前先用 Presidio 找出並替換個資;如果有多個 Agent,希望共用同一套規則,則可以把 LiteLLM+Presidio 放在模型入口集中處理。
例如改票 Agent 收到一筆申請後,後端可以先移除根本不需要送給模型的證號,再把剩下的內容交給 LiteLLM。LiteLLM 在 pre_call 階段呼叫 Presidio,檢查備註中是否還有姓名、電話或信箱,完成遮罩或阻擋後,才把內容送給模型。設定時可以在 config.yaml 或 Guardrails 介面指定 Presidio 服務與政策。要特別注意,logging_only 只會處理紀錄在 Log 裡的內容,不會改掉真正送給模型的 Prompt;另外,這一層主要處理的是文字,PDF、Excel 等附件怎麼解析,以及繁體中文個資辨識效果,仍要另外確認。來源:LiteLLM 的 Presidio 整合
最後真正要確認的,其實只有三件事:原始資料先交給誰、遮罩後的資料存在哪裡,以及下一個模型最後看到的是哪一個版本。 把這三件事畫清楚,就比較容易判斷現在做的是資料集清理、模型入口遮罩,還是文件本身的去識別化,也不會因為介面上出現一個「PII Masking」開關,就以為整條資料流程都已經受到保護。