
导读: 上一篇盘点了 DeepSeek Harness 桌面版 5 天 13 个版本的更新,发出去之后,被提得最多的一个缺口是:agent 会写网页、会起服务,但它自己看不见自己做出来的东西------页面得切到系统浏览器里打开,人在中间来回传话。这个需求听到了。8 月 24 日凌晨 1 点 35 分,一个改了 20 个文件、新增 3500 多行的提交合进了主干:给 agent 一个长在应用里的浏览器。它开在对话旁边,agent 能导航、能点击、能输入、能读控制台和网络请求;你全程看得见,随时能接管。这篇讲三件事:为什么 agent 需要一个自己的浏览器、它现在怎么工作,以及我让它当场考了一次试的完整记录。仓库:github.com/huyang218/d...,MIT 开源。
01 一个页面,被打开进了 Safari
先说这个功能的起点,它来自一次很日常的使用。
让 agent 写一个网页,它写完了,起了个本地服务,然后调用系统命令打开页面------页面弹在了 Safari 里。
从这一刻起,事情就别扭了。页面和产生它的对话,分属两个互相看不见的应用。你在 Safari 里看到按钮位置不对,得切回来用文字描述给 agent;agent 改完了,你再切过去刷新、再看、再切回来。它写的代码,它自己却看不见跑出来的样子------发现问题、确认修好,这两件本该它自己闭环的事,全靠人肉在两个窗口之间当传声筒。
更根本的问题在验收上。agent 说「改好了」,依据往往只是它读了一遍自己的 diff。代码看起来对和页面渲染出来对,中间隔着一整个浏览器:样式覆盖、脚本报错、接口 404,这些只有真的打开页面才知道。
上一篇文章发出去之后,这个缺口在反馈里被反复提到。所以有了这次更新。
02 现在,浏览器长在对话旁边
新版本里,主窗口右侧可以展开一个浏览器面板。它不是嵌了个网页预览那么简单------重点是 agent 能操作它。

agent 拿到了一整套动词:navigate 打开页面,snapshot 读取页面上有什么,click 真实点击,type 输入,还有滚动、拖拽、刷新、改视口尺寸、存截图,以及读 console 日志、network 请求列表和每个请求实际返回的内容。
其中 snapshot 值得单独说一句。它返回的不是一张图,而是一份带编号的页面清单,按人的方式描述页面:
arduino
heading "Counter"
text #count "0"
button "增加计数" [ref_0]
后续的 click、type 都拿这些 ref 编号做目标。agent 点的不是坐标,是「那个叫增加计数的按钮」------页面改版了、按钮挪了位置,操作照样成立。
另外两个设计决定了它用起来的感受。一是 agent 想「打开网页」的动作会被接进这个面板,不再甩给系统浏览器,对话和页面从此在同一个屋檐下;二是这个面板对你完全透明------agent 点了什么、页面变成了什么样,你全程看得见,地址栏、前进后退、刷新都在,随时可以自己上手。
配套还内置了一个 browser 技能,教 agent 一条纪律:改完 UI,不能只读 diff 就说做好了,要自己打开页面、亲眼看到渲染结果、点过、验过,才能交差。
03 让它自己考了一次试
功能好不好,说明书说了不算。我给 agent 出了一道完整的题:自己写一个带 bug 的网页,自己用内置浏览器把 bug 复现出来,再自己修好、自己复验,全程不许打开系统浏览器,没有真实工具输出不许说通过。

题目是一个计数器页面,故意埋了个小 bug:变量声明在了点击处理函数里面,每次点击都从零开始,连点两次计数还是 1。
接下来的过程完全不用人插手。它写好页面和一个无依赖的 Node 服务,在后台把服务跑起来,拿到端口,然后开始用浏览器动词干活:navigate 打开页面,snapshot 确认初始状态,真实 click 两次,再读 console------两条日志都写着 count = 1,bug 实锤;读 network------只有一条 200,排除接口问题。

然后它改代码,把变量挪到函数外面,reload,重新 snapshot,再真实点两次:第一次计数 1,第二次计数 2,页面状态翻成 PASS。console 无报错,network 无 4xx/5xx。最后它自己调 screenshot,把验收画面存进了项目目录。

这是它存下的那张图,也是整个闭环里唯一一张不是我截的图:

整个过程 1 轮对话、14 步,从发出指令到交出验收表用时 2 分 10 秒,其中工具调用只占 4.6 秒;首 token 1.4 秒,输出 82 tok/s,缓存命中 94%。写码、起服务、复现、修复、复验、留证据,一条指令跑完全程。
中间还有个小插曲:截图前我重启了一次应用,agent 之前起的服务跟着没了。再让它打开页面时,它发现服务不在了,自己把服务重新拉起来、换了新端口、继续验证------没有回来问我怎么办。
04 两处看不见的工程
这个功能的实现里有两个决定,外表完全看不出来,但值得写下来。
第一个是「一张动词表,三个出口」。浏览器的这套动作有三个消费者:模型侧的 MCP 工具、命令行的 dsh-browser、以及主进程里真正执行的引擎。三处如果各写各的,早晚会出现同一个动作在这边叫这个名、在那边叫那个名的漂移。现在三个出口读的是同一张动词表,一处定义,处处一致。连接用的 socket 地址和令牌通过环境变量传给 harness,不在磁盘上留任何秘密。
第二个是「两个记录员」。网络请求的记录走了两条路:一条是浏览器调试协议,能拿到请求 ID、响应体和调用栈;另一条是 Electron 自己的监听,它站在浏览器进程一侧,恰好能看见跨页面跳转那一瞬间发出的请求------那正是页面自己的脚本最容易落地的地方,也是第一条路的盲区。任何一条单独用都会漏,所以两条都开着,各自标记对方已经记过的条目。agent 问「刚才那个请求返回了什么」时,答案才是完整的。
05 怎么用上
这次更新已经合进主干,版本号 v0.1.14。写这篇稿子的时候,安装包还在打包的路上,发布以仓库的 Releases 页为准;等不及的可以直接从源码跑,npm install && npm start 两条命令。
旧的边界照旧交代:dsh 仍在 rc 阶段;本地端口没有鉴权,多人共用的机器要留意;macOS 安装包是 ad-hoc 签名,首次打开要在系统设置里点一次允许。
写在最后
这个系列写到第五篇,回头看是一条线:桌面版解决「装得上」,插件库解决「补得齐」,安卓版解决「带得走」,13 个版本解决「住得下」。这一篇补的是最后一块:agent 终于能亲眼看见自己做出来的东西了。
写代码的 agent 不稀奇,稀奇的是它交付前会自己打开页面点一遍。「做完了」和「验过了」之间的那段距离,以前靠人来回切窗口填,现在它自己走完。这大概就是熬这个大夜的意义。
评论区聊聊:有了会自己开网页、自己点按钮的 agent,你第一个想让它干的活是什么?还缺什么能力,也直接提------上一篇的需求就是这么变成这一篇的。
参考资料:
- 本文项目仓库:github.com/huyang218/d...
- 版本发布记录:github.com/huyang218/d...
- 上游仓库:github.com/deepseek-ai...
- 前作《5 天 13 个版本:那个「双击就能用」的 DeepSeek Harness,现在长什么样了》
说明:文中提交数、代码行数统计自 GitHub 仓库(2026 年 8 月 24 日),封面为 AI 生成;界面截图为实机实拍,其中一张验收截图由 agent 在测试中自行保存。