DeepSeek Harness 系列(三):从源码运行 Web UI
理解架构之后,下一步应该把工程跑起来。DeepSeek Harness 的运行方式分两类:直接用 npm 启动已发布的 dsh,或者从源码构建后启动。前者适合体验,后者适合读源码、改插件和验证架构判断。
本文以当前公开源码快照为依据,梳理从启动到 Web UI 使用的主路径,同时解释几个容易踩的点:Node 版本、profile 初始化、模型配置、工作区选择和配置 dump。
运行方式总览
根 README 给出的快速命令是:
npx @deepseek-ai/dsh web该命令会启动 Web UI,默认地址是:
http://127.0.0.1:3080如果你要从源码运行,README 给出的流程是:
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web当前源码快照的 package.json 声明了两个重要约束:
| 项 | 当前源码声明 |
|---|---|
| 包管理器 | pnpm@11.7.0 |
| Node 版本 | `^22.19.0 |
如果你用 Node 20 启动,后面遇到 node:sqlite 或构建相关问题,不要先怀疑业务代码。先看 Node 版本是否满足根 package.json 的 engines 要求。
从源码启动时发生了什么
pnpm dsh web 并不是“直接启动前端”。它会走 CLI,解析 profile,组合 Cordis 配置树,然后启动 Web 表层。
这里有一个很实用的判断:如果服务器没起来,不要只看最后一行报错。启动失败可能来自不同层。
| 失败位置 | 常见表现 | 应该检查 |
|---|---|---|
| CLI 参数层 | flag 不生效、帮助信息不符合预期 | 启动器 flag 必须写在应用参数前面。 |
| profile 层 | 找不到 profile 或 bundle | $DSH_HOME/profiles/<name> 和 dsh.profile.bundles。 |
| patch 层 | 配置项覆盖后缺字段 | patch 会替换整条 config,不是深度合并。 |
| Loader 层 | 插件无法加载或依赖等待 | 插件包是否存在,inject 的服务是否挂载。 |
| Web 层 | 前端 dist 不存在 | 是否执行了 pnpm run build。 |
| 模型层 | 会话无法请求模型 | 设置页模型与凭据配置是否完整。 |
用配置 dump 看运行真相
DeepSeek Harness 提供了查看最终配置树的方式。
dsh --profile web --dump-config对于源码运行,也可以通过项目脚本转发:
pnpm dsh --profile web --dump-config这条命令的意义很大。Profile、bundle、profile patch、home patch、CLI patch 都叠加后,最终到底启用了哪些插件,以 dump 出来的树为准。
我建议把 --dump-config 当成排障入口,而不是最后手段。插件化系统最怕“我以为启用了”,dump 能把猜测变成事实。
配置模型
Web UI 启动后,先进入:
设置 -> 模型DeepSeek 卡片提供 API 密钥字段。保存后,模型路由会在下一次请求生效,不需要重启服务器。源码文档里还有一个值得注意的安全细节:密钥是只写的,保存后页面只拿到脱敏描述符,明文密钥存储在 $DSH_HOME/.credentials.yaml,settings 保存凭据引用。
如果你使用企业网关、自建模型服务或 OpenAI 兼容端点,可以走“添加自定义提供方”。关键字段通常包括:
| 字段 | 含义 |
|---|---|
| Provider ID | 稳定路由 id,保存会话和默认模型会引用它。 |
| Base URL | 模型服务地址。 |
| API 协议 | 例如 OpenAI 兼容协议。 |
| Credential | API Key 或环境变量引用。 |
| Models | 至少声明一个模型 id。 |
自定义提供方的 Provider ID 不建议随便改。源码文档明确说,它会被请求、已保存会话、默认模型和凭据引用使用。要重命名时,更稳妥的方式是新增提供方,再删除旧提供方。
选择工作区
Web UI 启动时,dsh 进程会把调用目录作为默认文件系统位置,但新的 Web UI 在添加工作区前不会选中任何工作区。你需要在页面里点击“选择工作区”,添加并选中项目目录。
这一步很容易被忽略。没有选中工作区时,会话输入框不可用;这不是模型坏了,而是 Agent 还没有明确的工作空间。
一个最小验证任务
工作区选好后,可以先发送一个低风险任务:
Summarize this repository and identify its main packages.这个任务能验证几件事:
- 模型路由可用。
- Agent 能读取工作区。
- 会话事件能正常渲染。
- 工具调用权限策略能正常触发。
- Web UI 能从
session/event更新状态。
如果这个任务失败,比直接让它改代码更容易定位问题。
Web 模式与 Headless 模式的区别
web 和 headless 都建立在 dsh-base 上,但表层不同。
Web 模式适合交互式阅读、开发和调试。Headless 模式适合一次性自动化任务:
dsh --profile headless "run the tests and summarize failures"Headless 不挂 HTTP server,不打开 Web UI。它创建一个新的持久化 Agent,把命令行任务作为普通用户消息提交,等 Agent 停稳后把最后一条非空助手文本写到 stdout。
开发插件时的推荐路径
如果你要基于源码开发插件,不建议一上来就改默认 Agent Loop。更稳的路线是:
这条路线符合 Harness 的架构意图。新增行为尽量作为插件挂入 profile,借助 --dump-config 和 Web UI 验证,而不是在默认循环里加分支。
常见问题速查
| 问题 | 优先检查 |
|---|---|
pnpm install 或构建异常 | Node 是否满足 `^22.19.0 |
| Web URL 没打印 | 配置树可能结算失败,查看启动错误和 Web bundle 是否构建。 |
| 页面打开但不能输入 | 是否已添加并选中工作区。 |
模型报 MISSING_CREDENTIAL | 设置页是否保存了提供方密钥,或环境变量是否存在。 |
| 自定义模型不支持图片 | 自定义模型默认按文本处理,需要在 settings 里声明 input: [text, image]。 |
| 覆盖配置后启动失败 | patch 替换整条配置,检查是否遗漏原有必需字段。 |
| 某插件似乎没挂上 | 先 --dump-config,再看条目是否启用、依赖是否满足。 |
小结
运行 DeepSeek Harness 不难,难的是理解“我启动的到底是哪棵树”。web 不是一个固定内核,而是 dsh-base 加 Web bundle 再叠加用户 patch 的结果。模型配置、工作区选择、工具权限、Web 渲染都只是这棵树上的插件能力。
真正开始做企业接入前,建议先完成三步:能从源码构建并启动 Web;能用设置页配置一个模型;能用 --dump-config 看懂最终配置树。下一篇就用一个匿名化真实业务模式,讨论如何把企业的 AI 任务补偿和异常巡检包装成 Harness 可控工具,而不是让 Agent 直接越过业务系统。
系列导航:上一篇:核心架构与运行时骨架 | 下一篇:接入真实业务的工具化方法。
