# Windows 用 WSL 部署 DeepSeek Harness + Hermes Agent 实战避坑指南 > Windows11+WSL2 部署 dsh 与 Hermes 并 7x24 常驻的实战避坑(2026-09 实测) # Windows 上跑 DeepSeek Harness + Hermes Agent:用 WSL 部署的实战避坑指南 > 写作日期:2026-09-05 · 实测版本:DeepSeek Harness `@deepseek-ai/dsh@0.1.2-rc.1`、Hermes Agent `v0.21.0`、Node `v26`、Windows 11 + WSL2(Ubuntu 24.04) > > 本文不抄官方文档,全部来自在一台 Windows 11(GPD 掌机)上把两套 Agent 完整跑起来并“7×24 常驻”的真实踩坑记录。 --- ## 一、先说结论:为什么一定要用 WSL,而不是 Windows 原生 DeepSeek Harness(dsh)和 Hermes Agent 的工具链(终端检查、路径语义、bash/子进程、浏览器后端、各色 CLI)都是 **Linux-first**。 Windows 原生能装(dsh 官方有 PowerShell 支持、Hermes 有 install.ps1),但社区实测 + 我们实测的结论一致: - dsh 在 Windows 原生终端跑,会出现 `BashError: subprocess-local: terminal inspection is unsupported on platform win32`、`The path . is not an absolute path` 等持续报错; - 而 **WSL2 是完整 Linux 内核**,等于官方“最佳工况”,且和 macOS/Linux 服务器环境几乎同构——你从 mac/服务器迁移配置可以整包平移。 所以:**装进 WSL2 Ubuntu,浏览器从 Windows 侧打开**(界面走网络端口,不依赖图形系统)。 ### 资源占用(大家最关心) - WSL 空闲常驻内存约 **1.5~2.5GB**(Ubuntu 系统 + dsh/hermes 两个 node/python 进程),CPU 空闲接近 0,磁盘 3~6GB; - 一台 16~24GB 内存的机器常年挂着完全无感; - 可以封顶限流,见下文 `.wslconfig` 模板。 --- ## 二、WSL 前置配置(决定能不能“常驻”) WSL2 有两大坑,必须先处理,否则服务要么休眠、要么随会话死掉: ### 1. 关掉 WSL 空闲自动关机 默认 WSL2 在最后一个进程退出(或没有 wsl 客户端连接)约 60 秒后会把整个虚拟机休眠,**带走你所有后台服务**。 在 `C:\Users\<你>\\.wslconfig` 写: ```ini [wsl2] vmIdleTimeout=-1 # 禁用空闲自动关机(关键) memory=6GB # 内存上限(按需) processors=8 autoMemoryReclaim=gradual swap=2GB ``` 然后 `wsl --shutdown` 重启一次 WSL 生效。 ### 2. 无头服务可以关掉 WSLg(可选但推荐) WSLg/Weston 是 WSL 里跑 Linux 图形程序用的。dsh / hermes 都是**无头服务**,根本用不到图形,而 Weston 偶尔会段错误**把整个 WSL 虚拟机带崩**(我们一天崩了 3 次,dmesg 里全是 weston segfault)。 在 `.wslconfig` 加: ```ini guiApplications=false # 关闭 WSLg ``` 两个服务照常跑,浏览器访问不受任何影响(那是网络服务)。 ### 3. 在 Ubuntu 里开 systemd?——不推荐(重要) 很多教程让你 `systemd=true` 后把服务交给 systemd。**实测坑**:这台 WSL 里 systemd 会在 wsl 会话断开时把服务优雅停掉(journal 里出现 `Stopping dsh-web.service ... Deactivated successfully`),服务照样消失。 我们的最终方案是**绕开 systemd**,用“Windows 任务计划保持一条常驻 wsl 会话”,见第五节——比 systemd 可靠得多。 --- ## 三、安装 DeepSeek Harness(dsh) ### 1. Node 26 + 全局安装 Ubuntu 里先装 Node 26(源机/服务机建议同版本): ```bash curl -fsSL https://deb.nodesource.com/setup_26.x | bash apt-get install -y nodejs build-essential python3 ``` 然后装 dsh + pnpm(**npm 11 会拦截原生模块构建脚本,必须带 --allow-scripts 白名单**,否则 node-pty/ssh2/koffi 装完缺二进制): ```bash npm install -g --allow-scripts=@deepseek-ai/dsh-subprocess-local,koffi,node-pty,@google/genai,protobufjs @deepseek-ai/dsh@0.1.2-rc.1 pnpm@11.22.0 ``` > Hermes Agent 一条命令:`curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash`(自动装 uv + Python 3.11 + Node 等,装到 `~/.hermes`)。 ### 2. 从 mac/服务器迁移配置(最省事) dsh 是“配置即文本”架构,迁移别拷整个 `~/.dsh`(几百 MB 的 node_modules 是平台相关二进制,拷了会坏)。打包这些即可: ``` ~/.dsh/settings.yaml # 设置/供应商/默认模型 ~/.dsh/profiles/web/package.json # 插件“配方”(dependencies + dsh.profile.bundles 顺序) ~/.dsh/profiles/web/cordis.patch.yml ~/.dsh/profiles/web/pnpm-lock.yaml / pnpm-workspace.yaml ~/.dsh/skills、~/.dsh/.agent-presets、皮肤等 ~/.modlens/config.json ~/.modsearch/config.json # 视觉/搜索桥(易漏!在 ~/.dsh 之外) ``` 目标机解压到对应位置后: ```bash cd ~/.dsh/profiles/web && pnpm install ``` **三个必踩的坑:** 1. **pnpm 11 有 30 天发布年龄门**,新版本解析不到 → 在 `pnpm-workspace.yaml` 顶部加: ```yaml minimumReleaseAge: 0 ``` 2. **`dsh plugin add/remove` 会把“已从 bundles 手动摘除的插件”自动加回去**(依赖还在就按 dsh.bundle 重新收录)→ 每次动完插件复查 `package.json` 的 `dsh.profile.bundles`。我们因为插件不兼容 0.1.2 停用两个插件,被它反复“复活”,重启就崩。 3. 密钥 `~/.dsh/.credentials.yaml` 单独 scp + `chmod 600`;**不要迁移 telegram bot token**(每台机器配自己的)。 ### 3. 验证 ```bash dsh --profile web --dump-config # 看组合树; 无 "not found" 噪音 dsh web --no-open # 前台启动试跑 ``` --- ## 四、远程/局域网访问的三个坑(dsh) ### 坑 A:横幅打印的 LAN 地址是假的 dsh 启动会打印 `(LAN: http://172.17.x.x:3080)`——那是 **WSL 的 NAT 内网地址,局域网其他设备根本路由不到**。这是 WSL 拓扑限制(Linux 侧看不到 Windows 物理网卡),不是配置错误。 解法(Windows 侧转发 + 防火墙): ```bash netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=3080 connectaddress= connectport=3080 netsh advfirewall firewall add rule name="DSH-3080" dir=in action=allow protocol=TCP localport=3080 ``` > WSL 的 IP 会随 VM 重启变化,转发断了用 `wsl hostname -I` 取新 IP 刷新即可。 ### 坑 B:远程打开 Web UI 一直“连接中” 页面能开但永远“连接中”——这是 **trustedHosts 信任围栏**:实时通道(WebSocket/SSE)只放行 回环 + dsh 检测到的 LAN IP + 显式白名单。你从 `192.168.x.x` 访问,必须把它加白名单。 在 `~/.dsh/profiles/web/cordis.patch.yml` 加: ```yaml - id: web-runtime config: trustedHosts: - "192.168.38.58" # 换成你的 Windows 物理网卡 IP ``` 重启后远程即不再卡“连接中”。**本机访问**始终走 `127.0.0.1:3080/?token=...`(token 是官方鉴权,每次重启会换,记下启动日志里的 URL;手机/平板推荐用 dsh-pocket 插件开在 3081 端口的入口,有独立 PIN)。 ### 坑 C:想让横幅也打正确地址(可选补丁) 改 WSL 里全局包 `.../node_modules/@deepseek-ai/dsh-web-app/lib/index.js` 三处:Config 加 `lanHost`、`resolveLanTrust` 加 override 参数、调用处传 `config.lanHost`,再在 cordis.patch 里配 `lanHost: "你的IP"`。 ⚠️ 注意该包 schema 用的是 **Schemastery 不是 zod**——写 `.optional()` 会直接崩启动(`z.string(...).optional is not a function`),要用 `.default("")`。**dsh 升级会覆盖此补丁**,需重打,非必需可不做。 --- ## 五、让 dsh / hermes 7×24 常驻(任务计划 + 隐藏窗口 + 自愈) 这是最容易翻车的一环,把最终验证可用的方案完整给你: ### 1. WSL 内的循环脚本(带单实例锁) `/home//run-dsh-loop.sh`: ```bash #!/bin/bash export PATH="$HOME/.local/bin:$PATH" exec 9>/home//.dsh-loop.lock flock -n 9 || { echo "已有实例,退出"; exit 0; } # 单实例锁 while true; do source ~/.dsh/.env dsh web --no-open >> ~/.dsh-web.log 2>&1 & pid=$! sleep 6 grep -oE "http://127.0.0.1:3080/\?token=[A-Za-z0-9_-]+" ~/.dsh-web.log | tail -1 > ~/dsh-web-url.txt wait $pid sleep 5 # 崩溃自动重启 done ``` hermes 同理(`hermes gateway run --accept-hooks`,官方推荐 WSL 前台模式)。 ### 2. 隐藏窗口启动器 SSH/任务计划起的控制台程序默认会在桌面弹黑窗口。用一个 vbs 隐藏: ```vbs ' C:\Users\\run-hidden.vbs CreateObject("WScript.Shell").Run WScript.Arguments(0), 0, False ``` ### 3. Windows 计划任务(5 分钟自愈看门狗) 关键设计:**不要用 ONLOGON**(只在登录时拉一次,VM 中途重启不会复活);改成**每 5 分钟触发**——wsl.exe 每次触发会自己把 VM 拉起来,flock 保证只有一份真跑,其余秒退。VM 任何时刻重启,5 分钟内自动满血复活。 ```bash schtasks /Create /F /TN DSH-Web /SC MINUTE /MO 5 /RL HIGHEST /TR "\"wscript.exe\" \"C:\Users\\run-hidden.vbs\" \"wsl.exe -d <发行版> -u <用户> -- bash -lc /home//run-dsh-loop.sh\"" schtasks /Run /TN DSH-Web ``` ### 4. 运维必须知道的 schtasks 坑 - **`/Change /TR` 的嵌套引号会被 cmd 吞掉、静默无效** → 用 `/Delete + /Create` 重建,建完 `schtasks /Query /XML` 验证; - **`/Delete` / `/End` 杀不掉已在运行的子进程树**(wsl 会话变成孤儿继续跑,还会反复 `\`/Run` 攒出一堆抢服务的僵尸循环)→ 真正重启用 `taskkill /PID /T /F` 或进 WSL `pkill`; - 排查进程用 PowerShell:`Get-CimInstance Win32_Process | ? { $_.Name -match 'wsl|cmd' }` 按 SessionId/命令行过滤。 --- ## 六、Hermes Agent 的模型与网关配置 ### 1. 填模型凭据 hermes 读 `~/.hermes/.env`(原生 env 文件,注意不是 config): ```bash # ~/.hermes/.env DEEPSEEK_API_KEY=sk-xxx # KIMI_API_KEY=sk-xxx # 备用 ``` 设置默认模型: ```bash hermes config set model.provider deepseek hermes config set model.default deepseek-v4-flash hermes config set model.base_url https://api.deepseek.com hermes -z "测试" # 无头验证 ``` > 经验:aux 每次探测 nous/OpenRouter 未配置的报错是**纯日志噪音**;DeepSeek 不支持 structured-output 时标题生成会自动降级重试——都是预期行为,别被吓到。 ### 2. Telegram 网关 - **每台机器用自己的 bot**(两个网关共用同一 bot 会抢消息); - token 写 `~/.hermes/.env`:`TELEGRAM_BOT_TOKEN=...`; - 发送者白名单:`TELEGRAM_ALLOWED_USERS=<你的数字ID>`(不配会被 deny); - 运行:`hermes gateway run --accept-hooks`(WSL 前台,配进上面的任务计划);验证:`hermes gateway status` / `hermes doctor`。 --- ## 七、维护速查 ```bash # dsh 最新访问 URL(token 每次重启轮换) cat ~/dsh-web-url.txt # 服务状态 hermes gateway status curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3080/ # 干净重启一个服务(别用 schtasks /End) wsl -d <发行版> -u <用户> -- pkill -f "dsh web --no-open" # 循环脚本会自愈重启 ``` - 两套都处 developer preview,升级前先备份 `~/.dsh` 配置与 `~/.hermes`,升级后重跑 `hermes doctor`、复查 bundles; - 插件若与新版不兼容,优先**从 bundles 摘除等作者适配**(依赖保留),别硬留导致启动崩溃循环。 --- ## 参考 - DeepSeek Harness:https://github.com/deepseek-ai/deepseek-harness - Hermes Agent:https://github.com/NousResearch/hermes-agent - BrowserSkill(浏览器自动化后端,dsh 插件用):https://github.com/Tencent/BrowserSkill > 本文为个人实战记录,版本迭代快,以官方文档为准。 --- **分类**:教程 **标签**:WSL · dsh · hermes **作者**:子龙 **链接**:https://octohz.com/p/2108