用 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

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

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

小夜