# DeID｜醫療資料去識別化

以醫療文件為主要情境，也適用於合約、客戶名冊、客服信件與會議紀錄。支援 PDF、Word、Excel、TXT、Markdown 與貼上文字，透過**規則＋必要文字 LLM＋可選 PDF 視覺辨識**找出敏感資訊，經人工覆核後輸出去識別化文件。

本工具使用瀏覽器與 Node 後端共同運作。**文件文字會送至後端與設定的模型服務；不是離線工具，也不是零外傳的純前端網站。** PDF 視覺功能啟用時，另傳送頁面影像。

- [線上服務](https://medical-deidentification-57229660377.asia-east1.run.app)：需要管理者提供的服務存取碼。
- [Cloud Run 部署說明](https://github.com/gemini960114/medical-deidentification/blob/main/docs/cloud-run.md)
- [驗收端文字／視覺稽核指南](https://github.com/gemini960114/medical-deidentification/blob/main/docs/ai-audit-guide.md)
- [合成醫療語料產生器](https://github.com/gemini960114/medical-deidentification/blob/main/scripts/batch-test-generator/README.md)

本 README 使用文字說明，不使用與目前介面不一致的舊截圖。本機修改不會自動更新線上服務；畫面與功能以各環境實際執行的版本為準。

## 適用情境與使用界線

醫療情境包含出院摘要、照護與追蹤紀錄、病患總表，以及病歷號、就醫日期、醫師與聯絡人資訊的辨識。通用情境包含委外契約、報價單、客戶資料、客服往來與會議紀錄。

去識別化結果是供覆核的產出，不能視為「已找出所有個資」的保證。罕見病況、特殊事件、照片、影像內文字及上下文組合仍可能識別個人。可透過編碼表還原的輸出屬於可連結的編碼資料，不等於不可逆匿名化；部分保留也會揭露原文片段。

請先確認資料可以傳送至部署後端與模型供應商。合成範例可用於體驗；實際文件是否適合送入服務，應依所屬機構的資料處理要求決定。

## 辨識流程與資料去向

```text
選擇文件／貼上文字
  → 瀏覽器解析；無文字層的 PDF 先做 OCR
  → 內建與自訂規則偵測
  → 同源 /api/detect → 所選模型完成文字辨識（必要）
  → 若勾選 PDF 視覺：瀏覽器將每頁渲染成影像，再經後端送至模型
  → 合併可定位的結果；無法定位的發現列為待人工核對
  → 預覽、調整與確認
  → 瀏覽器產生文件與可選的 CSV 編碼表
```

| 資料 | 處理位置與保存方式 |
|---|---|
| 文件解析、OCR、預覽、遮蔽、輸出與還原 | 瀏覽器；文件與編碼表不寫入 localStorage |
| 擷取出的文字 | 經同源後端傳送至設定的模型端點，每份文件都會執行 |
| PDF 頁面影像 | 只有勾選視覺辨識時才傳送；包含該頁可見的原始內容 |
| 模型 API Key | 只在後端；Cloud Run 由 Secret Manager 注入 |
| 服務存取碼 | 預設使用目前分頁；按「儲存」後存入同瀏覽器、同網站來源的 localStorage，可清除 |
| 自訂規則、規則開關與保留設定 | 同瀏覽器、同網站來源的 localStorage；不跨裝置同步 |
| 模型輸入與編碼表 | 本服務不主動寫入資料庫或應用程式內容日誌；模型供應商端保存方式依其政策 |

每份文件使用所選的一個文字模型，**不會自動將三個模型全部呼叫一遍**。一般設定包含 `Inkling-Small`、`Muse-Glimmer-30B`、`gemma-4-31B-it`，實際選單由後端允許清單決定。

視覺選項僅對管理者列入視覺允許清單的模型開放。模型名稱出現在選單，不代表已驗證它適合所有版面。文字或已要求的視覺階段失敗，該文件不能以只有規則的結果當作辨識完成。

## 使用方式

### 1. 填存取碼，選辨識設定

首頁最上方整合了「服務存取碼」與「辨識設定」。

1. 在存取碼欄位輸入管理者提供的代碼，只需填代碼本身，不加 `AUDIT_ACCESS_TOKEN=`。可按「儲存」（或 Enter）記住代碼，重新整理或開始新的辨識時自動帶入；「清除」會移除儲存與目前欄位內容。共用電腦使用後請清除；儲存不代表已通過伺服器驗證。
2. 選擇文字模型。文字 LLM 是必要流程，不能關閉。
3. 處理 PDF 時，可勾選「PDF 視覺辨識」；其他格式仍只使用擷取出的文字。

服務存取碼與模型 API Key 是不同用途：前者讓使用者取得服務使用權，後者由管理者保存在後端，使用者不需要填寫模型金鑰。

### 2. 匯入文件或使用範例

「匯入你的文件」為全幅操作區。可拖曳、選擇多份文件，或貼上文字後按「開始去識別化」。

- 網頁接受 `.pdf`、`.docx`、`.xlsx`、`.txt`、`.md`、`.markdown`。
- 每批最多 10 份，每份原始檔最多 100 MB。
- 「載入辨識」將合成範例送入目前選定的辨識流程，會呼叫模型。
- 「下載原檔」只下載範例，不執行辨識。
- 「開始新的辨識」在新分頁開啟初始畫面，保留原分頁；已儲存的存取碼會自動帶入；未儲存的代碼與匯入的編碼表不會跟過去。

### 3. 人工覆核與輸出設定

辨識後逐筆核對預覽。可取消誤判、依類別調整、在文字預覽中圈選新增漏抓，或展開偵測清單定位。模型讀到但無法精確對應到擷取文字的內容會另列提醒，**不代表該內容已自動遮蔽**。

輸出有兩個獨立選項：

| 選項 | 可選方式 | 意義 |
|---|---|---|
| 保留方式 | 部分保留／全部隱藏／自訂 | 決定多少原始字元留在標記外；預設為部分保留 |
| 標記方式 | 編碼／類別／空白佔位 | 決定被隱藏部分的呈現，以及是否可用 CSV 還原 |

- **編碼**：例如 `[姓名:abc123]`，搭配編碼表可還原已替換的文字。
- **類別**：例如 `[姓名]`，不產生可還原的編碼表。
- **空白佔位**：以 `○` 取代字元，不產生可還原的編碼表；字數與部分結構可能保留。

「全部隱藏」只會完整隱藏已選定的敏感項目，不代表文件中未被偵測的內容也已清除。

### 4. 下載文件與對應編碼表

結果頁提供：

- **下載（文件＋編碼表）**：使用編碼標記時，一次取得成對檔案。
- **下載對應編碼表 CSV**：只下載目前文件、目前設定所對應的編碼表。
- 多份文件可打包下載 ZIP；各文件與其編碼表、清單放在同一包。

例如原始檔 `出院病歷摘要.docx`，會產生 `出院病歷摘要.deid.docx` 與 `出院病歷摘要.mapping.csv`，通常位於瀏覽器設定的下載資料夾。更改遮蔽項目或保留方式後，請重新下載兩份，不要與舊版本混用。

編碼表包含原始敏感內容或被隱藏的片段，請限制存取並獨立保管；不要與去識別化文件一起交給不應取得原始個資的收件者。

### 5. 第二批文件沿用編碼（選用）

只有需要跨文件關聯時才使用，例如同一病患的多次回診。各批不需要互相對照時，可以不匯入編碼表。

1. 保留第一批下載的 `.mapping.csv`；多檔情境可下載合併編碼表。
2. 在上傳區「進階：跨檔案共用編碼」選擇「匯入既有編碼表」。
3. 視需要勾選「整批共用編碼」，再載入新文件。
4. 同類別下完全相同的值沿用既有編碼；新值產生新編碼。不會自動辨認別名、拼字變化或不同人的同名。
5. 完成後從「更多」下載**合併編碼表**，下次匯入這份累積版本。

共用編碼會增加資料之間的可連結性。編碼本身也不是病患身分驗證或跨系統的唯一病患識別碼。

### 6. 還原與編碼表遺失

到「還原」頁，同時選擇去識別化文件與對應 CSV，系統將編碼標記替換回原文，再下載 `.restored` 文件。還原流程在瀏覽器內進行，不呼叫模型。

若編碼表遺失：

- 原辨識分頁仍開著且內容還在，可以再次下載；若換過輸出設定，應重新下載整組。
- 分頁已重新整理或關閉，本服務沒有保存可供找回的編碼表。請檢查下載資料夾或備份。
- 只有去識別化文件，無法單靠隨機編碼可靠地推回原文。
- 還有原始文件時可以重新處理，但會產生新的編碼，不能當作原編碼表的復原。

還原的是被替換的文字，不是任意格式的完整原始檔位元組。PDF 尤其不是還原成原掃描照片或原字型版面。

## 醫療與通用範例

所有範例均為程式產生的虛構資料，不使用真實病患紀錄。首頁分兩區，各六份；醫療區為綠色操作按鈕，通用區為靛藍色。

| 醫療範例 | 格式 | 原始檔 |
|---|---|---|
| 出院病歷摘要，病例 01 | Word | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/medical-discharge.docx) |
| 同病例的出院病歷摘要 | PDF | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/medical-discharge.pdf) |
| 出院摘要與追蹤紀錄，病例 02 | 多頁 PDF | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/medical-followup.pdf) |
| 出院照護紀錄 | Markdown | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/medical-notes.md) |
| 出院病歷摘要，病例 03 | TXT | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/medical-summary.txt) |
| 三筆合成病例的病患總表 | Excel | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/medical-roster.xlsx) |

| 通用範例 | 格式 | 原始檔 |
|---|---|---|
| 委外服務契約書 | Word | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/contract.docx) |
| 報價單 | PDF | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/quotation.pdf) |
| 客戶資料 | Excel | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/customers.xlsx) |
| 客服往來信件 | TXT | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/support-email.txt) |
| 專案會議紀錄 | Markdown | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/meeting-notes.md) |
| 掃描文件 | 掃描 PDF | [下載](https://github.com/gemini960114/medical-deidentification/blob/main/public/samples/scanned.pdf) |

還原頁另有一組合成病歷文字與編碼表，可直接體驗還原。這組範例以內建規則遮蔽，用來示範還原機制，不能視為模型已檢出全部個資的證明。

## 支援格式與已知限制

| 格式 | 目前處理方式 | 必須留意 |
|---|---|---|
| TXT／Markdown | 擷取文字，規則與文字模型辨識 | 不解析外部連結內的文件或圖片 |
| Word `.docx` | 處理主要文件與頁首頁尾中的文字；保留原有檔案結構 | 不做 Word 視覺辨識；內嵌圖片、註解、修訂紀錄與其他封裝內容不保證清除 |
| Excel `.xlsx` | 處理文字儲存格，保留工作表結構與樣式 | 數值型電話／證號、公式結果、工作表名稱、註解及內嵌圖片不在完整覆蓋範圍 |
| 文字 PDF | 依擷取文字及原座標重建，可選整頁視覺辨識 | 不保證保留圖片、圖形與原字型，也不代表未偵測文字已移除 |
| 掃描 PDF | 瀏覽器 OCR 後重建文字；可選視覺補查 | OCR 可能漏字或誤讀；不保留完整原頁影像，可保留的圖片仍須人工檢查 |
| JPG／PNG、舊版 `.doc`／`.xls` | 網頁不接受直接上傳 | 圖片可先轉成 PDF；獨立稽核 CLI 的圖片支援不代表網頁也支援 |

**視覺辨識是補查，不是圖片自動塗除工具。** 圖中讀到的值只有在擷取文字中可精確定位，才會加入文字遮蔽；無法定位時提示人工核對。照片、簽名或無法定位的影像資訊，不會因勾選視覺就自動被抹除。

內建醫療日期規則依賴標籤與日期的同一行關係。表格分欄、特殊院內編號與無標籤英文姓名仍可能是規則盲點；文字模型及人工覆核是補充，不能保證消除所有盲點。

後端另有文字與請求上限：每份最多 100,000 個文字字元；PDF 視覺預設最多 20 頁，每張影像 data URL 最多 4,000,000 個字元，整個 JSON 請求最多 24 MB。這些限制與原始檔的 100 MB 上限不同；大文件可能需要拆分。

## 偵測規則與保存

目前內建 18 類：姓名、英文姓名、身分證、手機、市話、地址、電子郵件、公司、統編、識別碼、國際電話、信用卡、SSN、IBAN、IP、生日、病歷號、醫療日期。

「偵測規則」頁可停用內建規則、新增／編輯／刪除自訂規則，並用測試文字確認命中後儲存。停用規則只影響規則引擎；模型仍可能辨識出同類資料。回到處理頁時會重新套用規則，保留既有模型發現，不會因改規則就重跑模型。

可複製下列規則，或用規則頁的「填入表單」：

| 用途 | 類別 | 規則（不加斜線或 flags） | 全合成測試值 |
|---|---|---|---|
| 檢體編號 | 識別碼 | `LAB-\d{8}` | `LAB-20261017` |
| 院內病歷編號 | 病歷號 | `HOSP-\d{8}` | `HOSP-00004521` |
| 研究受試者編號 | 識別碼 | `STUDY-[A-Z]\d{4}` | `STUDY-A0042` |

按「新增規則」或「儲存變更」後才保存。規則存於同瀏覽器、同網站來源的 localStorage；`localhost`、`127.0.0.1`、不同連接埠與 Cloud Run 網址各自獨立。重新整理後仍在，但清除網站資料、換瀏覽器或結束無痕工作階段可能遺失。它不是全站共用規則庫，沒有帳號同步。

## 本機啟動與設定

本專案使用 TypeScript、Vite 與 Node，沒有 UI 框架。建議使用 Node.js 24，亦可使用 22.13 以上的 22.x；容器使用 Node 24。舊版 Node 20 至少需要 20.19，實際工具需求以鎖定檔為準。

1. 執行 `npm ci`。
2. 複製 `.env.example` 為 `.env`，填入模型端點、金鑰、預設模型與自訂的服務存取碼。不要把真實金鑰或存取碼放進 README、截圖或版本庫。
3. 生產模式執行 `npm run build`、`npm start`，開啟 `http://localhost:8080`。
4. 開發時保留後端，再另開終端執行 `npm run dev -- --host 127.0.0.1`，開啟終端顯示的網址，通常是 `http://127.0.0.1:5173/`。

```dotenv
OPENAI_API_BASE=https://your-model-service.example/v1
OPENAI_API_KEY=replace-with-provider-key
OPENAI_MODEL=your-text-model
OPENAI_MODEL_Available=your-text-model,your-other-model
OPENAI_VISION_MODELS=your-tested-vision-model
AUDIT_ACCESS_TOKEN=replace-with-your-own-service-code
AUDIT_HOURLY_LIMIT=60
```

`OPENAI_MODEL_Available` 的大小寫沿用現有程式。視覺模型還必須出現在可用模型清單中；未設定視覺允許清單時不開放視覺。金鑰不得使用 `VITE_` 前綴。

只開 Vite 沒有模型 API，不能完成辨識。只開靜態 `dist/index.html` 也不能替代後端。修改 `.env` 後需重新啟動後端，重新整理頁面即可取得新的公開模型設定；雲端則需更新其 Secret／環境變數與版本，本機修改不會同步過去。

## Cloud Run 部署

目前服務使用 `gen-lang-client-0039151647` 專案、台灣 `asia-east1` 區域，服務名稱為 `medical-deidentification`，與 TransitX 分開管理。

正式方式沿用已成功驗證的 `gcloud run deploy --source .`、Dockerfile、專用執行帳號與 Secret Manager。Windows 使用已安裝 SDK 的 `gcloud.cmd`；步驟與參數見 [部署指南](https://github.com/gemini960114/medical-deidentification/blob/main/docs/cloud-run.md)。不要上傳 `.env` 作為建置來源，不要把金鑰寫進前端或映像。

GitHub workflow 僅跑程式檢查、測試、建置、模擬模型的瀏覽器 E2E 與保存建置產物；不部署 GitHub Pages，也不會自行更新 Cloud Run。網站與模型的費用依部署及供應商設定計算。

後端預設同時最多兩個辨識請求，單一暖機實例每小時最多 60 份；目前雲端設定最多一個實例。這是基本限流，會受重啟與實例生命週期影響，**不是全站持久配額，也不是供應商費用硬上限**。

## 合成資料、評測與品質檢查

測試資料只使用合成文件與模擬模型回應。以下命令在專案根目錄執行：

| 命令 | 用途 |
|---|---|
| `npm run lint` | 型別感知程式檢查 |
| `npm test` | 單元與整合測試；模型網路層使用模擬回應 |
| `npm run build` | TypeScript 檢查與正式建置 |
| `npm run test:e2e` | 真實瀏覽器操作五格式，模型後端使用模擬服務；需 Chrome／Chromium |
| `npm run examples:medical` | 以固定種子產生首頁醫療範例與成對還原範例 |
| `npm run examples` | 重建原有通用範例與相關驗收資料；需本機瀏覽器 |
| `npm run generate:test-docs -- --count 2 --format all --layout realistic` | 產生多頁與巢狀表格的合成病歷語料及 answer-key |
| `npm run benchmark-llm -- --docs 2` | 實際比較規則、各模型與聯集；會呼叫設定的模型 |
| `npm run test:models-live` | 合成文字與圖片的實際模型測試；會呼叫模型 |
| `npm run probe:vision` | 以合成圖片確認端點的視覺能力 |
| `npm run test:vision` | 合成圖像 PDF 的實際視覺稽核測試 |

固定種子語料驗收區分命中、重複位置殘留、類別錯誤、出現次數不符、過度遮蔽與萃取缺漏；已知限制帶上界及形狀約束。生成、解析、遮蔽、再解析與文字還原的測試，不等於真實世界的零漏抓承諾。

不在 README 維護容易過期的「通過測試數」徽章或模型準確率。最新狀態以該次測試輸出與 CI 為準。少量合成病例上的值層得分，不代表每個出現位置、影像或其他醫院版面都得到相同效果。

獨立的 `scan-pii` CLI 可稽核 TXT／Markdown／PDF／PNG／JPEG，與網頁接受格式、必要文字模型流程不同；不可將 CLI 的圖片功能當成網頁已提供。CLI 報告可能含原文個資，須依敏感資料保存。詳見 [稽核指南](https://github.com/gemini960114/medical-deidentification/blob/main/docs/ai-audit-guide.md)。

## 維護與授權

- `src/ui/`：匯入、預覽、規則、還原與範例介面。
- `src/core/`：規則、編碼、遮蔽、模型結果合併與前後端契約。
- `src/formats/`：各格式解析及輸出。
- `server/`：必要文字辨識、視覺呼叫、存取驗證與靜態網站服務。
- `scripts/`：合成資料、驗收與獨立稽核工具。
- `public/samples/`：六份醫療與六份通用原始範例，以及還原所需的靜態範例檔。

啟動樣式在 `index.html` 提前載入，等待主程式時顯示載入畫面；說明與 FAQ 仍保留給未啟用 JavaScript 的使用者。`public/llms.txt` 提供站台摘要，`llms-full.txt` 於建置時由本 README 產生。靜態 FAQ 文字與 JSON-LD 應同步修改。

本專案採 [MIT License](https://github.com/gemini960114/medical-deidentification/blob/main/LICENSE)，原始工具作者為 Dean Lin。Noto Sans TC、霞鶩文楷 TC、Caveat 各依其字型授權使用；OCR 採 Tesseract.js 及相關語言模型。第三方資源授權請連同專案內對應授權檔保留。
