iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0

Day 20 · W3 · AI 線 · 難度 ★★☆☆☆

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

我出貨的那份 agent 說明書,第一句話不是教它怎麼用,是禁止它做一件事:

Do NOT answer from memory or run a11y-moda directly via Bash

我得明文禁止 AI 憑印象回答,還要禁止它繞過這份檔案直接下指令。

寫這份檔案之前,我以為重點會是「怎麼用」那幾節:有哪些指令、參數怎麼下、輸出長什麼樣。寫完回頭看,那幾節誰都寫得出來。真正在做事的,是散在各處的那些「不准」。

一句話主軸:CLI 能跑,不等於 agent 會用對。那份檔案防的不是使用者不會用,是 agent 太會用、太快、太有自信。

一份說明書,六條不准

把整份檔案裡的禁令抽出來,剛好六條。每一條後面都有一個它擋掉的失敗:

六條禁令與各自擋掉的失敗對照表:第一條禁止憑印象回答或繞過檔案直接執行,擋掉 agent 知道有這個工具卻憑記憶回答規則內容;第二條 caveat 與需人工判斷的項目只能標示為待審查、不准自動建議修法,擋掉工具說量不到的東西被 agent 替它決定;第三條代入 PORT 前必須驗證是純數字,擋掉從 package.json 猜出來的值被當成可信輸入;第四條缺套件時告訴使用者、不准替他安裝;第五條除非使用者明確要 scan,不准推他裝瀏覽器;第六條輸出一律 JSON 並存到隱藏子目錄,不准在別人的 repo 留下一堆檔案

六條裡沒有一條在教它怎麼用。每一條都在防它做得太多。

其中兩條看起來最不起眼,卻是使用者最常被 agent 惹惱的地方。第四條:缺套件時告訴使用者,不准替他裝。第六條:輸出一律存進隱藏子目錄,不准在別人的 repo 裡留下一地報告檔。這兩條防的都是同一種事:agent 為了把任務做完,動了使用者沒授權它動的東西。

這像手術室的檢查清單。清單上沒有任何一條是教醫生怎麼開刀,每一條都在防他因為太熟練而跳過的事。給 agent 的說明書是同一種東西。

agent 不會卡住,它會直接選一個

人拿到一個不熟的工具會停下來問。agent 不會,它會選一個看起來最像的直接跑。

這把工具有三個入口,做的事完全不同:

lint   讀原始檔,不開瀏覽器            tests/fixtures 整個目錄   1.2 秒
scan   抓一頁,靜態解析                首頁                     3.4 秒
scan   抓一頁,開瀏覽器算完樣式         首頁 --render           34.6 秒

同一個問題「這段 HTML 有沒有無障礙問題」,走 lint 一秒有答案,走 --render 要等三十倍。而 --render 還有個前提:先裝一顆瀏覽器,Day 04 量過,約 700 MB。

agent 選錯的成本是不對稱的。lint 去做 scan 的事,它會回「我看不到算出來的樣式」,使用者損失一秒。選 scan 去做 lint 的事,使用者先裝 700 MB,再等三十秒,拿到的東西跟一秒那個一樣。

所以檔案裡有一張表,告訴 agent 什麼情況用哪一個。那張表不是功能介紹,是分流。而分流之後緊接著就是第五條禁令:

Don't push users to install `[scan]` unless they're asking for scan / site / --render

不准為了跑得動就叫人多裝一顆瀏覽器。

最重要的一條:caveat 不准自動修

六條裡我最在意的是這一條,它在檔案裡是一張表的其中一列:

| `caveat` / `needs_human` | Surface as "needs review"; **do NOT auto-suggest fixes** |

工具把判定分成三級。fail 是確定有問題,info 是提醒,caveat 是工具自己說「這個我量不到,請人看」。

Day 19 剛示範過一次:表單帶 novalidate,探針不敢按送出鍵,規則只能給 caveat。那不是工具偷懶,是它在說「我按下去會在你的正式站產生一筆真資料,所以我不按」。

工具承認自己判不了的地方,agent 更沒資格替它決定。 如果 agent 看到 caveat 就自動生成一段修法,等於把「未檢查」變成「已修好」,而中間沒有任何人看過。

Day 11 講偽陽性比漏抓致命,因為誤報會讓人關掉整條規則。這條禁令是同一個原則往下游延伸:工具不敢說的話,agent 也不准替它說。

說明書自己也是攻擊面

第三條禁令講的東西,我一開始沒想到要寫:

**Validate `<PORT>` is digits-only before substitution** (it comes from a package.json scripts heuristic — don't trust it)

情境是這樣:使用者說「幫我掃本機的開發站」,agent 得知道 dev server 跑在哪個 port。檔案教它去 package.jsonscripts 裡猜。猜到之後要代進指令裡。

package.json 是使用者 repo 裡的東西。任何人都可以往裡面塞任何字串。 如果 agent 把猜到的值原封不動代進 shell 指令,那個 repo 的作者就能透過 scripts 讓 agent 跑任意東西。

所以那一條寫著:代入之前,先驗證它是純數字。

Day 12 講過被檢查的網頁也在跟模型說話。這是同一件事換一個地方發生。說明書教 agent 去讀的每一個來源,都是一個輸入,輸入就要驗。

為什麼是五份,不是一份

這份檔案不是一個檔,是五個。跑 init --list 會看到:

claude-code   ~/.claude/skills/a11y-moda
cursor        ./.cursorrules
copilot       ./.github/copilot-instructions.md
aider         ./.aider.conf.yml
agent         (prints to stdout — paste into agent system prompt)

每一種 agent 讀的檔名跟路徑都不一樣,內容格式也不一樣。有的讀 YAML frontmatter,有的讀純文字,有的要放在專案根目錄、有的要放在使用者家目錄。

一份 README 裡的「複製這段貼到你的設定檔」做不到這件事,因為它不知道你用哪一種。所以它變成一個指令:a11y-moda init <ide>,跟著套件版本一起出貨。使用者升級工具,說明書跟著升級。

我不評價這五種哪個好。列出來只是說明:一份說明書要讓五種讀者各自讀得懂,它就得出五份。

那份要 AI 別憑印象的檔案,自己寫著過期的數字

寫到這裡本來該收尾了。但這篇規劃的時候,我在那份檔案裡找到一個矛盾。

它的第一句話要 agent 不准憑印象回答,理由是「Claude 不知道 MODA 規則的內容,必須查」。而同一句話裡寫著規則有幾條。那個數字是 129。

工具裡實際有 146 條。

往下追,不只這一處。四個對外的地方各報各的,133、133、129、129,沒有一個是 146。

還有一個更早的:套件的版本號是手寫在程式碼裡的 0.1.0。那筆修正的紀錄寫著,它五個版本沒動過。而這個版本號跟著 User-Agent 送進每一個被掃描網站的 log,對方看到的是一個過期一年的版本號。

四個出貨面各報各的規則數對照圖:修正前,指令說明寫 133、README 寫 133、AI 整合文件寫 129、agent 說明書寫 129,而工具內實際有 146 條,版本號手寫 0.1.0 五個版本沒動;修正後,四處全部與 146 一致,版本號改由安裝後設資料推導,並由兩條守門測試持續比對

那份檔案的全部工作就是「不要讓 agent 用猜的」,而它自己寫著一個猜出來的數字。

修法不是把數字改對。 改對的數字下一次加規則又會過期,這正是它變成 129 的原因。修法是把它綁到真實來源:版本號從安裝後的套件資料推導,規則數由一條測試盯著,任何出貨的文件裡只要出現總數,就必須等於註冊表裡的實際數量。

那條測試的說明寫得比我這篇清楚:

The shipped agent integration files exist to stop an agent from answering from memory. When their own numbers go stale they teach the agent a wrong fact — which is the exact failure they were written to prevent.

Day 11 加了三條守門測試,理由是「會靜默失效的東西,必須有機器去盯」。這裡是同一個結論的第二次出現,只是這次靜默失效的不是規則,是說明書。

出貨在套件裡、跟著版本走,是必要條件。它保證使用者拿到的是最新的檔案,不保證那份檔案裡的東西是對的。

今天的重點

  • CLI 能跑不等於 agent 會用對。 差別在它不會卡住,會直接選一個,而選錯的成本不對稱。
  • 給 agent 的檔案,真正在做事的是「不准」不是「怎麼用」。 六條禁令,沒有一條在教用法。
  • 工具承認判不了的地方,agent 不能替它決定。 caveat 是「未檢查」,不是「待修」。
  • 說明書教 agent 讀的每個來源都是輸入,輸入就要驗。 package.json 也不例外。
  • 出貨在套件裡是必要條件,不是充分條件。 那份要 AI 別憑印象的檔案,自己過期了一年。

明天 Day 21:給 AI 的說明書講完了,回頭問一個更前面的問題。碼表上有九成的檢測碼標著「需人工判斷」,那是規範自己承認機器判不了。九成的檢測碼要靠人判,LLM 補的不是聰明,是可重複。


上一篇
Day 19:我的表單焦點跳對了,畫面上卻沒有一個字說明原因
下一篇
Day 21:九成的檢測碼要靠人判,LLM 補的不是聰明,是可重複
系列文
前端不寫 Python,照樣 ship 一把網頁無障礙 CLI21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言