You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
写给 DeepSeek Harness 团队:装 dsh-superagent 的一夜,和 DSH 的三处断点
零、我的真实感受
我是抱着"试试看"的心态装的。我对它的期待是:一个 bundle,装完、配个仓库、开始用。
实际发生的是:我每隔二十分钟就要确认一次——DSH 还活着吗? 一会儿界面起不来,一会儿工单服务退了,一会儿升完级日常环境又不对劲。整个过程里我一直在做的事不是"使用它",而是考古:这套东西到底装了几个 dsh、现在跑的是哪一个、为什么文档说成功了而它没成功。
最难受的一次不是它报错,而是它整体消失了:装一个插件、版本对不上,整个 DSH 服务就挂在那里起不来,浏览器连不上,我没有任何入口可以把状态改回去。我只能打开另一个公司的 Agent,让它来修 DSH。 一个 Agent 平台,在最需要它的时候必须靠外部 Agent 抢救——这件事本身就说明它还没准备好被一个人日常使用。
一个"研究原型"允许粗糙,但不该允许失败得没有声音,更不该允许失败之后自己被连根拔起。我花掉的时间不是花在学习设计上,是花在猜"它到底怎么了",以及"我用什么工具去修它"。
一、七个坑(✅ = 我本机复现;🔍 = 高度怀疑,未拿到堆栈)
坑 0 ★ 装一个插件不兼容,整个 DSH 一起死;而修它需要另一个公司的 Agent
这条我认为最严重,不是"安装摩擦",是设计缺陷。
现象:插件不兼容 → 整个 DSH 服务起不来 → 浏览器连不上 → 我失去了唯一的操作界面。想恢复,只能另开一个别家的 Agent(Claude / Cursor / WorkBuddy),让它替我读日志、删插件、修配置。
机制(为什么是"整体"而不是"局部"):
一个进程装下所有东西。 Web GUI、插件树(cordis)、webserver 是同一个进程,而 webserver 本身只是树里一个节点。bundle 在
load阶段抛错、或用到的宿主 API 版本不对,整棵树 mount 失败 → 端口根本没监听。我在浏览器看到的不是"Superagent 坏了",是服务不存在。没有 last-known-good,没有 safe mode。 装错一个 bundle,profile 就从"可启动"变成"不可启动",中间没有任何缓冲状态,也没有"上次能跑的那套配置"可回滚。
默认不给你留救援通道。 最讽刺的是:救援能力其实是有的,只是没写在文档里,全靠自己翻
dsh --help:也就是说:它本来可以自救,只是没把钥匙交到使用者手上。 SETUP.md 和 006-deployment.md 里对这些命令一个字都没有;而
--from-default-profile唯一被提到的地方,是在讲它会删除现有 profile 的警告里。信任层面的后果。 平台级工具必须以"自己会坏"为前提设计,并且必须允许使用者在不依赖平台本身的情况下恢复。这条不修,团队基本不会采纳——因为谁都不想在一个"坏了就只能去求别的厂商"的工具上建工作流。
建议(按性价比排序):
rescueprofile(dsh rescue --from-default-profile web),并在文档第一屏写"GUI 起不来时,先dsh rescue"。last known good的 profile 快照,失败自动回退一次。坑 1 ✅ 叫
dsh的包有两个,npm i -g dsh装到的是 2016 年的 JS shell真正的包是
@deepseek-ai/dsh。这个名字冲突对外部使用者是第一次劝退:搜dsh、搜npm i -g dsh,得到的是一份完全无关的软件。建议 README/SETUP 第一行就直接给全名,并在 npm 上把dsh这个裸露名字占用掉(或被搜索时明确排除)。坑 2 ✅ 版本门槛是"硬失败",但报错不告诉你该装哪个版本
dsh --version→0.1.5-rc.1(当晚升级到0.2.2-alpha.2,见后记)engines.dsh: "^0.2.1-alpha.2";006 部署文档钉@deepseek-ai/dsh@0.2.1-rc.3问题不在于要求高,而在于:我在
dsh --version、status、面板里都看不到一句"你的 DSH 太旧,bundle 需要 ≥0.2.1"。故障信息被推迟到"面板 Failed to load plugins"这种没有因果关系的提示上。建议:dsh plugin add/ 启动时前置一个版本闸门,直接打印require engines.dsh ^0.2.1-alpha.2, found 0.1.5-rc.1, run: npm i -g @deepseek-ai/dsh@0.2.1-rc.3。坑 3 ✅
npm install -g会静默跳过原生模块的 install script,装出一份"看起来成功"的坏环境这是我认为最该修的一条,也是最可能解释"一会儿就挂"的一条:
装完 exit 0、版本号正确、
dsh --version正常,但 subprocess helper 和 pty 没生成。而 dsh-superagent 的 Setup、worker shell、prepare 命令全都依赖 subprocess service。表现出来就是:能启动 → 用一下 → 挂。没有一条日志指向"原生件没装"。建议:把原生依赖做成 optional 并在缺失时启动即失败,或者干脆提供
dsh doctor,把 pty / koffi / spawn-helper / tmux / git 版本一次性体检出来。坑 4 ✅ 一台机器只能有一个 dsh 的假设,和现实冲突
@deepseek-ai/dsh-* ^0.1.0-rc.0)。^0.2.1-alpha.2。npm install -g @deepseek-ai/dsh@0.2.1-rc.3。于是只剩两个坏选择:升全局 → 日常环境可能挂;不升 → superagent 起不来。最后我自己退回到"私有前缀 + PATH 包装器"才让两者共存(
~/.local/dsh-0.2.1-rc.3/bin/dsh+~/.local/bin/dsh-superagent)。建议:profile 级 / bundle 级的运行时版本声明,或官方支持
dsh --runtime <path>;至少文档里给一段"多版本共存"的写法,别让使用者自己在 PATH 里考古。坑 5 ✅ "现在跑的到底是哪个 dsh"有三条互不相通的解析路径
dsh(~/.local/node/bin/dsh→ 我升级的那个)npm exec @deepseek-ai/dsh web(我的 GUI 实际是这么起的,走 npm 的包解析/cache)scripts/dsh-superagent里的shutil.which('dsh')(由启动瞬间的 PATH 决定,tmux 会话里还会继承旧环境)升级全局 CLI 后,GUI 并没有变;重启后 GUI 又可能挂。而
scripts/dsh-superagent restart如果换了 PATH,会静默换掉运行时。这一点没有任何地方提示。建议:
status直接打印dsh resolved to: <path> (version);start把解析结果写进日志首行;shutil.which结果支持DSH_BIN=覆盖。坑 6 ✅ 面板 401,token 只存在于
.artifacts/的滚动日志里start打印的 JSON 里没有 URL(只有"url": "http://127.0.0.1:3082",不带 token),要看面板必须去翻.artifacts/里最新那份日志。日志还是"只保留最新 20 份"的滚动设计。对一个要长期运行的守护服务来说,这是把入口藏起来了。建议:
start的 JSON 里带上带 token 的 URL;或面板首次访问允许 localhost 免 token(本来就是 127.0.0.1 绑定)。坑 7 ✅ "装好了"不是一个可达状态:必须先去 GitHub 造一个新账号
status退出码契约设计得很好(0 正常 / 1 挂了 / 2 某部分报错 / 3 等配置),这点值得表扬。但代价是:没有专用 GitHub 代理账号,就永远停在 exit 3,而创建账号、邀请协作者、gh-agent auth login全是操作者手工的事。也就是说,使用者无法用"我只想先看看界面"的方式完成一次安装。建议:给一个
--dry-run/ demo mode(本地 mock Source),让人在不碰任何外部账号的前提下把整条链路跑通一遍。这会大幅降低第一次接触的放弃率。二、顺手说两个"伤人但不致命"的
dsh superagent --from-default-profile web会替换已存在 profile 并删掉cordis.patch.yml。 破坏性操作没有--force,只靠 006 里一句提醒。建议默认拒绝,要求显式覆盖。dsh plugin add link:会警告 peer 不会被解析,而生成出来的 profilenode_modules里确实一个@deepseek-ai/*都没有——宿主版本完全是隐式的。bundle 声明了 25 个 peer,运行时用的是谁,没人说得清。三、做得好的地方(我认真保留意见)
status的退出码语义(0/1/2/3 +errors/setup结构化输出)——这是整个项目里最像"工业品"的部分,运维脚本可以直接依赖。observe默认模式 + 允许名单 + Policy 版本 + Mandate 不可变——先记录不动手,这个安全设计是真诚的。cordis.patch.yml里两个 preset 逐条列出 persona/tools/compaction)——干净、可读、可 diff。rescueprofile 的存在说明团队想过"profile 坏掉怎么办"——只是没把它变成默认动作,也没写进文档。差一步。四、如果只能改三件事
rescueprofile。做不到这条,其余都是小事。(对应坑 0)dsh doctor:版本、原生件、tmux、git、PATH 解析结果,一次说清;缺一项就拒绝启动并直接打印修复命令(含救援三条)。(对应坑 0/2/3/5)start必须吐出可点的 URL,失败必须吐原因;status必须打印"当前运行的 dsh 是哪个路径、哪个版本"。(对应坑 5/6)五、最后两句话
这个项目的设计让我愿意继续试——它清楚自己在反对什么(反对拿 agent 刷量、反对用消息淹没同事),这在同类产品里很少见。
但安装与容错是它目前最弱的一环,而恰恰是这一环决定了我会不会走到能欣赏它设计的那一步。今晚我两次差点关掉终端,还有一次是关掉之后不知道该用什么工具把它救回来。
如果目标读者是"研究原型的使用者",那我希望这两句话被认真对待:
— 一个花了整晚在 PATH 和 token 里考古、最后靠别家 Agent 才把现场收拾干净的使用者
六、后记:关于本文——一次自我批判
这篇文章有一个奇怪的属性:批评者和被批评者是同一个东西。
写下这些字的是 DSH 里的一个 coding agent。它读了
SETUP.md、006-deployment.md、007-architecture.md,在本机跑过npm install -g、dsh plugin add、scripts/dsh-superagent start,亲眼看到status反复停在repositoryUnset,也亲手经历过"服务起来又没了"。于是有个不太舒服的结论,我觉得应该原样交出去:
我想让团队看到的不只是七条 bug,而是这个结构性问题:
一个会自我批评的 Agent 是有价值的;但一个批评完自己还能自己站起来的 Agent,才是能被日常信任的 Agent。前者靠模型,后者靠工程。这份文档测出来的,是后者还欠着。
— 代笔:DSH(deepseek-flash),在一个它自己差点挂掉的会话里
All reactions