iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
Modern Web

前端不寫 Python,照樣 ship 一把網頁無障礙 CLI系列 第 21

Day 21:九成的檢測碼要靠人判,LLM 補的不是聰明,是可重複

  • 分享至 

  • xImage
  •  

Day 21 · W3 · AI 線 + 無障礙線 · 難度 ★★★☆☆

本系列由 AI 協作撰寫。 內容、技術判斷、程式碼由 light-design 數位顧問團隊與 Claude 共同產出,最終由作者驗證後 publish。完整協作模式與把關方式見 Day 01

115.11 的碼表一共 244 條檢測碼。其中:

C(機器可檢)     28 條
E(需人工判斷)  216 條    ← 88.5%

規範自己承認:這裡面接近九成的東西,程式判不了。 那我們憑什麼說自動化了 146 條?

寫這篇之前,我原本以為答案會是「因為接了語言模型,所以判得比規則準」。結果實測完,那不是答案。51 次判定裡它只報了一次問題,而且那一次還可以爭。

它補的不是聰明。

一句話主軸:LLM 不是讓工具變聰明,是把「需人工判斷」變成「可重複執行的判斷」。

244 條裡,216 條規範自己說要靠人

碼號的最後一碼是 C 或 E。Day 06 講過怎麼讀,這裡只講分界線在哪。

C 是「可判定」:圖片有沒有 alt、表單欄位有沒有標籤、頁面有沒有 lang。有或沒有,一行程式就知道。

E 是「需要理解」:那個 alt 寫得好不好、標籤有沒有講清楚、連結文字看不看得出要去哪。這些問題的答案不在 HTML 的結構裡,在文字的意思裡。

244 條裡 216 條是 E。規範的制定者從一開始就預期,大部分的檢查要靠人的判斷。

但「標 E」不等於「一定要人」

先看我們做了什麼:

C   26 / 28    93%
E  110 / 216   51%

一半的 E 用純程式做掉了,一行模型都沒叫。

這不矛盾。規範標 E 的意思是「不保證機器判得準」,不是「機器判不了」。「連結文字是不是空的」被歸在 E,因為它跟「連結文字有沒有意義」放在同一條底下 —— 但前半段不需要理解,只需要讀字串長度。

我們把每一條 E 拆開,能用結構判的部分寫成規則,剩下真的要理解語意的部分才交給模型。這樣切完,146 條裡真的會呼叫模型的是 44 條,其中 8 條還要看截圖。

從 244 條檢測碼到 44 條呼叫模型的漏斗圖:碼表 244 條,其中需人工判斷的 E 有 216 條,我們用純程式實作了其中 110 條,真的需要呼叫語言模型的只有 44 條,44 條裡有 8 條要看截圖

九成標著「需人工判斷」,最後真的要模型出手的不到三成。

剩下的才是模型的位置

Day 05 拿四個工具掃同一個壞頁,「按這裡」那格四個全放過。要判斷「這段連結文字有沒有講清楚要去哪」,得理解語意,任何規則都寫不出來。

那 44 條就是這種格子。它們共同的特徵是:問題本身在文字的意思裡,不在標記的結構裡。

首頁掃一次,這 44 條裡有 13 條在頁面上找得到可以判的東西。其他 31 條連候選元素都沒有,模型根本沒被叫到。

那 13 條長什麼樣:連結文字有沒有描述目的地、標題有沒有描述內容、只用顏色傳達的訊息拿掉顏色還在不在、DOM 順序跟視覺順序一不一致、送出前有沒有說明會發生什麼事、裝飾性圖片是不是用 CSS 放的、整體文字易不易讀。每一條都是「這段東西的意思對不對」,沒有一條能靠數標籤解決。

這才是模型該站的位置。不是取代規則,是站在規則的盡頭。

51 次判定,1 次報問題

同一頁掃兩次,一次不帶模型,一次帶:

不帶模型    8 條   fail 2 · info 5 · caveat 1
帶模型      8 條   fail 2 · info 6 · caveat 0

那 1 條 caveat 是工具自己說「有 44 條規則需要語言模型,這次沒給模型參數,所以完全沒執行」。帶了模型之後它消失,換成 1 條 info:

GN1330200E | info | 送出前說明不足:表單未說明送出後會發生什麼事。

對象是頁首的搜尋框。

模型總共被叫了 51 次。為什麼是 51 不是 13:每條規則在頁面上找到幾個候選元素,就叫幾次,一個連結一次、一個標題一次。所以呼叫次數跟著頁面內容走,不跟著規則數走,一頁長文會比一頁首頁貴得多。

51 次裡 50 次判通過,1 次報了上面那條。

就一次。

這一條我不確定它對不對。 搜尋框要不要說明「按下去會搜尋」,可以爭。但它給的是 info 不是 fail,這個分級是對的:不確定就降級,不硬判。它沒有比規則更會抓,它只是把規則抓不到的那格補上一個可以拿去問人的判定。

補的不是聰明,是可重複

真正的差別在第二次跑。

同一個指令再跑一次:

第二次    28 秒   8 條逐條相同   模型呼叫 0 次,51 次全部命中快取

這是設計出來的。模型呼叫預設 temperature=0,每次判定的輸入跟輸出都寫進磁碟快取,快取的鍵是模型名稱加上完整的輸入。同一個模型、同一個輸入,第二次直接讀檔,不重付、不重等、答案不會變。

那模型本身穩不穩?把那 51 筆快取刪掉,讓它從頭再判一次:

第三次   245 秒   8 條逐條相同   模型重新呼叫 51 次
         51 筆判定原文,逐字相同

不是 issue 層級相同,是 51 段回覆一個字都沒差。 temperature=0 在這顆模型上不是「大致穩定」,是逐字重現。

可重複的機制圖:每次判定先把模型名稱與完整輸入做成鍵,查磁碟快取,命中就直接回傳、不呼叫模型;沒命中才以 temperature 為零呼叫模型,回覆寫入快取。要重判必須刪掉快取,那是一個刻意的動作。三次實測:第一次呼叫 51 次,第二次 28 秒全部命中,第三次刪快取後重叫 51 次,51 筆原文逐字相同

要讓它重判,你得刪快取。那是一個刻意的動作,不是每次跑都可能發生的意外。

這像把審稿人的意見錄下來。同一段文字,播放一百次都是同一句話。要換意見,得重錄,而不是每次播放都可能不一樣。

人工判斷的問題從來不是判不準,是每次判的人不同、同一個人不同天也不同、而且沒有人記得上次判了什麼。模型補的就是這件事:同一個問題,永遠同一個答案,而且答案留著。 留著的意思是字面上的:那 51 筆快取每一筆都是完整的輸入加完整的回覆,誰判了什麼、憑什麼判,事後可以一筆一筆翻,人工審查從來沒有這種紀錄。前提是同一個模型、同一組參數 —— 換模型會不會變,那是另一篇的事。

代價

冷跑一頁多花約 210 秒。第一次 245 秒,不帶模型 35 秒左右,差的就是 51 次呼叫。熱跑 28 秒,等於沒開。

時間之外還有錢。模型呼叫是有價的,本機跑是電費,接雲端是每次計費。快取讓判過的不重付:一個站掃第二次、改一頁再掃、CI 每天跑,都只付新出現的那些。這也是為什麼快取的鍵要含模型名 —— 換了模型就是新的判定,不能拿舊模型的答案冒充。

模型不在的時候呢?端點關掉再跑一次,13 條規則全部進 caveat,訊息是「模型呼叫失敗」。不是通過,是「我沒判到」。 這跟 Day 19 探針不敢按送出鍵是同一個原則:判不了就說判不了,不能把沒檢查寫成沒問題。

Day 12 講過被檢查的網頁也在跟模型說話,判錯的處置是三級狀態:確定的 fail、提醒的 info、不確定的 caveat。這三級不是介面設計,是把模型能力的邊界直接映射成資料。

不開模型也跑得動

146 減 44,102 條純程式。Day 04 那個 20 秒裝好、第一個 issue 就是在完全沒有模型的情況下抓到的。

模型是加值,不是前提。沒有它,你拿到的是 102 條的結果加一行「有 44 條沒執行」;有它,那一行換成 44 條各自的判定。兩種情況報告都誠實,差別只在覆蓋範圍。

順帶一提碼表自己的一處:GN1240500E 的碼號等級位寫的是 A,對應的成功準則 2.4.5 在 WCAG 是 AA。244 條裡就這一條前後對不上,我不確定是刻意還是排版所致。工具的處理是依碼號等級位為準,所以 --level A 就會跑它。

今天的重點

  • 九成的檢測碼規範自己標「需人工判斷」,那是官方認證的機器邊界。
  • 但標 E 不等於機器判不了:一半用純程式做掉了,真的要模型的是 44 條。
  • 模型不比規則會抓:51 次判定報 1 次,還是可以爭的 info。
  • 它補的是可重複temperature=0 加快取,同一輸入永遠同一判定,刪快取重判 51 筆逐字相同。
  • 模型不在時是 caveat 不是通過,不開模型也有 102 條跑得動。

明天 Day 22:模型講完了,回頭掃整站。自評表全過、標章核發之後,工具又長了四條 WCAG 2.2 規則 —— 同一份 CSS,五月的工具沒話說,九月的工具報了一整排 fail。


上一篇
Day 20:CLI 能跑,不等於 AI 會用對
系列文
前端不寫 Python,照樣 ship 一把網頁無障礙 CLI21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言