
把大模型从对话框里捞出来让它自己在桌面上看屏幕、点鼠标、敲键盘这件事我惦记很久了。最近终于把这条链路跑通截图识别屏幕内容交给大模型做翻译决策再模拟人工操作把结果写回界面整个流程不依赖任何软件二次开发纯靠“看见—思考—动手”的闭环完成。这个项目我起名叫“屏幕翻译管家”核心就是AI Agent接入桌面自动化。这篇文章把完整思路、关键代码、选型理由和踩坑记录全部摊开讲适合想从“只会调API做对话”进阶到“能让AI真正干活”的开发者也适合正在研究OCR、桌面自动化和智能体应用的同学参考。1. 从对话框到工作流Agent为什么要“动手”1.1 大模型的手脚为什么这么短把大模型关在对话框里它有再强的推理能力能输出的也只是文字。你要它帮你翻译一段外文软件界面它只能给你译文还得你自己切回窗口、照着译文去点按钮。这种模式本质上就是“只带眼睛带脑子的文本顾问”手和脚还是你自己的。AI Agent和普通API调用的分水岭就在这里Agent要在真实的运行环境中做闭环决策和执行。感知、规划、行动三个环节缺一不可。桌面自动化恰好提供了“行动”这一环的落地场景——通过模拟键鼠操作让Agent能在你已经打开的任何软件里像人一样看屏幕、移动光标、点击按钮、输入内容。这套玩法并不新鲜以前是企业级RPA的事用规则脚本硬编码坐标和流程现在有了大模型最难写的“根据屏幕内容决定下一步做什么”直接从编写规则变成了自然语言描述门槛一下子降下来了。1.2 三条技术路线为什么最后选了Agent加OCR做桌面自动化翻译路线不是只有一条。我把主流方案整理了一下路线实现方式优点缺点固定脚本自动化预设坐标、图像模板匹配、定时触发速度快、成本低、稳定界面一变就崩没法处理异常情况OCR加规则引擎用OCR读屏幕文字用if-else规则决定翻译和操作能感知真实文本比纯坐标健壮规则需要人工逐条枚举场景一多就维护不动OCR加大模型AgentOCR读屏大模型根据屏幕上下文动态决策适应界面变化能处理复杂语义有模型调用成本响应延迟相对高我最终选的是第三条路线理由很直接这个项目的核心目标是“翻译”而翻译天然需要理解上下文。比如屏幕上有很多按钮有的是菜单、有的是正文标签、有的是快捷键提示翻译时得区别对待这种判断用规则写起来非常痛苦但大模型几乎不费劲。至于成本和延迟我的判断是在桌面辅助场景里能接受。1.3 先跑通最小闭环再想全自动做这类项目最忌讳一上来就搞全自动几个模块叠在一起相互干扰出了问题都不知道是谁的锅。我的做法是先定一个“最小闭环”截取屏幕上目标区域OCR识别出文本和坐标把文本发给大模型让它翻译并返回结构化结果再把译文显示在屏幕上。这一圈先跑通再考虑自动点击、自动决策这些更“Agent”的能力。最小闭环有另一个好处它把各个模块的边界切得特别清晰截图、识别、推理、渲染四个阶段可以独立调试。哪个环节慢了、哪个环节不准日志一打就能定位。后面所有扩展都是在这个闭环上叠加新能力而不是推倒重来。2. 实战第一刀把屏幕变成可读文本2.1 OCR引擎怎么选本地还是云端屏幕上的文字对计算机来说只是像素必须先转成文本。OCR选型是这个项目最关键的第一步。我试了两条路本地开源OCR引擎和云服务OCR。本地引擎的优势是免费、离线、隐私安全适合在无人值守的情况下长时间跑也不用担心数据出网。缺点是识别准确率受界面清晰度、字体、背景干扰影响较大对少见小语种的支持也一般。云服务OCR识别精度更高、语种覆盖广但不光按调用量收费还要走网络请求延迟不稳定而且截图内容要上传到云端有些敏感窗口不适合这么做。我的选择是本地引擎为主。因为翻译场景里的软件界面通常是规整的UI文字字体清晰、背景干扰小本地引擎完全够用。代码层面我做了封装以后想切云端服务只需要替换函数体不影响上层逻辑。2.2 截图别整屏先定位再裁剪新手最容易踩的坑是上来就全屏截图让OCR识别整块屏幕。全屏截图有两个问题一是识别区域大耗时加倍二是无关信息多容易把任务栏、桌面图标、其他窗口的内容一起识别进来污染后续的大模型上下文。正确的做法是先按窗口标题定位目标软件的位置和大小只截取软件窗口区域。上代码# 下面的函数以占位模块示意读者可换成自己熟悉的截图库与窗口管理库 import auto_gui as gui def grab_window(win_title): # 根据窗口标题找到窗口在屏幕上的矩形区域 left, top, width, height gui.find_window(win_title) # 只截取目标区域不做全屏截图 img gui.capture_screen(region(left, top, width, height)) return img, (left, top)这个例子里的auto_gui是我对底层库的占位封装底层实现是一套跨平台的键鼠和截图工具读者用自己熟悉的实现替换即可。关键是 get_window 和 capture_screen 这两个动作要拆开定位一次多次截图时复用坐标省掉重复查找窗口的开销。实际使用中还发现一个隐藏问题很多笔记本电脑和Windows系统默认开启DPI缩放逻辑坐标和物理坐标不一致。截图区域取对了OCR识别的内容却和实际屏幕对不上就是因为坐标算的是逻辑值屏幕截的是物理像素。解决方式是在程序启动时声明进程感知DPI缩放确保两个坐标系对齐。这一点不改后面所有坐标换算都是错位的越往后越难排查。2.3 OCR输出为什么是“带坐标的文本”OCR引擎的输出往往不是一行行干净文本而是“文本加坐标加置信度”的结构。一个识别条目的长这样{ text: Open..., conf: 0.97, box: [[112, 88], [170, 88], [170, 104], [112, 104]] }这四个点分别是文本框的左上、右上、右下、左下坐标足够还原它在屏幕上的精确位置。这个结构化输出是后面一切操作的基础显示翻译结果时知道把字贴到哪个位置点击按钮时知道往哪里点。但OCR有个现实问题它会把同一行文字切得七零八落。比如横排菜单“File Edit View Help”可能被识别成四五个独立条目因为OCR是按“连通文字块”切分的字块之间空白太大就被拆开。这种情况下需要做“行合并”处理把中心点Y坐标接近的文本块归为同一行再按X坐标从左到右排序拼接成完整句子。合并算法不复杂但非常关键不合并的话大模型拿到的是碎块翻译出来的内容就完全不可读。同样一个界面合并前和合并后大模型的理解质量天差地别。合并后还能顺带做一件事通过文本内容和坐标关系粗略判断元素类型。比如文本很短、Y轴排列均匀、X轴间距恒定多半是菜单或工具条文本较长且位置靠中心区域多半是正文。把这些元信息一并传给大模型它翻译时就知道哪些该保留原样、哪些该意译。3. 实战第二刀Agent决策层与翻译闭环3.1 Agent不是简单包一层API很多人把Agent理解成“调大模型接口追问几句”这是误解。真正的Agent要让模型看到当前环境的状态并根据目标决定下一步行动。在我的系统里这一步表现为把OCR识别出的整屏文本、坐标、元素类型打包成结构化的“屏幕快照”描述连同明确的任务说明一起发给大模型让它返回不仅包含译文、还包含操作建议的结构化响应。我设计的屏幕上下文模板长这样你现在是一个桌面屏幕助手。 当前屏幕上的文本条目如下每行包含序号、坐标、元素类型和原文 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. 判断每个条目更适合保留原样还是翻译 3. 输出JSON格式为 {items:[{id:1,translation:文件,suggestion:translate}]} 不要输出任何解释。为什么要把坐标也带进去因为在真实桌面上翻译策略和位置强相关。菜单项翻译成中文后如果位置没变用户还是能凭位置找到它按钮里的英文可以翻译但快捷键字母要保留。这些判断没有坐标和元素类型是做不到的。3.2 大模型返回结果怎么解析才稳调用大模型时有些参数值得特别注意。第一个是temperature翻译和分类场景别开太高建议0.2左右让输出尽量稳定第二个是输出格式一次到位要求JSON但实际上模型偶尔还是会夹带说明文字比如在JSON前面多一句“好的以下是结果”。这种时候不能直接json.loads得先清洗。清洗代码我做了两层import json import re def parse_model_output(raw): # 第一层如果直接能解析就直接返回 try: return json.loads(raw) except json.JSONDecodeError: pass # 第二层提取最外层大括号包裹的内容 matched re.search(r\{.*\}, raw, re.S) if not matched: raise ValueError(模型输出中未找到JSON片段) data json.loads(matched.group(0)) return data在实际运行里这个兜底解析帮我挡住了大量偶发输出异常。因为模型哪怕被严格要求输出JSON遇到复杂的屏幕内容也可能擅自加个注释。别指望模型100%听话写一层后处理比改十版提示词更省事。关于调用大模型时的身份问题我统一用环境变量管理配置代码里不硬编码任何密钥。项目里做了个轻量封装调用方只需要盯着llm_client.generate(prompt)这个接口以后换模型服务商只需改这一个函数不用动业务逻辑。3.3 把译文“写”回屏幕三种方式对比拿到大模型返回的JSON后要把译文展示到屏幕上。这一步有三个可选方案我分别试过第一种是覆盖率最低但最直观的方式直接替换原界面文本内容。但这要求能操作目标软件内部UI组件多数情况根本拿不到句柄不现实。第二种是在原窗口上方叠一个透明置顶层把译文按坐标渲染成悬浮标签。我实际用的就是这种方式通过一个鼠标穿透的透明窗口根据OCR返回的box坐标画出带背景色的译文文本用户平时能看到译文却不影响点击原窗口。做成自动跟随后光标移到哪个按钮译文就浮在按钮旁边体验很像“屏幕自带翻译”。第三种是纯剪贴板方案把整屏译文记录到剪贴板用户需要时手动粘贴。实现最简单但最不自动化我只在调试阶段用过。透明悬浮层的实现逻辑不算复杂核心就是开一个无边框、全屏、置顶、透明背景的窗口在对应坐标绘制文本。需要注意两个细节一是窗口要设置成点击穿透否则悬浮层会挡住鼠标操作二是绘制前要把OCR坐标转换成悬浮层窗口的局部坐标坐标系不一致的话译文会整体漂移。3.4 全流程管线怎么拼装整条流水线的主循环长这样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) # 避免高频截图与识别这个循环的节奏很重要。我最初没有设置间隔连续跑几个小时后CPU占用居高不下风扇一直转。加了一个2秒的sleep之后整体占用降到可以忽略的级别。翻译场景不需要每秒刷新屏幕没有变化时重复识别反而浪费算力。更进一步的做法是截图前先算一下当前帧和上一帧的差异无变化就跳过识别既省资源又避免闪烁。整套链路的延迟表现我在后面第4部分给出实测数据。4. 常见问题与排查技巧实录4.1 高频翻车事件速查表实战过程中我遇到了不少问题整理成一张速查表给后来人排雷症状最常见原因解决方案截图区域是黑屏或白屏目标软件用了GPU渲染普通截图接口拿不到画面改用支持DXGI的系统级屏幕捕获把窗口置于前台后再截OCR返回乱码DPI缩放导致截图坐标和实际像素坐标偏移进程启动时声明感知DPI统一坐标基准译文位置漂移截图画布和渲染画布坐标系不同转换坐标前加一个偏移量补偿在用户可视区域内校准一次大模型输出JSON解析失败注意力漂移返回了多余说明温度调低增加JSON清洗函数提取大括号片段解析点击位置不准窗口被移动、缩放或最小化时仍用旧坐标每次任务开始前重新定位窗口矩形区域模拟键盘输入变成中文拼音系统处于中文输入法状态切换英文输入模式或者直接用剪贴板粘贴译文4.2 性能和延迟的实测数据这套系统到底跑多快我实测了一组数据环境是普通办公电脑模型接口走本地服务阶段首次实测耗时优化后耗时优化手段截图约20ms约10ms限制截图区域只截窗口范围OCR识别约150-400ms约90-150ms关闭角度检测按区域缓存结果大模型推理约800-2000ms约500-800ms精简上下文结构化输出渲染悬浮层约5ms约5ms保持窗口常驻只改文本内容优化后整条链路在1秒左右完成对于“把鼠标移到按钮上显示译文”这种场景基本够用。再想快就得靠增量识别了屏幕没变化就不重新识别只处理变化的窗口区域。这块我作为后续扩展方向目前还没有做进主循环。4.3 别让Agent干不该干的事Agent一旦能自己点击和输入安全边界就变得特别重要。我这套系统只做“读屏和翻译”但代码里仍然保留了必要的防呆机制。每次模拟点击之前程序会先在日志里记录“准备点击坐标”如果目标坐标漂移到窗口区域之外直接中止动作并告警。系统运行期间所有动作都会写入本地日志文件包括截图的缩略信息和点击的坐标方便后期回溯。另外我在主流程里设计了“人工确认模式”首次点击或输入之前先输出将要做的操作等待几秒确认无误后再继续。这种约束看起来繁琐但对桌面自动化来说十分必要——动作一旦错点轻则误操作软件重则把不该提交的内容提交上去。还值得提醒的是这类自动化只能用于提升个人效率和辅助学习。界面识别、键鼠模拟都必须在正常授权的软件环境中使用不要拿它去绕过任何系统的正常流程和安全机制合规是底线。5. 这套思路还能扩展到哪些场景5.1 从“翻译屏幕”到“表单数据搬运”屏幕翻译只是AI Agent桌面自动化的一个切片。同样的“感知—决策—行动”闭环换个提示词模板就是另一个工具。比如把OCR识别到的单据数据交给大模型按规则抽取字段再写入Excel或业务系统就变成了数据搬运工具。我刚跑通翻译场景后很快把同一套管线复用到某个模拟系统里做批量录入识别表格内容大模型抽字段自动化填表整个流程不需要任何接口对接纯靠模拟人工操作完成。5.2 给Agent加“短时记忆”做连续操作目前的系统是无状态的每次屏幕识别都是一次独立任务Agent不知道上一个操作是什么。要处理连续流程比如“先点开设置页再找到语言选项再切到中文”就必须引入状态管理。我计划把每次屏幕快照和操作结果压缩成一条短期记忆喂进下一轮上下文让Agent的决策基于历史而不是只看当下一帧。这一步做完Agent从“翻译器”才真正变成“办事员”。5.3 给新手的三条建议如果这个项目对你有启发想自己动手做一遍我的建议很明确先手抄一遍真实流程再写代码。把你想自动化的任务手动操作几十遍记录每一步屏幕有什么变化、光标往哪走这些记录就是你Agent的“需求文档”。然后按照“最小闭环”的原则把截图、OCR、模型调用、显示渲染四段分别跑通再拼装。别跳步别一上来就自动点击每一步都能看到中间结果开发心态会稳得多。最后说句实在话。跑完这个项目我最深的体会是AI Agent这条链路里推理模型反而是最省心的部分真正磨人的是“读得准、算得对、下得稳”。屏幕识别精度不够大模型再聪明也翻译错坐标转换出问题译文全贴在错误位置点击动作不够稳整套自动化就变成捣乱机器。想玩桌面自动化的朋友先把这些基础环节夯结实再考虑加多少智能这条路走起来会顺畅很多。我自己下一步打算做内容变更检测和短时记忆模块后面有阶段性成果再单独写一篇分享。