用 ai-sdk-provider-dsh 把 DeepSeek Harness 接到 AI SDK

前言

已經用 Vercel AI SDK 搭好應用的人,編排入口通常是 generateText / streamText:換模型只換 provider,工具、流式輸出和取消信號都走同一套接口。DeepSeek Harness(命令名 dsh)走的是另一條路——它是 DeepSeek AI 開源的智能體運行時,官方倉庫 deepseek-ai/deepseek-harness 寫明核心理念是「一切皆插件」:agent 循環、工具、技能、會話都可以在配置層組合。兩邊各管各的,常見結果是:要麼把業務遷進 dsh,要麼自己再寫一層進程封裝。

社區包 ai-sdk-provider-dsh 做的事情比較直接:把一份 dsh 運行時藏到 AI SDK 的 LanguageModel 後面。調用方式和普通語言模型一樣,實際跑起來的是 harness 裏的 agent 循環,工具也在 harness 內部執行。倉庫 README 把它類比爲 ai-sdk-provider-claude-code 驅動 Claude Code 的方式,編排面仍留在 AI SDK。

社區裏還有一份獨立的插件目錄 deepseek-harness-plugin.com。它和 DeepSeek / 幻方沒有官方從屬關係,不能當成官方應用商店。本文按該目錄詳情頁、GitHub 倉庫 README / package.json / 源碼,以及 npm 頁面交叉覈對後整理。

這是什麼

ai-sdk-provider-dsh 是一款社區開源的 AI SDK provider,由 krislavten 維護,倉庫是 krislavten/ai-sdk-provider-dsh,許可證 MIT,主要語言 TypeScript。目錄頁把它歸在「模型與提供方」,收錄日期 2026-08-14。npm 當前版本是 0.2.0(CHANGELOG 記在 2026-08-14;npm 發佈時間 2026-08-13)。截至本文查閱時,GitHub 星標爲 2(目錄頁當時顯示 1,以倉庫爲準)。

它解決的是這一類任務:

  • 現有應用已經用 AI SDK 編排,但希望調用 dsh 的完整 agent,而不是隻打一發 chat completion
  • 需要 bash、讀寫文件、subagent、會話持久化這些 harness 能力,同時不想換掉 streamText 這一層
  • 希望同一份構建同時跑在 AI SDK v6 和 v7 上

實現上,每個 provider 實例會惰性拉起一個 dsh 子進程,雙方走 stdio JSON-RPC(dsh SDK 協議)。doGenerate / doStream 把 AI SDK 的 LanguageModelV3CallOptions 轉成 dsh prompt,再把運行時的 session.event 映射回 AI SDK 的流式分片。

有兩點需要先說清楚。目錄頁給出的安裝命令是 dsh plugin add github:krislavten/ai-sdk-provider-dsh;覈對 package.json 後,這個包沒有聲明 dsh.bundle。README 的主路徑是 npm install ai-sdk-provider-dsh:在 Node 應用里拉起捆綁好的運行時,而不是往現有 dsh 進程裏再掛一張模型卡片。下文兩套命令都會寫,用法以倉庫 README 爲準。

核心功能

一份構建覆蓋 AI SDK v6 與 v7

源碼裏的語言模型實現 specificationVersion: 'v3',走的是 AI SDK 的 LanguageModelV3 接口。README 給出的兼容矩陣如下:

AI SDK @ai-sdk/provider 狀態
ai@^6 @ai-sdk/provider@^3 支持
ai@^7 @ai-sdk/provider@^4 支持(v7 把 V3 模型當作一等公民)

package.json 的 peer 依賴與上表一致:ai^6.0.0 || ^7.0.0@ai-sdk/provider^3.0.0 || ^4.0.0。運行時要求 Node.js >=22.19,模塊格式只有 ESM。

運行時打進包裏,版本釘死

dsh 目前仍是開發者預覽(0.1.0-rc.x),官方 README 寫明會有破壞性變更。這個 provider 把 harness 家族精確釘在 0.1.0-rc.6,依賴列表裏是 @deepseek-ai/dsh-sdk-jsonrpc-demo@deepseek-ai/dsh-bash-local@deepseek-ai/dsh-tool-fs 這類包的精確版本,而不是版本範圍。README 的態度是:升級 pin 必須是顯式決定,不能靠 range 漂移。

包內自帶默認組合 runtime/cordis.yml,以及 dsh-jsonrpc-agent 可執行入口。創建一個 provider 實例就會拉起一份能工作的運行時,不必再單獨安裝 dsh CLI。

默認組合暴露的模型側工具是:

  • bash(前臺執行,默認組合裏關閉了後臺 run)
  • read / write / edit(本地文件系統)
  • subagent
  • todo_write

另外還有 JSONL 會話持久化,以及自動上下文壓縮。工具在 harness 內部執行:流裏會出現 providerExecuted: true 的 tool 分片,AI SDK 不會再執行一遍。這也是它和普通「模型 provider」最大的差別——你調的是一個帶工具的 agent,不是裸模型。

默認 cordis.ymlskills.enabledfalse。技能若要啓用,走的是 dsh 原生機制:從 .dsh/skills.agents/skills$DSH_HOME/skills 發現 SKILL.md;README 寫明 不會套用 reskill 的 skills.json / skills.lock。目錄簡介裏提到的 MCP,在這份默認組合裏沒有對應插件,不能當成開箱即用。

多輪會話、元數據與錯誤分類

同一個 provider 實例對應一個運行時子進程。sessionId 默認是新 UUID;寫成固定值後,後續調用會繼續同一條 harness 會話(運行時持久化 session log)。倉庫 examples/multi-turn.ts 用「第一輪寫入暗號、第二輪再問」做過端到端驗證。

每次響應會在 providerMetadata['dsh'] 下帶:

字段 含義
sessionId 這次調用跑在哪條 harness 會話上
turnId 最後觀察到的輪次號(可選)
terminalReason 結束類型不是 completed 時的原因:abortederrormax-tokensblockedinterrupted

取元數據的位置因大版本而異:AI SDK v7 看 result.finalStep.providerMetadatastreamTextawait stream.finalStep);v6 看 result.providerMetadata

運行時邊界上的失敗會歸類成 AI SDK 的 APICallError,消息裏會附一段清洗過的 stderr 尾部。傳輸斷開、請求超時可重試;協議違例、運行時拒絕請求不可重試。選 deepseek-official 卻沒有 DEEPSEEK_API_KEY 時,會盡快拋出映射到 LoadAPIKeyError 的錯誤,而不是默默吐空輸出(回放模式 DSH_SNAPSHOT_FILE 除外)。

安裝與啓用

目錄頁上的安裝命令原文如下,在 DeepSeek Harness 終端裏運行:

dsh plugin add github:krislavten/ai-sdk-provider-dsh

如需可復現安裝,目錄頁給出的寫法是固定 commit 哈希:

dsh plugin add github:krislavten/ai-sdk-provider-dsh#commit

commit 換成實際哈希即可。插件以當前 dsh 進程的權限運行,安裝時可能執行代碼。安裝前請檢查源代碼倉庫和許可證。

對「在 AI SDK 應用裏調用 dsh」這條主路徑,倉庫 README 寫的是 npm:

npm install ai-sdk-provider-dsh

憑據走運行時環境變量:

export DEEPSEEK_API_KEY=sk-...
export DEEPSEEK_BASE_URL=https://api.deepseek.com

DEEPSEEK_API_KEY 必填(走 deepseek-official 時)。DEEPSEEK_BASE_URL 可選,指向任意 OpenAI 兼容網關都可以。

Linux 上默認組合依賴 node-pty,安裝時現場編譯,沒有預構建包。最小容器、缺少 libc6-dev 的 WSL 等環境編不過去時,改用包內的 runtime/cordis.minimal.yml(無 bash)。package.json 還把 node-pty@deepseek-ai/node-addon-landlock-run 列進了 onlyBuiltDependencies,安裝階段會跑原生構建,需要同樣謹慎。

典型用法

下面的示例均來自倉庫 README 與 examples/,把 import 路徑換成已發佈包名即可。用完後調用 await dsh.close() 拆掉子進程(冪等);拆卸順序是 EOF → SIGTERM → SIGKILL。

AI SDK v7:streamText

v7 用 instructions 寫系統說明:

import { streamText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({
  runtime: { provider: "deepseek-official", model: "deepseek-v4-flash" },
});

const result = streamText({
  model: dsh.languageModel("deepseek-v4-flash"),
  instructions: "You are a coding agent.",
  prompt: "run the tests",
});

const text = await result.text;
console.log(text);

createDsh 返回的對象可以 dsh.languageModel("deepseek-v4-flash"),也可以直接 dsh("deepseek-v4-flash"),這是 AI SDK provider 的慣例別名。runtime.providerruntime.model 都會在運行時握手時傳給 dsh;provider 可以是 deepseek-official,也可以是 pi-ai 目錄裏的路由。

AI SDK v6:字段名不同

v6 沒有 instructions,系統說明字段叫 system

import { streamText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({
  runtime: { provider: "deepseek-official", model: "deepseek-v4-flash" },
});

const result = streamText({
  model: dsh.languageModel("deepseek-v4-flash"),
  system: "You are a coding agent.",
  prompt: "run the tests",
});

generateText

一次性取完整文本:

import { generateText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({
  runtime: { provider: "deepseek-official", model: "deepseek-v4-flash" },
});
const { text } = await generateText({
  model: dsh.languageModel("deepseek-v4-flash"),
  prompt: "say hello",
});

固定 sessionId 做多輪

倉庫示例 examples/multi-turn.ts 的核心是:同一個 provider、同一個 sessionId,第二輪能讀到第一輪寫入的上下文。

import { streamText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({
  runtime: {
    provider: "deepseek-official",
    model: "deepseek-v4-flash",
    sessionId: "example-session-1",
  },
});

async function ask(prompt: string): Promise<string> {
  const result = streamText({
    model: dsh.languageModel("deepseek-v4-flash"),
    prompt,
  });
  return (await result.text).trim();
}

const t1 = await ask("The secret code is X7Q9. Reply: stored");
const t2 = await ask("What is the secret code? Reply with only the code.");

不傳 sessionId 時,每個 provider 實例仍會複用自己的子進程,但會話 id 是新的。需要跨調用記住上下文時,顯式傳入固定 id。

換一份最小運行時

不能編 node-pty 時,把 configPath 指到包內的最小組合:

runtime: {
  provider: "deepseek-official",
  model: "deepseek-v4-flash",
  configPath: require.resolve("ai-sdk-provider-dsh/runtime/cordis.minimal.yml"),
}

package.jsonexports 裏有 ./runtime/* 子路徑,所以 require.resolve 這一寫法是包正式支持的。常用覆蓋項還有:cwd(默認 process.cwd())、env(默認繼承 process.env,可傳 DSH_CWDDSH_SESSION_ROOT)、maxTokens(根 agent 每次請求的輸出 token 上限)。

讀錯誤裏的 stderr

import { generateText } from "ai";
import { createDsh, getErrorMetadata, isAPICallError } from "ai-sdk-provider-dsh";

try {
  await generateText({
    model: dsh.languageModel("deepseek-v4-flash"),
    prompt: "Hello!",
  });
} catch (error) {
  if (isAPICallError(error)) {
    console.error(getErrorMetadata(error)?.stderr);
    console.error("retryable:", error.isRetryable);
  }
}

適用場景與注意事項

比較適合:

  • 已經用 AI SDK 做編排,想把 dsh 當作「會跑工具的語言模型」嵌進去
  • 需要 harness 側的 bash / 文件編輯 / subagent / 會話日誌,同時保留 generateText / streamText 的調用面
  • 同一套代碼要同時兼容 AI SDK v6 與 v7

不適合當成「把任意 AI SDK tools 丟給 dsh 執行」的膠水。README 寫明:AI SDK 的 tools / toolChoice 不會由 AI SDK 執行;工具要改 cordis.yml$DSH_* 環境變量。temperaturetopPtopKstopSequencesseed 這些採樣參數會被接口收下,但不會轉發給 harness,採樣由 harness 自己管。

其他已經寫進 README 的邊界:

  • 只有 ESM,Node.js 必須 >=22.19
  • SDK 線上沒有「回合中途取消」:abort 會拒絕當前這次調用,子進程和 session log 還在,後續輪次可以繼續;要拆進程請 dsh.close()
  • dsh 仍是開發者預覽,破壞性變更是發佈政策的一部分;provider 版本和 harness 家族(當前 0.1.0-rc.6)都應該顯式釘住
  • 默認運行時在 Linux 上需要能編譯 node-pty;不行就改 cordis.minimal.yml(沒有 bash)

無論走目錄裏的 dsh plugin add,還是走 npm,安裝階段都可能執行構建腳本和原生插件。裝之前看源碼和許可證;要可復現,把 git 安裝釘到 commit,把 npm 依賴釘到具體版本。

小結

ai-sdk-provider-dsh 把 dsh 運行時封裝成 LanguageModelV3:AI SDK 這一側還是熟悉的 generateText / streamText,另一側是釘在 0.1.0-rc.6 的完整 agent 循環。工具在 harness 裏執行,會話可以靠固定 sessionId 續上,錯誤會收成 AI SDK 認識的 APICallError

它解決的是編排面統一,不是給 dsh Web UI 再加一個模型提供方。默認組合能用 bash 和文件工具,技能和 MCP 都不是開箱狀態。dsh 還在預覽期,版本要自己盯。

目錄頁:https://deepseek-harness-plugin.com/zh-CN/plugins/ai-sdk-provider-dsh/

GitHub:https://github.com/krislavten/ai-sdk-provider-dsh

npm:https://www.npmjs.com/package/ai-sdk-provider-dsh

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜