依藥品外觀特徵(刻字、顏色、形狀、刻痕)從衛福部食藥署(TFDA)公開資料 縮小候選藥品清單,供藥師與醫療人員人工比對確認。
這是「外觀條件檢索 → 候選藥物 → 人工確認」, 不是「輸入照片 → 系統宣稱這就是某藥」。 本站不使用任何 AI 推測藥品身份,也不提供相似度分數。
使用者最可能受害的情境不是「多給了幾個候選」,而是 「以為清單裡沒有 → 認定這顆不是那個藥」,但真正的藥被系統靜默排除了。 因此設計往這個方向倒:
| 情形 | 一般作法 | 本站作法 |
|---|---|---|
| TFDA 沒填顏色(217 筆) | 勾「白色」時直接消失 | 進「資料未提供」區,並說明無法排除 |
| TFDA 只記部分刻字(1,293 筆) | 輸入完整刻字時被排除 | 進「部分符合」區 |
藥錠拿反了、把 S 看成 5 |
回「找不到」 | 進「刻字可能看反或字形相近」區,並標示是哪一種 |
| 主區 0 筆 | 顯示「找不到」 | 只有每一筆都明確不符時才說找不到 |
| 資料檔載入失敗 | 顯示 0 筆結果 | 停用搜尋並明說「資料暫時無法載入」 |
刻意接受的漏檢只有兩處,都是為了避免清單長到在藥車前不可用:
- 單字元查詢不啟用「包含」級。輸入
S而回傳印著EVEREST、YSP的藥,在藥車前不可用。 - 變體區只接受「完全相等」,且單字元不套字形轉換。理由見下節。
刻字先切 token(大寫化、非英數視為分隔),每個查詢 token 都必須命中(不計順序), 完整命中依精確度分為三個互斥等級;另外保留「部分符合」「資料未提供」「刻字可能看反或字形相近」 三個無法排除區:
| 區 | 意義 |
|---|---|
| 完全符合 | 每段輸入都與某段刻字完全相同 |
| 字首符合 | 每段輸入都是某段刻字的開頭 |
| 包含 | 每段輸入都出現在刻字之中(單字元查詢不啟用此級) |
| 部分符合 | 至少一段命中、但非全部——TFDA 只記錄了部分刻字 |
| 資料未提供 | 該欄位 TFDA 未填,無法排除 |
| 刻字可能看反或字形相近 | 原樣不符,但倒讀或字形壓平後完全相同 |
顏色與形狀可複選(組內 OR),各條件之間 AND。 標註一/標註二不標示為正面/背面——TFDA 未定義兩欄的面向語意。
刻字原樣比對不符時,才額外試兩種確定性轉換。命中者一律進獨立的第五區, 不混進上面任何一區——現有四區的成員逐筆不變。
| 轉換 | 規則 |
|---|---|
| 180° 倒讀 | 固定 14 字元表:0 1 8 H I N O S X Z 自映射,6↔9、M↔W。任一字元不在表中即無變體 |
| 字形相近 | 壓平成 5 個等價類:0/O、1/I/L、5/S、2/Z、8/B |
四條收緊規則,全部是為了讓這一區維持可用長度:
- 只接受完全相等——變體不套字首級與包含級
- 單字元不做字形轉換(做了會讓查
I回 801 筆、5回 768 筆) - 不做組合變體:不倒讀後再壓平,也不允許不同 token 各靠不同規則湊成一次全中
- 不做編輯距離/模糊比對,不回傳分數或排名
每張卡自己標「倒讀」或「字形相近」,理由不掛在分區標題上——同一區裡兩張卡可能是不同原因進來的。
規則是對稱的,所以變體區可能比主區還大:查 SH 主區 37 筆,倒讀成 HS 後變體區 42 筆。
最壞情況是把廠商前綴看錯:查 Y5P 主區 0 筆、變體區 111 筆(YSP 永信本來就印在 111 個產品上)。
這種情況不壓——壓下去等於讓最常見的誤讀反而查不到。改由測試盯著:
A17 對完整可達查詢閉包(3,103 個查詢)跑指紋,規則一漂移就紅。
- 搜尋列 sticky、條件面板隨頁捲動——吸頂的東西永遠矮於視窗,不遮蔽結果
- 確定符合展開、低確定性收合:部分符合、資料未提供與變體區以原生
<details>呈現, 摘要保留分區名稱與筆數,首次展開才建立卡片(M 40這類查詢的低確定性候選達 2,379 筆) - 變體區排在最後:未提供區至少「這顆藥沒有和你的輸入牴觸」, 變體區則是「牴觸,但如果你看反了就不牴觸」——多疊一層假設,就排更後面
- 卡片與詳細視窗顯示官方鏡像圖(WebP,長邊 640),並可直連 TFDA 官方原圖
- 詳細視窗提供「查看 TFDA 仿單」(依許可證字號)
- 無圖或載入失敗一律換 placeholder,不留破圖
- 頁尾標示資料新鮮度:見下方「資料更新時間」
可安裝到手機主畫面,離線仍可搜尋——藥車旁常常沒有穩定網路。
| 離線可用 | 備註 | |
|---|---|---|
| 介面與搜尋邏輯 | ✅ | 約 118 KB(含 PWA 圖示 44 KB),安裝時預抓 |
| 全部 6,295 筆搜尋資料 | ✅ | 3.7 MB,首次載入時順帶存下,不預抓 |
| 藥品圖片 | 最近取得的 500 張 | 全量 87 MB,不做完整離線包。超過上限依取得順序淘汰(非 LRU) |
兩個刻意的取捨:
- 第一次造訪不具備離線能力,要再載入一次。預抓 3.7 MB 會讓只是點進來看一眼的人先付這筆流量。
- 離線時一定會講。頁面頂端出現「目前無法連線,顯示的是先前存下的資料(來源版本 ⋯)」。 臨床情境下,一個看起來正常、其實是幾週前的畫面,比一個明確的錯誤畫面危險得多—— 這也是為什麼快取不會在資料真的抓不到時偽裝成功,那種情況仍走「資料暫時無法載入」。
| 來源 | TFDA Open Data 藥品外觀資料集(infoId=42) |
| 筆數 | 6,295 筆(全量收錄,不做過濾),快照 2026年08月10日 |
| 圖片 | 6,273 筆有官方外觀圖,共 6,798 張;其餘紀錄來源未提供可用圖片。轉為 WebP(長邊 640)共 87 MB。官方原圖中位 1.5 MB,單頁 20 張約 68 MB,外連在行動網路不可行 |
| 圖片版本 | 檔名固定為 sha1(id)-n.webp,請求附 ?v=<sha256 前 8 碼>——官方原地換圖時不會讀到瀏覽器舊快取 |
| 更新 | GitHub Actions 每週一 04:17;任一驗證失敗即整批不發布,維持上一版。失敗會自動開 issue |
| 更新狀態 | data/status.json 記錄最後成功檢查日,與 appearance.json 分開存——後者必須維持位元組冪等(來源未變就不該產生 diff),把執行時間寫進去會直接破壞它 |
| 新鮮度 | 每季 HEAD 掃全量比對原圖長度 + 隨機抽 100 張深驗;偵測到官方換圖不自動更新,開 issue 待人工確認 |
它們不同義,所以分開顯示:
資料版本:2026年08月10日(TFDA 來源日期) · 最後檢查:2026年08月12日 · 收錄:6,295 筆 每週一自動檢查 TFDA 來源更新
只講「資料版本」的話,TFDA 三個月不更新就會讓這個站看起來像沒人維護。 分開之後,「檢查過但來源沒變」與「根本沒在跑」在畫面上分得開。
- 「最後檢查」只在整條管線(守門/鏡像/驗證/測試/發布)全部通過後才推進。 週更失敗時它停在上次成功的日期——那是正確行為,不是 bug
- 超過 14 天(=錯過兩次週更)未成功更新,該行轉為警示色並標出天數
- 狀態檔讀不到或壞掉時只會少顯示,絕不顯示一個猜出來的日期
無登入、無帳號、無 cookie、無 localStorage、無 analytics、無後端。搜尋與本地縮圖瀏覽不向第三方傳送使用者輸入;僅在使用者主動點擊「TFDA 官方原圖/仿單」時前往 TFDA 官方網站。連字型都不用 CDN。不索取姓名/病歷號/生日/處方內容。
離線功能會用到瀏覽器的 Cache Storage,存的只有本站自己的檔案(介面、搜尋資料、看過的圖片), 不含任何搜尋輸入、查詢紀錄或使用者狀態,也不會送到任何地方。 用瀏覽器的「清除網站資料」即可全部移除。
npm test # 137 項:搜尋語意/變體規則/正規化/管線 fail-closed/回歸 fixtures/靜態契約 npm run build -- --source <zip> # 建置 canonical(開發時用本機 zip 避免重複打 TFDA) uv run tools/fetch-images.py # 鏡像圖片(需 uv + Pillow) npm run verify -- --source <zip> --in data/appearance.json.staging npm run publish:metadata # 圖片尚未就緒時,先發布可搜尋資料 npm run publish:data # 資料就緒切換點(圖片未全部完成時會拒絕) node tools/write-status.mjs # 寫 data/status.json(**必須在 publish 之後**) uv run tools/fetch-images.py --in data/appearance.json --freshness # 季度新鮮度檢查
零依賴、無 build step、Node ≥ 22,GitHub Pages 靜態部署。Service Worker 手寫,不用 Workbox。
規格在 .ai-review/plan.md(v1.10),變體區的規格增補在 .ai-review/plan-imprint-variant.md(v0.3)。
同目錄另有各自的規格覆審與判定(plan-review*.md/plan-verdict*.md,變體區兩輪)、
一輪程式碼覆審(codex-review.md),以及 evidence/ 的人工驗收留檔。
工程慣例見 CLAUDE.md。
回歸 fixtures 的預期值由 tools/make-expected.mjs 獨立第二實作產生,全檔零 import search.js——
同一份程式碼驗自己不算驗證。變體規則加上靜態斷言(C20)守著這條獨立性。
以下兩項靜態測試驗不到,改動後請重跑對應的人工留檔:
| 改動 | 重跑 |
|---|---|
sw.js(快取上限、部署後首次重載拿到哪一版、離線文案) |
.ai-review/evidence/e11-2026年08月13日.md 六項 |
| 變體區的畫面(分區位置、逐卡理由標籤、收合摘要) | .ai-review/evidence/e12-2026年08月14日.md |
本工具依 TFDA 公開藥品外觀資料提供候選結果,外觀相似不代表為同一藥品。 實際藥品仍應依原包裝、藥袋、許可證資訊或由醫療專業人員確認。 本工具為輔助辨識用途,非最終調劑核對系統。
MIT