跳至主要内容

個資遮蔽

這個模組在本機把文字中的個資換成佔位符(placeholder),讓後續模組可以安全地把內容送給 LLM;拿回 LLM 的結果後,再用同一份對照表(map)把佔位符還原成原文。整個流程零相依、全非同步,明文個資不會隨 request 送出。

參數

FILE - 文字輸入檔,點擊「PICK」選取檔案、鍵盤輸入工作資料夾中的檔名,或使用 %FILENAME% 變數。ACTIONREDACT 時是要遮蔽的原文;為 RESTORE 時是 LLM 回傳、含佔位符的文字。

ACTION - 執行動作:

  • REDACT - 偵測 FILE 中的個資並換成佔位符,遮蔽後文字寫入 OUTPUT FILE,對照表寫入 MAP FILE
  • RESTORE - 讀取 MAP FILE,把 FILE 中的佔位符還原成原文後寫入 OUTPUT FILE

OUTPUT FILE - 輸出檔名,預設 output.txt,寫入工作資料夾。

MAP FILE - 對照表檔名,預設 redaction-map.json。REDACT 時產生,RESTORE 時讀取。

對照表就是明文個資本身

MAP FILE 的內容存的是未經處理的明文個資,不需要還原時直接刪掉這個檔案即可。

No-Code 編輯器

點擊「ADD NO-CODE COMMAND」可以加入下列無程式碼指令,指令會記錄在模組的指令列表,並在執行時套用到偵測規則上:

加入黑名單

填入的字串保證不會流出去:完全比對,命中就換成 {{BLOCKED_n}}。適合客戶姓名、未公開的專案代號等 regex 抓不到的機敏字串。

加入白名單

填入的字串即使被規則命中也不遮蔽,例如公司對外的客服信箱、官網網址。用來壓掉已知的誤報。

警告

黑名單內容本身是機敏資料。技能檔若會分享給他人,先確認裡面沒有不該公開的名字。

Low-Code 編輯器

在 Low-Code 編輯器中可以直接調整偵測選項,腳本在遮蔽執行前被呼叫,適合放無法用 No-Code 表達的規則。

input 輸入物件

{
text // FILE 的完整文字內容
}

options 選項物件

{
patterns, // 啟用的內建規則集合,例如 ['tw', 'generic', 'financial']
rules, // 自訂規則陣列
denylist, // 黑名單,等同 No-Code 的「加入黑名單」
allowlist, // 白名單,等同 No-Code 的「加入白名單」
disable // 要關閉的內建型別,例如 ['URL']
}

範例:

// 自訂員工編號規則
options.rules.push({ type: 'EMP_ID', pattern: /EMP-\d{6}/g })

// 黑名單與白名單
options.denylist.push('Best Ltd.')
options.allowlist.push('support@emily.tips')

// URL 通常不是個資,太吵就關掉
options.disable.push('URL')

內建型別

patterns 決定載入哪些規則集合,只啟用 ['tw'] 就真的只載入台灣規則。

集合型別priority備註
twTW_ADDRESS40最高。地址內含數字,不然會被電話/統編咬走
twTW_ID35 / 30帶「身分證:」標籤的走 35 且不驗檢查碼;裸 pattern 走 30 且驗檢查碼
twTW_ARC30新式 [A-Z][89]\d{8} 與舊式 [A-Z][A-D]\d{8}
twTW_MOBILE22
twTW_TEL20
twTW_GUI15裸 8 位數,誤報風險最高,檢查碼必驗
genericEMAIL30
genericURL / IPV425URL 常常不是個資,覺得太吵用 disable: ['URL']
financialCREDIT_CARD / IBAN30Luhn / mod-97
自訂規則50(預設)讓自訂勝過內建成為直覺行為

地址 priority 最高:自訂的數字規則若沒生效,先確認是不是被 TW_ADDRESS 蓋掉了。

LITERALBLOCKED 是保留型別,自訂規則不能使用。

核心不變式

restore(redact(x).redacted, redact(x).map).text === x

對任意輸入、任意規則組合、任意黑名單都成立,沒有例外分支。

做得到是因為偵測跑在正規化後的文字上(全形→半形、刪零寬字元),但替換發生在原始輸入上,對照表存的也是原始子字串。使用者輸入 0912-345-678 還原後仍是 0912-345-678,不會被靜默改成半形。

遮蔽只有一種模式:偵測到的 span 完整置換成佔位符。沒有 mask、沒有 format-preserving。

黑名單比對規則

黑名單是完全比對,比對對象是正規化後的文字(否則輸入全形公司名就抓不到),大小寫預設不敏感(只影響 ASCII,CJK 沒有大小寫概念)。

「優先級最高」的意思是保證覆蓋,不是搶佔佔位符:

情況結果
span 完全相同黑名單勝
黑名單 span ⊃ 其他 span黑名單勝
部分重疊黑名單勝,另一方捨棄
黑名單 span ⊂ 其他 span保留較大的那個

最後一列是刻意的。黑名單有「王小明」、地址規則抓到整串「台北市信義區信義路五段7號 王小明 收」時,若讓黑名單搶贏,結果是地址整串沒遮,優先級最高反而造成外洩。不提供「嚴格模式」,嚴格換來的是更差的保護。

沒有 wholeWord 選項:需要詞界的情境請用自訂規則寫 regex,CJK 沒有詞界、子字串比對是正確預設。

自訂規則

options.rules.push({
type: 'CASE_NO', // 必須符合 /^[A-Z][A-Z0-9_]*$/
pattern: /案號\s*[::]\s*(?<redact>\d{6,10})/g,
priority: 60,
validate: (m) => m.value !== '000000', // 可 async
})
  • redact 具名群組:有就只遮該群組,沒有就遮整個 match。用來處理「需要上下文才能確定,但不該把上下文一起遮掉」。
  • g flag 會自動補上(沒有 g 只會抓到第一個),並發一次 warning。
  • lastIndex 不會殘留:每次執行都複製一份 RegExp,同一個 rule 物件連續執行結果一致。
  • type 撞到內建型別會 fail fast,要取代內建請把它加進 disable
  • pattern 可以是字串,讓規則能從 JSON 設定檔來。但validate 的規則無法序列化,需要驗證邏輯的必須寫在腳本裡。

還原與模糊比對

模型會弄壞佔位符,還原時分層處理,maxTier 控制放寬到哪一層:

Tier處理預設
1完全比對
2大小寫不敏感
3全形/半形括號正規化({{{{)、缺一邊括號
4括號可選(TW_ID_1 裸寫也認)需明確指定

編號距離 ≤1 的猜測不實作。{{NAME_2}} 猜成 NAME_1 還原成錯的人,比不還原嚴重得多,使用者會相信畫面上的內容。還原失敗的 fail-safe 是保留佔位符原樣。

還原結果的 expected / matched / orphaned 是統計數字,不含個資,可以安全上報:

  • matched < expected → 模型吃掉了佔位符,應提示使用者有內容可能未正確還原
  • orphaned 非空 → 模型自己編了一個佔位符,通常是它模仿了格式

兩個數字都在告訴你送給 LLM 的 system prompt 指示夠不夠強。

佔位符注入防護

輸入本身含 {{NAME_1}} 時(使用者貼上的,或惡意構造),還原會被輸入牽著走,是資訊洩漏。模組會把它登記成 LITERAL 對應回自己:

輸入:  我叫王小明,參考 {{NAME_1}} 這個格式
遮蔽: 我叫{{BLOCKED_1}},參考 {{LITERAL_1}} 這個格式
map: { "{{BLOCKED_1}}": "王小明", "{{LITERAL_1}}": "{{NAME_1}}" }

還原是單次掃描、不遞迴{{LITERAL_1}} 還原出的 {{NAME_1}} 不會被第二輪再替換一次。

典型流程

  1. 個資遮蔽」模組 ACTIONREDACT,把使用者輸入遮蔽成 output.txt,對照表存 redaction-map.json
  2. AI Agent 或其他 LLM 模組讀取 output.txt 產生回答。
  3. 再放一個「個資遮蔽」模組,ACTIONRESTOREFILE 指向 LLM 的回答檔,MAP FILE 用同一份 redaction-map.json,還原後的文字即為最終結果。