Azure PII可以進行個資遮蔽, 但只能傳十串字, 一串字上限5120字, 鑑於整篇文件可能超過上限, 必須要依上限切割.
先列出SA規格:
- 如果未選擇偵測項目, 自動偵測台灣常用個資項目
- 上行傳入字串, 下行回傳遮蔽後字串, 空格和換行不遮蔽, 不受上限限制
- 上行傳入字串, 下行回傳PII完整結果, 不受上限限制
- 可選擇是否去掉空格和換行進行二次遮蔽
依以上需求再進一步分析拆解出SD規格…
在作Embedding時, 一般怕直接截斷文字會中斷了語意,開頭都會保留一部份上段文字,
ex. 第一段截取5120字,但第二段從第5000字 (Index=5000) 開始取, 但同樣取5120字, 以此類推,
但是和Embedding不同, 作完PII後, 我們要把字再組合回去時, 要考量到前後段重疊的部份.
以下是我對實作長文本PII 的設計構想, 當然! 我不是天才, 所以不是第一次就想得這麼全面, 而是不斷反覆思考是否適當, 不斷修改而來…
首先要作一個切割用function(以下簡稱*split), 本文以重疊5120-5000=120字來說明:
- 以上建成一個yield return的IEnumerable<(int, string)> function, 同時回傳index(字串在原字串的位置)和截取字串
- 建立迴圈, 每圈index+=5000, 意即每隔5千字就截取5120字
- 用Substring截取 但Substring傳入長度也要先判斷不能超過原字串
- 截取的字串去掉尾巴空白(TrimEnd), 如果字串長度>0就yield return該截取字串
其次, 依上/下行json格式各建一個class (含subclass), 以方便使用,
現在很多線上工具(ex. son2csharp), 把上下行json結果丟進去, 可自動產生class
再準備呼叫PII的functon(以下簡稱*pii): 參考 [AI] Azure PII進行資料遮罩(2025版)
- 建*pii 呼叫Azure PII
- 判斷傳入上行物件內至少有一筆字串才呼叫Azure PII
- 將回傳json轉下行物件
主流程如下:
- 宣告上行物件變數
- 迴圈跑*split, 回傳的index就當作上行的id, 塞入上行物件變數
- 每十筆呼叫一次*pii (未滿10筆也要呼叫)
- 二次呼叫*pii (詳細內容之後說明)
- 處理回傳結果, 回傳結果包含已遮蔽為*號的字串, 和每項個資, 即entity,
每個entity內容有個資字串, offset--本個資在本串文字的index, length--本個資長度 (詳細內容之後說明)
處理回傳結果, 為了方便呼叫者使用, 提供2種處理結果, 第一種是只回傳遮蔽字串:
- 由於.net字串組合時會重新占不同記憶體, 所以組合一般會用StringBuilder來累加字串, 這要考量重疊的字串組合回去,
但我們知道原文, 所以可以將原文轉char[] - 對結果字串迴圈(每串一圈即可, 不要跑成雙層變N圈了)
- 依每串結果的id(=index)去查看char[]及字串, 如果原字不是空格或換行, 而且結果已變*號時, 就改寫char為*號
結果處理的第二種是合併Azure PII下行的完整內容, 作法單純很多, 只是將各10串字的下行結果合併:
- 保留第一個下行結果
- 第2個之後的下行結果都併入第一個下行結果
二次呼叫*pii, 考量到因為文本可能被加上空白或換行(ex. OCR結果), 導致PII效果不佳,
可以去掉空白或換行進行二次PII, 但Azure費用會多一倍, 所以要考量效益和實用性再決定作不作:
- PII結果個數(最多10串字)先不合併, 而是分別建Dictionary<int, char>和StringBuilder,
對字串的每一字跑迴圈, 空格或換行就記index和移除字在Dictionary內, 其它就塞到StringBuilder - 這些處理過的StringBuilder再丟一次PII, 取得一樣數量的PII結果
處理方式想要回傳的結果有2種, 只回傳遮蔽字串, 這項最單純:
- 結果的字串要將空白和換行補回去, 先以原字串長度建char[], 迴圈一字字補回去,
跑到某index判斷若該index有在Dictionary內, 就把Dictionary值取出來補上去
如果要回傳完整結果, 除了以上遮蔽字串一樣要蓋回原結果以外, 還要改各Entity, 功能有點複雜, 所以如果不遮圖, 使用機率不高就不要作了:
- entity內要改回index和length數字, 才能對應到原字串, 承續先前說用char[]跑原字串還原移除字元, 迴圈前先宣告變數來記上次Entity,
在處理字串時同時一一比對entity.offset (一圈跑char[]即可, 不要跑成雙層變N圈了),
如果Entity的Index=迴圈現在的index, 而且變數≠現在entity, 就先把length+=offset - 1 即將length借記為目前字尾index,
再把offset改成現在迴圈的index, 即還原index - 如果變數Entity =現在entity 且 length(即字尾index)=迴圈index, length改成迴圈index-offset(還原index)+1, 即為還原長度
- 把這些enitity併回第一次呼叫結果的entities
Taiwan is a country. 臺灣是我的國家