版本:v0.1 草案 | 日期:2026-10-19 | 适用范围:Python 服务端集成 DeepSeek Harness SDK,同一台机器服务数百用户,每用户 1~2 个并发会话。
本方案把 DeepSeek Harness 的 SDK 形态(dsh --profile sdk 的 stdio JSON-RPC 运行时)嵌入一个 Python 单机服务端:Python 后端作为控制面与 API 面,为每个用户拉起独立的 DSH 运行时子进程,通过按行分帧的 JSON-RPC 2.0 协议驱动 agent。隔离与共享按三个正交维度展开:
| 维度 | 手段 | 粒度 |
|---|---|---|
| 用户隔离 | 每用户独立 $DSH_HOME + 独立 workspace 根 + 独立子进程 + bash 沙箱 + 资源配额(cgroup/rlimit/token) |
用户 |
| 会话隔离 | sessionId 以用户为命名空间;会话 JSONL 落在用户自己的 DSH_HOME 下;编排层只把会话路由给所属用户的运行时 |
会话 |
| 技能隔离/共享 | 三来源:用户私有 <DSH_HOME>/skills + 组织共享只读目录(Git/HTTP 同步)+ 远程技能注册中心;dsh-skill 注册表合并多提供方 |
技能/用户/角色 |
核心设计决策:
sessionId 在同一个进程内创建 agent,因此一个运行时子进程可承载该用户全部并发会话;按需拉起、空闲超时优雅关闭,把"数百用户 × 1~2 会话"映射为"数百个可控的 Node 子进程"。$DSH_HOME 环境变量(每用户一份配置文件/凭据/会话/技能)、InitializeParams.cwd 指定用户 workspace、dsh-bash-sandbox 的 workspace-write/read-only 模式限制文件影响、InitializeParams.maxTokens 做 token 配额。customSkillDirs/patch 配置),用户私有技能在各自 DSH_HOME;需要远程注册中心时,用一个轻量 Cordis 插件或目录同步把"服务端技能目录"变成每个运行时可发现的只读提供方。服务端技能 API 做 RBAC 过滤(按角色/用户返回可见技能)。shutdown 请求 → SIGTERM → SIGKILL 逐级升级)实现。dsh-server 命令独立部署,也可 from dsh_server.embed import create_app 嵌入其他 Python 项目(库模式挂载 FastAPI 路由/编程式 API),满足"方便其他项目引用"的最终目标。以下事实来自本机安装的
@deepseek-ai/dsh@0.2.0-rc.2及其插件包 README/代码。
dsh --profile sdk(@deepseek-ai/dsh-sdk-app)或 dsh --profile sdk-minimal:启动一个 stdio 服务,stdout 只承载 按换行分帧的 JSON-RPC 2.0,客户端是任意语言进程。--from-default-profile 可基于模板克隆自定义 profile(用于注入我们自己的技能提供方/策略 patch)。$DSH_HOME 下)。cordis.patch.yml 定制工具集、沙箱、技能目录、MCP 等——这是实现"按用户/角色差异化能力"的注入点。dsh-sdk-protocol)| 方向 | 方法 | 语义 |
|---|---|---|
| client→server | initialize |
握手:provider/model、可选 reasoningEffort、可选 maxTokens、cwd;返回 serverInfo.name = "deepseek-harness-sdk-runtime" |
| client→server | session/prompt |
按 sessionId 排队一条用户消息,立即返回 { messageId }(仅表示已入队,不是结果) |
| client→server | shutdown |
dispose 根运行时并退出(退出码 0) |
| server→client | session.event |
每个持久化事实(会话日志事件流) |
| server→client | session.status |
agent 整机 running/idle 状态转换 |
| server→client | subagent.started / subagent.finished |
子 agent 边界事件 |
关键限制(决定架构的事实):
messageId 只是准入回执;"这条提示词的最终答案"要由客户端自己观察 session.event/session.status 区间来定义(如等待 idle + 消息归档)。initialize 成功前拒绝 session/prompt;同一进程可服务该用户多个 sessionId。sessionId 持久化(JSONL),落在 $DSH_HOME 下;重启进程 + 同一 $DSH_HOME + 同一 sessionId 可恢复历史。headless profile 支持 --session-id 延续会话。dsh-skill + dsh-skill-filesystem)modelInvocable(模型可调用)/ userInvocable(用户可调用)。| rank | 来源 | 路径 |
|---|---|---|
| 100 | project-dsh | <projectRoot>/.dsh/skills(cwd 最近 .git 祖先) |
| 200 | project-agents | <projectRoot>/.agents/skills |
| 300 | custom | Config.customSkillDirs |
| 400 | user-dsh | <dshHome>/skills |
| 500 | user-agents | <agentsHome>/skills |
| 600 | bundled | $DSH_BUNDLED_SKILL_DIR / bundledSkillDir |
<name>/SKILL.md 或 <name>.md,YAML frontmatter 必填 name(kebab-case)+ description,可选 whenToUse、metadata、disable-model-invocation、user-invocable。ctx.skills.registerProvider)→ "远程技能注册中心"可以做成 Cordis 插件直连,也可以简化为目录同步(本方案推荐后者起步)。dsh-bash-sandbox)read-only(默认)/ workspace-write / danger-full-access;只约束文件影响,网络不受限。SANDBOX_UNAVAILABLE 失败,绝不静默无隔离运行。dsh-home-paths)$DSH_HOME > ~/.dsh。空/空白 $DSH_HOME 视为未设置。$DSH_HOME 下:profiles/、skills/、settings/、cache/、凭据存储、会话持久化。$DSH_HOME 即可获得配置/凭据/会话/私有技能的全部隔离;同时 profile 级 cordis.patch.yml 与 home 级 $DSH_HOME/cordis.patch.yml 是"按用户注入共享技能目录、沙箱策略、工具集"的配置层。┌───────────────────────────── 用户前端 / 技能管理控制台 ─────────────────────────────┐
│ HTTPS · JWT │
└─────────────────────────────────────────┬──────────────────────────────────────────┘
▼
┌────────────────────────────── Python 服务端(单机) ────────────────────────────────┐
│ ┌──────────────┐ RBAC ┌─────────────────────┐ 发布/审批/授权 ┌──────────────┐ │
│ │ API 网关/认证 │ ───────▶│ Python 应用层 │ ────────────────▶│ 技能服务中心 │ │
│ │ (FastAPI) │ │ 会话/技能/配额 API │ │ 目录·审批·版本│ │
│ └──────────────┘ └───────┬─────────────┘ └──────┬───────┘ │
│ │ 用户/授权/配额 │ 校验后发布
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐│
│ │ 运行时编排器 │ ◀──订阅同步── │ 共享技能目录(只读) ││
│ │ spawn/回收/健康/配额│ │ Git/HTTP 同步 ││
│ └───────┬──────────┘ └────────┬─────────┘│
│ spawn/stdio│监控 只读挂载(customSkillDirs) │
│ ┌─────────────────────────┼───────────────────────────┐ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ dsh 运行时A │ │ dsh 运行时B │ │ dsh 运行时… │ │ 私有技能 │ │
│ │ DSH_HOME[A] │ │ DSH_HOME[B] │ │ │ │ <DSH_HOME>│ │
│ │ workspace[A]│ │ workspace[B]│ │ │ │ /skills │ │
│ └──────┬──────┘ └──────┬──────┘ └─────────────┘ └───────────┘ │
└─────────┼─────────────────────────┼───────────────────────────────────────────────┘
▼ ▼
┌─────────────────────────────────────────────┐
│ DeepSeek 模型 API(各自凭据) │
└─────────────────────────────────────────────┘
| 层 | 组件 | 职责 |
|---|---|---|
| 接入层 | API 网关/认证(FastAPI 中间件) | 认证(JWT/OIDC 对接企业账号)、RBAC、限流、审计入口 |
| 应用层 | Python 应用服务 | 会话管理 API、技能管理 API、配额/计费 API、业务编排;把用户请求翻译成对运行时编排器的调用 |
| 编排层 | 运行时编排器(RuntimeManager) | 用户运行时生命周期(spawn/健康/回收/崩溃重启)、协议客户端之上的会话-进程路由、事件分发、配额执行(token/并发/存储) |
| 集成层 | JSON-RPC over stdio 客户端 | initialize / session/prompt / shutdown + 4 类通知;asyncio 行分帧;事件规范化 |
| 运行时层 | dsh --profile sdk 子进程(每用户一个) |
agent 执行、持久会话、工具、技能发现/加载、bash 沙箱 |
| 技能层 | 技能服务中心 + 共享技能目录 + 私有技能区 + 远程注册中心 | 技能全生命周期(编写→评审→版本→分发→回收);权限过滤 |
| 数据层 | 元数据库(用户/授权/配额)、会话持久化(JSONL in DSH_HOME)、技能版本库、审计日志 | 状态与证据 |
用户 = 一个运行时子进程 + 一份 $DSH_HOME + 一个/多个 workspace 根 + 一组资源配额 + 一组授权(Grant)
主机
├── /data/harness/
│ ├── users/<uid>/
│ │ ├── dsh-home/ # $DSH_HOME——配置/凭据/会话JSONL/私有技能
│ │ ├── workspaces/
│ │ │ └── <project>/ # 传给 InitializeParams.cwd 的 workspace 根
│ │ └── logs/ # stderr 诊断(stdout 是协议通道,不许混日志)
│ ├── shared-skills/ # 组织共享技能(只读,Git/HTTP 同步,版本化)
│ ├── metaservice.db # 用户/授权/配额/技能元数据(SQLite/Postgres)
│ └── audit/ # 审计
| # | 隔离维度 | 机制 | 破坏后果 |
|---|---|---|---|
| 1 | 认证/授权 | 网关 JWT;编排器只接受"已认证用户"的会话指令;会话 ID 带用户命名空间(u:<uid>:<uuid>) |
跨用户会话/进程注入 |
| 2 | 进程 | 每用户独立 Node 子进程;崩溃互不影响;OOM 由 cgroup 收割该进程 | 单用户拖垮全部 |
| 3 | 数据 | 每用户 $DSH_HOME + workspace 根;OS 用户/组权限(或目录 ACL);会话 JSONL 归属清晰 |
会话历史/凭据/私有技能泄露 |
| 4 | 文件执行 | dsh-bash-sandbox:默认 workspace-write(只能写用户自己的 workspace + /tmp);高危模式 danger-full-access 必须逐次审批 |
agent 越权读写其他用户文件 |
| 5 | 资源 | 每用户:并发会话数上限、InitializeParams.maxTokens token 上限、cgroup 内存/CPU、进程数 rlimit;编排器级限流 |
资源耗尽型 DoS 与费用失控 |
| 6 | 网络 | 说明:bash 沙箱不约束网络。模型 API 出口走每用户自己的凭据(credentials seam 内代理);严格的出网白名单(若业务需要)留作 Linux netns/代理演进项 | 数据外发、凭据滥用 |
sessionId 生成规则:u:<uid>:<uuid4>,由编排器统一生成;session/prompt 只允许携带"该用户命名空间内"的 sessionId,编排器校验后路由到该用户自己的运行时。sessionId → uid → runtime pid → DSH_HOME → workspace。$DSH_HOME + 同一 sessionId 可续接历史(协议层通过初始化后直接 session/prompt,历史来自持久化)。shutdown 该用户运行时(连同其全部会话)。若业务需要"只结束一个会话",两个选项:| 凭据 | 存放 | 说明 |
|---|---|---|
| 用户身份 | 服务端会话(JWT) | 网关签发,编排器不透传 |
| 模型 API Key | 每用户 $DSH_HOME 的 credentials seam |
一个用户一个 Key(或多 Key 按 profile 切换);绝不共享文件 |
| 技能仓库写权限 | 服务端(技能服务中心) | 用户端只读 |
| 来源 | 物理位置 | 可见性 | 读写方 | 优先级(rank) |
|---|---|---|---|---|
| ① 用户私有技能 | <uid>/dsh-home/skills(即 <dshHome>/skills,rank 400) |
仅该用户 | 用户本人(经技能 API 写入) | 400 |
| ② 组织共享技能 | 主机 /data/harness/shared-skills,注入各运行时的 customSkillDirs(rank 300) |
全体/角色子集 | 服务端 CI 只读同步;用户只读 | 300 |
| ③ 远程技能注册中心 | 服务端技能服务中心(HTTP API + 版本库) | 按角色过滤 | 运营/管理员 | 经②物化或插件直连 |
优先级语义:rank 越小,注册表裁决重名时越优先。因此 ②组织共享技能 > ①用户私有技能——组织希望"运营强制技能覆盖个人同名技能"时成立;若希望"个人可覆盖组织默认",调整挂载 rank 即可(如把共享技能挂到 rank 较低的用户域之外的位置,或用 web 注入覆盖;见 §5.4 配置点)。
方案 B1(推荐起步)——目录同步物化:远程注册中心/技能 Git 仓库 → CI 或后台任务 pull + 校验 + 物化 到 /data/harness/shared-skills/<version>/skills/,用原子符号链接 shared-skills/current -> <version> 做版本切换与秒级回滚。每个用户运行时通过 profile/home 级 patch 把 customSkillDirs: ["/data/harness/shared-skills/current/skills"] 注入。技能文件系统提供方监视目录,改文件即对存活会话生效(无需重启)。
方案 B2(进阶)——注册中心直连提供方:为 dsh profile 写一个 ~100 行的 Cordis 插件,ctx.skills.registerProvider 回调服务端 /api/skills?token=<user-scope-token>,目录远取、正文远取。优点:权限实时、无需常驻同步;缺点:依赖服务可用性、开发维护成本。作为 v0.2 演进。
两者可共存:
dsh-skill注册表本来就合并多提供方,多来源的"可见技能并集"是设计内的行为。
SkillGrant(role, skill_ref, version_constraint, enabled);disable-model-invocation / user-invocable 沿用官方语义,按技能内容审批时配置。$DSH_HOME/profiles/<name>/cordis.patch.yml:技能目录、工具集、MCP、沙箱默认模式的 profile 级定制;$DSH_HOME/cordis.patch.yml:home 级覆盖(按用户);dsh --profile <name> --patch <file>:逐次调用覆盖。
→ 编排器在 spawn 用户运行时前按用户档案渲染一份 patch 集,实现"同一套服务端代码、不同用户的技能/策略不同"。编写(SKILL.md) → 提交到技能仓库 → CI 校验(frontmatter/schema/lint)
→ 评审(人工/双人) → 打版本(语义化) → 发布(物化到共享目录 + 角色授权)
→ 运行时热发现生效 → 指标埋点(调用次数/效果) → 回收(下线版本 → 原子切 link)
shared-skills/current -> v2026.10.1;回滚即切 link,秒级、无重启。用户前端 Python 编排器 dsh 运行时(用户U) DeepSeek API
│ │ │ │
│ POST /sessions │ │ │
├─────────────────▶│ 校验 JWT/RBAC/配额 │ │
│ │ 若运行时不存在: │ │
│ │ spawn dsh --profile sdk├───进程启动──────────▶│
│ │ DS H_HOME=<uid>/dsh-home│ │
│ │ cwd=<uid>/workspaces/x │ │
│ ├────── initialize(model, cwd, maxTokens) ──────▶│
│ │◀────── serverInfo/ok ──────────────────────── │
│ │ │ │
│ POST /prompt │ │ │
├─────────────────▶│ session/prompt │ │
│ │ (sessionId, content) ├──────────────────────▶│
│ │◀────── {messageId} │ │
│ │◀──────── session.event / session.status (流) │
│ WS 推送事件 ◀──────┤ ├──── 模型调用 ─────────▶│
│ │ │◀──── 事件(消息/工具/…)─┤
│ │◀────── session.status(idle) ───────────────── │
│ 结果(最终文本/idle)│ │ │
│ │ (空闲超时/显式关闭) │ │
│ DELETE /sessions ├── shutdown ──────────▶│ │
│◀─────────────────┤◀────── {} + exit 0 │ │
│ │ 回收进程/账目/配额 │ │
事件→结果的桥接规则(协议只给事件不给"该条 prompt 的结果"):
session.event 流,标记 messageId 入队后出现的用户/助手/工具事件;session.status → idle 且不再有新事件为"本轮结束"信号;汇总该区间内最后一条助手消息为最终答案。区间标记由编排器维护(turnNeedsResult: messageId → observable interval)。| 端点 | 说明 |
|---|---|
POST /api/v1/sessions |
建会话(内部:确保用户运行时在跑;返回 sessionId = u:<uid>:<uuid>) |
POST /api/v1/sessions/{sid}/prompt |
发提示词(校验归属 → session/prompt → 返回 messageId) |
WS /api/v1/sessions/{sid}/events |
事件流(session.event/status/subagent.* 规范化后推送) |
GET /api/v1/sessions/{sid}/result |
轮询最终结果(编排器维护的区间汇总) |
DELETE /api/v1/sessions/{sid} |
结束会话(可配:仅下线该会话 / 关闭该用户运行时) |
GET/POST /api/v1/skills |
技能目录/发布/授权(RBAC) |
GET /api/v1/quota |
用户配额视图 |
absent → provisioning(拉起+initialize) → ready(可接会话)
│ │
│ 崩溃 空闲超时 / 显式关闭 / 配额
▼ ▼
restarting(退避) shutting_down(shutdown→SIGTERM→SIGKILL)
│ │
└───────── back to ready / removed
initialize 握手 + 心跳(可选每分钟一次 session.status 探测)。shutdown;会话持久化在 $DSH_HOME,回收不丢历史。// 内部统一事件
{ "event": "session.event" | "session.status" | "subagent.started" | "subagent.finished",
"sessionId": "u:42:…", "messageId": "…", // 有则带
"payload": { …原协议载荷… },
"ts": 1760850000000 }
SKILL.md frontmatter)name: code-review-python # kebab-case,必填
description: 对 Python 变更做评审并输出清单。 # 必填
whenToUse: 用户要求评审代码变更时
metadata:
version: 1.4.0 # 服务端版本管理使用
author: platform-team
requires_roles: [engineer] # 服务端 RBAC 过滤使用(不在 frontmatter 强制)
disable-model-invocation: false
user-invocable: true
| 项 | 估算 | 说明 |
|---|---|---|
| 用户数 | ~300 活跃 | 每用户 1~2 并发会话 |
| 运行时进程 | ≤ 300(一用户一进程) | 仅活跃用户拉起;Node 常驻基线 ≈150–300MB/进程(以实测为准) |
| 内存预算 | 300 × 250MB ≈ 75GB | 叠加上限按 cgroup 收紧到每进程 ~512MB,超限 OOM 只伤该用户 |
| CPU | 8–16 核 | agent 多为 I/O 与等待模型;峰值看并发 turn 数 |
| 磁盘 | 每用户会话 JSONL+workspace+技能;审计 | 建议挂 SSD;给配额(如单用户 10GB) |
| 网络 | 模型 API 出口为唯一依赖 | 出网白名单(可选) |
| 部署 | systemd 服务 1 个 Python 进程组(uvicorn workers)+ 子进程树 | cgroup v2 下按用户切片;OS 账号可每用户或共享运行账号+目录 ACL(推荐每用户 OS 账号以获得硬边界,成本自负) |
起步规模(<50 用户)可直接复用同一架构,仅调低 cgroup/配额;进程模型不变。
| 阶段 | 内容 | 验收 |
|---|---|---|
| P0(MVP) | 单用户→首个用户运行时打通:spawn sdk profile、initialize、prompt、事件流、shutdown;元数据库落地 | 端到端一个会话闭环 |
| P1(多用户隔离) | 每用户 DSH_HOME/workspace/配额/沙箱;会话命名空间与路由;崩溃恢复、空闲回收;审计 | 双用户互不可见/互不影响;故障注入通过 |
| P2(技能三来源) | 技能服务中心(版本/审批/授权);共享技能目录物化+原子切换;私有技能区;RBAC 过滤 | 角色 A 看不到角色 B 的共享技能;共享技能热更新;回滚 <1s |
| P3(规模与进阶) | 会话级运行时档位(高隔离)、注册中心直连插件(B2)、OTel 埋点、出网白名单、多机(shared storage + 编排层水平拆分) | 压测 300 用户达标 |
| # | 风险/开放问题 | 影响 | 缓解/答案 |
|---|---|---|---|
| 1 | 协议无逐会话关闭/取消 | "结束单会话"只能关整进程 | 默认一用户一进程;需要时升级会话级进程档位 |
| 2 | 官方 Python SDK 发布形态未在本环境核实 | 可能需自实现 JSON-RPC 客户端 | 协议面已文档化且仅 3+4 个方法;自实现 ~300 行 python 可控。落地时先查官方仓库 python/ 目录的 wheel 发布 |
| 3 | Node 进程内存占用 | 数百进程资源成本 | cgroup 限额 + 空闲回收 + 监控阈值告警 |
| 4 | 共享技能热更生效粒度 | 正文变更对"进行中的轮次"可能不一致 | 目录/正文分离是官方设计(正文每次加载重读);进行中轮次的快照语义可接受 |
| 5 | 技能代码性内容(脚本) | 沙箱之外仍需治理 | v0.1 依赖文件沙箱 + 审批;容器级技能执行沙箱列入 P3 |
| 6 | stdout 纯净性 | 任何 stdout logger 会破坏协议 | profile 组装时禁用 stdout logger(官方明确要求),stderr 归日志文件 |
| 7 | 版本兼容 | SDK 0.2.0-rc.2 为预发布,无版本协商 | 锁定 dsh 版本;升级走灰度;协议面增加回归测试 |
@deepseek-ai/dsh@0.2.0-rc.2(npm),dsh --version 输出 0.2.0-rc.2。dsh-sdk-app、dsh-sdk-jsonrpc-server、dsh-sdk-protocol、dsh-skill、dsh-skill-filesystem、dsh-bash-sandbox、dsh-home-paths、dsh-base、dsh-headless、dsh-api-session-controller、dsh-api-workspace-controller。github.com/deepseek-ai/deepseek-harness(python/ SDK 目录、docs/subsystems/skills.zh.md)。DeepSeek Harness SDK 集成 · 发布于 Cloudflare Pages · 由设计文档自动生成