树莓派CM4部署DeepSeek Harness踩坑全记录

AI摘要

在树莓派CM4(ARM64,内存仅1.8GB)上部署DeepSeek Harness:配置npmmirror镜像并用并发限制安装依赖;推荐官方预编译包规避本地构建OOM;服务默认仅监听127.0.0.1(防RCE),需通过socat端口转发实现局域网访问。

DeepSeek Harness(dsh)是 DeepSeek 官方开源的 Agent Harness,一切皆插件,支持 Web UI、文件编辑、Shell 执行、子代理等能力。本文记录在树莓派 CM4(Sipeed NanoCluster,ARM64)上从零部署并实现局域网访问的完整过程,包括所有踩过的坑

环境

项目配置
硬件树莓派 CM4(Sipeed NanoCluster 集群节点)
CPU/内存ARM64,内存仅 1.8GB(这是后面所有坑的根源)
系统Debian (rpt) Linux,6.18 内核
Node.jsv22.23.2(要求 `^22.19.0)
pnpm11.7.0(项目 packageManager 指定)

官方要求 Node.js 22.19+ 或 24+,先确认:node -v

第一步:克隆项目

git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness

第二步:配置国内镜像(关键!)

直连 registry.npmjs.org 在国内慢到离谱:单个请求 10~24 秒,tarball 下载速度 15~34 KiB/s,923 个包装完要几个小时,中途极易失败。

在项目目录创建 .npmrc

echo "registry=https://registry.npmmirror.com" > .npmrc

⚠️ 注意:npm i -g(全局安装)不会读取项目 .npmrc,仍会走官方源。需要全局镜像可另行配置 ~/.npmrc

第三步:安装依赖

CM4 内存小,必须限制并发防止 OOM:

pnpm install --network-concurrency=6 --child-concurrency=2

用 npmmirror + store 缓存命中后,923 个包 2 分 29 秒装完(直连官方源可能要数小时)。

常见坑:

  • pnpm: command not found:非交互 SSH 环境 PATH 里没有 pnpm。用绝对路径:/home/saiita/.npm-global/bin/pnpm,或在 ~/.bashrc 里把 pnpm 加进 PATH。
  • [WARN] Unsupported platform: wanted: {"cpu":["x64"],...}:部分包只有 x64 预编译(如 landlock-run),ARM64 上忽略即可,不影响安装。
  • DeprecationWarning: Setting the TLS ServerName to an IP address (DEP0123):直连官方源下载时的已知警告,无害;换 npmmirror 后不再出现。介意可 NODE_OPTIONS=--disable-warning=DEP0123 压掉。

第四步:构建 or 直接用官方包?

项目 README 的源码运行方式是:

pnpm run build   # tsc 编译 + web 前端构建
pnpm dsh web

但在 1.8GB 内存的 CM4 上,tsc -b 编译 238 个 workspace 项目必然 OOMFATAL ERROR: Reached heap limit,即使 --max-old-space-size=512 也崩)。

替代方案(推荐):直接用官方预编译包,跳过本地构建:

npm i -g @deepseek-ai/dsh

全局安装从官方源下载约 358MB,每个包 30~75 秒,node-pty 等原生模块会自动触发本地编译(需 gcc/make/python3,一般系统自带)。

第五步:启动服务

nohup dsh web --port 3080 > /tmp/dsh-web.log 2>&1 &

默认监听 127.0.0.1:3080,本机验证:

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3080/   # 200

⚠️ dsh web 故意拒绝 --host 0.0.0.0:"would expose remote code execution to the network"。这是 RCE 级工具,官方安全设计,不要强行绕过

第六步:局域网访问(本文最大难点,含 3 个连环坑)

直接访问 http://192.168.31.231:3080/ 会连不上或报错,一共三个坑:

坑 1:服务只监听 127.0.0.1

需要端口转发。先装 socat 转发试试,但注意 socat 是 TCP 层,后面会因此引出坑 3

sudo apt install -y socat
dsh web --port 9999 &   # 挪到内部端口
socat TCP-LISTEN:3080,fork,reuseaddr TCP:127.0.0.1:9999 &

坑 2:crypto.randomUUID is not a function(非安全上下文)

通过 http://局域网IP:3080 访问时,浏览器判定页面为非安全上下文(只有 HTTPS / localhost / file:// 是安全上下文),crypto.randomUUID 被禁用,模型目录加载失败。

Chrome 解法(零服务端改动):

  1. 地址栏输入 chrome://flags/#unsafely-treat-insecure-origin-as-secure
  2. 填入 http://192.168.31.231:3080,选 Enabled
  3. Relaunch 重启浏览器

坑 3:API 403 —— browser-trust fence 与 loopback-only 特权方法

页面能开了,但 /api/* 请求 403。dsh 的 /api 有浏览器信任栅栏isTrustedApiRequest),规则:

  1. Host 头必须是 loopback 或 --trusted-host 白名单
  2. 带 Origin 时,Origin 的 host 必须与 Host 完全一致
  3. 更狠的是:settings.describecredentials.describeagentPreset.read 等特权方法强制 loopback-only——即使配了 --trusted-host 也拒绝局域网访问(配置平面=设置+凭据,源码注释明说"no anonymous caller should have",直到未来有真正的认证层)

解法:nginx 反向代理,把 Host 和 Origin 都改写成 127.0.0.1(服务端视角变成"本机访问",浏览器无感):

sudo apt install -y nginx-light
# /etc/nginx/conf.d/dsh.conf
server {
    listen 3080;
    server_name _;

    location / {
        proxy_pass http://127.0.0.1:9999;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host 127.0.0.1:3080;
        proxy_set_header Origin http://127.0.0.1:3080;   # 关键
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}
sudo nginx -t && sudo systemctl reload nginx

同时 dsh 要带 --trusted-host 启动(放行非 localhost 的 Origin 来源):

dsh web --port 9999 --trusted-host 192.168.31.231:3080 &

验证:

curl -s -o /dev/null -w "%{http_code}\n" http://192.168.31.231:3080/                    # 200 首页
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  -H "Origin: http://192.168.31.231:3080" -H "Content-Type: application/json" \
  -d '{"type":"client-request","rpcId":"t","method":"settings.describe","payload":{}}' \
  http://192.168.31.231:3080/api/settings.describe                                     # 200 特权方法放行

顺带说明:isTrustedApiRequest 里 Host 与 Origin 必须完全一致(含端口),所以之前 socat 转发(无法改 HTTP 头)时特权方法必 403,这就是必须上 nginx 的原因。

第七步:配置模型

方式 A:DeepSeek 官方 API

设置 → 模型 → DeepSeek 卡片,填 platform.deepseek.com 的 API Key(存于 $DSH_HOME/.credentials.yaml,即 ~/.dsh/.credentials.yaml)。

方式 B:自定义提供方(任意 OpenAI 兼容网关)

适合自建网关(newapi / one-api / vllm 等)。设置 → 模型 → 添加自定义提供方,或直接编辑 ~/.dsh/settings.yaml

llm-pi-ai:
  providers:
    newapi:
      apiKeyEnv: NEWAPI_API_KEY          # 从环境变量读 key
      api: openai-completions            # 协议:openai-completions / openai-responses / anthropic-messages
      baseURL: http://192.168.1.100:3000/v1
      models:
        - id: deepseek-v4-flash
          name: deepseek-v4-flash

凭据存 ~/.dsh/.credentials.yaml

NEWAPI_API_KEY: sk-xxxx

设置默认模型(重要!)

配置了自定义提供方后,默认模型仍是 DeepSeek 官方路由,发消息会报:

llm-deepseek: no API key for provider route "deepseek-official";
store DEEPSEEK_API_KEY through the credentials service, or export DEEPSEEK_API_KEY

两种解决:

  1. UI 操作:聊天界面模型选择器选 newapi → deepseek-v4-flash并开新会话(已发过消息的会话保留旧模型,换默认不影响它)
  2. 配置文件~/.dsh/settings.yaml):
agent-default-model:
  provider: newapi          # 路由名:llm.providers 接口里查到的 provider id
  model: deepseek-v4-flash

改完重启 dsh web 生效。

常见报错对照表

报错原因解决
crypto.randomUUID is not a functionHTTP+IP 非安全上下文Chrome flag 把地址加入 secure origin
transport failure ... HTTP 403(agentPreset.list)Host/Origin 不在 trusted-host--trusted-host 192.168.31.231:3080
transport failure ... HTTP 403(settings.describe 等)特权方法强制 loopbacknginx 改写 Host+Origin 为 127.0.0.1
no API key for provider route "deepseek-official"默认模型没切到自定义提供方模型选择器切换 + 新会话,或配 agent-default-model
MISSING_CREDENTIAL凭据未保存/环境变量未设Models 页存 key 或 export 环境变量
UNKNOWN_MODEL模型不在提供方列表手动添加模型
tsc JavaScript heap out of memoryCM4 内存不够编译放弃源码构建,改用 npm i -g @deepseek-ai/dsh
Unsupported platform: wanted x64ARM64 无该包预编译忽略即可
pnpm: command not found(SSH 非交互)PATH 问题用绝对路径调用

安全提醒(务必看)

  • dsh 是能执行 Shell/编辑文件的 Agent 工具。本文的 nginx 方案等效于"局域网内任何设备都能读写配置和凭据"——仅限可信局域网使用,切勿通过 frp/端口映射暴露到公网
  • 官方拒绝 --host 0.0.0.0 是有意为之,nginx 改写 Host/Origin 已经绕过了 loopback-only 保护,公网暴露 = 裸奔的 RCE。
  • 密钥存于 ~/.dsh/.credentials.yaml,注意文件权限(chmod 600)。

参考资料


本文基于 2026-08 实际部署记录整理。环境:CM4 (ARM64, 1.8GB RAM) + Debian + Node 22 + pnpm 11.7 + dsh rc.6。

点赞 0