DeepSeek Harness SDK 集成:把 Agent 嵌进你的应用
上一篇讲清楚了 dsh 的三种运行形态,最后预告说「最值得深挖的是 sdk」。这一篇就兑现它------怎么把 DeepSeek Harness 当成一个库,嵌进你自己写的程序里。
Harness 的 SDK 分两条线:Python SDK 走一个开箱即用的 DeepSeekHarness 上下文管理器;TypeScript SDK 走一套叫 Typert 的 API Gateway,用 @Remote 装饰器把业务服务暴露成可远程调用的方法。两条线面向不同的人,但底层共享同一个「sdk profile + JSON-RPC 服务器」的运行时。
一、Python SDK:一个上下文管理器搞定
Python 这边做得很顺滑。先装:
sh
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk
装完自带一个匹配的原生 runtime wheel 和 dsh 命令,正常运行 SDK 不需要系统里装 Node.js(只有仓库贡献者构建产物时才需要)。
在你自己的程序里,核心就一个 DeepSeekHarness 上下文管理器:
python
from pathlib import Path
from deepseek_harness import DeepSeekHarness
workspace = Path("/absolute/path/to/disposable-workspace").resolve()
dsh_home = Path("/absolute/path/to/example-dsh-home").resolve()
with DeepSeekHarness(
provider="deepseek-official",
model="deepseek-v4-flash",
max_tokens=49_152,
cwd=str(workspace),
dsh_home=str(dsh_home),
profile="sdk-minimal",
) as harness:
result = harness.run(
"Inspect the repository and fix the failing tests.",
session_id="example-001",
)
print(result.final_response)
几个关键点:
- SDK 会惰性启动
dsh --profile sdk-minimal这个子进程,并在上下文管理器退出前一直复用它; - profile、它的持久 patch、home 级 patch,以及
patches元组里传入的有序 patch,共同构成应用配置; - 没有单独的 Python runtime bin,也没有「完整配置」这一说------一切还是走 profile 那套。

二、sdk-minimal:一个「极简到极致」的 profile
SDK 默认或例子常选的 sdk-minimal 值得单独看一眼,因为它清楚展示了「最小可用」长什么样:
| 项目 | 值 |
|---|---|
| 系统提示词 | DSH_SYSTEM_PROMPT,兜底 You are a helpful software engineer assistant. |
| 模型 | --model → DSH_MODEL → deepseek-v4-flash |
| 面向模型的工具 | 持久 bash(Linux/macOS)或 pwsh(Windows),加 str_replace_editor |
| Shell 超时 | 300 秒 |
| 编辑器输出上限 | 16000 字符 |
| 运行时上下文与压缩 | 无 |
| 会话持久化 | <dsh_home>/sessions 下的未压缩 JSONL |
它只有一个 bundle,把整棵树插在空 root 上,不包含 dsh-base ,所以后面 base-profile 的工具不会隐式出现。settings、托管凭据、遥测、Web 工具、子 agent、本地指令发现、压缩------这些统统不在里面。它 pin 了 danger-full-access,意味着持久 shell 和编辑器能改运行时可见的任何路径------所以官方反复强调:用一次性 checkout 或容器跑。

三、TypeScript SDK:Typert 的 @Remote 编程模型
TS 这边不是「start 一个进程」,而是声明式地把业务服务暴露成 Remote 方法 。核心是 @Remote / @RemoteScope 两个装饰器:
ts
import type { Agent } from '@deepseek-ai/dsh-agent'
import { TypertRemoteService, Remote, RemoteScope } from '@deepseek-ai/dsh-typert-protocol'
import type { Context } from '@deepseek-ai/cordis'
export interface CreateGoalRequest { objective: string }
export interface CreateGoalResult { accepted: boolean }
export class GoalService extends TypertRemoteService {
constructor(ctx: Context) {
super(ctx, 'goals') // 绑定 cordis service key + Remote namespace
}
@Remote('create')
createForClient(agent: Agent, request: CreateGoalRequest, signal: AbortSignal): CreateGoalResult {
signal.throwIfAborted()
return this.create(agent, request)
}
@RemoteScope('agent', 'current')
currentForClient(): CreateGoalResult {
return { accepted: true }
}
private create(_agent: Agent, request: CreateGoalRequest): CreateGoalResult {
return { accepted: request.objective.length > 0 }
}
}
@Remote表示调用 root Host Context 上的 cordis service;@RemoteScope先把身份解析成 scoped Context 再拿 service 调用;- 复杂对象(比如
Agent)不能直接过线,要用TypertLookupMap声明「wire 身份」→ Host 对象的映射,Gateway 会按agentId这种 wire 字段去解析回真实对象; - 方法最后一个参数必须是
signal: AbortSignal,用于协作式取消。
客户端这边,调用是具体函数,不是 JS Proxy:
ts
await ctx.remote.goals.create(agentId, { objective: 'ship it' })
await agentCtx.remote.goals.create({ objective: 'ship it' })
每个 namespace 是一个 trace 过的 cordis child service,注册成 remote.<namespace>;装配通过 ctx.remote.$mount() 挂载贡献,namespace 最后一个方法卸载后自动回收。

四、调用链:JSON-RPC 承载在 /api 路由上
Remote 调用走 Connection 的 /api 路由:客户端 connection.rpc.call('/api', '<namespace>/<method>', { args }, signal),HTTP 层映射成 POST /api/<namespace>/<method>,payload 只带一个命名的 args 对象。
Gateway 对每一次调用都会:解析描述符和「当前注册表里的 live service」(不缓存业务对象)、用 codec 校验 wire 值、通过 lookup/Context provider 解析对象、调用目标方法、再校验返回值。缺 provider、身份未知、参数对不上、schema 失败、方法不存在,都在进业务代码前或出业务代码后失败------这套严格校验是 TS SDK 比「裸 RPC」更值钱的地方。
五、插件持久化安装
不管哪条线,想把插件 bundle 持久装进 SDK profile,都用运行时自带的 dsh CLI:
sh
export DSH_HOME=/absolute/path/to/isolated-dsh-home
dsh --profile sdk --dump-default-config # 先初始化 profile
dsh plugin --profile sdk add file:/absolute/path/to/my-plugin-bundle
第一条初始化 profile,第二条把包管理转发给 pnpm 并记录下所有导出 dsh.bundle 层的包。只有这个管理命令需要 pnpm,启动已装好的 SDK 不需要。
六、避坑清单
- Python SDK 别默认它会读
~/.dsh。例子和 SDK 从不静默读取~/.dsh,workspace 和 home 都要显式给绝对路径,否则它不知道去哪找。 sdk-minimal是全访问,隔离优先 。它 pindanger-full-access,用一次性 checkout 或容器跑,别拿它直接啃你的真实仓库。- TS SDK 换传输不改契约。Remote 描述符和客户端接口独立于 Connection 载体,换 carrier 不需要动 Remote 定义------但反过来,别把 Remote 方法和流式/增量数据混为一谈,那些得走单独的数据协议。
- 改了 @Remote 签名要重建 。增删装饰器、改导出名/namespace/参数/返回值/lookup,都得
pnpm run build:lib让 Host 先生成严格契约,客户端才能编译到新贡献;只改方法体不用重建。 web是独立 CLI,不能服务 Python SDK 客户端 。想浏览器界面就单独dsh web,别指望同一个进程又当 web 又当 Python SDK server。
小结
SDK 集成的本质,是把前八篇反复强调的「seam」再往外推一层:对内,插件是 seam;对外,SDK 本身也是 seam 。Python 用 DeepSeekHarness 一个对象包住整个 agent 进程,TS 用 @Remote 装饰器把业务服务暴露成可远程调用的方法------两条路殊途同归,都是「把 Agent 变成你程序里的一个能力」。
下一篇我们往里收一步,讲怎么改行为而不碰源码------这就是 Harness 的配置与 Patch 体系。
参考:deepseek-ai/deepseek-harness 官方仓库 docs/user/guide/python-sdk.md、docs/api-gateway.md。