ByteNoteByteNote
Web Agent 验收了错误的服务器:GUI 反馈环复盘
字

字节笔记本

2026年10月6日 · 约 6 分钟读完

Web Agent 验收了错误的服务器:GUI 反馈环复盘

API中转
¥120

deepseek-harness 是 DeepSeek 开源的 agent 框架,仓库的 docs/postmortem 目录里维护着一组事故复盘文档。这类文档不写那一行代码的修复,只回答一个问题:流程为什么放行了这条 bug。0003 号复盘讲的就是一个典型案例:一个 Web agent 改了 GUI 源码,却始终不知道自己的会话跑在哪个 URL、由哪个进程承载,最后验收了一台与用户毫无关系的服务器。

Web Agent 验收了错误的服务器:事故时间线

事故是怎么一步步走偏的

事故会话运行在 3081 端口的 DeepSeek Harness Web GUI 里,而用户选中的 Workspace 是一个空的 test 目录。用户请求里只说改主题,既没提 GUI 本身,也没提它的源码检出位置、URL、服务进程或更新方式。仓库里暴露着 apps/web 目录和一个 Vite 开发脚本,完整的浏览器组合则藏在 dsh web 命令背后。这些零散信息拼在一起,模型把五种事实当成了可以互相替代的东西:源码修改、构建成功、HTTP 200、注入的启动 manifest,以及用户已经打开的旧页面。

复盘依据当次会话的持久化事件日志逐条还原了时间线:

  • 第 2 轮,agent 改完主题后,让用户自己运行 pnpm run demo:tui 或打开一个未指明地址的 Web 应用,没有对组装后的 Web 应用做任何验收。
  • 第 3 轮,agent 读了 apps/web/package.json,在 5173 端口启动裸 Vite 开发服务器,看到 HTTP 200 便宣布成功。浏览器那边实际抛出 window.DSH_BOOT 缺失的报错,渲染出来的是整页白屏。
  • 第 4 轮,agent 找到了完整的 dsh web 启动路径,重建 shell 后又在 3334 端口拉起一个不受管理的进程,并且只检查了这个新端口返回 200 和 boot manifest。它从头到尾没有探测过 3081。
  • 第 5 轮,用户反馈 3081 端口的页面早就显示出新主题。直到这时,agent 才去检查既有进程,把冗余服务器撤掉。

对用户来说,这意味着连续三次纠错:验收被推回给自己,给出的预览是一片白屏,报告成功的 URL 根本不是自己正在用的那个页面。那台失控的替代服务器还活过了整个轮次,直到用户提出质疑才被清理。值得一提的是,这次调查全程没有重启或改动只读的 3081 与 3082 试验服务。

根因:传输层就绪不等于应用就绪

复盘把问题归到三个层面。

第一,Web 组合没有给模型提供当前 GUI 的身份信息:规范 URL 是什么、跑在生产还是开发模式,模型统统看不到。会话工作目录确实正确指向了用户选的 Workspace,但模型把这个项目目录误当成应用目录,整个会话里也没有任何持久记录把源码检出、构建产物、服务进程、目标 origin 和浏览器验收关联起来。

第二,错误的启动路径看起来太合理了:裸 Vite 确实返回 HTTP 200。但 window.DSH_BOOT 只有完整宿主才会注入,传输层就绪不代表应用就绪。更耐人寻味的是,第一个回归测试以另一种形式重复了同一个错误:超时把 Vite 进程杀掉之后,非零退出的断言照样得到满足,这个误报直到真实复现才暴露出来。

第三,agent 用 shell 的 & 符号绕过了后台进程管理语义,任务身份、完成通知、结果收集和清理机制全部失效。于是验证 3334 端口只能证明第二台服务能跑起来,证明不了用户那个页面有任何变化。

修复后的验收闭环:四层防护

修复:让验收指向正确的事实

修复思路不是叮嘱模型更小心,而是把关键事实变成机器可查的状态:

  • Web 启动器在日志的 app:web-surface 提示词区段和受管的 DSH_WEB_URL、DSH_WEB_MODE 环境变量里公布规范回环 URL 与实际的生产或开发模式,模型随时可以查询。
  • 生产模式指南要求重建产物后刷新既有 URL 再验证;开发模式指南说明 dsh web --dev 只挂载 HMR 接收端,同一检出里的 pnpm run dev:web 还要重建客户端插件 bundle,shell 与普通包的改动仍然需要刷新页面。
  • apps/web 的裸 Vite 独立服务模式在配置阶段直接拒绝启动;配套的子进程测试验证进程自然退出,并给 Server.listen() 加了插桩,保证一次短暂的端口绑定也逃不过断言。
  • 分层的真实路径测试覆盖 CLI 请求、精确的生产与开发提示词、shell 运行时事实、同端口静态替换、源码 watcher 重建、宿主 stat 轮询,以及页面 identity 不变前提下的浏览器 HMR。
  • PR 证据保留了原始 3081 会话的截图和真实模型驱动的改动前后对比,验收以外部浏览器、HTTP、进程和会话日志的观测结果为准。

四条可以带走的教训

复盘最后总结了四条经验。其一,agent 必须先知道隐藏的运行时前置条件才能指导用户,启动模式属于应用上下文,不该靠口口相传。其二,HTTP 就绪、构建成功和 boot manifest 是三个不同的事实,验收必须点名确切的 origin,并从外部观察所请求的改动是否在那里生效。其三,替代服务证明不了既有页面已经改变,确需启动长驻进程时,应使用受管的任务生命周期。其四,回归测试必须能针对所报告的机制失败:进程超时不等于快速失败,进程退出后端口可用也证明不了端口从未被绑定。

对任何在终端里跑 agent 的人来说,这篇复盘都值得读一遍。它展示的不是模型能力不行,而是一个工程团队如何把隐性知识变成显性约束,让同一类 bug 下次自己拉响警报。

相关文章

分享: