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

參數
FILE - 文字輸入檔,點擊「PICK」選取檔案、鍵盤輸入工作資料夾中的檔名,或使用 %FILENAME% 變數。ACTION 為 REDACT 時是要遮蔽的原文;為 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 | 備註 |
|---|---|---|---|
tw | TW_ADDRESS | 40 | 最高。地址內含數字,不然會被電話/統編咬走 |
tw | TW_ID | 35 / 30 | 帶「身分證:」標籤的走 35 且不驗檢查碼;裸 pattern 走 30 且驗檢查碼 |
tw | TW_ARC | 30 | 新式 [A-Z][89]\d{8} 與舊式 [A-Z][A-D]\d{8} |
tw | TW_MOBILE | 22 | |
tw | TW_TEL | 20 | |
tw | TW_GUI | 15 | 裸 8 位數,誤報風險最高,檢查碼必驗 |
generic | EMAIL | 30 | |
generic | URL / IPV4 | 25 | URL 常常不是個資,覺得太吵用 disable: ['URL'] |
financial | CREDIT_CARD / IBAN | 30 | Luhn / mod-97 |
| — | 自訂規則 | 50(預設) | 讓自訂勝過內建成為直覺行為 |
地址 priority 最高:自訂的數字規則若沒生效,先確認是不是被 TW_ADDRESS 蓋掉了。
LITERAL 與 BLOCKED 是保留型別,自訂規則不能使用。
核心不變式
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。用來處理「需要上下文才能確定,但不該把上下文一起遮掉」。gflag 會自動補上(沒有 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}} 不會被第二輪再替換一次。
典型流程
- 「個資遮蔽」模組
ACTION設 REDACT,把使用者輸入遮蔽成output.txt,對照表存redaction-map.json。 - 以 AI Agent 或其他 LLM 模組讀取
output.txt產生回答。 - 再放一個「個資遮蔽」模組,
ACTION設 RESTORE,FILE指向 LLM 的回答檔,MAP FILE用同一份redaction-map.json,還原後的文字即為最終結果。