Skip to content

docs(adapter): 新增 dsh 接入指南 - #883

Closed
liuhaoyang wants to merge 7 commits into
deepcoldy:masterfrom
liuhaoyang:docs/dsh-integration
Closed

docs(adapter): 新增 dsh 接入指南#883
liuhaoyang wants to merge 7 commits into
deepcoldy:masterfrom
liuhaoyang:docs/dsh-integration

Conversation

@liuhaoyang

Copy link
Copy Markdown
Contributor

改动

新增 docs/dsh.md:DeepSeek Harness(dsh)适配器的接入指南,覆盖:

  • 快速接入:安装运行时、机器人配置项、验证
  • 凭据配置:~/.dsh 全局配置不生效,凭据只能通过环境变量注入;两种注入方式及沙箱注意事项
  • 自定义组合:DSH_CORDIS_CONFIG 覆写行为、默认组合内容与 zen/go 网关示例
  • 原理速览:进程链、版本兼容、会话目录
  • 常见问题:5 类现象的原因与处理

为什么

dsh 适配器(#858)引入后缺少面向用户的接入文档。dsh 在 botmux 里的凭据链路(不读 ~/.dsh)和默认组合覆写行为与单独使用 dsh 时差异较大,实际接入容易踩坑,整理成文档便于按步骤接入与排错。

影响面

纯文档新增,无代码变更,不影响任何运行时行为。

验证

  • 内容与 src/adapters/cli/dsh.tssrc/dsh-runner.ts 实现逐项核对(环境变量名、默认组合、provider 路由、model 选项、会话目录)
  • 结构与语气对齐仓库既有接入指南 docs/riff.md

@liuhaoyang
liuhaoyang requested a review from deepcoldy as a code owner August 14, 2026 14:45

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢贡献这份 dsh 接入文档 🙏 整体质量很高,能看出是对着 src/adapters/cli/dsh.tssrc/dsh-runner.ts 逐项核过的——显示名、cliIdmodel 默认值与候选、固定 deepseek-official provider、DSH_CORDIS_CONFIG 覆写、每次启动覆写 ~/.botmux/dsh/cordis.yml、不读 ~/.dsh 等都与实现一致;对 master 也 merge 干净。

审下来有 3 处会把用户带偏的事实性问题,建议修订后再合入(纯文档,改动都很小):

1)沙箱下 per-bot env 的说明写反了(安全相关,优先)

「凭据配置 → 注入方式」里写沙箱模式下 per-bot env「可能不会传递,遇到时改用 daemon 环境」,FAQ 里也有对应一条。按当前启动链,这个结论正好相反:

  • worker 始终把 bots.jsonenv 作为 injectEnv 传给后端;
  • PTY 后端把它并入启动 bwrap 的父进程环境;tmux 后端通过 pane 内 /usr/bin/env KEY=VAL … bwrap 注入;
  • bwrap 参数里没有 --clearenv,只用 --setenv 覆盖少量键,所以 DEEPSEEK_API_KEY 会被子进程继承。

实测(真实 bwrap:父环境放一个测试变量 + 沙箱同款 --setenv/--unsetenv、无 --clearenv)子进程能读到该变量。保留原文案会引导用户把本可以按 bot 隔离的 key 搬到 daemon 全局环境,反而扩大泄露 / 串 bot 的风险。

建议:删掉这条 caveat 和对应 FAQ。真正值得提醒的是另一件事——DSH_CORDIS_CONFIG 指向的文件也必须在文件沙箱可见范围内,否则沙箱内 resolveConfigPath()existsSync 判空会静默回退到内置组合;最稳妥是放在 ~/.botmux/dsh/(适配器已把该目录声明为 authPaths,例如 ~/.botmux/dsh/custom.yml)或放在 workingDir 内。

2)「自定义组合」示例其实不是默认组合,整块复制会改变会话落盘位置

这段文案同时称「默认组合如下」又称「以下为走 zen/go 网关的示例」,实际展示的是 zen/go 示例。相对 src/dsh-runner.ts 里真正的 VENDORED_CONFIG,示例少了 4 个 config 子树:

  • agent-core.config.workspaceContext.maxBytes: 65536
  • sessions.config.root: !!js process.env.DSH_SESSION_ROOT ?? './.sessions'
  • bash.config.cwd: !!js process.env.DSH_CWD ?? process.cwd()
  • fs-local.config.cwd: !!js process.env.DSH_CWD ?? process.cwd()

llm-deepseek 段是示例有意修改,不算遗漏。)

最实际的后果:用户整块复制成 DSH_CORDIS_CONFIG 后,sessions 的 root 会退回 workingDir 下的 ./.sessions,与文档后面「会话目录在 ~/.botmux/dsh/」直接冲突。

建议:先原样贴出完整的真实默认组合,再单独给出 zen/go 的最小 diff;或者把示例补全并明确标注「示例(基于默认修改)」。

3)daemon 重启命令

「daemon 环境」里的 pm2 restart --update-env 缺少进程目标参数,按 PM2 CLI 语法不能直接执行,而且会绕过 botmux 自己的安全重启 / 会话恢复入口。建议改成 botmux restart;若明确是在仓库 checkout 内操作,用 pnpm daemon:restart

两个小 nit(非阻塞)

  • 结尾的 GET /api/cli-options:实际返回的是 options 数组,dsh 项形如 { id: "dsh", available: … },不是顶层 dsh: available。建议改成「查看 id: dsh 条目的 available」。
  • 「安装 dsh 运行时」可顺带注明需要 Python >=3.10

关于安装命令本身:核过 PyPI,在当前只有 pre-release(0.1.0rc6)的情况下,裸 pip install deepseek-harness-sdk 会解析到该 rc,无需额外加 --pre,保持现状即可。

改好后我再复核一遍~ 再次感谢 🙏

- 删除「沙箱下 per-bot env 不传递」的错误结论与对应 FAQ:worker 始终把 bots.json env 作为 injectEnv 传给后端,bwrap 无 --clearenv,key 会被子进程继承;改为提醒 DSH_CORDIS_CONFIG 文件须在沙箱可见范围内,否则 existsSync 判空静默回退内置组合
- 自定义组合章节改为先原样贴出 VENDORED_CONFIG 完整默认组合(含 agent-core workspaceContext、sessions root、bash/fs-local cwd 四个子树),再单独给出 zen/go 网关的最小 llm-deepseek 改动,避免整块复制导致会话落盘退回 workingDir/.sessions
- daemon 重启命令由 pm2 restart --update-env 改为 botmux restart(checkout 内可用 pnpm daemon:restart),避免绕过安全重启与会话恢复
- 安装章节补充 Python >= 3.10 要求;/api/cli-options 返回结构更正为 options 数组中 id=dsh 条目的 available
- 凭据配置改为「组合 + settings」两层叙事,讲清 apiKeyEnv 是环境变量名引用;环境变量表只留通用项(DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL / DSH_CORDIS_CONFIG),配置相关的变量名改为通用规则说明
- 新增「走 pi 插件配置多 provider」:自定义组合追加 settings/credentials/llm-pi-ai 三插件,~/.dsh/settings.yaml 的 llm-pi-ai 段配 provider 列表(apiKeyEnv 只引用变量名),含休眠/热加载行为与沙箱下 sandboxPaths.readOnly 注意事项
- 自定义路由示例泛化为占位值,文档不出现具体 provider 端点与 key 名
- 快速接入 provider 行与 pi 章节交叉引用,消除「不支持更换 provider 路由」的表面矛盾
- 安装章节补充 Python >= 3.10;开头补 cordis 组合白话注释
@latte-gh

Copy link
Copy Markdown

评审意见已逐条修订,推了两个 commit(bffd0b7f、49077265):

1)沙箱下 per-bot env(安全相关):已删除「沙箱下 per-bot env 可能不传递」的 caveat 和对应 FAQ。核对启动链确认结论相反:worker 始终把 bots.jsonenv 作为 injectEnv 传给后端(PTY 并入子进程环境、tmux 通过 pane 内 /usr/bin/env KEY=VAL 注入),bwrap 参数无 --clearenvDEEPSEEK_API_KEY 会被子进程继承。改为提醒真正的坑:DSH_CORDIS_CONFIG 指向的文件必须在文件沙箱可见范围内,否则 resolveConfigPath()existsSync 判空会静默回退到内置组合;建议放 ~/.botmux/dsh/(适配器已声明为 authPaths)或 workingDir 内。

2)默认组合:自定义组合章节改为先原样贴出 VENDORED_CONFIG 完整默认组合(含 agent-core.workspaceContext.maxBytessessions.rootbash/fs-localcwd 四个子树,与 src/dsh-runner.ts 逐字节一致),再单独给出改路由的最小 llm-deepseek 段;并明确提示不要删 sessions.config.root 等子树,否则会话落盘会退回 workingDir 下的 ./.sessions,与会话目录说明冲突。

3)daemon 重启pm2 restart --update-env 已改为 botmux restart(仓库 checkout 内可用 pnpm daemon:restart),并说明不要直接用 pm2 命令的原因(绕过安全重启与会话恢复)。

nits

  • /api/cli-options 已更正为 options 数组中 id: "dsh" 条目的 available
  • 安装章节补充 Python >= 3.10(PyPI requires_python 核实)
  • pip install 命令保持不变(当前只有 pre-release,裸 pip 会解析到 rc,无需 --pre

另外补充了「走 pi 插件配置多 provider」一节:自定义组合追加 dsh-settings-file / dsh-credentials-local / dsh-llm-pi-ai 三插件后,可在 ~/.dsh/settings.yamlllm-pi-ai: 段配置多 provider(apiKeyEnv 只引用环境变量名);文档中不出现任何具体 provider 端点与 key。

麻烦再复核一遍~

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复核了更新后的版本(head 4907726),先前 review 提的 3 项 + 2 个 nit 都已按预期处理,逐条对源核过:

  1. 沙箱 per-bot env:已更正为「沙箱模式下同样生效——worker 把 env 作为 injectEnv(PTY 并入子进程环境 / tmux 走 pane 内 /usr/bin/env),bwrap 不清理环境,key 一路继承」,与实现一致;并补了「DSH_CORDIS_CONFIG 需在文件沙箱可见范围,否则 existsSync 判空静默回退内置组合」这个真正该提醒的点。
  2. 默认组合 YAML:现与 src/dsh-runner.tsVENDORED_CONFIG 逐字一致(4 个 config 子树已补全),zen/go 改成占位示例 + 明确「只调整 llm-deepseek 段、其余保持默认」并说明删掉 sessions.root/cwd 的后果。
  3. 重启命令:已改为 botmux restart(checkout 内 pnpm daemon:restart),并说明为何不要用 pm2 restart --update-env

nit:/api/cli-options 措辞已改成「id: dsh 条目的 available」;「安装 dsh 运行时」已注明 Python >=3.10;安装命令保持不加 --pre(正确——当前只有 pre-release 时裸 pip install 会解析到该 rc)。

另:新增的「走 pi 插件配置多 provider」一节,botmux 侧的集成点我核对无误——sandboxPaths.readOnly 是真实 bot 配置字段(src/types.ts)、适配器只声明了 ~/.botmux/dshauthPaths(所以 ~/.dsh 默认不在沙箱内、需显式加进 sandboxPaths.readOnly 的提醒正确)。该节里 dsh 自身插件的行为(dsh-settings-file / dsh-credentials-local / llm-pi-ai 的挂载语义、settings.yamlllm-pi-ai: / agent-default-model: 段、热加载与「默认休眠」)属于 dsh 运行时范畴,botmux 代码内无从核对——建议对照 dsh 自身的插件文档再确认一遍这几个名称与字段。

我这边先前的意见都已解决,LGTM。

@latte-gh

Copy link
Copy Markdown

感谢复核 🙏 pi 一节里 dsh 侧的名称与字段已对照本地安装的 dsh 插件文档逐项确认(@deepseek-ai/dsh-basecordis.patch.yml 注释 + 各插件 README):

  • 三个插件的 id / name:settings@deepseek-ai/dsh-settings-filecredentials@deepseek-ai/dsh-credentials-localllm-pi-ai@deepseek-ai/dsh-llm-pi-ai,与 bundle 完全一致
  • llm-pi-ai: settings 段 + 热加载:bundle 注释明确「$DSH_HOME/settings.yaml, hot-reloaded,llm-deepseek:llm-pi-ai: 段覆写 adapter 条目,无需重启」
  • 「默认休眠」:bundle 注释「mounted dormant: zero routes … until a llm-pi-ai: settings section supplies provider profiles … drop again when the section empties」,与 pi README 的 dormant 描述一致
  • agent-default-model: 段:dsh-agent-default-model README 确认 composition 条目是该 Settings 段的 base,settings provider 会把用户选择层叠上去
  • credentials 来源:bundle 注释「inherited environment over the managed $DSH_HOME/.credentials.yaml, with project and user .env fallbacks」
  • 「model 必须是路由模型列表里的 id」:pi README 明确路由未配置的 model 会以 UNKNOWN_MODEL 失败

均与文档措辞一致,无需再改。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

更新:撤回上一条 approve,改为 request changes。

R2 里我确认的前三项修订(沙箱 per-bot env、默认组合 YAML 逐字对齐、重启命令)仍然成立。问题出在新增的「走 pi 插件配置多 provider」一节——把它拿真实发行版跑下来,有两个硬阻断,照文档做会让 bot 起不来 / 承诺的能力实际不存在。

阻断 1:按文档装到的运行时加载不了这份三插件组合

当前 PyPI 只有 deepseek-harness-sdk / deepseek-harness-runtime-bin0.1.0rc6。装上后用打包的 dsh-jsonrpc-agent 逐个插件做启动探针(这正是 src/dsh-runner.ts 实际的加载方式):

  • @deepseek-ai/dsh-llm-pi-ai → 能解析(随后因最小探针缺 llm seam 而 pending,说明插件确实在发行闭包里)
  • @deepseek-ai/dsh-settings-fileERR_MODULE_NOT_FOUND: Cannot find package
  • @deepseek-ai/dsh-credentials-localERR_MODULE_NOT_FOUND
  • 文中 agent-default-model: 段隐含依赖的 @deepseek-ai/dsh-agent-default-modelERR_MODULE_NOT_FOUND

发行闭包里实际存在的是 @deepseek-ai/dsh-settings@deepseek-ai/dsh-credentials(不带 -file/-local 后缀)。所以文档让用户把 dsh-settings-file / dsh-credentials-local 加进 cordis 后,dsh 会在握手前直接 plugin tree failed to loadsettings.yaml 根本没机会生效——而且这是单文件 wheel 的既定发行闭包,无法靠“另外 npm install”补救。

阻断 2:agent-default-model: 不会替 botmux 选 provider/model,「多 provider」在当前适配器里不成立

  • botmux src/dsh-runner.ts:650 固定发送 initialize { provider: "deepseek-official", model }——provider 是写死的字面量,不从任何 config/env/model 派生。
  • dsh 官方 JSON-RPC server(packages/sdk/server/src/server.ts:117-118, 224-231)把这两个显式参数原样存下并用来创建每个 Agent。
  • 官方 agent-default-model 的语义(其 README):deployment default「an Agent entry point uses only when a session has no selection of its own。botmux 每次都传显式 provider+model,session 始终有 selection,所以 agent-default-model 永远不会被查到——不是“server 忽略它”,而是当前 botmux 的调用路径结构上就绕过它。

所以文档里「在 agent-default-model: 段选择会话默认 provider/model」是事实错误;顶部与该节的「多 provider」承诺也超出当前 botmux 能力。今天 model 只能是 deepseek-official 路由下的模型,无法把会话切到 pi 的其它 route。

建议二选一

  • 本 PR 维持纯文档(推荐,快):删掉整段「走 pi 插件配置多 provider」及前文的相关跳转,明确当前 botmux 仅支持 deepseek-official;自定义组合只讲现有 llm-deepseek 的 key / baseURL / model。这样 PR 立刻是准确、可合的纯文档。
  • 真正支持多 provider(另开功能 PR):给 dsh adapter/runner 增加 provider 配置并传入 JSON-RPC initialize、处理 dashboard 模型候选,同时要求 dsh runtime 把 settings / credentials 插件纳入发行闭包;用真实 wheel 做启动 + 选路集成测试。不要用 agent-default-model 兜底——它不是这个 JSON-RPC 入口的选择源。

(附:若将来确实保留 credentials 文件方案,credentials-local 要求凭据文件 chmod 600,否则会拒绝启动,值得在文档里点一句。)

前三项已经修得很好,这节删掉或降级后我再复核一遍。

@liuhaoyang liuhaoyang closed this Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants