news 2026/9/3 18:32:26

DeepSeek Harness(dsh)从零到全栈【2】环境搭建与第一次对话

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepSeek Harness(dsh)从零到全栈【2】环境搭建与第一次对话

环境搭建与第一次对话

本章导读:DeepSeek Harness(dsh)从零到全栈【1】认识 DeepSeek Harness:Agent、Harness 与“一切皆插件“ 回答了"DeepSeek Harness 是什么",本章回答"怎么让它跑起来"。你会走过两条启动路径,npx一键启动与源码运行,把环境要求精确到补丁号,拿到并安放好你的 API Key,然后按官方 quickstart 完成第一次对话:配置模型、选择工作区、发出第一个提示词、观察权限审批。最后我们把 CLI 形态、SSH 远程场景与源码构建排错一并交代。学完本章,你将拥有一个可以日常使用的 dsh 环境,它也是后续所有章节的实验场。

摘要:本章带你从零跑通 DeepSeek Harness(dsh)。先对比npx一键启动与源码构建两条路径的适用场景;再把环境要求精确到补丁号——Node 引擎下限^22.19.0 || >=24.0.0node:sqlite、原生类型剥离与依赖链三重约束决定,并识破"Node 18 可用"的误区。随后完成 API Key 配置与 Web UI 首次对话全流程(配模型 → 选工作区 → 新建会话 → 发提示词),理解密钥只写地存放在$DSH_HOME/.credentials.yaml。最后概览web/headless等五种 profile 形态、SSH 远程访问与源码构建排错,并通过三个动手实验巩固所学。

文章目录

  • 环境搭建与第一次对话
    • 学习目标
    • 正文
      • 2.1 两条启动路径:买成品家具,还是进木工房
      • 2.2 环境要求:版本精确到补丁号
      • 2.3 获取并设置 API Key
      • 2.4 Web UI 首次使用全流程
      • 2.5 界面功能导览
      • 2.6 CLI 形态概览:`dsh` 不止是一个聊天窗口
      • 2.7 SSH 远程服务器场景
      • 2.8 从源码构建排错
    • 动手实验
      • 实验一:环境自检(约 3 分钟)
      • 实验二:Web UI 完整首跑(约 10 分钟)
      • 实验三:headless 一发入魂(约 3 分钟)
    • 常见坑
    • 小结
    • 参考资料

学习目标

  • 能独立通过npx @deepseek-ai/dsh web或源码路径(pnpm installpnpm run buildpnpm dsh web)启动 Web UI,并说出两条路径各自适合的场景。
  • 能解释engines.node写作^22.19.0 || >=24.0.0的三重原因(node:sqlite、原生类型剥离、依赖下限),并识破网上"Node 18 可用"的错误说法。
  • 能完成"配置 API Key → 选择工作区 → 新建会话 → 首次对话"全流程,并说出密钥的真实存放位置是$DSH_HOME/.credentials.yaml,而不是 settings。
  • 能说出webheadless两个内置 profile(运行档位)各适合什么,并列出至少 3 个来自官方 CLI 参考的启动命令。
  • 能在 SSH 远程服务器上启动 dsh,并用本地端口转发在浏览器里访问它。

正文

2.1 两条启动路径:买成品家具,还是进木工房

  • dsh 有两种合法的启动方式。把它们想成买家具:一条是快递送来的成品(npx拉取 npm 上已构建的包),开箱即用;另一条是自己进木工房(clone 源码自己构建),费事,但图纸和木料都归你改。

路径一:npx 一键启动(推荐大多数读者)

# 来自 README.zh.md「运行」节npx @deepseek-ai/dsh web
  • 这条命令会从 npm 拉取@deepseek-ai/dsh包并启动 Web UI,默认监听http://127.0.0.1:3080。本机启动时它会用默认浏览器打开页面;通过 SSH 启动时只打印宿主机 URL,因为本地转发地址由 SSH 客户端或编辑器持有。如果你只想起服务器、不想让它自动开浏览器,传--no-open

路径二:从源码运行(适合二开与跟读源码)

gitclone https://github.com/deepseek-ai/deepseek-harness.gitcddeepseek-harnesspnpminstallpnpmrun buildpnpmdsh web
  • 这里有一个初学者最容易误解的细节:pnpm dsh web不会重新构建。按官方 README 的原话,"pnpm run build会准备仓库产物。pnpm dsh web会直接使用这些已构建产物,不会重新构建。"也就是说,builddsh是两个独立步骤:前者把 monorepo 里的包和 Web 前端编译成产物;后者通过node --import tsx/esm直接运行apps/cli/src/bin.ts这个 TypeScript 入口,加载的却是磁盘上已存在的构建结果。

  • 作为对照,npm 安装版运行的是构建后的apps/cli/lib/bin.jsapps/cli/package.json中的"bin": { "dsh": "lib/bin.js" }),同样不触发重新构建。

  • 补两个npx的实用细节。其一,npx 第一次运行会下载并缓存包,之后的启动走缓存,不是每次都重新下载;想钉死版本以精确复现本教程的行为,可以显式带版本号:npx @deepseek-ai/dsh@0.1.2-alpha.1 web,npm 的版本号语法对所有包一致。其二,npx 拉取的是 npm 上的发布产物,行为以官方 release 为准;本系列以0.1.2-alpha.1为版本基准,而项目处于开发者预览期、迭代很快,若你读到本文时版本已更新,个别界面文案可能与描述有出入,一切以你手上版本的实际行为为准。

  • 怎么选?如果你只想体验产品、跟着本系列前四章学习使用,用npx,它永远与官方发布的版本对齐。如果你打算改源码、写插件、或者像本系列后面章节那样"源码对照着学",走源码路径:改完代码跑一次pnpm run build,再用pnpm dsh验证。两者共用同一套$DSH_HOME配置与凭据,切换路径不需要重配任何东西。

2.2 环境要求:版本精确到补丁号

  • 先看根目录package.json的原文:
// 来自 package.json(版本基准 0.1.2-alpha.1) "packageManager": "pnpm@11.7.0", "engines": { "node": "^22.19.0 || >=24.0.0" }
  • 解释:^22.19.0表示 22.19.0 及以上、但小于 23 的 22.x 版本;>=24.0.0表示 24 及更高。Node 23 整条线被排除在外,Node 18、20 更是连门都没有。

💡深挖:下限为什么偏偏是 22.19?仓库里的决策记录.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md给出了完整推理,概括为三重约束:

  1. node:sqlite内置模块。会话持久化与存储层直接使用 Node 内置的 SQLite 绑定(例如packages/storage/storage-sqlite/src/schema.ts顶层import { DatabaseSync } from 'node:sqlite')。该模块在 Node 22.13(LTS 线)才取消--experimental-sqlite标志要求,更早的版本在导入时就抛异常。
  2. 原生 TypeScript 类型剥离。Node 从 22.18(LTS 线)起默认支持直接运行.ts文件(剥离类型),更早需要--experimental-strip-types标志。
  3. 依赖链的下限。LLM 适配器@deepseek-ai/dsh-llm-pi-ai依赖的@earendil-works/pi-ai自己声明engines.node >=22.19.0,把 22.x 线的门槛从 22.18 再抬到 22.19。

而 Node 23 之所以被排除:23.0–23.5 上上述源码特性仍需要标志,且 23 是已结束生命周期(EOL)的非 LTS 线,宣传它只会增加一条没人该用的运行时。这种"精确到补丁号"的引擎声明在开源项目里少见,但它是后续一切安装问题的第一现场。

pnpm 从哪来:源码路径需要 pnpm。仓库用packageManager字段钉死了pnpm@11.7.0,标准做法是用 Corepack(Node 官方的包管理器版本管理器,随 Node 22 附带)启用:

corepackenable# 之后在仓库目录里 pnpm 会自动切到 11.7.0
  • 若你的 Node 发行版没有附带 Corepack,npm install -g corepack后再执行上面命令即可。

各操作系统差异:本系列主环境是 Windows(作者机器即 Windows + Git Bash),仓库的测试门禁覆盖 Linux 与 Windows(package.json里还能看到专门的 Windows 门禁与 Wine 方案),Python SDK 则官方支持 Linux x64/arm64、macOS 14+(arm64)与 Windows x64。macOS/Linux 上 shell 命令按 POSIX 习惯即可;Windows 上建议用 PowerShell 或 Git Bash,路径分隔符差异由 Node 自行处理。

  • 命令层面三平台几乎一致,差异集中在环境变量的写法:Git Bash 用export NAME=value,PowerShell 用$env:NAME="value",CMD 用set NAME=value——后文遇到需要设环境变量的地方会提醒你按所用的 shell 选择。安装 Node 本身:macOS 推荐用 Homebrew 装 LTS 线;Linux 发行版软件源里的 Node 往往过旧,用 nvm 或 NodeSource 管理;Windows 直接下官网安装器即可。

  • 还有一个值得说透的疑问:engines只是一份声明,包管理器默认并不强制执行。npm 在版本不匹配时只给警告照常安装;pnpm 同样默认放行,除非开启engine-strict——仓库的下限决策记录里,正是用pnpm install --engine-strict来保证"宣传的 LTS 分支不低于依赖链的下限"。所以"装上了"不等于"支持":^22.19.0 || >=24.0.0是官方 CI 矩阵(22.19、24、26 三个版本)实测过的承诺边界,你硬要用 22.19 以下或 23.x 运行,没人拦你,但出了问题只能自己兜底。

Python SDK 的额外要求:如果你后续想从 Python 程序里嵌入 dsh(pip install deepseek-harness-sdk),需要 Python 3.10 或更高版本;SDK 的 wheel 打包了同一个dsh命令和原生运行时,普通 SDK 使用不需要系统安装 Node.js。详见docs/user/guide/python-sdk.zh.md,本篇不展开。

⚠️常见误区:"Node 18 可用"是错的。网上能搜到一些第三方教程(包括某云厂商的帮助文档)声称 Node 18 即可运行,这与仓库engines的声明直接矛盾。它们多半写于项目发布早期或干脆照抄了别的工具的要求。判据永远只有一个:你手头检出的仓库里package.jsonengines字段。用 Node 18/20 实际运行时,报错往往不是"版本不支持",而是在node:sqlite导入或模块解析处抛出莫名其妙的异常——不直说版本问题,这正是它难排查的原因。

2.3 获取并设置 API Key

  • dsh 出厂对接的是 DeepSeek 官方 API。到 platform.deepseek.com 注册并创建一个 API Key(形如sk-...的字符串),充少量余额即可开始。
  • 然后回到 Web UI:打开设置 → 模型,DeepSeek 卡片上有一个 API 密钥字段,粘贴并保存。官方 quickstart(docs/user/guide/index.zh.md)的原话是:“模型路由会立即可用,不需要重启服务器。” 这是很多工具做不到的体验:你不用重启dsh web进程,下一次请求就走新模型。
  • 密钥去了哪里?docs/user/guide/providers.zh.md写得很明确:

密钥是只写的。保存后,页面只会收到脱敏描述符,永远不会收到明文密钥。密钥存储在$DSH_HOME/.credentials.yaml中,settings 只保留它的凭据引用。

  • $DSH_HOME是 dsh 的家目录,默认是你用户目录下的.dsh(源码依据:packages/util/home-paths/src/index.tsDSH_HOME_DIR_NAME = '.dsh'defaultDshHome()返回homedir() + '.dsh'),可用环境变量DSH_HOME改指别处。也就是说,Windows 上你的密钥在C:\Users\<你>\.dsh\.credentials.yaml。这个"凭据与配置分离"的设计意味着:备份或迁移settings.yaml时不会顺手把密钥带出去;想换机器,单独搬.credentials.yaml

💡深挖:凭据的完整解析顺序。apps/cli/reference/README.zh.md的「共享部署行为」节规定,提供方凭据依次从四个位置解析:继承环境 →$DSH_HOME/.credentials.yaml→ 调用目录的.env$DSH_HOME/.env。搜索类工具查找DEEPSEEK_API_KEY(另接受DEEPSEEK_SEARCH_BASE_URL)。这意味着除了在 Web UI 里粘贴,你还有两条等价的路:其一,环境变量,DeepSeek 模型适配器的默认凭据引用就是DEEPSEEK_API_KEYpackages/llm/llm-deepseekDEFAULT_API_KEY_ENV = 'DEEPSEEK_API_KEY',README 的apiKeyEnv字段表同),export DEEPSEEK_API_KEY=sk-...之后 Web UI 里那一步都可以省掉;其二,在启动dsh的目录放一个普通.env文件写上DEEPSEEK_API_KEY=sk-...,适合"每个项目一把钥匙"的项目级隔离。三种方式的取舍:GUI 保存最省事且密钥只写;环境变量适合 CI 与一次性运行;调用目录.env适合多项目并行的隔离需求。另外注意文档的一个细节:“受管文档从不物化进process.env,而两个.env文件都是普通启动环境层”,.credentials.yaml里保存的密钥不会被注入子进程环境,凭据体系的边界是刻意的。

  • 顺带交代隐私默认值:会话遥测默认按反馈门控,在你主动执行/feedback之前,不会有任何数据上传;DSH_TELEMETRY_MODE=DISABLED则让全部数据留在本地。对要在公司环境里试用的读者,这是个值得记住的默认。

2.4 Web UI 首次使用全流程

  • 现在把官方 quickstart(docs/user/guide/index.zh.md)完整走一遍。以下五步是本章的主干,建议对照着做。

第一步:启动并进入页面。在你想当作工作目录的地方运行npx @deepseek-ai/dsh web。终端会打印一行dsh web:开头的 URL——注意这不是普通的http://127.0.0.1:3080,而是携带进程 token 的启动 URL。浏览器打开它后,会用这个 token 换取一张签名会话 cookie,然后重定向到干净的根 URL(这是 v0.1.2 引入的一次性 token 认证:token 只在启动时有效一次,页面凭证由 cookie 承担)。本机启动时浏览器自动打开;若操作系统交接失败,stderr 会打印不含凭据的诊断,服务器继续运行——你手动打开终端里那个带 token 的 URL 即可。

第二步:配置模型。就是上一节的内容:设置 → 模型 → 粘贴 DeepSeek API Key → 保存。保存后模型选择器里会出现 DeepSeek 的模型。

第三步:选择工作区——新手第一大卡点。官方指南原文:“点击选择工作区,添加启动dsh时所在的项目目录,然后选中它。选中工作区前,会话输入框不可用。

  • 为什么必须选?这里要把一个概念掰开:dsh进程会把启动时所在的目录作为默认文件系统位置,但新打开的 Web UI 不会自动选中任何工作区,你要在界面里手动添加并选中一个。工作区(workspace)决定 agent 能看见、能读写的文件边界:它就是 agent 的"工位",工位之外的文件它既看不见也翻不动。所以"我的项目怎么找不到"这类问题,十有八九是工作区没选对,而不是 agent 笨。

  • 这也解释了一个新手常困惑的现象:你在哪个目录运行npx @deepseek-ai/dsh web,那个目录就成了"默认文件系统位置"。所以最佳实践是把它当成项目的随行工具,cd进项目根目录再启动,Web UI 里添加的也就是同一个目录,两边天然对齐。工作区之后还可以随时换,但第一次选对能省掉很多"它怎么看不到这个文件"的来回。

⚠️常见误区:把父目录或 home 目录当工作区。如果你把D:\或用户主目录选成工作区,agent 每次列目录都会面对成千上万无关文件,既浪费上下文又危险。正确姿势:启动dsh前先cd到项目根目录,Web UI 里也选中同一个目录,两边对齐。

第四步:新建会话,发出第一个提示词。官方示例原话是:

Summarize this repository and identify its main packages.

把它发给 agent(智能体)。它会读取和编辑工作区文件、运行命令、委派工作并维护计划。官方指南同时预告了接下来会发生什么:"如果根据当前权限策略,某项操作需要审批,Web UI 会先询问你。"新会话默认使用workspace-write权限预设(docs/subsystems/permission-presets.zh.mdworkspace-write预设 = 沙箱模式workspace-write+ 审批策略ask)——Bash 和文件系统修改被限制在会话工作区与平台临时根目录内,读取和网络访问不受限制。所以上面这条总结请求中的读取操作会直接执行;而一旦操作要越过权限策略,界面会弹出审批请求(approval),等你点批准或拒绝。

第五步:读回复。对话流里你会看到模型的回答、工具调用树(哪些文件被读了、哪些命令跑了)以及引用的文件链接。回复结束后,你可以继续追问,也可以换一个会话从头再来——每个会话都是独立持久化的,关掉浏览器甚至关掉服务器,会话都在。

  • 两个值得在第一次对话时就养成的观察习惯。第一,看审批:读取类操作(读文件、列目录)在workspace-write下不经过你;真正弹审批的,是要越过权限策略的操作。第一次亲眼见到审批弹窗很重要——后面所有关于权限的讨论,都建立在你见过这个界面的前提上。第二,看会话的持久性:官方 Python SDK 指南提到 home 下有sessions/目录存放未压缩的 JSONL 会话日志(docs/user/guide/python-sdk.zh.md),dsh 的会话以事件溯源方式逐条记录,Web UI 的会话历史也来自同一套持久化。你不需要现在就去读这些日志,但知道"每一个会话、每一次工具调用都落了盘",等第 06 章教排错时你会回来找它们。

2.5 界面功能导览



首次对话走通后,快速认一遍界面。以下每一项都能在packages/client/README.zh.md的包表中找到对应实现,不是凭印象描述:

  • 侧边栏ui-sidebar):工作区与会话导航,会话历史按会话列出。
  • 工作区选择器ui-workspace):选择与创建工作区,即 2.4 第三步用的那个入口。
  • 对话区与输入框ui-conversation):输入框支持@file/@session引用(ui-reference),可以把具体文件或历史会话挂进提示词。
  • 模型选择器ui-model-selection):已配置的提供方出现在这里;按providers.zh.md的说法,“选择模型也会将其设为新会话的默认值”,已发送过请求的会话保留自己日志中记录的模型。
  • 权限预设选择器ui-permission-presets):在workspace-writedanger-full-access(沙箱danger-full-access+ 审批never)之间切换当前会话的访问模式;后者完全放权,新手不建议日常使用。
  • 计划模式指示ui-plan):显示 plan mode(计划模式)状态与退出控件——开了它,agent 会先给计划再动手,复杂任务前很有用(第 04 章细讲)。
  • 审批弹窗ui-approval):需要你决策的权限请求在这里出现。
  • 目标与后台任务ui-goal/ui-jobs):跨轮目标与当前会话的后台任务状态。
  • 产出物引用ui-deliverables):一轮结束生了哪些文件,轮次尾部会生成可点击的文件引用,不用自己去目录里翻。
  • 轨迹视图ui-trajectory):agent 活动的其他观察角度,想回看它每一步做了什么时有用。
  • 设置页ui-settings及其分区):模型(ui-settings-models,就是配 Key 的地方)、常规(ui-settings-general)、插件(ui-settings-plugins)等。

两个 v0.1.2 相关的细节值得单独说。其一是界面语言:在"设置 → 常规"中从已注册语言里选择,UI 文案立即切换;内置zhen两种(packages/client/locale/README.zh.md),选择在 loopback 页面上以locale.preference存进$DSH_HOME/settings.yaml。其二是一次性 token 认证,2.4 第一步已经说过。

💡深挖:agent 也能"看见"这个网页。packages/bundle/web-app/README.zh.md记录了一个默认开启的配置项surfaceContext:它给 agent 提供当前 GUI 的定位上下文(规范本地 URL、“this page” 指代什么),并把DSH_WEB_URL环境变量暴露给它的 shell 命令。这意味着你可以直接对 agent 说"打开你自己的页面看看",它知道自己在为哪个界面服务。这类"自我指涉"的能力来自 patch 层里的一行配置——它长什么样、为什么能这样写。

2.6 CLI 形态概览:dsh不止是一个聊天窗口

  • Web UI 只是 dsh 五种内置运行形态之一。dsh 的启动器(launcher)用 profile 来区分形态——profile(运行档位)是一组按顺序叠加的插件组合包配置层,这个概念第 03 章正式定义,这里先记住"一个 profile 一种形态"即可。下表完整取自apps/cli/README.zh.md的「入口模式」表:
命令用途
dsh --profile <name>启动位于$DSH_HOME/profiles/<name>的指定 profile。
dsh --profile acp通过 ACP stdio 为自动化 client 提供服务,直至断开连接。
dsh --profile headless "job"运行一个全新的持久化会话,打印最终答案并退出。
dsh --profile sdk通过 JSON-RPC stdio 为 SDK client 提供服务,直至关闭或断开连接。
dsh --profile sdk-minimal以独立极简 agent 配置树为 SDK client 提供服务。
dsh web--profile web的别名。
dsh plugin --profile <name> <pnpm args>通过在 profile 目录中转发给 pnpm 来管理该 profile 的插件。
  • 日常最常用的是两个极端:web给人看(浏览器里的交互式 GUI),headless给程序用(headless,无头模式,没有界面,标准输入输出就是它的脸)。

    • headless的行为契约在apps/cli/reference/README.zh.md里规定得非常细,几条最值得记住:stdout 只打印最终文本;提供方的推理分片(reasoning delta)以dsh: reasoning:为标题流式写入 stderr;任务原因为completed时以 0 退出,否则以 1 退出;没有任务的调用是该应用的用法错误。此外随附的 headless profile 不挂载浏览器连接、HTTP 服务器与 Web 运行时,不打开任何监听端口,它就是一个纯 stdio 的一次性执行器。这份契约让dsh --profile headless "..."可以直接嵌进 shell 脚本和 CI 里用:退出码判断成败,stdout 拿结果,stderr 看过程。
  • 注意五个内置 profile 里没有终端交互式聊天形态——想要 TUI(终端用户界面),社区的turtle-ui是现成例子(来自官方参考的示例):dsh plugin --profile tui add github:deepseek-harness/turtle-ui,装完dsh --profile tui启动。

  • 启动器自身的 flag(必须写在应用参数之前,遇到第一个无法识别的 token 就停手,其后全部交给 profile 里的应用去解析):

flag行为
--profile <name>选择要启动的 profile。
--patch <path>追加 patch 覆盖层,可重复:--patch a.yml --patch b.yml
--dump-config不启动,打印组合后的完整配置树。
--dump-default-config只打印组合包各层,不含用户层与--patch
-V/--version打印启动器版本。
--help打印启动器帮助。
  • "参数边界"值得用一个官方示例说透(来自apps/cli/README.zh.md):
# 来自 apps/cli/README.zh.md「应用参数」节dsh--profileweb--port8080# --port belongs to the web appdsh--profileheadless"run the tests"dsh--profileweb--help# the web app's flags, not the launcher'sdsh--help# the launcher's own help
  • 同一个--help,跟在--profile web后面打印的就是 web 应用自己的参数帮助,裸写才是启动器的帮助——启动器解析到--profile web之后的第一个陌生 token 就收手,把控制权连同剩余参数整个交给 profile。这条规则解释了 dsh 命令行的一个独特观感:不同 profile 有各自完全不同的第二段参数表,因为那段参数本来就属于注入该 profile 的应用插件,而不是启动器。

  • --dump-config--dump-default-config互斥(args.ts里对同时给出两者的情况直接报错 “mutually exclusive”),且 dump 模式不接受应用参数。这对组合是检查"我的配置到底长什么样"的官方入口——第 03 章的动手实验就会用它打印可被 patch 的组装树。

  • web 应用的参数--host--port、可重复的--trusted-host--no-open。flag 优先于配置行——源码里 web 端口的配置行写作port: !!js ctx.webStartup.port ?? 3080,flag 填充了表达式前面的那个值。两个边界要记住:dsh web之后的 flag 属于 web 应用而不是启动器;CLI有意不支持--host 0.0.0.0,传了会以用法错误退出(要不要对局域网开放是安全决策,不能一行 flag 顺手带走,见 2.7)。

  • 插件管理dsh plugin --profile <name> add <package>在 profile 目录里转发给 pnpm 安装插件,remove/why/update等 pnpm 子命令同样可用(前提:pnpm 在 PATH 上)。这是 dsh"一切皆插件"在操作层面的样子——第 01 章的口号,落地成一个安装命令。

2.7 SSH 远程服务器场景

  • 在一台没有桌面的远程服务器上跑 dsh、用自己的笔记本浏览器访问,是本工具设计里明确支持的场景。机制如下(依据apps/cli/reference/README.zh.md):
    • 当继承环境中SSH_CONNECTIONSSH_TTY非空时,dsh跳过浏览器交接,因为"本地转发地址由 SSH 客户端或编辑器持有;宿主机 URL 仍会打印"。这与 README 的说法一致:SSH 下只打印宿主机 URL。("编辑器"指的正是 VS Code Remote-SSH 这类场景——编辑器内置的端口转发代替了你手动建隧道,dsh 不区分这两者,一律不抢着开浏览器。)
    • 所以流程是:服务器上启动dsh web(加不加--no-open均可,反正 SSH 下本来就不会尝试开浏览器),复制打印出的带 token 的 URL;本地终端建一条端口转发,再在本地浏览器打开同一地址:
# 示例代码:本地终端执行,把服务器的 3080 转发到本机 3080ssh-L3080:127.0.0.1:3080 user@your-server# 然后本地浏览器打开 http://127.0.0.1:3080(或服务器打印的带 token URL)

一个安全细节:带 token 的启动 URL 在转发后的本地浏览器里打开完全没问题,因为转发的就是127.0.0.1上的这条 TCP 链路,token 只在你的机器与服务器之间走了一趟。但如果 URL 里的 token 被泄露给同网段的其他人,对方就能用 token 换到合法 cookie——所以别把启动 URL 贴进群聊或 issue,这是它"一次性"设计想压缩的风险窗口。

  • 默认情况下 GUI 只接受本机连接。如果你的拓扑需要局域网内其他机器访问,CLI flag 不允许0.0.0.0,需要通过配置绑定其他网络接口,并用可重复的--trusted-host把访问用的主机名加入/api浏览器信任围栏。Host 与 Origin 检查控制可达性,token 交换认证每个 Host API 方法与 WebSocket stream——两道门缺一不可。细节见packages/bundle/web-app/README.zh.md

2.8 从源码构建排错

  • 源码路径偶尔会卡住,这里按"症状 → 根因"列全官方文档可证的部分,外加两条实践中反复出现的经验。

症状一:pnpm dsh启动即报模块解析错误,且错误里没有任何构建指引。这是官方文档点名的行为:“Typert Host 产物缺失时,profile 启动会因不含构建指引的模块解析错误而失败。”(apps/cli/reference/README.zh.md「源码执行」节。)根因是没跑pnpm run build,或构建中途失败。解法:在仓库根目录重新pnpm run build

症状二:改了前端代码但页面行为没变。官方原话:“启动器不会检查产物是否为最新,因此已有的陈旧组合包可能继续运行旧版浏览器代码,直至重新构建。”pnpm dsh只管加载、不负责新鲜度,改完代码记得重新构建。

症状三:构建中途内存溢出。package.jsonbuild:lib:host脚本显式带着--max-old-space-size=4096——官方自己就把 TypeScript 构建的堆上限提到 4 GB,说明这是真实瓶颈。机器内存紧张时,关掉大程序再构建,或给 Node 调更大的堆。

症状四:pnpm 版本不对。仓库用packageManager: "pnpm@11.7.0"钉死版本。别用全局随手装的旧版 pnpm;corepack enable之后在仓库目录里执行pnpm -v确认它自动切到了 11.7.0——Corepack 读到的正是packageManager字段,在仓库目录内外可能给出不同版本,这是特性不是故障。若输出的版本号对不上,多半是 shell 里还缓存着别的 pnpm(which pnpm看看它从哪来)。

两条经验型建议(非官方文档明文,属社区与笔者实践):其一,仓库路径尽量用纯英文且不含空格——monorepo 深层依赖路径加上非 ASCII 字符或空格,是 Windows 上工具链出幺蛾子的高发组合;其二,Windows 默认 260 字符路径上限可能被pnpm install的深层 node_modules 打穿,开启系统长路径支持或把仓库放到短路径下(如D:\work\dsh)。

最后一条与网络有关:企业代理环境下,pnpm installHTTP_PROXY/HTTPS_PROXY;而运行 dsh 源码进程时,官方要求"当支持环境代理的 Node 版本必须遵循HTTP_PROXYHTTPS_PROXY时,请设置NODE_USE_ENV_PROXY=1"——Node 不会默认为内部请求启用环境代理,这个开关是官方给出的答案。

动手实验

实验一:环境自检(约 3 分钟)

node-v# 期望:v22.19.0 ~ v22.x,或 v24.x+;出现 v18/v20 则先升级npx @deepseek-ai/dsh-V# 期望:打印形如 0.1.2-alpha.1 的版本号(以你安装的为准)
  • 第一条对不上^22.19.0 || >=24.0.0就不要往下走。第二条验证 npm 包可以被拉取、启动器可执行。想看启动器完整帮助,把-V换成--help

实验二:Web UI 完整首跑(约 10 分钟)

# 示例代码:准备实验目录并启动mkdirdsh-lab&&cddsh-labecho"dsh tutorial says hi">notes.txt npx @deepseek-ai/dsh web
  1. 浏览器自动打开带 token 的启动 URL,落地到干净的根页面;
  2. 设置 → 模型,粘贴 DeepSeek API Key,保存——不重启服务器;
  3. 选择工作区,添加并选中刚才的dsh-lab目录,确认输入框从不可用变为可输入;
  4. 新建会话,发送官方示例提示词Summarize this repository and identify its main packages.,观察它的工具调用与回答;
  5. 再发一条请读取 notes.txt,告诉我里面写了什么。——让它读一个你自己准备的本地文件,确认工作区打通;
  6. (可选)发一条在工作区创建 hello.txt,内容为今天日期。,如果权限策略要求审批,观察弹窗的样式与选项,然后批准,回工作区目录确认文件真的出现了。

实验三:headless 一发入魂(约 3 分钟)

npx @deepseek-ai/dsh--profileheadless"用一句话解释什么是 agent harness"echo$?# Git Bash 查看退出码;completed 时应为 0

预期:stdout 只有最终的一句回答(没有过程日志),推理分片若有则出现在 stderr 且带dsh: reasoning:前缀,退出码为 0。同目录下你会得到一个已持久化的会话——它和 Web UI 的会话存在同一个$DSH_HOME里。

常见坑

症状原因解法
启动报端口错误,或浏览器打不开127.0.0.1:30803080 被其他进程占用dsh web --port 8080换端口(flag 优先于配置行),或结束占用进程
浏览器没有自动打开SSH 会话自动跳过交接;或 OS 交接失败复制终端打印的带 token URL 手动打开;本机想彻底禁用自动打开用--no-open
会话输入框灰置、无法输入新打开的 Web UI 不会自动选中工作区点击"选择工作区",添加并选中启动dsh的目录
agent 说"找不到我的项目/文件"选错了工作区目录(选成父目录或别的目录)重新选择正确的项目根目录;工作区决定 agent 的文件可见与修改范围
安装或运行时在奇怪的位置报错第三方文档误导装了 Node 18/20(如某云厂商帮助文档称 Node 18 可用,与engines矛盾)以仓库package.jsonengines为准:^22.19.0 || >=24.0.0,用 nvm 等工具切版本
源码启动报"不含构建指引的模块解析错误"没跑pnpm run build或构建失败仓库根目录重新pnpm run build
改了代码但行为没变启动器不检查产物新鲜度,继续用旧产物重新pnpm run build后再pnpm dsh

小结

  • 两条启动路径:npx @deepseek-ai/dsh web拉取 npm 成品开箱即用;源码路径pnpm installpnpm run buildpnpm dsh web,其中pnpm dsh通过node --import tsx/esm运行apps/cli/src/bin.ts,直接使用已构建产物、不重新构建。
  • Node 引擎下限^22.19.0 || >=24.0.0由三重约束决定:node:sqlite需 22.13+、原生类型剥离需 22.18+、依赖pi-ai宣称>=22.19.0;Node 23 整线被有意排除。"Node 18 可用"的说法与engines矛盾,是错的。
  • API Key 在 Web UI 的"设置 → 模型"里粘贴保存后立即生效,无需重启服务器;密钥是只写的,实际存放在$DSH_HOME/.credentials.yaml(默认~/.dsh下),settings 只保留凭据引用。
  • Web UI 首跑四步:配模型 → 选择工作区 → 新建会话 → 发提示词;未选中工作区前输入框不可用,这是新手第一大卡点。
  • 新会话默认workspace-write权限预设:Bash 与文件修改限制在工作区与临时目录内,读取与网络不受限,越界操作会触发审批弹窗。
  • 五个内置 profile 中,web给人用、headless给程序用:stdout 只出最终文本、reasoning 走 stderr、completed退出码 0;启动器 flag(--profile/--patch/--dump-*)必须写在应用参数之前。
  • SSH 下 dsh 跳过浏览器交接、只打印宿主机 URL,本地转发交给 SSH 客户端;CLI 有意不支持--host 0.0.0.0,局域网访问走配置加--trusted-host

参考资料

官方文档(仓库路径):

  • README.zh.md——运行节:npx 与源码两条路径的官方命令
  • docs/user/guide/index.zh.md——Web UI 使用指南(本章 quickstart 主干)
  • docs/user/guide/providers.zh.md——配置模型:密钥存放位置与只写语义(深入配置留待第 05 章)
  • docs/user/guide/python-sdk.zh.md——Python SDK 前置条件
  • apps/cli/README.zh.md——CLI 入口模式与应用参数边界
  • apps/cli/reference/README.zh.md——CLI 行为参考:flag、headless 契约、凭据解析顺序、SSH 行为
  • packages/bundle/web-app/README.zh.md——web 应用配置:token 认证、trusted-host、surfaceContext
  • docs/subsystems/permission-presets.zh.md——权限预设的组合语义
  • .agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md——Node 引擎下限的完整决策记录

源码:

  • package.json——enginespackageManagerdshscript、build:lib:host内存参数
  • apps/cli/package.json——bin: { "dsh": "lib/bin.js" }
  • apps/cli/src/args.ts——启动器 flag 解析与--dump-*互斥校验
  • packages/util/home-paths/src/index.ts——$DSH_HOME默认为~/.dsh
  • packages/storage/storage-sqlite/src/schema.ts——node:sqlite的真实使用
  • packages/client/locale/README.zh.md——界面语言切换
  • packages/client/README.zh.md——Web UI 各功能与包的对应表

外部资料:

  • DeepSeek 开放平台(获取 API Key):https://platform.deepseek.com/
  • Node.js 官网(下载 LTS):https://nodejs.org/
  • Corepack(Node 包管理器版本管理):https://github.com/nodejs/corepack
  • 仓库与文档站:https://github.com/deepseek-ai/deepseek-harness / https://deepseek-harness.github.io/deepseek-harness/
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 18:24:59

灵枢血络论|解读:相之奈何?盛坚横以赤039

摘要&#xff1a;本段经文描摹出盛坚横赤的病态血络&#xff0c;以人身拓扑络网、水管郁结为喻&#xff0c;正本清源破除千年放血误区。外泻放血为治标下策&#xff0c;松解拓扑结构、内通气血为治本上策&#xff0c;仅危急重症可临时放血救险。同时揭秘慢病真相&#xff1a;反…

作者头像 李华
网站建设 2026/9/3 18:23:42

Odoo 18企业版二次开发:无源码也能高效落地的实战指南

简介&#xff1a;Odoo 18企业版源代码是一套基于Python编写的开源ERP套件完整代码&#xff0c;覆盖销售、采购、库存、财务、CRM、制造、人力资源等核心业务模块&#xff0c;适合企业开发者、实施顾问及二次开发人员用于学习系统架构、功能定制与企业级部署。压缩包共2000个文件…

作者头像 李华
网站建设 2026/9/3 18:23:37

高通QNN平台YOLOv5量化部署实战:从PyTorch到边缘设备的高效移植

简介&#xff1a;本资源是一套面向嵌入式AI开发者与边缘计算工程师的YOLOv5模型量化部署工具集&#xff0c;专为高通QNN平台&#xff08;Qualcomm Neural Processing SDK&#xff09;定制&#xff0c;解决PyTorch训练模型向骁龙芯片高效迁移难、量化调优门槛高、全流程链路不贯…

作者头像 李华
网站建设 2026/9/3 18:18:04

从模型到系统:YOLOv8+PySide6车型识别检测实战指南

当我把 YOLOv8 车型识别模型跑通之后&#xff0c;才意识到真正耗时的是后面这段路&#xff1a;怎么用 PySide6 把它封装成一个能交付的检测系统。很多教程会把“模型训练出来”当作终点&#xff0c;但实际项目中&#xff0c;模型只是最小的一块拼图。你需要处理数据集、训练策略…

作者头像 李华
网站建设 2026/9/3 18:15:43

从“过来握手”到本地智能体:语音识别与动作执行链路搭建

“过来握手”这四个字&#xff0c;这些年经常出现在 AI 发布会或智能硬件 Demo 里&#xff1a;对着机器人说一句“过来握手”&#xff0c;它要能听懂、转头、移动&#xff0c;最后抬起手完成动作。看着很自然&#xff0c;实际做一遍就会发现&#xff0c;这件事背后是一条完整的…

作者头像 李华
网站建设 2026/9/3 18:14:55

《迷你世界》开发模式触发器Bug排查与实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华