它们的差别恰好是一个 bundle。web 在 dsh-base 上叠 dsh-web-app,在 127.0.0.1:3080 提供浏览器 UI;headless 叠 dsh-headless,跑一次会话、打印结果、退出。交互用 web,脚本化用 headless,并且把它们保持为两个独立 profile,好让插件集互不影响。
两个自带 profile 看起来像两个产品,其实不是。它们的差别恰好是一个 bundle——搞清楚这点,你就不会再把 「无头模式」当成正主的缩水版。
真正的差别
web | headless | |
|---|---|---|
| Bundle | dsh-base + dsh-web-app | dsh-base + dsh-headless |
| 界面 | 127.0.0.1:3080 的浏览器 UI | stdout |
| 生命周期 | 跑到你停它为止 | 一次会话,然后退出 |
| 服务端 | 有 | 无 |
| 调用 | dsh web | dsh --profile headless "任务" |
dsh-base 里的一切——模型适配器、工具、持久化、沙箱与审批策略、设置、凭证、遥测——两边都有。你选的是
套在 agent 外面的壳,不是 agent 的能力。
web profile 为什么存在
为了干活,更重要的是为了看。
web profile 是那份 session log 变得可读的地方。模型看到的一切都记进一份只追加的日志,resume、fork、 search、replay 全部基于这条事件流,还有按来源检视会话的 trajectory 视图。一边让 agent 干活一边读它, 是你建立「它实际在做什么」这一准确认知的方式——区别于「你以为它在做什么」。
这不是新手的拐杖。当一次编排出问题时——某个子 agent 在打转、某个 workflow 阶段悄悄什么都没产出—— trajectory 就是你找出原因的地方。
headless profile 为什么存在
任何你会写进脚本的东西。
dsh --profile headless "总结一下测试套件为什么在挂"一次会话,结果打到 stdout,退出。不占端口,没有要收拾的东西。而且关键在于:无效命令、属于别的模式的
选项、配置错误和启动失败,一律非零退出——这正是它能被安全地塞进流水线里一个 if 后面的原因。
它天然的归宿是 CI、定时任务、或者 git hook。对于一个你不想为之开浏览器标签页的一次性问题,它也是 对的选择。
两个都留着
它们不是二选一,而把它们当成二选一是有实际代价的。
插件装进 profile 自己的 node_modules,patch 层也是按 profile 的。所以两个独立 profile 白送你:
- 不同的插件集。 交互用的那个可以带实验性插件;自动化那个只带任务需要的。
- 不同的审批策略。 审批和沙箱策略是
dsh-base的行,因而可 patch。一个你盯着看的交互式 agent 和 一个 CI 里无人值守的 agent,本就不该有同样的权限——分开 profile 就不必如此。 - 不同的爆炸半径。 搞坏本地 profile 的实验,不会碰到流水线。
各自组装成什么,验一下
dsh --profile web --dump-config
dsh --profile headless --dump-config把这两份输出 diff 一下,是看清 dsh-web-app 和 dsh-headless 各自到底贡献了什么最直接的办法——同时也
能确认:你在一个 profile 里收紧的策略,没有在另一个里还是出厂默认。
一句话各自的选法
web:有人在回路里、你还在摸清 agent 的行为、或者你需要 trajectory 视图去调试一次编排。
headless:没人在看的时候——CI、cron、hook,或者一个你打算检查退出码的脚本化单次任务。
常见问题
headless 的能力比 web 弱吗?
不弱。两者都叠 dsh-base——模型适配器、工具、持久化、沙箱与审批策略、凭证、遥测都在。差别是套在 agent 外面的壳,不是 agent 本身。
一个 profile 能同时干两件事吗?
那是在跟设计对着干。profile 很便宜且互相隔离,插件又按 profile 安装——分开留着,你白得独立的插件集和独立的审批策略。
该从哪个开始?
web。在你摸清这个 agent 会干什么的阶段,能看到 trajectory 视图,比早早脚本化一个你还不理解的东西值钱得多。