Skip to content

事故复盘(postmortem) 0003:Web agent(智能体)验收了替代服务器,而非其当前 GUI

English | 中文

状态:已解决

摘要

Web agent 修改了 GUI 源码,却不知道当前会话对应哪个 URL、由哪个进程承载。它把验收交还给用户,随后在 window.__DSH_BOOT__ 缺失导致白屏的情况下,仍把裸 Vite 返回的 HTTP 200 当作成功;最后,原页面其实已经加载了重建产物,它却去验收另一个端口上的替代 dsh web 服务器。修复让当前 URL 和运行模式对模型可见且可由 shell 查询,在独立 Vite 开始监听前拒绝启动,并依据外部状态验收生产模式刷新与开发模式 HMR(热模块替换)。

概述

该会话运行在端口 3081 的 DeepSeek Harness Web GUI 中,而用户选择的 Workspace 是空的 test/ 目录。模型请求既未指明该 GUI,也未提供它的源码检出目录、URL、进程或更新模式。仓库在 apps/web 中提供了 Vite 开发脚本,完整的浏览器组合则由 dsh web 提供。

由此产生的各个动作单看都合理,却没有指向同一个验收目标。源码修改、成功构建、HTTP 200、注入的启动 manifest(元数据清单)和用户原本打开的页面,被当成了可以相互替代的事实。

证据源是 session-3eb796c2-5159-4686-affe-df8719f6f987 的持久化事件日志,其头部记录的 cwd 为 /Users/tn.shen/Documents/deepseek-harness-gui-master/test。初始请求头位于序列 6;面向用户的交接、裸 Vite 启动、替代宿主启动、启动 manifest 探测,以及首次探测 3081 进程,分别位于序列 30939、31865、34309、34441 和 34681。下方时间线以这些事件为依据,而不是根据后续报告反推意图。

影响

用户不得不连续指出三个错误:agent 把验收交还给用户;建议预览的页面一片空白;报告成功的 URL 并不是用户正在使用的页面。一个不受管理的替代服务器还持续运行到下一个轮次,直到用户提出质疑。

本次调查没有重启或修改只读的 3081 和 3082 试验服务。

时间线

  • 在第 2 个轮次中,agent 修改主题后,在序列 30939 的消息中让用户运行 pnpm run demo:tui 或打开一个未明确指定的 Web 应用。它没有对组装后的 Web 应用执行任何验收。
  • 在第 3 个轮次中,agent 读取 apps/web/package.json,在序列 31865 于端口 5173 上启动裸 Vite,观察到 HTTP 200 后便宣布成功。浏览器却抛出 client-modules: window.__DSH_BOOT__ is missing or not an object,并显示白屏。
  • 在第 4 个轮次中,agent 找到了完整的 dsh web 启动路径,重新构建 shell,在序列 34309 于端口 3334 上启动一个不受管理的进程,并且只在序列 34441 检查了这个替代服务是否返回 200 和启动 manifest。它从未探测端口 3081。
  • 在第 5 个轮次中,用户在序列 34556 报告 3081 已经显示新主题。直到序列 34681,agent 才检查既有进程并移除冗余服务器。

根因

Web 组合没有向模型提供当前 GUI、规范 URL 或运行模式的身份信息。会话 cwd 正确标识了用户选择的 Workspace,但模型把这个项目目录当成了应用目录。系统也没有持久记录将 GUI 源码检出目录、构建产物、服务进程、目标 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,而 Web shell 和普通包的改动仍然需要刷新页面。
  • apps/web 的独立 Vite 服务模式会在配置阶段拒绝启动。其子进程测试验证进程自然退出,并插桩 Server.listen(),确保短暂绑定端口也不会漏检。
  • 分层的真实路径测试覆盖 CLI(命令行界面)请求、精确的生产/开发模式提示词、shell 运行时事实、同端口静态产物替换、源码 watcher 重建、宿主 stat 轮询,以及页面 identity 不变的浏览器 HMR。
  • PR(Pull Request)证据保留了原始 3081 会话的截图,以及真实模型驱动的 GUI 修改前后对比;验收以外部浏览器、HTTP、进程和会话日志的观测结果为准。

教训

  • agent 必须先知道隐藏的运行时前置条件,才能指导用户;启动模式属于应用上下文,不应依赖团队口口相传。
  • HTTP 就绪、构建成功和启动 manifest 是不同的事实。验收必须明确指定确切的 origin,并从外部观察所请求的改动是否在该 origin 生效。
  • 替代服务无法证明既有页面已经改变。确实收到启动长时间运行进程的请求时,应使用受管任务生命周期。
  • 回归测试必须能够针对所报告的机制失败。进程超时不等同于快速失败,进程退出后端口可用也不能证明该端口从未被绑定。

基于 MIT 许可证发布。