Presidio 是一套開源的機敏資料辨識與去識別化工具。它可以找出文字中的姓名、電話、Email、信用卡號等資料,再依指定方式替換、遮蔽或移除。
例如,客服系統想請 LLM 整理客訴內容,但不需要把客戶的聯絡方式一起送出去,就能先把:
請協助處理退款,聯絡信箱是 alice@example.com。
處理成:
請協助處理退款,聯絡信箱是
<EMAIL_ADDRESS>。
Presidio 可以作為 Python 函式庫放進應用程式,也可以部署成服務。如果搭配 LiteLLM,則能在 Agent 呼叫模型之前,集中處理送出的文字。
不過,採用前需要先弄清楚:它如何判斷哪些字串是機敏資料、中文辨識需要哪些模型,以及遮罩應放在 Agent 的哪個位置。
Presidio 的文字處理主要分成兩個部分:
| 元件 | 負責的工作 |
|---|---|
| Analyzer | 找出機敏資料的類型、文字位置與辨識分數 |
| Anonymizer | 按照這些位置,執行替換、遮蔽或刪除 |
Analyzer 會執行多個 Recognizer,也就是辨識器。每個辨識器負責一種或多種資料類型,底下可以使用格式規則、專門的解析函式庫,或機器學習模型。
| Recognizer | 負責辨識 | 使用的方法 |
|---|---|---|
EmailRecognizer |
正規表示式找候選字串,再檢查網域結構 | |
CreditCardRecognizer |
信用卡號 | 正規表示式+Luhn 檢查碼 |
PhoneRecognizer |
電話號碼 | 使用 phonenumbers,依地區編碼規則解析 |
SpacyRecognizer |
人名、地點等實體 | 讀取 spaCy NLP 引擎產生的辨識結果 |
PatternRecognizer |
自訂格式與指定詞彙 | 正規表示式或詞彙清單 |
Presidio 的 PhoneRecognizer 會呼叫:
phonenumbers.PhoneNumberMatcher(
text, region, leniency=self.leniency
)
這是原始碼的呼叫節錄。PhoneNumberMatcher 來自外部的 python-phonenumbers 套件,是 Google libphonenumber 的 Python 移植版。
以這段文字為例:
My phone number is +1 425 882-8080.
套件的處理是:
Presidio 預設使用的嚴格程度是 VALID,因此不只是「數字夠長就算電話」。
CreditCardRecognizer 先用正規表示式找出符合候選格式的字串,再移除空格、連字號,執行 Luhn 檢查碼演算法。
Luhn 的計算方式是:
8 × 2 = 16,改計為 1 + 6 = 7。例如常見的測試號碼 4111111111111111:
原始:4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
處理:8 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1
加總:30 → 通過
若只把最後一位改成 2,加總變成 31,就不通過。
這能排除部分輸入錯誤與隨機數字,但通過 Luhn 不代表真有這張卡,也不代表卡片可以交易。這是檢查碼驗證,不是銀行查驗。
Presidio 還會利用候選資料附近的詞彙,調整辨識分數。
例如電話辨識器設定了 phone、telephone、mobile、call 等關鍵字。對:
My phone number is +1 425 882-8080.
電話候選先由號碼解析器找到;接著,上下文增強器擷取附近經 NLP 處理的詞彙,包含詞形還原結果,再與辨識器預先設定的關鍵字比較。
查閱的預設實作使用不區分大小寫的子字串比對,也提供完整詞彙比對模式。這個步驟不是請 LLM 判斷句意,也不是計算向量相似度。
若命中支持詞,預設加 0.35,並限制分數上下界。電話的初始分數是 0.4,在這個例子中可提高為:
0.4 + 0.35 = 0.75
找到多個支持詞,不會每個都累加一次。這個機制也只調整已經找到的候選,不會因為出現 phone,就把任意一串文字變成電話。分數是程式的判斷依據,不能直接解讀成「有 75% 的機率是真的」。
姓名沒有像信用卡號那樣固定的檢查碼。辨識「王小明」是人名,通常需要 NER(Named Entity Recognition,命名實體辨識)模型,根據文字與上下文標出人名、地點等範圍。
Presidio 的預設英文配置使用 spaCy 的 en_core_web_lg;也提供 Stanza、Transformers 等 NLP 整合方式。使用 Transformers 時,需要的是能完成 NER 任務的模型,以及將模型標籤對應到 Presidio 實體類型的設定。
找出範圍後,Anonymizer 才執行文字操作。例如將 Email 換成 <EMAIL_ADDRESS>、將指定字元換成 *,或直接刪除整段。辨識模型決定遮哪裡;遮罩操作決定怎麼改。
不一定,取決於要新增的是規則,還是模型能力。
| 需求 | 通常採用的方式 | 是否需要自行準備訓練資料 |
|---|---|---|
| 格式明確的證件號碼、員工編號 | 正規表示式、適用的檢查碼、上下文 | 不需要訓練模型 |
| 現成 NER 模型已支援的姓名、地點 | 接入現成模型,對應實體標籤 | 不一定需要 |
| 模型不支援的新類型,或特定領域辨識不佳 | 新增規則,或微調 NER 模型 | 選擇微調時需要標註資料 |
例如公司員工編號固定是 EMP- 加上六位數,可以定義 EMP-\d{6} 這類規則,再補上符合業務資料的邊界條件。這是在寫辨識規則,不是在訓練模型。
身分證字號與護照號碼也應先依發證國家與證件種類判斷格式。若有適用的檢查碼,就加入對應驗證;不能把信用卡的 Luhn 演算法直接套用到所有證件。Presidio 提供自訂辨識器的擴充入口,可以將這些規則註冊到 Analyzer。
較困難的是「格式相同,但用途不同」。例如同樣一串九位數,可能是護照號碼,也可能是訂單編號。此時就要結合「護照」「passport」等附近詞彙,或應用程式已知的欄位資訊,降低誤判。
如果最後決定訓練模型,資料也不能只是丟進一批未標記文件。一般監督式 NER 微調需要指出:
旅客 [王小明/PERSON] 已完成報到。
也就是提供文字、實體範圍與類型,讓模型學習應該標記哪一段。完成訓練後,再把模型接回 Presidio。
PIIBench 整合十個資料集,比較 Presidio、spaCy、BERT 等八種系統。原論文使用 1,398 筆測試資料、3,383 個標註實體;辨識範圍與類型都必須符合標註,才算命中。
以下是原論文 Table 5 的結果,依 F1 排序:
| 系統 | Precision:找到的有多少正確 | Recall:應找的有多少找到 | F1 |
|---|---|---|---|
| Presidio | 0.1522 | 0.1271 | 0.1385 |
| SpanMarker BERT | 0.1723 | 0.0588 | 0.0877 |
spaCy en_core_web_lg |
0.0636 | 0.1395 | 0.0873 |
| SpanMarker mBERT | 0.1477 | 0.0570 | 0.0823 |
| BERT-base NER | 0.0791 | 0.0562 | 0.0657 |
| XLM-RoBERTa NER | 0.0508 | 0.0482 | 0.0494 |
| Piiranha DeBERTa | 0.0604 | 0.0375 | 0.0463 |
| XtremeDistil FiNER | 0.6990 | 0.0213 | 0.0413 |
這組結果的意義是:現成的辨識系統各有擅長的資料類型,不能期待換上一個模型,就能完整辨識所有機敏資料。
Presidio 結合規則與 NLP,在原論文測試中取得最高 F1,支持將兩種方法搭配使用的方向。例如,電話與信用卡交給格式、解析與檢查碼;姓名等需要上下文判斷的內容,再交給 NER 模型。但所有系統的整體分數仍低,顯示這些現成配置不足以完整涵蓋該測試的資料類型。
對遮罩應用而言,「找到的資料很準」還不夠,「該遮的資料有沒有漏掉」同樣重要。例如 FiNER 的 Precision 較高、Recall 卻很低,代表它在有把握的類型上表現較好,但大量其他實體仍會通過,無法單獨承擔全面遮罩。
作者後續使用修正後的測試資料,針對資料微調的 DeBERTa 取得 F1 0.6476,高於同次測試中最佳的原有比較系統 SpanMarker BERT(0.1723)。這提供了一個實務方向:讓訓練資料與目標類型貼近使用情境,值得優先評估。 不過,這是另一版實驗,不能與上表直接比較。
因此,評估繁體中文 Presidio 時,可以先用規則處理格式明確的證件與電話,再比較中文 NER 對姓名、地址的辨識效果。是否需要微調,應由實際漏判決定;這份研究本身並未證明哪個繁中模型最適合 Presidio。
社群專案 pii-detect-model 公布了 Presidio 搭配不同中文 NER 與相同規則組的比較:
| 組合 | 合併測試集 strict micro F1 |
|---|---|
Presidio 2.2.363 + CLUENER + cn_common_v5 |
0.9474 |
Presidio 2.2.363 + ERNIE + cn_common_v5 |
0.8968 |
僅 cn_common_v5 規則 |
0.6710 |
測試使用 5,000 篇正式文字、3,000 篇聊天文字,涵蓋姓名、電話、身分證、護照等八類;實體的起點、終點與標籤都要一致才算命中。這裡的 F1 綜合了誤判與漏判表現,並不是「文件有幾成完全遮乾淨」。
這是社群專案公布的中文合成資料評測,不是 Presidio 官方評測。
LiteLLM 提供模型呼叫介面與 Proxy 服務。本文使用的是 LiteLLM Proxy:讓應用程式先把模型請求送到共同入口,再由它轉送到後方的模型服務。
搭配 Presidio 後,可以在這個入口集中執行機敏資料處理。這對多個 Agent、不同程式語言,或同時使用多家模型供應商的環境特別有用,因為辨識規則與遮罩政策可以在共用服務管理。
這個組合官方依據:
以「送出前遮罩」為例,資料流如下:
sequenceDiagram
participant A as Agent/應用程式
participant L as LiteLLM Proxy
participant P as Presidio Analyzer
participant M as Presidio Anonymizer
participant V as 模型服務
A->>L: 模型請求,含待處理文字
L->>P: 分析文字
P-->>L: 實體類型、位置、分數
L->>M: 文字、辨識結果與遮罩規則
M-->>L: 處理後的文字
L->>V: 使用處理後文字呼叫模型
V-->>L: 模型回覆
L-->>A: 回傳結果
部署者需要準備 Presidio Analyzer、Anonymizer 服務,並在 LiteLLM 的 Guardrail 設定中選擇 presidio、套用對象與執行時機。若目的是防止原文送進模型,就要使用模型呼叫前的 pre_call 路徑;logging_only 只處理記錄用途,不會改變真正送出的請求。
LiteLLM 也提供 MASK 與 BLOCK 等政策:前者替換辨識到的資料,後者在命中條件時阻擋請求。
自行開發的應用程式,可以在組合好模型輸入後呼叫 Presidio;多個 Agent 共用政策時,則可以讓模型請求經過 LiteLLM Proxy。這類處理通常不適合只做成讓 Agent 自行選擇呼叫的工具,因為那不能保證每次送出前都會執行。
不同工具的接入位置如下。
| 工具/框架 | 接入位置 |
|---|---|
| Codex CLI | 在 ~/.codex/config.toml 設定自訂模型供應商與 base_url,將模型請求指向相容 Gateway;需確認 Responses API 相容性。文件 |
| Claude Code | 透過 ANTHROPIC_BASE_URL 等 Gateway 設定調整模型連線入口,並配合認證與模型設定。文件 |
| Claude Agent SDK | 在執行 SDK 的環境配置模型 Gateway;若指的是一般 Anthropic SDK 應用,也能在程式呼叫模型前加入 Presidio。文件 |
| OpenClaw | 在模型供應商設定中加入 LiteLLM,再將 Agent 使用的模型指向該供應商。文件 |
| Agno | 建立 Agent 時,使用指向 LiteLLM Proxy 的模型物件,例如官方的 LiteLLMOpenAI 整合。文件 |
需要注意的是,模型 Gateway 只處理經過它、且被 Guardrail 涵蓋的資料。Agent 直接呼叫外部 API、寫入記憶或記錄日誌,是其他資料路徑;在模型入口加上 Presidio,不會自動涵蓋這些地方。
正式採用前,可以用一小批貼近業務的資料回答三個問題:
遮得完整嗎?
若「王小明」只辨識到「王小」,剩下的字仍會保留。應檢查完整文字範圍,而不只是有沒有找到人名。
會遮掉不該遮的內容嗎?
訂單號、產品型號與測試數字都應列入測試,也要加入完全沒有機敏資料的文件。
遮罩後,任務仍能完成嗎?
若不同人物全部變成 <PERSON>,模型可能難以追蹤誰說了什麼。需要維持人物關聯時,應另外設計一致代號與對應管理。