前言¶
在 DeepSeek Harness(DSH)裏,一個網關往往掛多種模型,背後可能是不同上游。同一 host、同一 API key、同一請求形態下,有的模型在本地區正常,有的會因區域限制、限速或只能經公司代理訪問而失敗——這與「隔壁模型」無關。
進程級環境變量代理按 host 路由:HTTP_PROXY 無法區分同一 host 上的不同模型。整網關走代理會把本來正常的模型一起拖進代理;完全不設代理則受限模型一直失敗。下面介紹社區插件 dsh-model-proxy,它從每次請求體裏讀出 model 字段,只把你指定的模型名發往代理,其餘保持原有出站路徑。
這是什麼¶
dsh-model-proxy(GitHub:windwhiterain/dsh-model-proxy,維護者 windwhiterain)是 DSH 的 bundle 插件,分類爲模型推理。一句話:在 Harness 內做按模型名的出站代理路由,並在代理 TCP 不可達時可選地回退到插件加載前的直連路徑。
插件以當前 dsh 進程權限運行;安裝前應自行查看倉庫源碼與 MIT 許可證。社區目錄頁見 SkillHub 插件頁,與 DeepSeek / 幻方無官方從屬關係。
路由方式¶
DSH、pi-ai 與各類 provider SDK 通過 globalThis.fetch 發請求,最終落到 undici 的全局 dispatcher 槽位(Symbol.for('undici.globalDispatcher.1'))。本插件佔用該槽位,按順序決策:
- Origin:目標 host 不在
origins內,或請求無 body,則保持插件加載前的路由。 - 代理可達性:在開啓
fallbackToDirect且代理host:portTCP 拒絕連接時,候選請求仍走上述「先前路由」,避免代理停掉後拖垮全進程調用。 - 模型名:讀取 body 頭部 JSON 的
"model"字段;若匹配models中的模式,則經proxy出站。
爲解析模型名,插件只緩衝 body 的前若干 chunk,並在解析前 yield 已讀 chunk,不改變 chunk 邊界、背壓與流式響應行爲。配置裏的 direct 指進程在裝插件前已有的路由;若啓動環境未設代理,則未匹配請求與未裝插件時行爲一致。
核心配置項¶
bundle 自帶的 cordis.patch.yml 會插入 id: model-proxy 行;也可在 <DSH_HOME>/profiles/<profile>/cordis.patch.yml 熱重載編輯,或通過 settings 層覆蓋同 id。無效配置會導致激活失敗,而不是靜默走錯路由。
- id: model-proxy
config:
enabled: true
proxy: http://127.0.0.1:10793
origins:
- opencode.ai
models:
- grok-*
- gpt-*-luna
fallbackToDirect: true
probeTimeoutMs: 300
probeCacheMs: 2000
peekBytes: 16384
| 字段 | 含義 |
|---|---|
enabled |
爲 false 時不安裝攔截,所有請求保持原路由 |
proxy |
匹配模型使用的代理端點,支持 http: / https: 及可選憑據 |
origins |
僅對這些 host 上的請求做模型匹配 |
models |
模型名模式,* 匹配任意字符段 |
fallbackToDirect |
代理不可連時,匹配請求改走先前路由 |
probeTimeoutMs |
可達性探測 TCP 連接超時 |
probeCacheMs |
探測結果緩存時長,突發請求只探一次 |
peekBytes |
爲找 model 字段最多窺視的 body 字節數 |
安裝與啓用¶
在目標 profile 的 package.json(路徑:<DSH_HOME>/profiles/<profile>/package.json)註冊依賴與 bundle:
{
"dependencies": {
"dsh-model-proxy": "github:windwhiterain/dsh-model-proxy"
},
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "dsh-model-proxy"]
}
}
}
本地開發可用 link:C:/path/to/dsh-model-proxy 等形式。隨後安裝該 profile 並重啓 dsh 一次;bundle 的 cordis.patch.yml 會寫入默認 model-proxy 配置。
不要同時爲 dsh 進程設置 HTTP_PROXY / HTTPS_PROXY。啓動環境中的代理會讓 dsh 安裝進程級 dispatcher,本插件會把該路徑當作「非代理模型」的保留路由,與「僅部分模型走代理」的目標相反。啓動環境保持直連,由本插件單獨處理需要代理的流量。
典型用法¶
同一網關內,部分模型需翻牆或區域代理¶
README 示例:OpenCode Go 在同一網關提供 grok-* 與 gpt-*-luna,在受限地區上游返回 unsupported_country_region_territory,而 GLM、Kimi、DeepSeek 等同 key 同 region 正常。用上文 YAML 只把兩類受限模型經支持地區的代理發出,其餘直連;關閉代理後,在 fallbackToDirect: true 下直連模型不受影響。
直接調該網關時,請求仍需帶 x-opencode-session 頭(否則 400 MissingSessionID),與是否啓用本插件無關。
單一 provider 走公司代理¶
- id: model-proxy
config:
proxy: http://proxy.corp.example:3128
origins: [api.example.com]
models: ['*']
整段網關經不同出口,進程其他 host 不變¶
與上例相同,將 models 設爲 ['*'];路由仍按請求決策,之後在別處新增的 host 不會被這一條連帶代理。
診斷與自測¶
決策會寫入宿主 logger,例如:
dsh-model-proxy: models grok-*, gpt-*-luna on opencode.ai via http://127.0.0.1:10793, direct while it is down
dsh-model-proxy: route=proxy via=http://127.0.0.1:10793 reason=model:grok-4.7
dsh-model-proxy: route=direct via=direct reason=model-other:glm-5.3-flash
dsh-model-proxy: route=direct via=direct reason=proxy-down
dsh-model-proxy: route=direct via=direct reason=origin
卸載時會恢復被替換的 dispatcher,便於後續插件佔用同一槽位。
倉庫提供 probe 腳本(需配置可讀憑據):
node probe/smoke.mjs # 代理可達
node probe/smoke.mjs --proxy-down # 回退路徑
適用場景與注意¶
適合誰:在同一 DSH 網關混用多模型、且只有部分模型需要獨立出站代理的智能體或網關維護者;需要代理宕機時其餘模型仍可用的部署。
進程內生效:路由邏輯只在 dsh 進程內生效;shell 工具拉起的 curl、git 等仍遵循啓動環境,本插件刻意不修改後者。
模型名來源:僅從 body 讀取 model;僅寫在 header、或 model 出現在 peekBytes 之後的請求不會匹配,保持直連。
代理探測範圍:探測回答「端口是否在監聽」,不驗證上游是否可用;代理能連但上游失敗時,該請求仍會失敗。
單條配置一個 proxy:不同模型族要走不同代理,需要多條 model-proxy 行(README 說明當前實現對此有約束,截斷處未展開完整表述)。
依賴版本:插件將 undici 固定爲 ~8.10.0;8.11.0 在 Node 24 下接管全局 dispatcher 時可能破壞 content-encoding 解碼。與 @deepseek-ai/dsh-http-proxy 的 undici 約束同類,鎖文件升級需謹慎並重跑 probe 斷言。
兼容性:依賴 Node 自帶 fetch 所暴露的全局 dispatcher 槽位;除「帶 fetch 的 Node」外 README 未假定更細版本。
結尾¶
dsh-model-proxy 解決的是「同一 API host、不同模型不同可達性」下的出站問題:用模型名模式選擇代理,可選在代理不可連時回退,並避免整進程 HTTP_PROXY 的一刀切副作用。目錄與源碼: