前言¶
在 DSH 或同类 Agent 宿主里跑模型请求,往往还要经过本机代理或上游网关。常见问题是:同一条代理对某个 API 通、对另一个不通,或者整段窗口里某条路全挂;客户端只配一个 HTTP_PROXY 时,失败只能反复在同一条死路上重试,既拖慢响应,也不利于排查。
DeepSeek Harness(DSH)社区把能力拆成插件维护。dsh-dual-proxy(维护者 gambn,分类:联网工具)把「Agent 到模型端点的网络路径」当成可管理资源:在多个候选网关、环境变量里的代理与直连之间自动选路,运行中用真实请求做通道验证,失败立刻切到下一条,无需重启宿主、也无需改客户端里的固定代理地址。
下面介绍它的定位、行为要点,以及作为 DSH 插件时的安装与配置。
这是什么¶
dsh-dual-proxy 的英文简介是:Agent gateway manager for DSH — online multi-gateway failover and channel health checks。同一套核心提供两种形态:
- DSH 插件(推荐):随 DSH 启停,在「设置 → 插件」里改配置,可通过
routeHostFetch把宿主进程内的fetch引到本代理; - 独立进程:全局安装 CLI,或挂 systemd 用户服务,非 DSH 的 Agent 与脚本也能用。
npm 包版本为 0.7.1,许可证 MIT。仓库:https://github.com/gambn/dsh-dual-proxy;社区目录页:https://www.skillhub.cn/plugins/gambn/dsh-dual-proxy(独立站点,与 DeepSeek / 幻方无官方从属关系)。
核心功能¶
候选路与选路顺序¶
客户端连到本地监听(默认 127.0.0.1:7898),由插件在候选之间切换。候选包括:upstreamHost:upstreamPort(或 upstreamUrl)、配置里的 gateways、启动环境中的 HTTP(S)_PROXY / ALL_PROXY(去重并排除自身监听端口),最后补 直连。示意如下:
客户端 → 127.0.0.1:7898
├── 网关 A(TLS 探测通过)→ 上游代理
├── 网关 B(TLS 探测通过)→ 上游代理
└── 都探测失败 → 直连
健康检查与失败处理¶
- 按
healthIntervalMs对healthUrl发GET,响应码<500视为通道可用(401/403 也算端点活着);通过者设为全局首选,全部失败则清首选、下次请求重新扫描;healthIntervalMs设为0可关闭后台验证。 - 每 host 偏好缓存(默认 TTL 60 秒,最多 1000 条 LRU):命中可跳过探测,失败即切换。
- 失败网关默认冷却 30 秒,期间排到队尾;任一路刚失败会触发一次健康检查(1 秒内防抖),不必等下一个周期。
HTTPS CONNECT 与普通 HTTP¶
- CONNECT(443):对候选先做 TLS 握手探测,通过的才开隧道;隧道建立后只转发字节,隧道内失败无法换路。
- 普通 HTTP:请求体先缓冲(上限 8 MB),按顺序换路重放;建连用
attemptTimeoutMs,等响应头用responseTimeoutMs。
重放与安全相关默认¶
自 0.6.0 起,POST / PATCH 在连接建立后失败默认不再自动换路重放,返回 502;需要旧行为可打开 retryNonIdempotentAfterSend。可选 retryNonIdempotentWithIdempotencyKey:仅当请求带非空 Idempotency-Key / X-Idempotency-Key 时,允许发送后重放非幂等请求。
上游代理 URL 可带 user:pass,连接时发送 Proxy-Authorization: Basic;0.7.0 仅支持 http:// 上游代理,upstreamUrl 传 https:// 会报错。日志只写主机、网关与原因,不记录请求内容。
接入 DSH 宿主¶
打开 routeHostFetch 后,进程内所有 fetch(不限于模型调用)会先到本代理再选路;pnpm、git 等子进程不继承,仍走启动环境代理。插件禁用或卸载时会还原原来的 undici dispatcher。
安装与启用¶
运行要求 Node.js ≥ 22.19.0(undici 8);插件形态还需要 dsh ≥ 0.1.7。
先做插件安装:
dsh plugin --profile web add dsh-dual-proxy
也可仅安装独立 CLI:
npm install -g dsh-dual-proxy
安装后确认 ~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 含 dsh-dual-proxy(包内 cordis.patch.yml 会参与组合),再重启 dsh web。若曾跑过独立进程或 systemd 用户服务,先释放默认端口 7898:
systemctl --user disable --now dsh-dual-proxy 2>/dev/null
插件以当前 DSH 进程权限运行;安装前建议自行查看仓库源码与 MIT 许可证。
典型配置(DSH 插件)¶
启用后在「设置 → 插件」中可改下列字段(节选,默认值以 README 为准):
| 字段 | 默认 | 说明 |
|---|---|---|
listenHost |
127.0.0.1 |
监听地址 |
listenPort |
7898 |
监听端口 |
upstreamHost / upstreamPort |
127.0.0.1 / 7897 |
第一个候选网关 |
upstreamUrl |
空 | 非空时覆盖 host/port,可含认证 |
gateways |
空 | 额外候选,host:port 逗号分隔 |
routeHostFetch |
false |
bundle patch 里默认可能为打开 |
healthUrl |
https://api.deepseek.com |
通道验证;空则关闭后台验证 |
healthIntervalMs |
60000 |
健康检查间隔;0 关闭 |
fastFailTimeoutMs |
2000 |
缓存/首选路径的建连超时 |
cooldownMs |
30000 |
失败网关冷却 |
attemptTimeoutMs |
5000 |
每次尝试/探测超时 |
responseTimeoutMs |
60000 |
普通 HTTP 等响应头上限 |
verifyTls |
true |
443 是否 TLS 探测 |
独立进程与 Claude Code 启动器¶
不挂 DSH 时,可直接跑 CLI,例如:
node bin/dsh-dual-proxy.js --listen-port 7898 --upstream-port 7897 \
--gateway 127.0.0.1:12334 --health-url https://api.deepseek.com
Claude Code 为原生二进制,不能像 DSH 那样改进程内 fetch;可用包内 claude-dual-proxy 在启动器里起代理,再给子进程设 HTTP_PROXY / HTTPS_PROXY:
claude-dual-proxy --listen-port 7896 --gateway 127.0.0.1:12334 -- --resume
systemd 用户服务模板在仓库 contrib/。
适用场景与注意¶
适合:本机有多条代理或网关、模型/API 请求需要在线 failover 与通道探测的 DSH web 配置;或非 DSH 脚本希望统一走一个本地入口选路。
边界(README 已说明):只处理 HTTP/1.1 的 CONNECT 与普通 HTTP;不做透明代理、不接管 UDP;不校验证书、不做鉴权;默认只绑回环,把 listenHost 改成非回环会在启动时告警。全部候选失败返回 502。
结尾¶
dsh-dual-proxy 把多网关、环境代理与直连握在同一本地入口,用健康检查与失败切换减少「单点代理拖死整批请求」的情况,并可选把 DSH 宿主内的 fetch 接入选路逻辑。