前言¶
在 DeepSeek Harness(DSH)里,智能体调用大模型通常走 Node 内置的 fetch,底层是 undici。维护者 mqnu00 在 README 里记录过一个很具体的问题:connectTimeout 固定为 10 秒(DNS、TCP、TLS 一并计入),没有环境变量或参数可调;在 WSL 直连 opencode.ai 时,新建握手曾测到约 11.6 秒,容易被截断。pi-ai 每次重试会 new OpenAI(...),当场通过 Shims.getDefaultFetch() 读取全局 fetch,于是每次尝试都是新的 TCP+TLS,超时后重试又重连。对比之下,OpenCode 的 Go 客户端可以复用已建立的 HTTP/2 连接,握手只做一次,同类网络下往往更稳。
dsh-undici-timeout 针对的是这条 LLM 出口链路:用可配置超时和 keep-alive 的 undici Agent 接管白名单域名的直连流量,尽量做到「长超时 + 保活复用」,同时不与代理插件抢 setGlobalDispatcher。下文按定位、能力、安装与排障说明用法;事实以 GitHub 仓库 README 为准。
这是什么¶
- 名称:
dsh-undici-timeout(GitHub:mqnu00/dsh-undici-timeout) - 维护者:mqnu00
- 分类:联网工具(社区目录 SkillHub 条目)
- 定位:给 DSH 的 LLM 出口(
globalThis.fetch→ Node 内置 undici)换一条可配置超时、可保活复用的直连通道;单文件补丁、零 npm 依赖,只从 GitHub 分发,不发 npm、不进插件市场。
README 明确把它写成「补丁」而非长期功能块:一旦上游解决固定 10s 连接超时、或 DSH LLM 层可自行指定 dispatcher/超时,或换掉内置 fetch,即可卸载。
核心功能¶
包装全局 fetch,只接管白名单直连¶
请求路径大致为:agent 回合 → dsh-llm-retry → dsh-llm-pi-ai → pi-ai → OpenAI 客户端每次尝试读取全局 fetch → 本插件包装后的 globalThis.fetch。
- 命中
hosts白名单且未检测到代理环境变量时:使用自建的 undiciAgent调用undici.fetch(url, { dispatcher })。 - 代理模式、非白名单域名或
Request输入等:透传原来的fetch。
插件不调用 setGlobalDispatcher,避免与 @deepseek-ai/dsh-http-proxy 冲突;当存在 http_proxy / https_proxy / ALL_PROXY(大小写均可)且 delegateOnProxyEnv 为默认 true 时自动让路,可与 dsh-net-proxy、dsh-net-autoswitch 共存。
可调超时与连接复用¶
通过 cordis.patch.yml 中本插件的 config 配置(节选 README 表格中的核心项):
| 键 | 默认 | 说明 |
|---|---|---|
connectTimeoutMs |
30000 |
DNS+TCP+TLS 总预算(对比内置固定 10s) |
headersTimeoutMs |
300000 |
等待响应头 |
bodyTimeoutMs |
0 |
相邻 body 块间隔;0 表示关闭 |
keepAliveTimeoutMs |
60000 |
空闲连接保留,便于复用 |
keepAliveMaxTimeoutMs |
600000 |
保活上限 |
pipelining |
1 |
每连接并发请求数 |
family |
0 |
0 跟随系统;4/6 强制 IP 族 |
allowH2 |
false |
默认关闭 HTTP/2(见下文注意) |
hosts |
["opencode.ai"] |
含子域;"*" 表示全部直连流量 |
HTTP/2 默认关闭¶
undici 8 将 allowH2 默认改为 true。README 记录:用 harness 解析到的 undici 8 建 Agent 时,ALPN 可能变成 h2,而 opencode.ai 经 Cloudflare 时,h2 上的 SSE 长流可能出现 NGHTTP2_ENHANCE_YOUR_CALM 等流错误。插件默认 allowH2: false,在保持 keep-alive 复用的前提下继续走 h1。
状态路由与探针¶
- 只读状态:
routePath默认为/_dsh/undici-timeout。 - 启动探针:
probeOnStart默认true,默认打 LLM 网关 models 端点(不消耗 token)。 - 日志:默认写入
$DSH_HOME/logs/dsh-undici-timeout/host.log;logFailures默认记录失败及 cause。
安装与启用¶
官方安装命令(README 原文):
dsh plugin --profile web add github:mqnu00/dsh-undici-timeout
装完后重启 DSH 生效。需要钉版本时:
dsh plugin --profile web add github:mqnu00/dsh-undici-timeout#v0.1.1
package.json 要求 Node >=22.19.0、DSH >=0.2.0,许可证 MIT。零 npm 依赖、无构建步骤;勿与 file:// 直插 profile 的同名 insert id undici-timeout 同时使用,否则会冲突。
卸载示例(README):
dsh plugin --profile web remove dsh-undici-timeout
典型用法¶
1、用 GitHub 源安装后按需改配置¶
在 profile 的 cordis.patch.yml 里找到本插件的 config,按网络环境调整。WSL 或 IPv6 无出口时,README 路线 B 示例会设 family: 4 并保持 allowH2: false:
config:
enabled: true
hosts: [opencode.ai]
connectTimeoutMs: 30000
family: 4
allowH2: false
bodyTimeoutMs: 0
keepAliveTimeoutMs: 60000
2、查看运行状态¶
在本机回环访问状态路由(Host 白名单仅放行回环/本机网卡):
curl -s http://127.0.0.1:3080/_dsh/undici-timeout | python3 -m json.tool
关注 stats.matched / ok / failed / byError 等字段,判断接管次数与失败类型。
3、手动触发探针¶
curl -s -X POST http://127.0.0.1:3080/_dsh/undici-timeout \
-H 'x-dsh-undici-timeout: 1' -H 'content-type: application/json' \
-d '{"action":"probeNow"}'
适用场景与注意¶
适合谁
- DSH
webprofile 下频繁调用 LLM API,直连握手经常顶到内置 10sconnectTimeout的用户。 - 需要拉长连接阶段预算、并通过 keep-alive 减少重复握手的环境。
- 与 net-proxy / autoswitch 同机部署,希望代理开启时插件自动让路、关闭时代理直连的用户。
使用前请知悉
- 插件以当前 DSH 进程权限运行,安装前应在 GitHub 阅读源码与 MIT 许可证,自行评估是否纳入环境。
- 它是针对 Node/undici 上游行为的补丁;DSH 或 Node 侧一旦提供等价能力,应按 README 建议卸载,避免长期叠补丁。
connectTimeoutMs调大不能单独解决「双栈 happy eyeballs 在无效 IPv6 上耗光整轮」类问题;README 实测建议在无 IPv6 出口时考虑family: 4。- 社区目录 SkillHub 与 DeepSeek / 幻方无官方从属关系;DSH 生态强调「一切皆插件」,本文介绍的是社区维护的联网工具插件。
结尾¶
dsh-undici-timeout 用最小改动接管 LLM 出口的直连 fetch:可调连接/头/body/保活超时、默认关 h2、代理环境自动退让,并提供状态路由与探针便于排障。需要细节配置表、h2 问题分析与卸载条件时,以仓库 README 为准。