屏插件實(shí)戰(zhàn))
如果你的編碼代理一直跑在終端里屏幕對(duì)你來(lái)說(shuō)就只是擺設(shè)。上周我改一個(gè)前端暗色主題的細(xì)節(jié)deepseek harness 在終端里跑得很順代碼改完、測(cè)試通過(guò)但它沒(méi)法告訴我按鈕在暗色模式下到底好不好看。于是我做了一件有點(diǎn)“野”的事給這套 harness 寫(xiě)了一個(gè)識(shí)屏插件讓它能在處理任務(wù)前先截一張當(dāng)前屏幕把看到的內(nèi)容變成上下文。這件事做完以后我發(fā)現(xiàn)問(wèn)題不在于“AI 能不能看圖”而在于一套原本只處理文本的工作流需要怎樣接入視覺(jué)信息。識(shí)屏插件的價(jià)值不是給 agent 多一只眼睛而是把現(xiàn)實(shí)屏幕上的信息重新拉回文本和工具調(diào)用的循環(huán)里。表面看是一個(gè)小工具背后涉及模型能力、上下文拼裝、本地代理、錯(cuò)誤排查和工程化邊界。這篇記錄一下一個(gè)像 catch 一樣的插件是怎么被一步步磨出來(lái)的。1. 為什么缺“識(shí)屏”這個(gè)能力會(huì)讓本地編碼代理變得不完整1.1 agent 只能看到文本屏幕是另一個(gè)世界先說(shuō)一個(gè)很容易被忽略的事實(shí)deepseek harness 這類(lèi)編碼代理本質(zhì)上生活在一個(gè)純文本世界里。它能看到什么倉(cāng)庫(kù)文件、終端輸出、測(cè)試報(bào)告、日志、你輸入的指令。它看不到什么瀏覽器渲染出來(lái)的真實(shí)界面、彈出來(lái)的報(bào)錯(cuò)窗口、設(shè)計(jì)稿的排版、IDE 的高亮顏色。對(duì) agent 來(lái)說(shuō)這些都不存在。這不是模型能力的問(wèn)題而是輸入通道的問(wèn)題。OpenAI 那套 API 格式、Anthropic 的 messages 結(jié)構(gòu)、DeepSeek 的 OpenAI 兼容接口核心都是文本 token 的交換。你可以給模型傳圖片前提是模型本身支持視覺(jué)輸入并且你的調(diào)用方真正把圖片放進(jìn)了正確的字段。大部分編碼代理默認(rèn)不會(huì)自動(dòng)截屏也不會(huì)把屏幕截圖塞進(jìn)請(qǐng)求里。所以當(dāng) agent 說(shuō)“改完了”而你需要確認(rèn)頁(yè)面效果時(shí)它只能告訴你它改了哪些代碼無(wú)法告訴你頁(yè)面看起來(lái)怎么樣。你問(wèn)它“你看一下屏幕”它只能回你一個(gè)禮貌的抱歉。這個(gè)缺口看似很小但一旦你經(jīng)常做前端、爬蟲(chóng)、自動(dòng)化驗(yàn)收、界面 bug 修復(fù)就會(huì)變成每天都要踩的坑agent 的工作鏈路里缺的不只是眼睛而是“屏幕上下文”這個(gè)入口。1.2 deepseek harness 的核心工作流是一套本地執(zhí)行鏈路要理解識(shí)屏插件為什么可行先要理解 deepseek harness 到底是什么。它不是一個(gè)魔術(shù)盒也不是 DeepSeek 官方發(fā)布的一個(gè)大型桌面應(yīng)用。更準(zhǔn)確地說(shuō)它是一套社區(qū)里常見(jiàn)的編碼代理方案以開(kāi)源 CLI 代理為骨架把模型 Provider 配置成 DeepSeek通過(guò)本地代理或者配置切換讓終端里的 agent 用 DeepSeek 模型驅(qū)動(dòng)完成讀倉(cāng)庫(kù)、改代碼、跑測(cè)試、提交改動(dòng)這類(lèi)工程任務(wù)。很多人喜歡把它理解成“Codex 接入 DeepSeek”的一種玩法。Codex 本身是 OpenAI 的編碼代理形態(tài)但作為開(kāi)源組件它的模型 Provider 是可以替換的。deepseek harness 就是這套替換實(shí)踐的產(chǎn)物用 DeepSeek 的 API 作為大腦用本地 CLI 作為手和腳。這個(gè)方案的價(jià)值有幾個(gè)層面成本DeepSeek 比常見(jiàn)海外模型便宜太多適合長(zhǎng)時(shí)間掛著讓 agent 反復(fù)改代碼。本地可控它的配置、日志、請(qǐng)求鏈路都在本地你能看到每一輪請(qǐng)求到底發(fā)了什么。模型可換harness 這個(gè)英文詞本身就有“套具、控制裝置”的意思在 agent 語(yǔ)境里它定義的是執(zhí)行循環(huán)感知、決策、行動(dòng)、觀察。但正是“本地可控”這個(gè)優(yōu)點(diǎn)讓插件化成為可能。如果它是一個(gè)黑盒 SaaS你很難在請(qǐng)求發(fā)出前插入一個(gè)截圖步驟。而 deepseek harness 這類(lèi)方案通常允許你在調(diào)用鏈路的某個(gè)環(huán)節(jié)塞自己的邏輯。這也就解釋了為什么識(shí)屏插件值得做因?yàn)槟憧刂频牟皇且粋€(gè)遠(yuǎn)程服務(wù)而是一條本地執(zhí)行鏈路。1.3 識(shí)屏解決的不是“截圖”而是上下文斷裂好現(xiàn)在把問(wèn)題說(shuō)透。假設(shè)你正在做一個(gè)前端頁(yè)面還原。你給 deepseek harness 發(fā)指令按這份設(shè)計(jì)稿把登錄頁(yè)的間距調(diào)一下。設(shè)計(jì)稿是一張圖。agent 如果只處理文本它就沒(méi)有“看到”這張圖如果你把圖片路徑給它它可能會(huì)嘗試用文件讀取工具讀圖片但大多數(shù)純文本模型會(huì)把圖片當(dāng)成二進(jìn)制數(shù)據(jù)讀不出語(yǔ)義。再比如你在跑一個(gè)桌面應(yīng)用程序彈了一個(gè)錯(cuò)誤窗口這個(gè)窗口的內(nèi)容不在終端輸出里不在日志文件里只在屏幕上。agent 看到測(cè)試失敗了卻看不到錯(cuò)誤彈窗。你手動(dòng)把文字敲給它效率就下來(lái)了。這些場(chǎng)景的共同點(diǎn)是什么工作流中間斷了一截。人類(lèi)是通過(guò)屏幕感知世界的而 agent 是通過(guò)文本感知世界的。識(shí)屏插件做的事情就是把屏幕上的內(nèi)容重新轉(zhuǎn)換成 agent 能消費(fèi)的輸入無(wú)論是文字還是圖片。所以識(shí)屏插件真正的價(jià)值不是“給 AI 加一雙眼睛”而是把被斷開(kāi)的上下文重新接回 agent 的感知循環(huán)里。明白了這一點(diǎn)你才能理解后面每一步實(shí)現(xiàn)選擇——為什么不能直接截個(gè)大圖丟給它為什么要在 OCR 和視覺(jué)模型之間做取舍為什么要在 harness 的擴(kuò)展點(diǎn)里注冊(cè)工具而不是魔改源碼都是為了讓這條上下文鏈路穩(wěn)定、可控、可維護(hù)。2. 先從一次失敗嘗試說(shuō)起識(shí)屏插件不是簡(jiǎn)單截個(gè)圖2.1 第一版實(shí)現(xiàn)截圖、編碼、塞進(jìn)消息剛開(kāi)始我很天真。我的想法非常簡(jiǎn)單注冊(cè)一個(gè)get_screen_info工具讓 agent 在需要的時(shí)候調(diào)用它然后截屏、保存、讀成 base64、塞進(jìn) user 消息的image_url字段。這不就完了嗎第一版代碼長(zhǎng)這樣import { execFileSync } from node:child_process; import { readFileSync, writeFileSync } from node:fs; import { tmpdir } from node:os; import path from node:path; function captureScreen() { const file path.join(tmpdir(), harness-screen-${Date.now()}.png); if (process.platform darwin) { execFileSync(screencapture, [-x, file]); } else if (process.platform win32) { // Windows 走 PowerShell 截屏 } else { // Linux 嘗試 gnome-screenshot 或 grim } return file; } function imageToBase64(file) { return readFileSync(file).toString(base64); }然后我把它接進(jìn)工具調(diào)用截圖 → base64 → 塞進(jìn)消息 → 調(diào)模型。看起來(lái)邏輯完整。如果用的模型本身支持視覺(jué)輸入這一步通常就能跑通。但問(wèn)題恰恰出在“通?!边@兩個(gè)字上。2.2 撞上的問(wèn)題thinking 模式必須回傳 reasoning_content第一次完整運(yùn)行直接 400。錯(cuò)誤長(zhǎng)這樣cc switch local proxy failed while handling codex endpoint /responses. provider: deepseek; upstream_status: http 400; cause: the reasoning_content in the thinking mode must be passed back to the api.看到這個(gè)報(bào)錯(cuò)的瞬間我的第一反應(yīng)是“代理掛了”但仔細(xì)看cause那一段問(wèn)題其實(shí)出在多輪消息結(jié)構(gòu)上。DeepSeek 的推理模型在工作時(shí)會(huì)返回一個(gè)reasoning_content字段里面是模型自己的思考過(guò)程。如果你用 thinking 模式下一輪請(qǐng)求時(shí)這個(gè)reasoning_content必須被原樣帶回給 API否則 API 會(huì)視為非法請(qǐng)求。而我在識(shí)屏插件里做的事情本質(zhì)上是在“上一次模型回復(fù)”之后插入了新的 user 消息里面帶著截圖。問(wèn)題就出在我在拼裝新消息時(shí)沒(méi)有把上一輪的reasoning_content正確保留或者在某個(gè)版本里插件把舊的 assistant 回復(fù)重新組裝但漏掉了這個(gè)字段。這種錯(cuò)誤最討厭的地方在于它不是識(shí)屏邏輯本身的錯(cuò)誤而是上下文拼裝環(huán)節(jié)踩到了模型的服務(wù)端約束。后來(lái)我反復(fù)測(cè)試發(fā)現(xiàn)這類(lèi)“thinking mode 必須回傳 reasoning_content”的要求在推理類(lèi)模型里不算少見(jiàn)。只要你的 harness 允許自定義上下文修改就很容易踩到這個(gè)坑。2.3 排查鏈路先用最小請(qǐng)求確認(rèn)再擴(kuò)大改造那次失敗以后我做了一個(gè)很機(jī)械但很有用的排查。順序是去掉識(shí)屏插件恢復(fù) harness 的默認(rèn)調(diào)用。確認(rèn)默認(rèn)鏈路沒(méi)問(wèn)題。單獨(dú)用腳本調(diào) DeepSeek API把上一輪的reasoning_content原樣帶回確認(rèn) API 可以通過(guò)。用調(diào)試面板看實(shí)際請(qǐng)求體對(duì)比“默認(rèn)請(qǐng)求”和“接了識(shí)屏插件之后的請(qǐng)求”找出消息結(jié)構(gòu)到底差在哪。一點(diǎn)一點(diǎn)加回插件的邏輯直到定位到是某個(gè)字段被覆蓋。如果你也遇到類(lèi)似的 400按這個(gè)順序走通常能很快定位。不要一開(kāi)始就在插件里改來(lái)改去那樣很容易把問(wèn)題繞暈。還有一點(diǎn)很重要不要直接去改 harness 核心代碼。一旦你改了源碼下次更新工具就會(huì)沖突而且你很難判斷是官方邏輯的問(wèn)題還是自己改動(dòng)的問(wèn)題。插件的價(jià)值是“可插拔”不是“改得越深越好”。我知道很多人會(huì)覺(jué)得“看見(jiàn)屏幕”這件事很驚艷但真正讓它可落地的恰恰是這些不驚艷的排查細(xì)節(jié)。3. 一個(gè)可落地的識(shí)屏插件結(jié)構(gòu)抓屏、壓縮、OCR/編碼、拼裝上下文3.1 抓屏跨平臺(tái)的幾種通用方式識(shí)別屏幕的第一步是拿到屏幕圖像。這里沒(méi)有統(tǒng)一標(biāo)準(zhǔn)不同操作系統(tǒng)有不同命令。macOS 最簡(jiǎn)單screencapture -x /tmp/harness-screen.pngWindows 上一般用 PowerShell示例結(jié)構(gòu)大概是Add-Type -AssemblyName System.Windows.Forms,System.Drawing $b [System.Windows.Forms.Screen]::PrimaryScreen.Bounds $bmp New-Object System.Drawing.Bitmap $b.Width, $b.Height $g [System.Drawing.Graphics]::FromImage($bmp) $g.CopyFromScreen($b.Location, [System.Drawing.Point]::Empty, $b.Size) $bmp.Save($env:TEMP\harness-screen.png)Linux 桌面環(huán)境比較碎常見(jiàn)的是gnome-screenshot或者grim視你用的桌面環(huán)境而定。在 Node 里封裝一層很容易做成平臺(tái)無(wú)關(guān)的調(diào)用import { execFileSync } from node:child_process; import path from node:path; import os from node:os; export function captureScreen() { const file path.join(os.tmpdir(), harness-screen-${Date.now()}.png); if (process.platform darwin) { execFileSync(screencapture, [-x, file]); } else if (process.platform win32) { execFileSync(powershell, [-File, path.join(__dirname, capture.ps1), file]); } else { execFileSync(gnome-screenshot, [-f, file]); } return file; }如果只需要截某個(gè)窗口可以進(jìn)一步限定窗口 ID 或坐標(biāo)區(qū)域。這個(gè)我沒(méi)有在插件里做得太復(fù)雜但設(shè)計(jì)上應(yīng)該留出參數(shù)。注意截屏屬于敏感操作。插件默認(rèn)只截全屏可能是最省事的但最負(fù)責(zé)任的做法是讓用戶(hù)配置“截圖區(qū)域”而不是什么都抓。3.2 圖像預(yù)處理為什么不能直接塞原圖第一版我直接拿 4K 截圖轉(zhuǎn) base64結(jié)果一測(cè)就發(fā)現(xiàn)問(wèn)題圖片太大請(qǐng)求體動(dòng)輒幾 MBAPI 延遲高token 消耗也莫名其妙地上去了。所以后來(lái)我加了一個(gè)預(yù)處理步驟縮放最長(zhǎng)邊壓到 1024 或 768。這個(gè)尺寸對(duì)大多數(shù)視覺(jué)識(shí)別的需求都?jí)蛄诉€能顯著減少 token。換編碼如果不是必須保留透明背景優(yōu)先用 JPEG質(zhì)量 80。PNG 的 base64 體積通常比 JPEG 大很多。裁剪如果只需要某塊區(qū)域提前裁掉無(wú)關(guān)區(qū)域信息更干凈。一個(gè)簡(jiǎn)單的處理思路from PIL import Image def preprocess_screen(image_path, max_side1024, quality85): img Image.open(image_path) img.thumbnail((max_side, max_side)) if img.mode in (RGBA, P): img img.convert(RGB) output_path image_path.replace(.png, .jpg) img.save(output_path, JPEG, qualityquality) return output_path預(yù)處理的核心不是“壓縮”而是讓模型看到它真正需要的信息同時(shí)不把無(wú)關(guān)像素浪費(fèi)在 token 里。3.3 兩條喂圖路線多模態(tài)直讀 vs OCR 轉(zhuǎn)文本做完預(yù)處理之后就要決定把什么內(nèi)容喂給模型。這里有兩套路線選擇取決于你的模型是否支持視覺(jué)輸入。路線輸入形式優(yōu)點(diǎn)局限適合場(chǎng)景多模態(tài)直讀image_urlbase64模型能看到布局、顏色、文字、圖片細(xì)節(jié)要求模型支持視覺(jué)輸入token/延遲較高前端還原、視覺(jué)驗(yàn)收、設(shè)計(jì)圖比對(duì)OCR 轉(zhuǎn)文本純文本塊成本低、兼容普通文本模型、上下文穩(wěn)定丟失布局和顏色識(shí)別可能有誤差報(bào)錯(cuò)彈窗、終端輸出、文檔信息提取我建議的做法是優(yōu)先走 OCR 轉(zhuǎn)文本因?yàn)?deepseek 主流文本模型便宜、快、穩(wěn)定。OCR 后的文字能直接塞進(jìn)系統(tǒng)提示詞或 user 消息不依賴(lài)視覺(jué)能力。但如果你確實(shí)需要讓 agent“看懂”頁(yè)面布局比如讓它判斷按鈕是否居中、卡片間距是否一致那 OCR 不夠必須走視覺(jué)模型直讀。一個(gè)折中方案是把兩種模式都做成插件選項(xiàng)route: ocr默認(rèn)抓屏 → OCR → 返回文本。route: vision抓屏 → 壓縮編碼 → 返回 image_url。route: both兩個(gè)都返回。這樣你在不同任務(wù)里可以自由切換而不用改插件代碼。3.4 上下文拼裝示例無(wú)論走哪條路最終都要把結(jié)果拼進(jìn)模型請(qǐng)求。如果模型支持多模態(tài)輸入標(biāo)準(zhǔn)結(jié)構(gòu)是{ role: user, content: [ { type: text, text: 這是當(dāng)前屏幕截圖請(qǐng)根據(jù)看到的內(nèi)容繼續(xù)處理。 }, { type: image_url, image_url: { url: data:image/jpeg;base64,BASE64 } } ] }如果走 OCR 文本就非常簡(jiǎn)單{ role: user, content: 當(dāng)前屏幕 OCR 內(nèi)容如下\nTEXT }注意一個(gè)問(wèn)題不要每次調(diào)用都把這輪截圖永久留在上下文里。識(shí)屏結(jié)果應(yīng)該是“當(dāng)前這一輪”的臨時(shí)上下文用完就清理。否則幾百輪對(duì)話下來(lái)圖片的 token 累積會(huì)讓上下文爆炸成本也會(huì)失控。4. 在 deepseek harness 里把它接進(jìn)去自定義工具而不是魔改 CLI4.1 先搞清 harness 暴露的擴(kuò)展點(diǎn)hooks 還是 toolsdeepseek harness 這類(lèi)工具通常不會(huì)提供一個(gè)“插件市場(chǎng)”讓你一鍵安裝。它的擴(kuò)展點(diǎn)一般有兩種hooks在特定時(shí)機(jī)執(zhí)行腳本比如每次請(qǐng)求前、響應(yīng)后。tools向 agent 注冊(cè)一個(gè)可被調(diào)用的外部工具函數(shù)。這兩種方式各有適用場(chǎng)景。hooks 更像“監(jiān)聽(tīng)器”適合做日志、攔截、注入環(huán)境信息tools 更像“手”適合做實(shí)際動(dòng)作。我最后選擇的是 tools。原因很簡(jiǎn)單我不希望每次請(qǐng)求都強(qiáng)制截屏而是希望模型在需要時(shí)主動(dòng)去調(diào)用截屏工具。在系統(tǒng)提示詞里加一句規(guī)則當(dāng)用戶(hù)提到屏幕、界面、報(bào)錯(cuò)彈窗、頁(yè)面效果、視覺(jué)驗(yàn)證時(shí)先調(diào)用 get_screen_info 工具再回答問(wèn)題。這樣模型自己會(huì)決定什么時(shí)候看屏幕而不是每輪都盲截一張圖浪費(fèi) token 和時(shí)間。4.2 一個(gè)通用接入方式作為外部工具注冊(cè)具體到實(shí)現(xiàn)我在自己的插件里注冊(cè)了一個(gè)外部工具。大致結(jié)構(gòu)是這樣const screenTool { name: get_screen_info, description: Capture the current screen and return OCR text or image. Use when user asks about screen, UI, popup, page preview., parameters: { type: object, properties: { route: { type: string, enum: [ocr, vision, both], default: ocr } }, required: [] }, async run({ route }) { const imagePath captureScreen(); const processedPath preprocess(imagePath); if (route vision || route both) { const base64 imageToBase64(processedPath); return { image_base64: base64, ...(route both ? { ocr_text: ocr(processedPath) } : {}) }; } return { ocr_text: ocr(processedPath) }; } };具體注冊(cè)位置在不同版本里不一樣有的在 plugin 目錄有的在配置文件里聲明 tools。落地前請(qǐng)先看對(duì)應(yīng)版本的 README 或--help不要照抄網(wǎng)上的舊代碼。這里要特別說(shuō)明這段代碼不是某款工具的官方 API而是一個(gè)比較通用的工具注冊(cè)結(jié)構(gòu)。它的意義在于幫你理解“識(shí)屏插件在 harness 里的定位”而不是告訴你某個(gè)具體版本應(yīng)該怎么寫(xiě)。4.3 加一個(gè)本地調(diào)試面板用 dsh web 看每一輪請(qǐng)求寫(xiě)完插件之后我開(kāi)始頻繁使用一個(gè)本地調(diào)試面板。在我用的方案里入口是pnpm dsh web。它會(huì)把每一輪請(qǐng)求的輸入、輸出、耗時(shí)、報(bào)錯(cuò)都顯示出來(lái)。這個(gè)面板對(duì)我的幫助特別大。因?yàn)楫?dāng)你把屏幕信息插入到請(qǐng)求里時(shí)你非常需要確認(rèn)一件事截圖內(nèi)容到底有沒(méi)有被正確送到模型那邊。如果模型返回 400你可以在面板里看請(qǐng)求體。對(duì)比“加了插件”和“沒(méi)加插件”的請(qǐng)求很快就能發(fā)現(xiàn)是哪個(gè)字段被覆蓋了是reasoning_content丟了還是messages順序亂了。如果你用的 harness 沒(méi)有類(lèi)似面板至少要在插件里保留請(qǐng)求日志。logs/ screen-tool.log request-body.log errors.log不要小看日志。識(shí)屏插件一旦跑起來(lái)失敗模式比普通工具復(fù)雜得多——截圖可能失敗OCR 可能返回空API 可能 400請(qǐng)求體可能因?yàn)樯舷挛倪^(guò)長(zhǎng)被截?cái)?。沒(méi)有日志你只能瞎猜。5. 識(shí)屏插件真正適合的場(chǎng)景和不適合的場(chǎng)景5.1 已經(jīng)驗(yàn)證有效的幾個(gè)場(chǎng)景第一個(gè)場(chǎng)景是前端代碼修改后的自檢。以前 agent 改完樣式我只能心累地看?,F(xiàn)在它改完之后可以自己調(diào)用識(shí)屏工具截一張瀏覽器里的實(shí)際渲染效果然后判斷“間距是否合理”“是否居中了”“是否被遮擋”。如果用了視覺(jué)模型它甚至能直接指出哪里不對(duì)。第二個(gè)場(chǎng)景是讀取系統(tǒng)級(jí)報(bào)錯(cuò)彈窗。很多桌面端工具的報(bào)錯(cuò)并不進(jìn)入終端而是彈在屏幕上層。普通測(cè)試輸出看不到日志里也可能沒(méi)有。識(shí)屏插件通過(guò) OCR 把彈窗文字提取出來(lái)agent 就能準(zhǔn)確理解錯(cuò)誤內(nèi)容。第三個(gè)場(chǎng)景是用設(shè)計(jì)圖指導(dǎo)編碼。如果你截一張?jiān)O(shè)計(jì)稿給 agent并配上視覺(jué)模型它可以根據(jù)屏幕上的設(shè)計(jì)稿來(lái)調(diào)頁(yè)面。這個(gè)場(chǎng)景對(duì)多模態(tài)支持的要求比較高但對(duì)前端開(kāi)發(fā)的吸引力也最大。5.2 不建議做的場(chǎng)景識(shí)屏插件不是萬(wàn)能的。以下場(chǎng)景我明確不建議使用場(chǎng)景為什么不建議實(shí)時(shí)連續(xù)桌面監(jiān)控截圖頻率一高token 和延遲直接爆炸且安全風(fēng)險(xiǎn)不可控高權(quán)限交互界面自動(dòng)化一旦 agent 看到敏感內(nèi)容可能誤觸發(fā)危險(xiǎn)操作純文本任務(wù)強(qiáng)行識(shí)屏只會(huì)增加延遲和錯(cuò)誤率沒(méi)有收益銀行、密碼、內(nèi)部系統(tǒng)頁(yè)面截圖內(nèi)容可能包含敏感數(shù)據(jù)不建議發(fā)給任何外部模型服務(wù)屏幕內(nèi)容轉(zhuǎn)發(fā)到不可信服務(wù)如果插件把截圖上傳到你無(wú)法控制的 API 或存儲(chǔ)風(fēng)險(xiǎn)很高這里做一個(gè)原則性表格比參數(shù)更重要判斷問(wèn)題通過(guò)標(biāo)準(zhǔn)這個(gè)信息能通過(guò)文本拿到嗎能則不要截屏任務(wù)是一輪一輪執(zhí)行的嗎是識(shí)屏合適需要連續(xù)流不合適截圖內(nèi)容允許發(fā)到當(dāng)前模型服務(wù)端嗎不允許則只 OCR 本地處理或不做模型支持視覺(jué)輸入或 OCR 夠用都不支持識(shí)屏無(wú)意義5.3 一個(gè)簡(jiǎn)單的判斷框架要不要做識(shí)屏先回答三個(gè)問(wèn)題我后來(lái)總結(jié)出三個(gè)問(wèn)題每次想給 harness 加識(shí)屏能力時(shí)先自問(wèn)一遍這個(gè)信息能不能先從文本拿到如果可以?xún)?yōu)先文本。截圖是最后手段。任務(wù)是不是快照式的識(shí)屏適合“看一眼當(dāng)前狀態(tài)然后做判斷”的場(chǎng)景不適合持續(xù)追蹤動(dòng)態(tài)畫(huà)面。你承擔(dān)得起截圖帶來(lái)的上下文和隱私成本嗎如果截圖內(nèi)容敏感或模型不支持視覺(jué)就要換方案。這三個(gè)問(wèn)題能過(guò)濾掉至少一半“想給 harness 加識(shí)屏”的沖動(dòng)。它不是每時(shí)每刻都需要但當(dāng)你需要的時(shí)候它是唯一能接上上下文的方式。6. 如果你也想寫(xiě)自己的 harness 插件建議按這個(gè)順序來(lái)6.1 先用默認(rèn)配置跑通最小任務(wù)不要一上來(lái)就寫(xiě)識(shí)屏插件。先確認(rèn)你的 harness 環(huán)境本身沒(méi)問(wèn)題模型配置正確API 能連通README里的最小示例能跑通。比如讓 agent 讀一個(gè)文件改一個(gè)函數(shù)跑一次測(cè)試。這個(gè)步驟看起來(lái)多余但它能隔離問(wèn)題。如果你在最開(kāi)始就引入插件遇到一個(gè) 400你很難判斷是插件的問(wèn)題還是環(huán)境的問(wèn)題。只有默認(rèn)鏈路穩(wěn)定了你才有資格談擴(kuò)展。6.2 用一條樣例確認(rèn)輸入通道不要直接把插件完整寫(xiě)完。先把“截圖”這一步單獨(dú)跑一遍把“OCR”這一步單獨(dú)跑一遍把“調(diào)用 API”這一步單獨(dú)跑一遍。我的流程是先手動(dòng)截一張圖確認(rèn)文件存在。再手動(dòng)跑一次 OCR確認(rèn)輸出文本正確。用腳本構(gòu)造一個(gè)最小請(qǐng)求把 OCR 文本發(fā)給模型確認(rèn)返回正常。最后才把這三步串進(jìn) harness 的工具調(diào)用鏈。每個(gè)環(huán)節(jié)單獨(dú)驗(yàn)證能讓你在后續(xù)排查時(shí)立刻知道是哪一環(huán)壞了。6.3 識(shí)別擴(kuò)展機(jī)制而不是抄一堆社區(qū)片段deepseek harness 這類(lèi)工具更新很快不同版本的 hooks 和 tools 接口可能完全不同。如果你在網(wǎng)上看到一個(gè)舊插件片段不要直接復(fù)制進(jìn)去。先做三件事打開(kāi)本地安裝后的源碼或類(lèi)型定義找到工具注冊(cè)入口??词纠渲美镉袥](méi)有聲明自定義 tools 的地方。用最小改動(dòng)跑通一個(gè)“hello world”工具比如返回當(dāng)前時(shí)間然后再替換成識(shí)屏邏輯。很多人的插件問(wèn)題不是邏輯寫(xiě)錯(cuò)了而是注冊(cè)方式不對(duì)。這一點(diǎn)比識(shí)屏本身更重要。6.4 日志、錯(cuò)誤重試、上下文管理是長(zhǎng)期使用門(mén)檻工具能跑起來(lái)和能長(zhǎng)期使用完全是兩回事。識(shí)屏插件一旦放進(jìn)日常開(kāi)發(fā)流就會(huì)遇到各種臟場(chǎng)景截圖命令在某些環(huán)境被權(quán)限攔截。OCR 在低分辨率下返回空字符串。API 因?yàn)樯舷挛闹邪瑘D片而產(chǎn)生更高的 token 成本。上一輪截圖內(nèi)容沒(méi)有清理導(dǎo)致后續(xù)請(qǐng)求越來(lái)越大。所以我建議至少在插件里做到# 每次抓屏后保留原始文件路徑 # 每次 OCR 后寫(xiě)入 result 到日志 # API 400 時(shí)記錄 request body 的前 200 個(gè)字符 # 上下文清理策略識(shí)屏結(jié)果只在當(dāng)前輪有效當(dāng)一個(gè)工具同時(shí)具備“干凈的輸入、可觀測(cè)的執(zhí)行過(guò)程、明確的失敗反饋、可清理的副作用”時(shí)它才算從實(shí)驗(yàn) hack 變成了真正的工程工具。6.5 從一次性插件走向可復(fù)用工具最后一步是參數(shù)化。不要把你的截圖區(qū)域?qū)懰啦灰涯愕妮敵瞿J綄?xiě)死。用配置控制screen.capture.area全屏還是指定區(qū)域。screen.capture.routeocr / vision / both。screen.capture.maxSide最長(zhǎng)邊。screen.capture.qualityJPEG 質(zhì)量。screen.context.expire識(shí)屏結(jié)果保留幾輪。把這些參數(shù)從代碼里拿出來(lái)放進(jìn)配置文件你才能把它復(fù)用到其他項(xiàng)目上。這才是插件真正“可復(fù)用”的樣子。7. 收尾工具的價(jià)值不只是“看見(jiàn)屏幕”做這個(gè)識(shí)屏插件最大的收獲并不是“我的 agent 能看見(jiàn)屏幕了”。真正讓我想明白的是另一件事agent 工具的進(jìn)化方向不是越來(lái)越像人而是越來(lái)越能補(bǔ)上工作流里斷掉的環(huán)節(jié)。一個(gè)能改代碼的代理很好但它再?gòu)?qiáng)也看不到你屏幕上的報(bào)錯(cuò)彈窗一個(gè)能調(diào) API 的代理很好但它讀不到設(shè)計(jì)稿的排版偏差。識(shí)屏插件解決的不是“視覺(jué)能力”而是“輸入通道”。這件事反映出來(lái)的趨勢(shì)更值得關(guān)注deepseek harness 這類(lèi)本地編碼代理正在把 agent 從“一個(gè)固定的黑盒助手”變成“一條你可以自己改造的工作流基礎(chǔ)設(shè)施”。今天你可以給它加識(shí)屏明天你可以給它加語(yǔ)音輸入、加定時(shí)任務(wù)、加瀏覽器控制、加自定義工具。它的邊界不是官方功能列表而是你對(duì)工作流的理解。我現(xiàn)在的建議很具體如果你也一直在用編碼代理先不要急著寫(xiě)復(fù)雜插件。找一個(gè)小到不能再小的痛點(diǎn)比如“每次跑完前端都想讓 agent 看一眼頁(yè)面”從一條截圖命令開(kāi)始把鏈路跑通再說(shuō)。識(shí)屏這條路最難的不是截圖不是 OCR也不是拼裝上下文而是你終于意識(shí)到工作流斷了的地方才是工具最該生長(zhǎng)的地方。