
如果你正在用 deepseek harness 这类本地命令行工具跑 Agent 任务一定会遇到一个很现实的问题它能读文件、能敲命令但看不到屏幕。我做了一个识屏插件把它们补上——在 Agent 分析问题时先截屏、OCR、再把屏幕内容整理成上下文传给模型让工具能“看见”正在运行的页面报错、聊天记录、PDF 内容或桌面应用状态。这篇文章先把这套插件的思路和踩坑拆开讲适合已经跑过 harness 基础任务、想给它增加屏幕感知能力的人。我不会把它包装成一套“官方插件规范”因为 deepseek harness 本身在社区里就有多种分支用法。更实际的做法是把识屏能力做成一个独立脚本再通过 harness 的 command 工具或自定义插件机制调用。下面按实际落地顺序拆一遍解决什么问题、需要什么环境、核心代码怎么写、接口怎么接、怎么验证、常见报错怎么排查。1. 识屏插件到底在解决什么问题1.1 deepseek harness 是什么为什么需要识屏deepseek harness 在社区里一般指的是一类把 DeepSeek 模型接入本地 Agent 和 CLI 工具的工程化框架。它把模型能力包进一个可以在终端、桌面端运行的壳里你可以给它配置模型、API 地址、工作目录、任务规则然后它像命令行助手一样帮你改代码、读日志、执行命令。它的定位和常见的 Codex Harness 有点像只是后端换成了 DeepSeek 系列模型。这类工具最常见的使用姿势是在终端里跑一个 Agent 任务比如“查看当前项目有什么问题并修复”harness 会自己调用文件读取、命令执行最后给出修改建议或直接改代码。这种模式处理纯文本任务很顺但有一个明显盲区它看不到屏幕。很多开发场景里问题恰恰只出现在屏幕上浏览器控制台里一个报错、某个配置页面上的状态、PDF 里的一页数据、聊天工具里别人发截图中的文字。如果 harness 没有识屏能力就只能靠你手动复制内容再塞过去。识屏插件就是解决这个断层的让 Agent 先截屏再把屏幕上的文字或图像信息转换成模型能理解的输入。1.2 适合哪些场景我实际测试下来最值得用识屏插件的场景有几类网页或桌面应用里的报错。浏览器页面、开发者工具、Electron 应用窗口里经常有错误提示手动复制既长又乱截图让 OCR 提取更快。只读文件或图片型内容。比如 PDF 扫描件、网页截图、聊天图片Agent 直接读不到识屏可以先把文字捞出来。让模型理解“当前界面状态”。比如想让 Agent 帮忙看某个后台管理页面哪些按钮可用、哪些区域报错截图加 OCR 后的上下文比纯命令行描述更准确。跨应用信息汇总。屏幕上同时开着多个窗口识屏插件可以按设定只抓指定区域把关键信息聚合成一份上下文。这些场景的共同点是信息不是以文件形式出现在工作目录里的而是以像素形式出现在屏幕上。识屏插件本质上就是把屏幕变成 Agent 可以读取的一个“输入源”。1.3 两种识屏路线OCR 文本优先图片直传兜底这里先说明一个关键判断DeepSeek 接口是否支持图片输入取决于你配置的模型和 API 版本。如果模型不支持视觉直接把截图传过去会被拒绝或者得到无意义的输出。所以在设计插件时我把路线分成两条主路线截图后用 OCR 引擎把屏幕上的文字提取成纯文本再把文本按板块整理成上下文发给模型。兜底路线如果模型支持视觉输入则直接按 OpenAI 兼容的图片消息格式上传截图如果模型不支持只能走 OCR。这样做的好处是插件在纯文本接口下也能跑不至于因为模型不支持图片就整个报废。实际落地时我建议默认先做 OCR 路线图像直传作为可选功能开关由配置控制。2. 动手前先确认环境2.1 本地运行依赖写这个插件前我先把环境确认了一遍避免后面调试时在工具链上反复折腾。整体依赖分三块harness 运行环境、调用 DeepSeek 接口所需配置、屏幕采集和 OCR 工具。如果你用的是社区里常见的 deepseek harness 桌面版或 CLI 版本一般需要先装 Node.js 和 pnpm然后通过 pnpm 安装或启动相关服务。常见命令像pnpm install、pnpm dev、pnpm dsh web这类。这里要注意每个版本的命令可能不一样原始材料里出现过“卡在 pnpm dsh web”的讨论说明启动阶段确实容易遇到网络或依赖卡顿后面第六节我会专门讲排查思路。识屏插件本身不一定要和 harness 进程深度绑定可以做成独立脚本然后通过 harness 的 command 工具调用。这样做的好处是插件出问题不会拖垮主进程也方便单独调试。2.2 DeepSeek API 的基本调用方式调用 DeepSeek 模型时最常见的做法是使用 OpenAI 兼容的接口设置base_url、api_key、model然后发 chat completion 请求。deepseek harness 通常也会在配置里让你填 API Key 和模型名。写识屏插件时我们要复用这套配置不建议在插件里写死 Key而是从 harness 配置或环境变量读取。如果模型接口在 messages 里支持附加图片内容参数可能是image_url或type: image_url这种格式如果不支持就需要把 OCR 文本放在user消息里。这条要在调试前先确认清楚否则很容易出现“接口能通但识屏内容没传上去”的问题。2.3 屏幕读取和 OCR 工具选择屏幕读取可以分成两步截图和文字识别。截图这一步最容易用的是系统自带命令。macOS 可以用screencaptureWindows 可以用 PowerShell 的截图能力Linux 桌面可以用gnome-screenshot或importImageMagick。如果你希望插件跨平台更好的方式是调用 Python 的pyautogui、mss这类库它们统一了截图逻辑缺点是需要装 Python 依赖。OCR 引擎方面常见选择是 Tesseract。它有命令行支持多语言训练数据适合快速验证。如果你的屏幕内容包含中文、英文、代码、数字混排建议同时安装中英文语言包并在初始化时指定中文和英文。除了 TesseractPaddleOCR 对中文和复杂文本的识别率通常更好但依赖更重安装成本更高。项目刚起步时用 Tesseract 足够验证流程等确实遇到识别率瓶颈再切换到 PaddleOCR。2.4 环境检查清单开始写代码前先按这个清单检查一遍检查项需要确认的内容如果缺失会怎样Node.js 和 pnpm版本能满足 harness 安装要求安装依赖失败或启动服务卡住DeepSeek API Key配置好且余额充足请求返回 401 或 402截图工具系统命令或 Python 库可用截屏步骤直接报错OCR 工具Tesseract 命令行可用语言包完整识别结果为空或乱码输出目录插件有权限写入截图和日志任务卡住但无清晰报错这一轮检查其实花不了多少时间但能省掉后面一半的排查时间。我一般会用一个最小脚本逐项验证先截一张屏再 OCR 这条截图确认输出文本正常然后再进入插件集成。3. 插件的核心实现截图、OCR、组装上下文3.1 插件最小流程识屏插件的核心流程可以拆成四步采集调用截图工具保存当前屏幕或指定区域到临时文件。识别对截图做 OCR输出带坐标的文本块或纯文本。整理把文本按位置、长度、标题过滤去掉明显无用的内容。传递把整理后的上下文拼进 user 消息发送给 DeepSeek 接口。这个流程写起来不复杂但每一步都有细节。比如截图保存格式用 PNG 比 JPEG 更适合 OCR因为压缩损失更少又比如 OCR 前可以把图片先放大一倍小字号文字识别率会明显变好。这些都属于做完一轮测试后才会注意到的细节。3.2 示例代码截屏加 OCR下面给一个最小示例用 Python 做截屏和 OCR输出一段标准文本。这个脚本不是固定模板只是为了把流程讲清楚落地时要根据你的系统路径和工具位置调整。import subprocess import sys import os import tempfile def capture_screen(output_path: str): system sys.platform if system darwin: subprocess.run([screencapture, -x, output_path], checkTrue) elif system win32: # Windows 下可以用 PowerShell 截图这里保留扩展点 subprocess.run( [powershell, -Command, fAdd-Type -AssemblyName System.Windows.Forms; f$b [System.Windows.Forms.SystemInformation]::VirtualScreen; f$bmp New-Object System.Drawing.Bitmap($b.Width, $b.Height); f$g [System.Drawing.Graphics]::FromImage($bmp); f$g.CopyFromScreen($b.Left, $b.Top, 0, 0, $bmp.Size); f$bmp.Save({output_path})], checkTrue) else: subprocess.run([gnome-screenshot, -f, output_path], checkTrue) def ocr_image(image_path: str) - str: result subprocess.run( [tesseract, image_path, stdout, -l, chi_simeng], capture_outputTrue, textTrue ) if result.returncode ! 0: return return result.stdout.strip() def main(): tmp_dir tempfile.mkdtemp(prefixscreen_plugin_) image_path os.path.join(tmp_dir, screen.png) capture_screen(image_path) text ocr_image(image_path) print(text) if __name__ __main__: main()capture_screen负责截图ocr_image负责识别。这里的screencapture、gnome-screenshot是系统自带的命令Windows 分支用 PowerShell 调取虚拟屏幕。实际测试时如果 OCR 结果为空优先检查截图文件是否生成、tesseract 的语言包是否完整而不是先调模型接口。3.3 把识屏结果转换成模型能看懂的消息拿到 OCR 文本后不能直接扔给模型就完事。屏幕上的内容往往很杂地址栏、按钮、广告、列表、弹窗混在一起。直接全量传过去既浪费 token又会干扰模型理解重点。我习惯把 OCR 结果简单分块标题区文本通常是字号较大、较靠上的内容。正文区文本中间主要区域的文字。报错信息文本包含 error、failed、Exception、WARN 等关键字的内容优先保留。无意义文本太短的单字符、纯数字、重复内容直接过滤。插件里可以只做轻量过滤比如按行长度过滤掉过短的片段按关键词保留报错类型文本再按固定格式拼成[屏幕内容开始] 区域: 顶部 文本: ... 区域: 报错弹窗 文本: ... [屏幕内容结束]这种格式的好处是模型能一眼看到这是一个结构化的屏幕快照而不是一段杂乱的 OCR 输出。3.4 注册到 harness 的命令或插件目录具体怎么注册取决于你用的 deepseek harness 的版本。有的版本支持在配置里声明自定义命令有的版本支持插件目录还有的版本主要靠命令行直接调用。因为原始材料里没有给出固定接口我建议先走最通用的方案把识屏脚本做成可执行命令然后在 harness 的任务描述里告诉模型“当需要查看屏幕内容时运行screen-read命令”。这样模型就能在任务里自发调用识屏能力。你不需要改 harness 内核只需要保证脚本有可执行权限。脚本输出结果能通过标准输出返回给 harness。脚本执行超时时间不要设太短截图和 OCR 第一次运行时可能比较慢。注意这里不要一上来就做成后台监听屏幕的自启动服务先用命令模式跑通再考虑要不要常驻。4. 与 DeepSeek 接口对接的正确姿势4.1 使用 OpenAI 兼容端点调用DeepSeek 的 API 调用方式对开发者来说很友好基本沿用 OpenAI 格式。调用时要注意base_url、api_key和模型名这三个参数任何一项配置错都会导致请求失败。如果在 harness 里已经配置好了 DeepSeek 接口识屏插件最好直接复用同一套配置避免出现“harness 能调用模型、插件却要用另一个 Key 或另一个模型”的割裂问题。我实际遇到过这种情况harness 主配置用的是某个中继地址插件里却写了官方接口地址结果一个能通一个不能通。统一从环境变量或共享配置文件读取是更稳的做法。4.2 文本模式下如何处理 OCR 结果如果模型接口不支持图片识屏插件就只能在消息文本里传 OCR 结果。这里有一个重要的 token 控制问题屏幕 OCR 出来的文字有时候非常多尤其是开了很多窗口、网页内容密集的情况下几千 token 是常有的事。我会在插件里加两个限制最大字符数默认只保留前 4000 到 6000 个字符超出部分提示“后续内容未截取”。最大行数如果行数太多优先保留包含 error、failed、warn、exception 等关键词的行。这样做并不是为了演示而是因为长文本会推高接口成本还可能把模型注意力带偏。先解决核心问题需要更多细节时再让模型主动调用识屏命令获取更详细内容。4.3 图片直传模式的限制与注意事项如果你的 DeepSeek 接口支持视觉模型可以尝试把截图以 base64 形式塞进 messages。示意结构如下{ model: your_vision_model, messages: [ { role: user, content: [ {type: text, text: 请查看这张截图列出所有报错信息。}, {type: image_url, image_url: {url: data:image/png;base64,...}} ] } ] }但要注意几点支持图片输入是模型能力不是所有 DeepSeek 模型都默认支持必须提前看接口文档。截图文件可能很大base64 之后更大要注意接口上传限制。图片直传的 token 消耗通常按图片尺寸或视觉 token 计算成本比纯文本高。如果接口不支持图片会返回类似“不支持的图片类型”或直接 400不要硬扛切回 OCR 文本模式。4.4 超时、重试和 token 控制调用模型接口时识屏插件最容易犯的错是没设超时。截图和 OCR 本身就可能耗时 3 到 10 秒再加上模型推理 10 到 30 秒一个完整请求可能要十几秒甚至更久。如果不设超时任务会一直挂着看起来像卡死。我建议至少设置三档控制请求超时比如 60 秒或 120 秒根据任务复杂度调整。重试次数遇到网络抖动或 429 限流时自动重试 2 次。最大输出 token限制模型回复长度避免长任务下输出无限膨胀。以下参数只是示例实际以你的接口为准。{ timeout: 60000, max_retries: 2, max_tokens: 2048 }调试时可以把日志打到控制台确认每次请求实际耗时、token 用量和返回状态。这样能更早发现问题而不是等到批量任务时才崩溃。5. 验证和调优从单次识屏到闭环任务5.1 第一步单独验证截图和 OCR不要一上来就把插件接进 harness。先把两段能力独立验证运行截图命令检查生成的图片是否能正常打开分辨率是否符合预期。对一张已知包含文字的图片运行 OCR检查识别结果是否和原图文字一致。这一步能定位大部分基础问题。我见过很多所谓“插件不工作”的情况其实只是截图工具没权限访问屏幕或者 tesseract 语言包没装好。基础能力没验证后面全是在瞎排查。5.2 第二步验证上下文是否正确传入模型插件生成好上下文后先手动构造一个请求发给 DeepSeek 接口看模型能不能根据 OCR 文本回答问题。比如把“屏幕上有几个 error 关键词”作为问题看它是否答对。这一步要确认三件事消息结构对不对、上下文是否完整传了过去、模型能理解格式化后的屏幕快照。如果模型回答得很空洞大概率是 OCR 文本太乱或者传参格式有问题。5.3 第三步在 harness 里跑真实任务基础验证通过后再把识屏脚本注册进 harness。第一次跑建议用简单任务比如“读取屏幕内容找出所有报错关键词。”“屏幕上的第一段文字是什么”这些任务反馈快也容易判断结果是否合理。等这些简单任务稳定了再进入更复杂的场景比如让模型根据屏幕内容修改代码、总结页面状态或生成操作步骤。5.4 参数调优清晰度、语言、窗口、过滤规则实际测试中有几个参数值得反复调参数影响调整建议截图分辨率小字号文字识别率截图前可放大屏幕或对图片做缩放增强OCR 语言包中英文混排准确率优先使用chi_simeng组合识别区域避免无关窗口干扰只截取当前窗口或指定坐标区域上下文长度token 消耗和模型注意力先保留 4000 字以内按需扩展行过滤规则减少噪声保留 error、warn、exception 等关键词行这里有一个判断标准如果模型能准确说出屏幕上的报错信息和大概位置说明上下文质量合格如果它复述的都是无关内容先看 OCR 输出是否被噪声干扰再看过滤规则是否保留得太宽。6. 常见问题与排查顺序6.1 安装或启动阶段pnpm 相关命令卡住社区里有人提到 deepseek harness 启动时容易“卡在 pnpm dsh web”。这种问题多数不是 harness 本身坏了而是依赖安装或网络请求没有结束。排查顺序应该是看 pnpm 安装日志是否有依赖包下载失败。看启动命令是否真的在等待某个 web 服务启动端口是否被占用。看是否因为网络原因导致某个依赖源不可达。换用镜像源或清理 pnpm 缓存后重试。如果只是想让识屏插件先跑起来可以不走 web 桌面端直接跑 CLI 模式。桌面端通常只是展示层插件核心还是要能在命令行里被调用。6.2 接口调用返回 400尤其是 thinking mode 的 reasoning_content 问题社区里还出现过一类报错大意是代理层在转发请求时开启了 thinking mode但后续请求没有把reasoning_content字段回传给 API导致上游返回 HTTP 400。这类问题不是识屏插件独有的而是接入 harness 或代理层时容易遇到的通用问题。碰到这类 400 报错我的排查顺序是先复现请求看返回体里的具体 error message。检查是否开启了思维链或深度思考模式如果开启确认请求里是否带上了必要的 reasoning 字段。检查代理层有没有对请求体做了字段过滤导致reasoning_content被丢弃。检查模型名是否有效尤其是要确认模型名是否真的存在。最简单的方式先关掉 thinking mode 试一次再逐步打开定位。这类问题经常被误判成“API 不稳定”实际是字段透传和模式开关的问题。我一般会保留最近的请求日志先看最后一次发出去了什么再决定改哪个配置。6.3 OCR 结果为空或乱码OCR 输出为空先检查截图文件是否存在、是否全黑或全白。再确认 tesseract 命令行能否单独识别本地图片。如果单独识别正常说明问题出在插件传参上如果单独识别也不行就换一张更清晰的图片测试。中文乱码通常是语言包问题重新安装chi_sim.traineddata并检查系统环境变量即可。至于代码和英文混排建议不要关掉英文语言包否则会有大量识别错误。6.4 屏幕内容太多导致上下文超限如果截图区域包含大量文本OCR 结果可能超过模型的上下文限制或者让请求变慢。处理办法是插件端先截断而不是靠模型端硬抗。我这里有一个简单策略先把文本按行分去掉空格行和单字符行再按关键词排序报错相关行优先最后截取前 N 个字符。如果确实需要完整内容就让模型分多次调用识屏命令每次只读取屏幕上的指定区域。6.5 通用排查顺序清单先看插件日志截图是否生成、OCR 是否有输出、请求是否发出。再看输入条件当前屏幕是否有内容、文字是否清晰、区域坐标是否正确。再看环境配置API Key、base_url、模型名、语言包、系统权限。再看参数设置超时、重试、最大 token、上下文截断。最后看 harness 版本和插件注册方式是否匹配。我见过不少项目因为路径权限或编码问题导致插件静默失败这类问题在日志里往往只有一行 trace排查时一定要从源头开始看。7. 边界、限制和后续优化方向7.1 识别准确率的边界需要先说清楚OCR 不是 100% 准确的。屏幕上文字的大小、字体、颜色对比度、背景图案都会影响识别结果。低对比度、艺术字体、图片上的水印、代码中的特殊字符都可能识别错。识屏插件能做的是尽量把准确率提高到“模型可以理解大意”的程度而不是把每个字符都还原成原生文本。如果你要识别的场景非常关键比如数字、金额、订单号这类必须精确的内容不要只依赖一次 OCR 结果。建议在插件里加二次校验或者把截图文件同时保留让人工确认。7.2 多显示器、缩放开缩和隐私边界多显示器环境下系统截图命令默认截的是主屏幕还是所有屏幕不同平台行为不一样。如果你的工作区是双屏最好在插件里指定要截取的屏幕或区域。另外操作系统开启缩放后截图的物理分辨率逻辑分辨率和 OCR 缩放处理也不同需要按平台单独调。识屏插件天然会读取屏幕上所有可见内容这就涉及隐私边界问题。如果你在团队或共享环境里使用建议限制插件的识别范围只允许截取当前活动窗口或指定区域并且不要自动把所有截图上传到线上。个人自用也要注意截图里可能包含密码、验证码、聊天隐私等敏感信息最好在日志里做脱敏不打印完整截图路径和 OCR 全文。安全建议不要用识屏插件读取密码输入框或敏感页面这类数据既不应该被模型处理也不应该在本地日志里留下副本。7.3 后续优化区域截图、定时轮询、事件触发、队列化等基础版本稳定后可以考虑几个优化方向区域截图只对目标窗口或固定坐标区域做识别既快又省 token。定时轮询对于实时变化的页面每隔几秒抓一次屏幕让 Agent 感知最新状态。事件触发检测到窗口标题变化、新报错弹窗出现时再触发识屏减少无效调用。队列化多个识屏请求排队执行避免并发调用导致截图互相覆盖。本地缓存对重复出现的屏幕内容做相似度判断相同内容不重复识别省 OCR 成本。这些优化不一定要做全取决于你的实际任务。如果只是个人开发时偶尔用一下手动调用命令就够了如果要做成团队共享工具或定时巡检任务再逐步引入队列和事件机制。7.4 我的最终建议回到标题“给 deepseek harness 写一个识屏插件”这件事真正的价值不是把截图变成文字而是把 Agent 的能力边界从“能读文件”扩展到“能看屏幕”。如果你也想搭一个建议按这个顺序推进先确认模型是否支持图片输入再选好截图和 OCR 工具接着写最小脚本跑通截图到识别的链路然后封装成 harness 命令最后再考虑订阅机制和批量任务。单任务跑通是第一步批量稳定运行是第二步不要跳级。整体来看识屏插件这类工具最值得关注的不是功能有多花哨而是在普通环境里能不能稳定完成“截图、识别、整理、传模型”这条链路。抓准这个主线后面再逐步扩展窗口控制、区域识别、事件触发等功能就不会把插件越写越复杂却不好用了。