化:讓大模型看屏幕點(diǎn)鼠標(biāo)完成翻譯)
把大模型從對話框里撈出來讓它自己在桌面上看屏幕、點(diǎn)鼠標(biāo)、敲鍵盤這件事我惦記很久了。最近終于把這條鏈路跑通截圖識(shí)別屏幕內(nèi)容交給大模型做翻譯決策再模擬人工操作把結(jié)果寫回界面整個(gè)流程不依賴任何軟件二次開發(fā)純靠“看見—思考—?jiǎng)邮帧钡拈]環(huán)完成。這個(gè)項(xiàng)目我起名叫“屏幕翻譯管家”核心就是AI Agent接入桌面自動(dòng)化。這篇文章把完整思路、關(guān)鍵代碼、選型理由和踩坑記錄全部攤開講適合想從“只會(huì)調(diào)API做對話”進(jìn)階到“能讓AI真正干活”的開發(fā)者也適合正在研究OCR、桌面自動(dòng)化和智能體應(yīng)用的同學(xué)參考。1. 從對話框到工作流Agent為什么要“動(dòng)手”1.1 大模型的手腳為什么這么短把大模型關(guān)在對話框里它有再強(qiáng)的推理能力能輸出的也只是文字。你要它幫你翻譯一段外文軟件界面它只能給你譯文還得你自己切回窗口、照著譯文去點(diǎn)按鈕。這種模式本質(zhì)上就是“只帶眼睛帶腦子的文本顧問”手和腳還是你自己的。AI Agent和普通API調(diào)用的分水嶺就在這里Agent要在真實(shí)的運(yùn)行環(huán)境中做閉環(huán)決策和執(zhí)行。感知、規(guī)劃、行動(dòng)三個(gè)環(huán)節(jié)缺一不可。桌面自動(dòng)化恰好提供了“行動(dòng)”這一環(huán)的落地場景——通過模擬鍵鼠操作讓Agent能在你已經(jīng)打開的任何軟件里像人一樣看屏幕、移動(dòng)光標(biāo)、點(diǎn)擊按鈕、輸入內(nèi)容。這套玩法并不新鮮以前是企業(yè)級RPA的事用規(guī)則腳本硬編碼坐標(biāo)和流程現(xiàn)在有了大模型最難寫的“根據(jù)屏幕內(nèi)容決定下一步做什么”直接從編寫規(guī)則變成了自然語言描述門檻一下子降下來了。1.2 三條技術(shù)路線為什么最后選了Agent加OCR做桌面自動(dòng)化翻譯路線不是只有一條。我把主流方案整理了一下路線實(shí)現(xiàn)方式優(yōu)點(diǎn)缺點(diǎn)固定腳本自動(dòng)化預(yù)設(shè)坐標(biāo)、圖像模板匹配、定時(shí)觸發(fā)速度快、成本低、穩(wěn)定界面一變就崩沒法處理異常情況OCR加規(guī)則引擎用OCR讀屏幕文字用if-else規(guī)則決定翻譯和操作能感知真實(shí)文本比純坐標(biāo)健壯規(guī)則需要人工逐條枚舉場景一多就維護(hù)不動(dòng)OCR加大模型AgentOCR讀屏大模型根據(jù)屏幕上下文動(dòng)態(tài)決策適應(yīng)界面變化能處理復(fù)雜語義有模型調(diào)用成本響應(yīng)延遲相對高我最終選的是第三條路線理由很直接這個(gè)項(xiàng)目的核心目標(biāo)是“翻譯”而翻譯天然需要理解上下文。比如屏幕上有很多按鈕有的是菜單、有的是正文標(biāo)簽、有的是快捷鍵提示翻譯時(shí)得區(qū)別對待這種判斷用規(guī)則寫起來非常痛苦但大模型幾乎不費(fèi)勁。至于成本和延遲我的判斷是在桌面輔助場景里能接受。1.3 先跑通最小閉環(huán)再想全自動(dòng)做這類項(xiàng)目最忌諱一上來就搞全自動(dòng)幾個(gè)模塊疊在一起相互干擾出了問題都不知道是誰的鍋。我的做法是先定一個(gè)“最小閉環(huán)”截取屏幕上目標(biāo)區(qū)域OCR識(shí)別出文本和坐標(biāo)把文本發(fā)給大模型讓它翻譯并返回結(jié)構(gòu)化結(jié)果再把譯文顯示在屏幕上。這一圈先跑通再考慮自動(dòng)點(diǎn)擊、自動(dòng)決策這些更“Agent”的能力。最小閉環(huán)有另一個(gè)好處它把各個(gè)模塊的邊界切得特別清晰截圖、識(shí)別、推理、渲染四個(gè)階段可以獨(dú)立調(diào)試。哪個(gè)環(huán)節(jié)慢了、哪個(gè)環(huán)節(jié)不準(zhǔn)日志一打就能定位。后面所有擴(kuò)展都是在這個(gè)閉環(huán)上疊加新能力而不是推倒重來。2. 實(shí)戰(zhàn)第一刀把屏幕變成可讀文本2.1 OCR引擎怎么選本地還是云端屏幕上的文字對計(jì)算機(jī)來說只是像素必須先轉(zhuǎn)成文本。OCR選型是這個(gè)項(xiàng)目最關(guān)鍵的第一步。我試了兩條路本地開源OCR引擎和云服務(wù)OCR。本地引擎的優(yōu)勢是免費(fèi)、離線、隱私安全適合在無人值守的情況下長時(shí)間跑也不用擔(dān)心數(shù)據(jù)出網(wǎng)。缺點(diǎn)是識(shí)別準(zhǔn)確率受界面清晰度、字體、背景干擾影響較大對少見小語種的支持也一般。云服務(wù)OCR識(shí)別精度更高、語種覆蓋廣但不光按調(diào)用量收費(fèi)還要走網(wǎng)絡(luò)請求延遲不穩(wěn)定而且截圖內(nèi)容要上傳到云端有些敏感窗口不適合這么做。我的選擇是本地引擎為主。因?yàn)榉g場景里的軟件界面通常是規(guī)整的UI文字字體清晰、背景干擾小本地引擎完全夠用。代碼層面我做了封裝以后想切云端服務(wù)只需要替換函數(shù)體不影響上層邏輯。2.2 截圖別整屏先定位再裁剪新手最容易踩的坑是上來就全屏截圖讓OCR識(shí)別整塊屏幕。全屏截圖有兩個(gè)問題一是識(shí)別區(qū)域大耗時(shí)加倍二是無關(guān)信息多容易把任務(wù)欄、桌面圖標(biāo)、其他窗口的內(nèi)容一起識(shí)別進(jìn)來污染后續(xù)的大模型上下文。正確的做法是先按窗口標(biāo)題定位目標(biāo)軟件的位置和大小只截取軟件窗口區(qū)域。上代碼# 下面的函數(shù)以占位模塊示意讀者可換成自己熟悉的截圖庫與窗口管理庫 import auto_gui as gui def grab_window(win_title): # 根據(jù)窗口標(biāo)題找到窗口在屏幕上的矩形區(qū)域 left, top, width, height gui.find_window(win_title) # 只截取目標(biāo)區(qū)域不做全屏截圖 img gui.capture_screen(region(left, top, width, height)) return img, (left, top)這個(gè)例子里的auto_gui是我對底層庫的占位封裝底層實(shí)現(xiàn)是一套跨平臺(tái)的鍵鼠和截圖工具讀者用自己熟悉的實(shí)現(xiàn)替換即可。關(guān)鍵是 get_window 和 capture_screen 這兩個(gè)動(dòng)作要拆開定位一次多次截圖時(shí)復(fù)用坐標(biāo)省掉重復(fù)查找窗口的開銷。實(shí)際使用中還發(fā)現(xiàn)一個(gè)隱藏問題很多筆記本電腦和Windows系統(tǒng)默認(rèn)開啟DPI縮放邏輯坐標(biāo)和物理坐標(biāo)不一致。截圖區(qū)域取對了OCR識(shí)別的內(nèi)容卻和實(shí)際屏幕對不上就是因?yàn)樽鴺?biāo)算的是邏輯值屏幕截的是物理像素。解決方式是在程序啟動(dòng)時(shí)聲明進(jìn)程感知DPI縮放確保兩個(gè)坐標(biāo)系對齊。這一點(diǎn)不改后面所有坐標(biāo)換算都是錯(cuò)位的越往后越難排查。2.3 OCR輸出為什么是“帶坐標(biāo)的文本”O(jiān)CR引擎的輸出往往不是一行行干凈文本而是“文本加坐標(biāo)加置信度”的結(jié)構(gòu)。一個(gè)識(shí)別條目的長這樣{ text: Open..., conf: 0.97, box: [[112, 88], [170, 88], [170, 104], [112, 104]] }這四個(gè)點(diǎn)分別是文本框的左上、右上、右下、左下坐標(biāo)足夠還原它在屏幕上的精確位置。這個(gè)結(jié)構(gòu)化輸出是后面一切操作的基礎(chǔ)顯示翻譯結(jié)果時(shí)知道把字貼到哪個(gè)位置點(diǎn)擊按鈕時(shí)知道往哪里點(diǎn)。但OCR有個(gè)現(xiàn)實(shí)問題它會(huì)把同一行文字切得七零八落。比如橫排菜單“File Edit View Help”可能被識(shí)別成四五個(gè)獨(dú)立條目因?yàn)镺CR是按“連通文字塊”切分的字塊之間空白太大就被拆開。這種情況下需要做“行合并”處理把中心點(diǎn)Y坐標(biāo)接近的文本塊歸為同一行再按X坐標(biāo)從左到右排序拼接成完整句子。合并算法不復(fù)雜但非常關(guān)鍵不合并的話大模型拿到的是碎塊翻譯出來的內(nèi)容就完全不可讀。同樣一個(gè)界面合并前和合并后大模型的理解質(zhì)量天差地別。合并后還能順帶做一件事通過文本內(nèi)容和坐標(biāo)關(guān)系粗略判斷元素類型。比如文本很短、Y軸排列均勻、X軸間距恒定多半是菜單或工具條文本較長且位置靠中心區(qū)域多半是正文。把這些元信息一并傳給大模型它翻譯時(shí)就知道哪些該保留原樣、哪些該意譯。3. 實(shí)戰(zhàn)第二刀Agent決策層與翻譯閉環(huán)3.1 Agent不是簡單包一層API很多人把Agent理解成“調(diào)大模型接口追問幾句”這是誤解。真正的Agent要讓模型看到當(dāng)前環(huán)境的狀態(tài)并根據(jù)目標(biāo)決定下一步行動(dòng)。在我的系統(tǒng)里這一步表現(xiàn)為把OCR識(shí)別出的整屏文本、坐標(biāo)、元素類型打包成結(jié)構(gòu)化的“屏幕快照”描述連同明確的任務(wù)說明一起發(fā)給大模型讓它返回不僅包含譯文、還包含操作建議的結(jié)構(gòu)化響應(yīng)。我設(shè)計(jì)的屏幕上下文模板長這樣你現(xiàn)在是一個(gè)桌面屏幕助手。 當(dāng)前屏幕上的文本條目如下每行包含序號(hào)、坐標(biāo)、元素類型和原文 1. [x86, y92, typemenu] File 2. [x150, y92, typemenu] Edit 3. [x290, y140, typebutton] Save As... 4. [x300, y200, typetext] Document body here 請完成 1. 將文本翻譯成簡體中文 2. 判斷每個(gè)條目更適合保留原樣還是翻譯 3. 輸出JSON格式為 {items:[{id:1,translation:文件,suggestion:translate}]} 不要輸出任何解釋。為什么要把坐標(biāo)也帶進(jìn)去因?yàn)樵谡鎸?shí)桌面上翻譯策略和位置強(qiáng)相關(guān)。菜單項(xiàng)翻譯成中文后如果位置沒變用戶還是能憑位置找到它按鈕里的英文可以翻譯但快捷鍵字母要保留。這些判斷沒有坐標(biāo)和元素類型是做不到的。3.2 大模型返回結(jié)果怎么解析才穩(wěn)調(diào)用大模型時(shí)有些參數(shù)值得特別注意。第一個(gè)是temperature翻譯和分類場景別開太高建議0.2左右讓輸出盡量穩(wěn)定第二個(gè)是輸出格式一次到位要求JSON但實(shí)際上模型偶爾還是會(huì)夾帶說明文字比如在JSON前面多一句“好的以下是結(jié)果”。這種時(shí)候不能直接json.loads得先清洗。清洗代碼我做了兩層import json import re def parse_model_output(raw): # 第一層如果直接能解析就直接返回 try: return json.loads(raw) except json.JSONDecodeError: pass # 第二層提取最外層大括號(hào)包裹的內(nèi)容 matched re.search(r\{.*\}, raw, re.S) if not matched: raise ValueError(模型輸出中未找到JSON片段) data json.loads(matched.group(0)) return data在實(shí)際運(yùn)行里這個(gè)兜底解析幫我擋住了大量偶發(fā)輸出異常。因?yàn)槟P湍呐卤粐?yán)格要求輸出JSON遇到復(fù)雜的屏幕內(nèi)容也可能擅自加個(gè)注釋。別指望模型100%聽話寫一層后處理比改十版提示詞更省事。關(guān)于調(diào)用大模型時(shí)的身份問題我統(tǒng)一用環(huán)境變量管理配置代碼里不硬編碼任何密鑰。項(xiàng)目里做了個(gè)輕量封裝調(diào)用方只需要盯著llm_client.generate(prompt)這個(gè)接口以后換模型服務(wù)商只需改這一個(gè)函數(shù)不用動(dòng)業(yè)務(wù)邏輯。3.3 把譯文“寫”回屏幕三種方式對比拿到大模型返回的JSON后要把譯文展示到屏幕上。這一步有三個(gè)可選方案我分別試過第一種是覆蓋率最低但最直觀的方式直接替換原界面文本內(nèi)容。但這要求能操作目標(biāo)軟件內(nèi)部UI組件多數(shù)情況根本拿不到句柄不現(xiàn)實(shí)。第二種是在原窗口上方疊一個(gè)透明置頂層把譯文按坐標(biāo)渲染成懸浮標(biāo)簽。我實(shí)際用的就是這種方式通過一個(gè)鼠標(biāo)穿透的透明窗口根據(jù)OCR返回的box坐標(biāo)畫出帶背景色的譯文文本用戶平時(shí)能看到譯文卻不影響點(diǎn)擊原窗口。做成自動(dòng)跟隨后光標(biāo)移到哪個(gè)按鈕譯文就浮在按鈕旁邊體驗(yàn)很像“屏幕自帶翻譯”。第三種是純剪貼板方案把整屏譯文記錄到剪貼板用戶需要時(shí)手動(dòng)粘貼。實(shí)現(xiàn)最簡單但最不自動(dòng)化我只在調(diào)試階段用過。透明懸浮層的實(shí)現(xiàn)邏輯不算復(fù)雜核心就是開一個(gè)無邊框、全屏、置頂、透明背景的窗口在對應(yīng)坐標(biāo)繪制文本。需要注意兩個(gè)細(xì)節(jié)一是窗口要設(shè)置成點(diǎn)擊穿透否則懸浮層會(huì)擋住鼠標(biāo)操作二是繪制前要把OCR坐標(biāo)轉(zhuǎn)換成懸浮層窗口的局部坐標(biāo)坐標(biāo)系不一致的話譯文會(huì)整體漂移。3.4 全流程管線怎么拼裝整條流水線的主循環(huán)長這樣import time def main_loop(win_title): while True: img, origin grab_window(win_title) lines ocr_engine.run(img) payload build_screen_context(lines, origin) result llm_client.generate(payload) parsed parse_model_output(result) render_translation(parsed, lines) time.sleep(2) # 避免高頻截圖與識(shí)別這個(gè)循環(huán)的節(jié)奏很重要。我最初沒有設(shè)置間隔連續(xù)跑幾個(gè)小時(shí)后CPU占用居高不下風(fēng)扇一直轉(zhuǎn)。加了一個(gè)2秒的sleep之后整體占用降到可以忽略的級別。翻譯場景不需要每秒刷新屏幕沒有變化時(shí)重復(fù)識(shí)別反而浪費(fèi)算力。更進(jìn)一步的做法是截圖前先算一下當(dāng)前幀和上一幀的差異無變化就跳過識(shí)別既省資源又避免閃爍。整套鏈路的延遲表現(xiàn)我在后面第4部分給出實(shí)測數(shù)據(jù)。4. 常見問題與排查技巧實(shí)錄4.1 高頻翻車事件速查表實(shí)戰(zhàn)過程中我遇到了不少問題整理成一張速查表給后來人排雷癥狀最常見原因解決方案截圖區(qū)域是黑屏或白屏目標(biāo)軟件用了GPU渲染普通截圖接口拿不到畫面改用支持DXGI的系統(tǒng)級屏幕捕獲把窗口置于前臺(tái)后再截OCR返回亂碼DPI縮放導(dǎo)致截圖坐標(biāo)和實(shí)際像素坐標(biāo)偏移進(jìn)程啟動(dòng)時(shí)聲明感知DPI統(tǒng)一坐標(biāo)基準(zhǔn)譯文位置漂移截圖畫布和渲染畫布坐標(biāo)系不同轉(zhuǎn)換坐標(biāo)前加一個(gè)偏移量補(bǔ)償在用戶可視區(qū)域內(nèi)校準(zhǔn)一次大模型輸出JSON解析失敗注意力漂移返回了多余說明溫度調(diào)低增加JSON清洗函數(shù)提取大括號(hào)片段解析點(diǎn)擊位置不準(zhǔn)窗口被移動(dòng)、縮放或最小化時(shí)仍用舊坐標(biāo)每次任務(wù)開始前重新定位窗口矩形區(qū)域模擬鍵盤輸入變成中文拼音系統(tǒng)處于中文輸入法狀態(tài)切換英文輸入模式或者直接用剪貼板粘貼譯文4.2 性能和延遲的實(shí)測數(shù)據(jù)這套系統(tǒng)到底跑多快我實(shí)測了一組數(shù)據(jù)環(huán)境是普通辦公電腦模型接口走本地服務(wù)階段首次實(shí)測耗時(shí)優(yōu)化后耗時(shí)優(yōu)化手段截圖約20ms約10ms限制截圖區(qū)域只截窗口范圍OCR識(shí)別約150-400ms約90-150ms關(guān)閉角度檢測按區(qū)域緩存結(jié)果大模型推理約800-2000ms約500-800ms精簡上下文結(jié)構(gòu)化輸出渲染懸浮層約5ms約5ms保持窗口常駐只改文本內(nèi)容優(yōu)化后整條鏈路在1秒左右完成對于“把鼠標(biāo)移到按鈕上顯示譯文”這種場景基本夠用。再想快就得靠增量識(shí)別了屏幕沒變化就不重新識(shí)別只處理變化的窗口區(qū)域。這塊我作為后續(xù)擴(kuò)展方向目前還沒有做進(jìn)主循環(huán)。4.3 別讓Agent干不該干的事Agent一旦能自己點(diǎn)擊和輸入安全邊界就變得特別重要。我這套系統(tǒng)只做“讀屏和翻譯”但代碼里仍然保留了必要的防呆機(jī)制。每次模擬點(diǎn)擊之前程序會(huì)先在日志里記錄“準(zhǔn)備點(diǎn)擊坐標(biāo)”如果目標(biāo)坐標(biāo)漂移到窗口區(qū)域之外直接中止動(dòng)作并告警。系統(tǒng)運(yùn)行期間所有動(dòng)作都會(huì)寫入本地日志文件包括截圖的縮略信息和點(diǎn)擊的坐標(biāo)方便后期回溯。另外我在主流程里設(shè)計(jì)了“人工確認(rèn)模式”首次點(diǎn)擊或輸入之前先輸出將要做的操作等待幾秒確認(rèn)無誤后再繼續(xù)。這種約束看起來繁瑣但對桌面自動(dòng)化來說十分必要——?jiǎng)幼饕坏╁e(cuò)點(diǎn)輕則誤操作軟件重則把不該提交的內(nèi)容提交上去。還值得提醒的是這類自動(dòng)化只能用于提升個(gè)人效率和輔助學(xué)習(xí)。界面識(shí)別、鍵鼠模擬都必須在正常授權(quán)的軟件環(huán)境中使用不要拿它去繞過任何系統(tǒng)的正常流程和安全機(jī)制合規(guī)是底線。5. 這套思路還能擴(kuò)展到哪些場景5.1 從“翻譯屏幕”到“表單數(shù)據(jù)搬運(yùn)”屏幕翻譯只是AI Agent桌面自動(dòng)化的一個(gè)切片。同樣的“感知—決策—行動(dòng)”閉環(huán)換個(gè)提示詞模板就是另一個(gè)工具。比如把OCR識(shí)別到的單據(jù)數(shù)據(jù)交給大模型按規(guī)則抽取字段再寫入Excel或業(yè)務(wù)系統(tǒng)就變成了數(shù)據(jù)搬運(yùn)工具。我剛跑通翻譯場景后很快把同一套管線復(fù)用到某個(gè)模擬系統(tǒng)里做批量錄入識(shí)別表格內(nèi)容大模型抽字段自動(dòng)化填表整個(gè)流程不需要任何接口對接純靠模擬人工操作完成。5.2 給Agent加“短時(shí)記憶”做連續(xù)操作目前的系統(tǒng)是無狀態(tài)的每次屏幕識(shí)別都是一次獨(dú)立任務(wù)Agent不知道上一個(gè)操作是什么。要處理連續(xù)流程比如“先點(diǎn)開設(shè)置頁再找到語言選項(xiàng)再切到中文”就必須引入狀態(tài)管理。我計(jì)劃把每次屏幕快照和操作結(jié)果壓縮成一條短期記憶喂進(jìn)下一輪上下文讓Agent的決策基于歷史而不是只看當(dāng)下一幀。這一步做完Agent從“翻譯器”才真正變成“辦事員”。5.3 給新手的三條建議如果這個(gè)項(xiàng)目對你有啟發(fā)想自己動(dòng)手做一遍我的建議很明確先手抄一遍真實(shí)流程再寫代碼。把你想自動(dòng)化的任務(wù)手動(dòng)操作幾十遍記錄每一步屏幕有什么變化、光標(biāo)往哪走這些記錄就是你Agent的“需求文檔”。然后按照“最小閉環(huán)”的原則把截圖、OCR、模型調(diào)用、顯示渲染四段分別跑通再拼裝。別跳步別一上來就自動(dòng)點(diǎn)擊每一步都能看到中間結(jié)果開發(fā)心態(tài)會(huì)穩(wěn)得多。最后說句實(shí)在話。跑完這個(gè)項(xiàng)目我最深的體會(huì)是AI Agent這條鏈路里推理模型反而是最省心的部分真正磨人的是“讀得準(zhǔn)、算得對、下得穩(wěn)”。屏幕識(shí)別精度不夠大模型再聰明也翻譯錯(cuò)坐標(biāo)轉(zhuǎn)換出問題譯文全貼在錯(cuò)誤位置點(diǎn)擊動(dòng)作不夠穩(wěn)整套自動(dòng)化就變成搗亂機(jī)器。想玩桌面自動(dòng)化的朋友先把這些基礎(chǔ)環(huán)節(jié)夯結(jié)實(shí)再考慮加多少智能這條路走起來會(huì)順暢很多。我自己下一步打算做內(nèi)容變更檢測和短時(shí)記憶模塊后面有階段性成果再單獨(dú)寫一篇分享。