前言¶
給智能體接 OpenAI 兼容模型時,有一個不太顯眼但很致命的兼容性問題:部分網關的流式響應只推送內容增量和結束標記,從頭到尾不發 finish_reason 字段。DSH 的模型客戶端 pi-ai 會把這種流判定爲截斷(Stream ended without finish_reason),宿主再把它映射成一個 TRANSPORT 錯誤 finish。結果是:每次請求內容都完整送達了,回合卻以失敗告終。
典型的例子是 Snowflake Cortex 的 REST 網關(*.snowflakecomputing.com/api/v2/cortex/v1):它的 Chat Completions SSE 流會推送內容增量和一個終止的 data: [DONE],但從不發 finish_reason——純文本如此,工具調用也如此(非流式響應返回的是 finish_reason: "")。
要繞開它,要麼改 pi-ai 源碼,要麼在網關前架一層代理補字段,都要額外維護一處。下面介紹的 dsh-llm-finish-reason-tolerance 選了第三條路:在 harness 側把這條特定終態錯誤改寫成內容本應得到的成功 finish。
這是什麼¶
dsh-llm-finish-reason-tolerance 是一個 DeepSeek Harness(DSH)宿主插件,由 michael-han-il 維護,當前版本 0.2.0,MIT 許可證。它做的事很剋制:只針對「流式響應結束但沒有 finish_reason 且內容已送達」這一種終態錯誤做改寫,其餘一切原樣透傳。
實現上它沒有修改 pi-ai、dsh-llm 或 dsh-llm-pi-ai 的任何代碼,純靠 harness 的掛載機制工作。
工作原理¶
插件監聽 harness 的 llm/stream 瀑布流,全局註冊並前置,它返回的可迭代對象就是消費方實際迭代的那一個,因此能包裝每一個模型流。改寫規則分三種情況:
1、已交付 tool-call 塊 → 終態 finish 改爲 { kind: 'tool-calls' };
2、已交付文本內容 → 終態 finish 改爲 { kind: 'stop' };
3、未交付任何內容,或是其他錯誤 → 原樣透傳。
改寫是雙重門控的:pi-ai 的終態錯誤消息必須恰好是 Stream ended without finish_reason,且已有內容交付。真正的中途截斷在 pi-ai 裏表現爲不同的錯誤消息,不會被這條規則掩蓋。
安裝與啓用¶
先確認環境:需要 DeepSeek Harness(較新的部署已隨附 @deepseek-ai/cordis ≥ 4 與 @deepseek-ai/schemastery ≥ 3),Node ≥ 20。
這個插件是宿主級、單實例的:必須安裝進一個 profile(如 web)並掛載在宿主組合中,不要掛進 agent preset。
Bundle 安裝(推薦)¶
包以 profile bundle 形式發佈,自帶 cordis.patch.yml 補丁層(通過 package.json 裏的 dsh.bundle.patch 聲明),安裝即掛載,不需要手動編輯宿主組合:
dsh plugin --profile web add /path/to/dsh-llm-finish-reason-tolerance-0.2.0.tgz
這一條命令做了三件事:
1、pnpm 把包裝進 profile 的依賴樹;
2、dsh plugin CLI 檢測到 dsh.bundle.patch,自動把包追加進 profile 的 package.json 中 dsh.profile.bundles 列表:
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-llm-finish-reason-tolerance"] } }
3、下次啓動時,bundle 自帶的補丁層把 llm-finish-reason-tolerance 行插入宿主組合。
其他 profile 換成對應名字即可,例如 dsh plugin --profile tui add …。注意官方文檔給出的安裝形式是本地 tgz 路徑,沒有提供從 npm 或 GitHub 直接安裝的命令。
Plain 安裝(備選)¶
如果偏好普通依賴方式,先在 profile 目錄(如 ~/.dsh/profiles/web)裝包:
pnpm add /path/to/dsh-llm-finish-reason-tolerance-0.2.0.tgz
再在 ~/.dsh/profiles/<profile>/cordis.patch.yml 裏手動添加掛載行。profile 補丁層是一組補丁操作的列表,新行必須包在 insert: 裏——裸的 id: 行是對已有條目的覆蓋操作,匹配不到就靜默跳過:
# ~/.dsh/profiles/<profile>/cordis.patch.yml
- insert:
- id: llm-finish-reason-tolerance
name: dsh-llm-finish-reason-tolerance
config:
providers:
- snowflake-cortex
兩種安裝方式不要混用,否則掛載行會被插入兩次。
重啓¶
無論哪種方式,安裝後必須重啓 harness——插件是新模塊,只在啓動時掛載。重啓後,插件列表裏會出現 llm-finish-reason-tolerance。
配置¶
只有一個配置項:
| 字段 | 類型 | 默認值 | 含義 |
|---|---|---|---|
providers |
string[] |
[] |
本容錯行爲生效的 provider 路由鍵(對應 llm-pi-ai 設置段裏的命名)。[] 表示所有 provider。 |
Bundle 安裝默認帶的是 providers: [],即對所有 provider 路由生效。要限定到特定路由,在你自己的 profile 補丁層覆蓋這一行的 config:
# ~/.dsh/profiles/<profile>/cordis.patch.yml
- id: llm-finish-reason-tolerance
config:
providers:
- snowflake-cortex
能這樣覆蓋,是因爲 bundle 層先應用,它插入的行可以被後續層定位。注意 config 是整體替換,所以要寫全量。Plain 安裝則直接改 insert: 行裏的 providers 值即可。
配合 Snowflake Cortex¶
Snowflake 的 provider 要配置在 ~/.dsh/settings.yaml 的 llm-pi-ai 設置段下,路由鍵與插件的 providers 列表對應。默認空列表對所有路由生效,無需匹配;如果限定了範圍,路由鍵必須精確一致(如 snowflake-cortex)。
# ~/.dsh/settings.yaml
llm-pi-ai:
providers:
snowflake-cortex:
displayName: Snowflake Cortex (SG)
apiKeyEnv: SNOWFLAKE_CORTEX_API_KEY
api: openai-completions
baseURL: https://<account>.snowflakecomputing.com/api/v2/cortex/v1
models:
- id: claude-sonnet-5
name: Claude Sonnet 5
# 例如你賬號裏可用的其他模型
# - id: deepseek-r1
# name: DeepSeek R1
幾點說明:
1、api 用 openai-completions,baseURL 寫完整的 …/api/v2/cortex/v1;
2、OpenAI SDK 會自動用 apiKeyEnv 指向的環境變量做 Authorization: Bearer 認證,不需要額外配置 headers;
3、插件只解決缺失的 finish_reason;deepseek-r1 在該端點已棄用,裝不裝插件都無法使用。
適用場景與注意事項¶
適合的場景很明確:你的 DSH 接了會省略 finish_reason 的 OpenAI 兼容網關(典型是 Snowflake Cortex),每次請求內容都到、回合卻以 TRANSPORT 錯誤收場。裝上插件、重啓,回合就能正常完成。
安全性方面,改寫條件收得很緊:
1、只在 pi-ai 終態錯誤恰爲 Stream ended without finish_reason 且已有內容交付時觸發;
2、鑑權、限流、配額、真正的中途截斷、空響應等錯誤全部原樣透傳;
3、既沒有 finish_reason 也沒有內容的響應,保留原來的錯誤 finish。
需要自己留意的幾點:
1、插件以當前 dsh 進程的權限運行,安裝前建議先審查源碼和許可證(本項目爲 MIT,倉庫地址見文末);
2、宿主級單實例,裝進 profile,不要掛進 agent preset;
3、兩種安裝方式不可混用。
想參與開發的話,倉庫結構很直接:lib/index.js 是插件本體(純 ESM,無構建步驟,harness 直接加載),test/finish-reason.test.mjs 是自包含的包裝器測試。常用命令:
npm test # node --test,用合成的分塊流測試包裝邏輯
npm pack # 構建分發包
小結¶
一句話總結:當你接的 OpenAI 兼容網關不發 finish_reason 時,這個插件把「內容送達但回合失敗」變成「內容送達、回合正常完成」,且只改寫這一種情況,不掩蓋其他任何錯誤。安裝成本也低——bundle 形式一條命令,重啓即生效。
插件頁在社區目錄:https://www.skillhub.cn/plugins/michael-han-il/dsh-llm-finish-reason-tolerance (目錄頁將其歸在「模型推理」分類下;該目錄是獨立站點,與 DeepSeek、幻方無官方從屬關係)。源碼與 README:https://github.com/michael-han-il/dsh-llm-finish-reason-tolerance 。