chart-scrub 去識別化前後對照:左邊是含姓名、病歷號、生日、電話、地址的原始紀錄,右邊同一筆變成 PT-0001 代號與遮罩標記,主訴與理學檢查完全保留

病歷不能就這樣貼給 AI:我怎麼先把身分拿掉

門診遇到一個有意思的個案,想查一下教科書怎麼講、想跟 AI 對一對自己的鑑別診斷有沒有漏掉什麼。這件事我幾乎每週都在做。 但病歷不能就這樣貼上去。 這跟「我信不信任那家 AI 公司」是兩回事。資料一旦離開這台電腦,它去了哪裡、留多久、被誰看過,就不再是我能決定的事。所以我想要的不是一個更好的承諾,是一個機械上的動作:在資料離開之前,先把身分拿掉。 我把自己在用的那套整理乾淨、開源了,叫 chart-scrub。 程式碼:github.com/drpwchen/chart-scrub 線上試用:drpwchen.github.io/chart-scrub(整頁在你的瀏覽器裡跑,什麼都不會上傳) 它做什麼 一筆門診紀錄丟進去: 病歷號碼:1234567 姓名:王大明 出生:1971/03/05 主訴:右肩痛三個月,夜間痛醒。王大明表示上個月在李阿嬤介紹的推拿館推過。 電話 0912-345-678,住新北市板橋區文化路一段100號5樓。 出來變成: [代號 PT-0001,55歲] 病歷號碼:PT-0001 姓名:PT-0001 出生:[55歲] 主訴:右肩痛三個月,夜間痛醒。PT-0001表示上個月在[稱謂]介紹的推拿館推過。 電話 [電話],住[地址]。 臨床內容一個字沒動,身分不見了。 為什麼不是直接遮成「[姓名]」就好 這是整件事我最先想通的地方。 如果只是把名字換成 [姓名],那同一個病人下次回診、下下次回診,三筆紀錄之間就沒有任何東西把他們串起來了。可是「這個人三個月前打過針、當時有效、這次又痛回來」,這種前後對照本來就是臨床判斷的一部分,弄丟了,剩下的資料就沒什麼討論價值。 所以第一層做的是定向抽換:這個人變成 PT-0001,下次回診他還是 PT-0001。代號對回真名的那張表,只存在我自己電腦上的一個 SQLite 檔,不進雲端、不進筆記庫、也不會給任何 AI 讀。 生日也一樣:直接刪掉可惜,換算成年齡剛好。臨床要的是「55 歲」,不是那個日期。 第二層:一張寧可多遮的網子 定向抽換只能處理「我知道他是誰」的那個人。紀錄裡還會有一堆我事先不知道的東西:電話、身分證、地址、順口提到的其他人。 第二層就是一組正規表示式規則,把這些東西通通遮掉。設計取向很單純:寧可多遮。偶爾誤遮一個醫學名詞,代價是我重看一次;漏掉一個名字,代價大得多。 有一個順序上的細節,是實作時才想清楚的:定向抽換一定要在通用規則之前跑。反過來的話,名字已經先被遮成 [姓名] 了,代號就再也接不上去,跨次就診的那條線也就斷了。 機器自己驗自己 寫完之後我最不放心的是:它到底有沒有真的遮乾淨? 所以最後一步是殘留檢查:程式跑完,拿它自己知道的每一個真名、每一個病歷號,回頭搜自己剛產出的那份輸出。只要有一個活著,這筆就判定失敗、離開碼給 2,我不會拿去用。 另外還有兩個訊號是抓「漏網」的形狀:一個中文姓名黏在已經被遮掉的身分證旁邊、以及長得像身分證卻沒被遮的字串。這兩種都不是「我知道他叫什麼」,而是「這個位置本來就該有東西被處理掉」。 這件事的通則 自動化最怕的不是失敗,是安靜地成功。所以每一段自動化的最後,都值得補一個「回頭檢查自己」的動作,而且要讓它有辦法大聲喊失敗。 開源的過程中,抓到自己三個 bug 這部分我覺得比工具本身有意思。為了公開,我補了測試、逐條檢查,結果揪出三個原本在用的版本就有的問題,而且三個都是那種不會報錯的失敗。 第一個:我習慣的手寫格式是「身分證號+姓名」擠在同一行。但實際的紀錄常常長這樣: 陪同者 A123456789 李小華 程式抓到的姓名是 「陪同者」,不是李小華。因為規則寫成「身分證號旁邊的 2 到 4 個中文字」,而「陪同者」剛好也是三個中文字,還排在前面。結果就是:一個角色詞被登記成病人,真正的名字反而漏掉。修法是要求姓名的第一個字必須是常見姓氏。 第二個:醫院系統匯出的文字檔編碼很雜,UTF-8、UTF-16、Big5 都遇過,所以程式會一個一個試。問題是一個 Big5 檔用 UTF-16 去解,不會出錯,會解出一堆亂碼。而亂碼不會命中任何一條規則,所以程式一路跑到底、印出「全數通過」,實際上它什麼都沒遮到。修法是:只有在檔案真的帶著 UTF-16 的標記時才用 UTF-16 去解。 ...

August 14, 2026 · 1 分鐘 · 130 字 · 陳柏威 Po-Wei Chen