工程實作 19 min read ★ Featured

我怎麼把別人的 skill 吸進自己產線,做成更強的 skill:一次語意標註實作覆盤

看到好用的 skill,大部分人裝來用;如果你有自己的產線,更值錢的做法是拆它的零件焊進你的流程。我原本有一套截圖審查+手冊產線(/screenshot-review),但手冊圖是「乾圖」沒有圖上標註——這正是 app-screenshots 的 annotate.js 補得上的洞。本文覆盤:我怎麼把它拆進產線,過程撞到一個架構級的坑(標註截不進圖=OVERLAY GONE:agent-browser 每個 CLI 指令是獨立 CDP 呼叫,eval 注入的 overlay 下一指令就蒸發),查根因、用 batch 把 open→eval→screenshot 綁進同一 session 解掉、封裝成 annotate-shot.sh、寫回自己的 skill。全程真的跑真的驗,附 batch 解法的真實成果圖與踩坑總表。

我怎麼把別人的 skill 吸進自己產線,做成更強的 skill:一次語意標註實作覆盤
本文目錄 · 13

# 我怎麼把別人的 skill 吸進自己的產線,做成更強的 skill:一次語意標註的實作覆盤

看到一個好用的 Claude Code skill,大部分人的反應是「裝來用」。但如果你原本就有一條自己的產線,更值錢的做法是:把它的零件拆下來,焊進你自己的流程,補上它有、你沒有的那一塊

上一篇我介紹了 Alexander Opalic 的 app-screenshots skill——它能讓 AI 自動在截圖上畫語意標註(紅框圈按鈕 + 帶編號的說明)。我原本就有一套自己的截圖手冊產線(/screenshot-review),能 debug + 看 UI/UX + 產操作手冊,但手冊裡的圖是「乾圖」——沒有圖上標註。這正是 app-screenshots 補得上的一塊。

這篇是實作覆盤:我怎麼把它的 annotate.js 拆進我的產線,過程中撞到一個架構級的坑(標註截不進圖),查出根因、用 batch 解掉、封裝成一支可重用的 wrapper,最後寫回我自己的 skill。全程真的跑、真的驗。

TL;DR:看到好用的 skill,大部分人裝來用;如果你有自己的產線,更值錢的是拆它的零件焊進你的流程。我把 app-screenshots 的 annotate.js 標註能力接進自己的截圖手冊產線,過程撞到架構級的坑(OVERLAY GONE:agent-browser 每個 CLI 指令是獨立 CDP 呼叫,eval 注入的 overlay 下一指令就蒸發),查根因後用 batch 把 open→eval→screenshot 綁進同一 session 解掉,封裝成 annotate-shot.sh 寫回自己的 skill。

重點速覽

我原本的產線長怎樣

我有一個自寫的 Claude Code command /screenshot-review,它的定位是「一舉三得」:對一個專案的所有 UI 截圖做批判性審查,看 → 不行就修 code → 重截 → 重看,循環到能誠實說出「邏輯合理、UI/UX 優秀」,順帶把驗過的截圖收斂成一份同模板的操作手冊

它的靈魂是「反橡皮圖章」——不是看一眼說「都很棒」就交差,而是用設計師 + 資深工程師的眼睛逐張挑毛病,毛病解掉才准說「優秀」。手冊呈現有固定骨架(單檔 HTML、base64 inline 圖、左側欄佈局),跨專案看起來像同一條產品線出來的。

這套很成熟,但有一個一直沒補的洞。

缺的那一塊:圖上語意標註

我的手冊圖是乾圖——就是一張乾淨的頁面截圖。要說明「點這裡送出」「這是搜尋框」,我只能寫在圖外面的文字 caption 裡。

問題是:操作手冊最有效的表達,是圖上直接畫——紅框圈住那個按鈕、旁邊寫「① 送出」。讀的人視線不用在「圖」和「文字說明」之間來回跳。這正是 app-screenshots 的 annotate.js 做的事,而我的產線沒有。

所以目標很明確:把 annotate.js 的語意標註能力,接進我的手冊產線,讓 /screenshot-review 產的圖可以帶「紅框 + 中文說明 + 編號」。

第一次嘗試,撞牆:OVERLAY GONE

annotate.js 的用法看起來很直覺——它是純前端腳本,靠 agent-browser 的 eval 注入頁面。我照著最自然的分步流程跑:

agent-browser open https://example.com
agent-browser eval --stdin <<EOF     # 注入 annotate.js + 呼叫 annotateMulti
$(cat annotate.js)
annotateMulti([{sel:'h1', label:'主標題', type:'box'}])
EOF
agent-browser screenshot out.png     # 截圖

eval 那步回傳 annotated box #1: 主標題——標註確實畫上去了。但 screenshot 截出來的 out.png……是一張沒有任何標註的乾圖。標註憑空消失了。

第一反應是「selector 錯了吧」「時序問題吧」,試了加 wait、換 selector、確認 #__annotations overlay 真的被建立……全都沒用。eval 回報成功,screenshot 就是截不到。

查根因:為什麼標註會蒸發

停下來,不再瞎試。去理解 agent-browser 的執行模型。

關鍵事實:agent-browser 的每個 CLI 指令,是一次獨立的 CDP(Chrome DevTools Protocol)呼叫agent-browser eval ...agent-browser screenshot ... 是兩個分開的進程呼叫,即使 daemon 讓瀏覽器跨指令保持開著、頁面沒重新載入——

eval 注入的那層

SVG overlay,是掛在當下那次 eval 執行上下文的 DOM 操作。到了下一個獨立指令(screenshot),它拿到的頁面狀態已經不含那層剛注入的 overlay 了。overlay 沒了,截圖自然是乾的。

我在 0.27 和 0.31 兩個版本上都驗過——行為一致。所以這不是某版的 bug,是它「每指令獨立 CDP 呼叫」這個架構的必然結果。我把它記成 OVERLAY GONE

這個認知是整件事的轉折點:問題不在「怎麼注入」,在「注入和截圖必須在同一次 session 上下文裡發生」。

解法:batch 把注入和截圖綁進同一個 session

agent-browser 有一個 batch 指令,能把多個操作塞進同一次 session 執行。這正是解藥——把 openeval(注入)、screenshot 三步綁成一氣呵成:

agent-browser batch --bail \
  "open https://example.com" \
  "eval -b <base64 的 annotate.js + annotateMulti 呼叫>" \
  "screenshot out.png"

同一個 batch 內,頁面狀態在指令之間保留,eval 注入的 overlay 撐到 screenshot 那一刻還在,標註就進圖了。--bail 讓任何一步失敗就停,不會默默截出一張沒標註的空圖。

eval -b 用 base64 傳 JS,是為了避開把整支 annotate.js + JSON 塞進 shell 時的跳脫地獄——引號、換行、中文全部包成一段 base64 就沒事了。)

跑起來,截圖裡真的有標註了。下面這張是實際輸出——同一段流程,分兩步截到空圖、用 batch 一氣呵成截到有標註:

batch 解法對照:分兩步 overlay 蒸發截到乾圖,batch 同 session 標註留到截圖

紅色虛線框 + ❶「主標題」框住 h1、藍色實心圓 + ❷「更多資訊連結」箭頭指向連結——中文 label 正常、box/click 兩種語意都對、顏色紅→藍輪替。這張圖從開頁到產出零手動。

封裝成可重用的 wrapper

解法有了,但每次手打 base64、拼 batch 太煩,而且容易忘掉 --bail、忘掉 round-trip 驗。所以我把它固化成一支 wrapper annotate-shot.sh,放進 skill 的 references/ 下:

annotate-shot.sh <url> <out.png> '<annotateMulti 的 JSON 陣列>'

例如:

annotate-shot.sh https://example.com out.png \
  '[{"sel":"h1","label":"主標題","type":"box"},
    {"sel":"a.cta","label":"送出","type":"click"}]'

它內部做三件事:(1) 定位 annotate.js(同目錄或常見安裝位置);(2) 把 annotate.js 全文 + annotateMulti(...) 呼叫 base64 編碼;(3) 用 agent-browser batch --bailopen → eval → screenshot。跑完提醒你用 Read 開圖確認框真的畫出來(標註若回 FAILED: 代表 selector 錯,別交空圖)。

現在產一張中文語意標註圖是一行指令的事,而且把「必須用 batch」這個非顯而易見的知識封在裡面,下次不用再重新踩坑。

為什麼不直接用原生 --annotate

有人會問:agent-browser 自己就有 screenshot --annotate,為什麼不用它?

因為兩者做的事不一樣。原生 --annotate自動標所有互動元素,標籤是冷冰冰的 [1][2][3](對應 @eN ref,給 AI 看著操作用的),它的定位是「讓多模態 AI 看圖點按鈕」。

但我要的是操作手冊——我要選定幾個重點元素、給它們語意分類(這是可點的按鈕 vs 這是一整塊區塊)、寫中文說明(「點這裡送出」不是「[2]」)。原生 --annotate 三件都給不了:不能選、只有數字、不能中文。

所以這是一個清楚的分工:

需求用哪個
AI 看圖自動操作(給機器讀)原生 screenshot --annotate([N] = @eN)
操作手冊語意標註(給人讀)annotate.js + batch(box/click + 中文 + 編號)
我借的正是原生給不了的那一塊。

焊回自己的產線

最後一步:把這個能力寫回我自己的 /screenshot-review,不是讓它躺在一支孤立的腳本裡。我在那個 command 的「截圖踩過的坑」區塊,加了一條:

手冊要「圖上有語意標註」→ 用 agent-browser batch,別信原生 --annotate:要語意標註(box/click + 自訂中文)得用 app-screenshots 的 annotate.js。坑:agent-browser 每個 CLI 指令是獨立 CDP 呼叫,eval 注入的 overlay 到下一個指令就蒸發(OVERLAY GONE,0.27/0.31 皆然,是架構特性)。解:把 open → eval → screenshot 塞進同一個 agent-browser batch --bail。已封裝成 references/annotate-shot.sh

這樣下次任何人(包括未來的我)跑 /screenshot-review 產手冊,想在圖上加標註時,產線本身就會告訴他:用 annotate-shot.sh、別踩 OVERLAY GONE。知識固化進流程,而不是留在某個人的記憶裡。

這次「吸收式升級」的方法論

回頭看,這次做的不是「裝一個 skill」,是一套可複製的動作:

  • 先有自己的產線:你得先有一條成熟的流程,才知道別人的東西補的是哪個洞。沒有產線,借來的零件無處可焊。

  • 拆零件而非整包吞:我沒有把 app-screenshots 整套流程搬進來,只拆了它最扎實的 annotate.js(298 行純 SVG,零依賴)。整包吞會帶進你不需要的工作流。

  • 撞到坑先查根因、別瞎試:OVERLAY GONE 瞎試半天無解,停下來理解「每指令獨立 CDP」的執行模型,答案立刻浮現。查根因的時間遠比亂試省。

  • 固化,不只解決:解掉當下問題只值一次;封裝成 wrapper + 寫回產線 skill,才讓這個知識每次都自動生效。

  • 判斷「借哪塊」要精準:原生 --annotate vs annotate.js 的分工想清楚,才不會借錯——我要的是「給人讀的語意標註」,不是「給機器讀的自動編號」。
  • 踩坑總表

    症狀根因
    OVERLAY GONEeval 回報標註成功,但 screenshot 截到乾圖每個 CLI 指令是獨立 CDP 呼叫,注入的 overlay 下一指令蒸發agent-browser batch 把 open→eval→screenshot 綁同 session
    shell 跳脫地獄把 annotate.js + JSON 塞進 eval 引號/中文爆掉多層引號 + 換行 + 中文eval -b 用 base64 傳整段 JS
    靜默空圖batch 有一步失敗但還是產了張沒標註的圖沒設失敗中止--bail,任一步失敗就停
    selector 打錯 = 隱形標註圖產出來但少了某個標記annotateMulti 命中不到元素看回傳有沒有 FAILED:,截完 Read 開圖確認
    版本錯覺以為升到 0.31 就會好OVERLAY GONE 是架構特性不是版本 bug0.27/node22 一樣能跑,核心是 batch 不是版本

    FAQ

    Q:為什麼不乾脆直接用作者的 app-screenshots skill 就好,幹嘛自己焊?
    因為我原本就有一條成熟的截圖審查 + 手冊產線(/screenshot-review),它做的 debug + 反橡皮圖章審查 + 同模板手冊是 app-screenshots 沒有的。我要的不是換一套流程,是在我的流程上補一塊標註能力。拆零件焊進去,比整包換划算。

    Q:OVERLAY GONE 是 agent-browser 的 bug 嗎?會被修掉嗎?
    不是 bug,是它「每個 CLI 指令 = 一次獨立 CDP 呼叫」這個架構的必然結果。我在 0.27 和 0.31 都驗過,行為一致。所以別等它「修好」,正解就是用 batch 把注入和截圖綁進同一次 session。

    Q:annotate-shot.sh 一定要 node 24 / agent-browser 0.31 嗎?
    不用。我實測 node v22 + agent-browser 0.27 一樣能跑。這支腳本的核心是「用 batch 保住 session」,不綁特定版本。0.31 只是需要 node ≥24,跟標註能不能截進圖無關。

    Q:一張圖可以標幾個?
    沿用 annotate.js 的設計:每張最多 3 個標註(顏色輪替紅藍綠琥珀紫最多 5 色,但擠太多會互相避讓到看不清)。要標更多就拆成多張圖,這也是好手冊的通則——一張圖講一件事。

    Q:這套只能標 example.com 這種簡單頁嗎?真實 App 行不行?
    真實 App 完全行,這正是它的用途——標你自己專案的 dev server 頁面產操作手冊。真實站要多注意的是先關 cookie 橫幅、擋廣告腳本(annotate.js 那篇有講),以及 selector 要先驗(避免隱形標註)。

    Q:焊進 /screenshot-review 後,我要怎麼觸發標註?
    產手冊那一步,想在某張圖加語意標註時,直接呼叫 references/annotate-shot.sh <頁面url> <輸出png> ''。command 的踩坑區已經寫明用它 + 為什麼要用 batch,所以流程本身會提醒你正確用法。

    ---

    延伸資源

    author
    陳彥彤

    AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

    support

    覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。

    related

    相關文章

    [AI 工具] · 17min
    app-screenshots 是什麼?讓 Claude Code 自動產「標註版截圖文件」的 skill
    app-screenshots 是 Alexander Opalic 寫的 Claude Code skill:你只要說「幫我截圖這個網站做文件」,AI 就自己開頁、找元素、在截圖上畫好帶編號的語意標註(紅框圈按鈕、藍圈標連結)、產出圖文對照的 Markdown。底層靠 agent-browser 驅動 Chrome + 一支 298 行純前端 SVG 注入腳本 annotate.js。本文拆解它的六階段工作流、那支沒被作者講清楚的標註引擎(canvas 量測字寬、六候選點避讓、失敗大聲回報)、三種標註類型(box/click/circle)怎麼選、實際跑出來長怎樣、安裝使用、以及適合誰不適合誰。附我用它底層引擎跑出來的真實標註圖。
    [AI 工具] · 15min
    agent-browser 截圖標註:3 招讓 AI 看著畫面操作
    agent-browser 能在截圖上畫圖、寫字——不是給人塗鴉的小畫家,而是自動把 AI 看得懂的標記畫進截圖像素裡。這篇接續上一篇純文字流,實測它在截圖上畫東西的三種畫法——screenshot --annotate(在每個可互動元素上畫編號 [1][2][3] 並寫出對照清單,每個對應 @eN ref,截完可直接 click @e2)、highlight(在指定元素外畫高亮框做聚焦驗證)、diff screenshot(把畫面差異畫成像素級比對圖,抓「長相變了」,配合 diff snapshot 抓「結構變了」)——把只能讀純文字 snapshot(一頁 200-400 tokens)的 agent 升級成「看著畫面操作」,補上 icon 按鈕、canvas、純視覺狀態這些純文字抓不到的盲區。核心是 annotate 把視覺感知跟動作指令對齊:畫上去的 @eN 編號文字流跟視覺流共用同一套 ref,多模態模型看圖推理完直接產生跟純文字流一樣的指令。踩坑含 annotate 只支援 CDP 後端(Chrome/Lightpanda)Safari 不行、ref 會過期、diff threshold 不調會一直假警報。版本 v0.27.2,全程 Mac 實測。
    [AI 工具] · 12min
    AI agent 額度爆了不用乾等:ccs 多帳號無縫切換(Claude Code + Codex,同一場對話接得起來)
    額度撞 rate limit 最痛的不是要等刷新,是對話脈絡斷掉。ccs 這套把這個痛點直接做掉——一鍵切到第二個帳號,把同一串對話搬過去用它的額度繼續跑,脈絡完全不斷。核心原理 provider-agnostic:每一場對話其實是一個 .jsonl 檔躺在硬碟上,用 CLAUDE_CONFIG_DIR / CODEX_HOME 環境變數隔離兩個帳號身份,撞 limit 時把那場對話檔複製到第二個帳號目錄、再用它 resume 就好,額度算第二個帳號的、歷史一字不差。支援 Claude Code 與 Codex,連 Codex 斷了用 Claude 接的跨工具接力都涵蓋。安全上用「按需複製單一對話檔」而非 symlink 整個資料夾(後者雙開會 corrupt),token 永遠留在你自己的 Keychain、不被搬移。本文最後一段可以直接複製貼給 Claude Code,它會自己 git clone + 跑 install.sh 幫你裝好——唯一要你自己按的只有第二個帳號的 login。