DeepSeek Harness + k8e-sandbox 接入指南(分步)
DeepSeek Harness(dsh)是一个插件化的 agent harness。dsh-k8e-sandbox 插件族把 dsh 的执行世界——文件系统读写与子进程执行——整体搬到 K8E 沙箱中。配置完成后,dsh 内置的 bash、文件、终端、LSP 工具全都跑进 gVisor / Kata / Firecracker 沙箱 Pod,无需修改 dsh 源码;同时 k8e-sandbox skill 也会装进 dsh,让模型知道怎么用沙箱。
KIP-20 · 从 npm
latestdist-tag 安装(无需锁版本)· 预计耗时 约 10 分钟
前置条件
- 已部署沙箱矩阵、沙箱 gRPC 网关监听
:50051的 K8E 集群,或带可达网关端点的远程节点(见 Sandbox CLI 与 沙箱运行时部署)。 - Node.js 18+ ——
dsh通过npx @deepseek-ai/dsh运行,无需全局安装。
第 1 步 —— 安装并连接沙箱 CLI
CLI 是单个带平台后缀的二进制,k8e-sandbox-cli 这个不带后缀的名字是符号链接(不要改名,用 ln -s):
# 1. 下载对应平台的二进制
curl -sLO https://github.com/xiaods/k8e/releases/latest/download/k8e-sandbox-cli-darwin-arm64 # linux-amd64 / linux-arm64 / darwin-amd64 / windows-amd64.exe
chmod +x k8e-sandbox-cli-darwin-arm64
# 2. 建立符号链接并加入 PATH
ln -s k8e-sandbox-cli-darwin-arm64 k8e-sandbox-cli
mkdir -p ~/.local/bin && mv k8e-sandbox-cli* ~/.local/bin/
export PATH="$HOME/.local/bin:$PATH"
连接网关并安装 agent skill(这一步同时生成 ~/.k8e/sandbox/profiles.yaml):
# 本机节点 —— 自动发现 TLS 证书:
k8e-sandbox-cli connect
# 远程 —— 先在服务端创建 API key(默认 TTL 30 天):
k8e sandbox-apikey create my-agent # → {"name":"my-agent","key":"k8e-…","e2b_key":"e2b_…","ttl_days":30,...}
k8e-sandbox-cli connect --endpoint <host>:50051 --apikey k8e-...
connect 会顺带安装 k8e-sandbox skill。要同时装给 dsh,加 --agent dsh(或 --agent all)——会把 skill 写到 ~/.dsh/skills 与 ~/.agents/skills:
k8e-sandbox-cli connect --agent dsh # 或 --agent all 一次性装给所有 harness
验证网关可用:
k8e-sandbox-cli status # → {"available":true, ...}
k8e-sandbox-cli run 'echo hi' # → {"stdout":"hi\n","exit_code":0,...}
第 2 步 —— 把 dsh 插件 bundle 装进 profile
npx @deepseek-ai/dsh plugin --profile web add @k8e-sandbox/dsh-k8e-sandbox-bundle
安装注意事项
dsh plugin add底层转发给 pnpm。出现的ERR_PNPM_IGNORED_BUILDS/dsh: pnpm failed是无害的——pnpm 跳过了protobufjs/node-pty的构建脚本(运行时并不需要),并以非零码退出。- 正因为这个非零退出码,
dsh plugin add可能跳过把 bundle 追加进dsh.profile.bundles的步骤。装完后请确认~/.dsh/profiles/web/package.json的dsh.profile.bundles里包含@k8e-sandbox/dsh-k8e-sandbox-bundle;若缺失需手动补上——否则插件装了也不会加载。- 不带版本的安装始终解析
latestdist-tag(当前 0.3.x)。只有当你刻意停留在旧版本时才需要锁版本(如...bundle@0.3.5)。
第 3 步 —— 确认 skill 已装进 dsh
插件挂载了沙箱「缝」,但模型还需要 skill 才知道怎么用沙箱。第 1 步用了 --agent dsh 就已经装好:
ls ~/.dsh/skills/k8e-sandbox/SKILL.md # dsh 用户根(优先级最高)
ls ~/.agents/skills/k8e-sandbox/SKILL.md # 跨 harness 兜底
没装的话现在补装(不需要拨网关):
k8e-sandbox-cli connect --agent dsh --skill-only
dsh 会从 ~/.dsh/skills 与 ~/.agents/skills 自动发现该 skill。skill 里带 dsh 专属执行路径:在 dsh 内 fs/subprocess 缝已经落在沙箱里,所以模型直接使用插件的 k8e_sandbox_exec / k8e_sandbox_run_background / k8e_sandbox_poll 工具和文件工具即可,不需要再 shell out 执行 k8e-sandbox-cli。
第 4 步 —— 启动 dsh 并确认连接
npx @deepseek-ai/dsh web
打开 设置 → K8E 沙盒,面板会显示:
提示: 任何时候都可以运行
k8e-sandbox-cli doctor——它会检查 CLI、profile、mTLS 证书、网关探测、dsh bundle 注册(dsh.profile.bundles)与安装、以及 skill 安装,并为每项失败打印精确的修复命令(有异常时退出码为 1)。脚本化可用--json。
- 连接状态 —— 网关握手成功显示
● 已连接。 - Connect 地址(自动发现) —— 面板会标注端点来源:
自动发现自 ~/.k8e/sandbox/profiles.yaml(profile: default)—— 来自 CLI profile(最常见,无需任何配置)来自环境变量 K8E_SANDBOX_ENDPOINT来自 dsh profile 的 dsh-k8e-sandbox 配置—— 显式覆盖- 什么都没配置时,面板给出
k8e sandbox connect ...的黄色引导。
端点解析顺序(与
k8e-sandbox-cli一致):插件显式配置 → 环境变量K8E_SANDBOX_ENDPOINT→~/.k8e/sandbox/profiles.yaml→ CLI 本地自动发现。单集群的常见场景什么都不用配——插件自己找到端点,并保持一条持久 gRPC 连接承载所有 fs/exec/session 操作。
第 5 步 —— 上手体验
在 dsh 里开个聊天,按顺序试试:
1. 直接聊天请求(文件 + bash 都进沙箱)。
“写一个 Python 脚本,计算斐波那契数列前 20 项,保存到
/workspace/fib.py并运行”
dsh 的文件工具会写进沙箱 /workspace,bash 跑在 Pod 内——模型返回输出,完全不碰你的宿主文件系统。
2. 显式沙箱工具。
“用 k8e_sandbox_exec 跑
hostname && python3 --version”
k8e_sandbox_exec 返回 Pod 内的 stdout / stderr / exit code。
3. 终端面板。 在设置页点 打开终端(或沙箱活动时自动弹出的终端面板)。跑 tty——应该看到 /dev/pts/N;ls /workspace 能看到第 1 步写的文件。
4. skill 驱动任务。
“用 k8e-sandbox 跑一下这个仓库的单元测试”
模型加载 k8e-sandbox skill(目录名即目录名),按 dsh 执行路径在沙箱里跑测试。
5. 验证隔离性。 让模型在沙箱里跑 ps aux | head,再在宿主机跑 ps aux 对比——进程集完全不同。回到宿主,k8e-sandbox-cli sessions 能看到活跃会话,k8e-sandbox-cli log <sid> 能看到执行记录。
6. 暴露 web 服务(KIP-24)。
"在 /workspace/app.py 写一个返回 'hello from sandbox' 的 Flask 应用,后台跑在 8080 端口,然后暴露 8080 并给我 URL。"
模型先用 k8e_sandbox_run_background 启动服务,再调用 k8e_sandbox_expose {port: 8080}——k8e API Gateway 会把 http(s)://<gateway>/k8e/expose/<sid>/8080/ 反向代理进 pod。浏览器打开该 URL 即可访问;停止服务用 k8e_sandbox_unexpose {port: 8080}。
7. 出网白名单(KIP-24)。
"允许本会话出站访问 registry.npmjs.org。"
模型调用 k8e_sandbox_allow_hosts {hosts: [...]}——会话出网白名单实时替换 (网络策略立即重放)。传空列表则回落到集群默认值。
可选配置
默认配置(cwd=/workspace、runtimeClass=gvisor、端点自动发现)适合大多数场景。需要覆盖时编辑 $DSH_HOME/profiles/web/cordis.patch.yml:
- id: k8e-sandbox
name: '@k8e-sandbox/dsh-k8e-sandbox'
config:
endpoint: 10.0.0.5:50051 # 覆盖自动发现(例如指向另一个网关)
cwd: /workspace
runtimeClass: kata # gvisor(默认)| kata | firecracker
多集群用 CLI profile(k8e-sandbox-cli --profile prod connect ...)——插件自动读取 ~/.k8e/sandbox/profiles.yaml 中的当前 profile。
故障排查
| 现象 | 原因 / 解决 |
|---|---|
面板显示 未发现 connect 地址 | 没有解析到端点。远程跑 k8e-sandbox-cli connect --endpoint <host>:50051 --apikey <key>,本地跑 k8e-sandbox-cli connect,然后重启 dsh。 |
终端/工具报 authentication handshake failed: EOF | 网关不可达,或缓存的 CA/客户端证书属于已被轮换的服务端 CA。恢复网关后重跑 connect(信任错乱时删 ~/.k8e/sandbox/ca.crt 重新引导)。 |
run/终端命令无限卡死 | 已修复(沙箱 CLI 会自愈陈旧的 creating 会话状态,所有 RPC 带 deadline)。更新 CLI 即可;旧的卡死状态文件会在下次运行时自动恢复。 |
| 聊天很慢(旧版插件) | 0.3.0 之前每个 fs 操作都 spawn 一次 CLI 进程;0.3.0+ 改用一条持久 gRPC 连接 + deadline。升级 bundle 并重启 dsh。 |
工具调用报 "value.durationMs" must be a number / is not a declared property | 近期版本已修复(工具输出与严格 schema 对齐:durationMs 为数字、无多余字段)。更新 bundle 并重启 dsh。 |
读取沙盒文件报 not a regular file | 近期版本已修复(stat 类型位从完整 st_mode 中掩码提取)。更新 bundle 并重启 dsh。 |
dsh 启动即崩,报 ctx.k8eSandbox is not mounted | bundle 未真正加载——重装(latest)、重启 dsh、跑 k8e-sandbox-cli doctor --fix。 |
工具调用报 reading 'getClient' | 近期版本已修复(工具改为从 ctx 解析 owner)。更新 bundle 并重启 dsh。 |
| 模型看不到 skill | 重启 dsh 会话;确认第 3 步(--agent dsh)。模型通过 skill 工具或聊天点名加载。 |