[DeepSeek Harness深度拆解-01]DSH是如何跑起来的?

发布时间:2026/10/21 11:23:43
[DeepSeek Harness深度拆解-01]DSH是如何跑起来的? DeepSeek Harness插件内核花了较多的篇幅介绍DeepSeek Harness以下简称DSH最核心的部分作为支撑框架和核心引擎的Cordis。这个系列开始我们将正式探讨DSH。DSH发布已经有一段时间了相信你已经看了很多介绍DSH的内容我争取提供一些不同的东西。对于DSH来说一切都是插件但这句话只有在你对Cordis具有充分理解之后才可能有深刻的体现而Cordis和插件将是本系列文章唯一的视角因为在我看来只有从这个视角来审视DSH才可能真正了解DSH的本质。顺便说一下DSH会涉及的一些文件和目录的路径会因操作系统而不同如果没有显式指定具体的操作系统默认采用的是Windows环境。1. 直接通过 npm 运行 DSH我们可以在无须安装的情况下利用npx直接执行deepseek-ai/dsh这个包以指定的Profile启动DSH。比如我们可以采用如下的方式执行npx deepseek-ai/dsh --profile web命令启动DSH并将profile设置为web。C:\Users\jinnannpx deepseek-ai/dsh--profileweb Need toinstallthe following packages: deepseek-ai/dsh0.1.5-rc.1 Ok to proceed?(y)ynpmnotice run npxnpmnotice run dsh web dsh web: http://127.0.0.1:3080/?tokend439ezBiKnz_S067FVeZo5P5AUKQCogQyAu4qpGlYyY dsh web: opening the default browser;pass --no-open to disable上面执行的这个命令将绑定默认的端口3080启动的Web站点作为一个对话式的Agent。针对Agent的首次访问将会引导我们提供一个访问DeekSeek模型的API-Key。我们可以设置也可以跳过这一步这里我们选择设置对应的API Key。随后如下这个简洁的聊天界面出现由于访问LLM的API-Key已经设置好了我们可以直接开始与Agent的对话。我们可以作进一步设置。如果我们可以选择使用其他的LLM就可以点击左下角的Settings图标并在出现的页面中选在Model选项卡进行设置。我通过如下的方式选择预定义的LLM提供者azure-openai-responses顾名思义它代表承载于Azure上采用Responses API而不是针对传统基于文本补齐的Completion API的OpenAI系列模型。我们设置了对应的API-Key和Base URL。设置完成后回到主页面可以从文本下面的模型下拉框中选择OpenAI系列的LLM这里我们选择的是GPT-5.4 mini。所有的设置将会转换成DSH运行时目录通过环境变量DSH_HOME定义默认位置为~/.dsh或者%userprofile%.dsh下相应配置文件的内容。比如上面的设置将体现在%userprofile%.dsh/settings.yaml配置文件中如下所示的正式完整的配置内容。ui-onboarding:welcomeNoticeVersion:2026-08-13.1llm-pi-ai:providers:azure-openai-responses:baseURL:https://eap2410.openai.azure.com/openai/v1apiKeyEnv:AZURE_OPENAI_RESPONSES_API_KEYagent-default-model:provider:azure-openai-responsesmodel:gpt-5.4-minisettings.yaml中的三个配置选项说明如下ui-onboarding欢迎或更新公告的版本号。DSH的前端通过读取这个版本号来判断是否需要在用户打开界面时弹窗显示最新的特性介绍。如果与最新版本一致系统就会自动跳过弹窗避免重复打扰llm-pi-aipi-ai 内置了针对若干LLM的适配。它为所有支持的LLM Provider提供了一个确定的名称基于Response API的Azure OpenAI在这里被被命名为azure-openai-responses并通过baseURL和apiKeyEnv字段来设置基础地址和API-KEY。agent-default-modelAgent默认采用的LLM由指定的LLM Provider和LLM名称来决定。从配置文件的内容可以看出我们设置的API-KEY并没有出现在settings.yaml中它会出现在%userprofile%.dsh/.credentials.yaml配置中。顾名思义该文件专门用来存储用户凭证信息所以针对DeekSeek和Azure OpenAI的API Key都会出现在这里。当我们通过dsh web启动系统时前端UI与后台CLI通信需要安全的Session握手。DSH 会在此处生成一个类型为grant的版本化授权其secret字符串用于同步校验并签发浏览器会话的Cookie.credentials.yaml中的第一部分存储的即为此内容。version:1records:client-connection/browser-session:kind:grantpayload:version:1secret:6by5HqT2BXjXgl-d4sg_gFZ0rymGEtcg0Cyvt56ttkErefs:DEEPSEEK_API_KEY:...AZURE_OPENAI_RESPONSES_API_KEY:...DSH还可以进行一些额外的设置大家可以自行探索这里就不一一赘述了。完成了必要的配置之后我们可以测试一下能否正常与DSH交互。2. 从源码启动DSH为了探究DSH的本质我们直接执行如下的命令把源代码下载到本地装好依赖并编译打包成随时可以运行的状态。gitclone https://github.com/deepseek-ai/deepseek-harness.gitcddeepseek-harnesspnpminstallpnpmrun build我们以包名deepseek-ai/dsh进行搜索发现这个包对应的目录为./apps/cli。查看对应的package.json我们发现它的bin字段具有如下的定义由此可知入口程序为lib/bin.js。{name:deepseek-ai/dsh,...bin:{dsh:lib/bin.js},...}进一步查看tsconfig.json中定义的编译选项可以可知对应的TypeSript源文件为./apps/cli/src/bin.ts所以我们在克隆代码的根目录下执行如下的命令也可以启动DSH。npx tsx apps/cli/src/bin.ts--profileweb3. 启动程序通过上面的启动方式我们知道作为DSH入口的是apps/cli/src/bin.ts这个TypeScript文件我们不妨看看这个文件的完整内容。从如下的代码所示DSH CLI最终是通过执行runCli函数被启动的。import{readFileSync}fromnode:fsimport{fileURLToPath}fromnode:urlimport{loadLayeredEnv}fromdeepseek-ai/dsh-app-bootimport{parseDshArgs}from./args.tsfunctionreadVersion():string{constmanifestJSON.parse(readFileSync(fileURLToPath(newURL(../package.json,import.meta.url)),utf8),)as{version?:unknown}returntypeofmanifest.versionstring?manifest.version:0.0.0}exportasyncfunctionrunCli():Promisevoid{constinvocationparseDshArgs(process.argv.slice(2),readVersion())switch(invocation.mode){caseprofile:{const{runProfile}awaitimport(./profile-boot.ts)awaitrunProfile({environment:loadLayeredEnv(dsh),profile:invocation.profile,fromDefaultProfile:invocation.fromDefaultProfile,patchFiles:invocation.patches,args:invocation.args,})break}caseplugin:{const{runPlugin}awaitimport(./plugin.ts)process.exit(runPlugin(invocation.profile,invocation.args))break}casedump-config:{const{runDumpConfig}awaitimport(./dump-config.ts)runDumpConfig(invocation.profile,invocation.defaultOnly,invocation.patches,invocation.fromDefaultProfile,)break}default:invocation satisfiesneverthrownewError(dsh: unhandled invocation mode${JSON.stringify(invocation)})}}if(import.meta.main){awaitrunCli()}4. DSH的三种执行模式runCli函数会首先解析我们输入的命令行参数并创建一个代表DSH执行方式的DshInvocation对象。DshInvocation是针对ProfileInvocation、DumpConfigInvocation和PluginInvocation三种类型的Discriminated Union其mode字段代表三种执行DSH的模式。exporttypeDshInvocationProfileInvocation|DumpConfigInvocation|PluginInvocationinterfaceProfileInvocation{mode:profileprofile:stringfromDefaultProfile?:string|undefinedpatches:string[]args:string[]}interfaceDumpConfigInvocation{mode:dump-configprofile:stringfromDefaultProfile?:string|undefineddefaultOnly:booleanpatches:string[]}interfacePluginInvocation{mode:pluginprofile:stringargs:string[]}三种执行模式说明如下profile以不同的形态和交互方式启动DSH。比如基于WebUI的web基于终端UI的tui以及基于单次调用的headless等dump-config不论采用那种Profile最终被拉起的DSH都是一个Cordis程序。由于Cordis采用一切皆插件的设计和运行方式Profile本质就是针对一组插件的配置我们可以采用这种执行方式将指定Profile的配置数出来plugin该命令将参数直接透传给pnpm包管理器帮助我们根据特定Profile安装、卸载或者更新用来提供插件的包。比如我们按照如下的方式执行npx tsx apps/cli/src/bin.ts --dump-config --profile web命令可以将web这个Profile的完整配置输出来。E:\dsh\deepseek-harnessnpx tsx apps/cli/src/bin.ts --dump-config --profile web npm notice run deepseek-ai/dsh-root0.1.5-alpha.2 npx npm notice run tsx apps/cli/src/bin.ts --dump-config --profile web # deepseek-ai/dsh-base - id: timer name: deepseek-ai/cordis-plugin-timer - id: hmr name: deepseek-ai/cordis-plugin-hmr disabled: true config: root: - . - id: llm name: deepseek-ai/dsh-llm - id: deepseek-llm-api-extensions name: deepseek-ai/dsh-deepseek-llm-api-extensions - id: session name: deepseek-ai/dsh-session - id: session-log-deepseek name: deepseek-ai/dsh-session-log-deepseek - id: typert name: deepseek-ai/dsh-typert-registry - id: typert-loader name: deepseek-ai/dsh-typert-loader - id: typert-gateway name: deepseek-ai/dsh-api-gateway - id: session-title name: deepseek-ai/dsh-session-title config: fallbackMaxWords: 5 fallbackMaxBytes: 40 maxTitleBytes: 80 - id: session-title-llm name: deepseek-ai/dsh-session-title-first-prompt-llm config: targetWords: 5 targetCjkCharacters: 10 maxInputBytes: 4096 maxOutputTokens: 64 timeoutMs: 60000 - id: user-questions name: deepseek-ai/dsh-user-questions - id: agent name: deepseek-ai/dsh-agent - id: plugin-package-inventory-deepseek name: deepseek-ai/dsh-plugin-package-inventory-deepseek - id: agent-default-model name: deepseek-ai/dsh-agent-default-model config: provider: deepseek-official model: deepseek-v4-flash - id: jobs name: deepseek-ai/dsh-jobs-local - id: llm-retry name: deepseek-ai/dsh-llm-retry - id: settings name: deepseek-ai/dsh-settings-file - id: credentials name: deepseek-ai/dsh-credentials-local - id: llm-pi-ai name: deepseek-ai/dsh-llm-pi-ai - id: session-persistence-jsonl name: deepseek-ai/dsh-session-persistence-jsonl config: root: !!js dshHomePath(sessions) - id: attachment-local name: deepseek-ai/dsh-attachment-local - id: session-query-sqlite name: deepseek-ai/dsh-session-query-sqlite config: path: :memory: openAt: never - id: session-projection name: deepseek-ai/dsh-session-projection - id: storage name: deepseek-ai/dsh-storage - id: storage-json name: deepseek-ai/dsh-storage-json config: root: !!js dshHomePath(storages) - id: storage-domain name: deepseek-ai/dsh-storage-domain config: backend: json - id: session-projection-cache name: deepseek-ai/dsh-session-projection-cache config: writeEveryEvents: 200 writeIntervalMs: 5000 - id: session-telemetry-otel name: deepseek-ai/dsh-session-telemetry-otel config: mode: !!js process.env.DSH_TELEMETRY_MODE || FEEDBACK_ONLY shutdownTimeoutMillis: 3000 exporter: url: !!js - process.env.DSH_TELEMETRY_OTLP_URL ?? https://harness-telemetry.deepseeksvc.com/v1/logs compression: gzip timeoutMillis: 1000 processor: scheduledDelayMillis: 10000 maxQueueSize: 2048 maxExportBatchSize: 2048 exportTimeoutMillis: 1500 - id: subprocess name: deepseek-ai/dsh-subprocess-local - id: sandbox name: deepseek-ai/dsh-sandbox-local - id: sandbox-policy name: deepseek-ai/dsh-sandbox-policy config: mode: !!js process.env.DSH_PERMISSION_MODE ?? workspace-write workspaceRoot: !!js process.cwd() - id: bash-sandbox name: deepseek-ai/dsh-bash-sandbox disabled: !!js process.platform win32 config: timeoutMs: 60000 - id: pwsh-sandbox name: deepseek-ai/dsh-pwsh-sandbox disabled: !!js process.platform ! win32 - id: approval name: deepseek-ai/dsh-user-approval config: policy: !!js - (process.env.DSH_PERMISSION_MODE ?? workspace-write) danger-full-access ? never : ask - id: permission name: deepseek-ai/dsh-permission-presets config: presets: read-only: sandbox: read-only approval: ask workspace-write: sandbox: workspace-write approval: ask danger-full-access: sandbox: danger-full-access approval: never - id: shell-env name: deepseek-ai/dsh-shell-env # deepseek-ai/dsh-base, patched by deepseek-ai/dsh-web-app - id: tool-bash name: deepseek-ai/dsh-tool-bash disabled: true - id: tool-pwsh name: deepseek-ai/dsh-tool-pwsh disabled: true - id: tool-jobs name: deepseek-ai/dsh-tool-jobs disabled: true # deepseek-ai/dsh-base - id: fs-observation-policy name: deepseek-ai/dsh-fs-observation-policy # deepseek-ai/dsh-base, patched by deepseek-ai/dsh-web-app - id: tool-fs name: deepseek-ai/dsh-tool-fs disabled: true - id: tool-fs-search name: deepseek-ai/dsh-tool-fs-search config: sampleOverCapGlobResults: false disabled: true - id: agent-instructions name: deepseek-ai/dsh-agent-instructions config: maxBytes: 65536 disabled: true # deepseek-ai/dsh-base - id: skill name: deepseek-ai/dsh-skill # deepseek-ai/dsh-base, patched by deepseek-ai/dsh-web-app - id: skill-filesystem name: deepseek-ai/dsh-skill-filesystem disabled: true # deepseek-ai/dsh-base - id: skill-badge name: deepseek-ai/dsh-skill-badge disabled: true # deepseek-ai/dsh-base, patched by deepseek-ai/dsh-web-app - id: tool-skill name: deepseek-ai/dsh-tool-skill disabled: true # deepseek-ai/dsh-base - id: commands name: deepseek-ai/dsh-commands - id: command-feedback name: deepseek-ai/dsh-command-feedback - id: goal name: deepseek-ai/dsh-goal - id: goal-round-driver name: deepseek-ai/dsh-goal-round-driver # deepseek-ai/dsh-base, patched by deepseek-ai/dsh-web-app - id: command-goal name: deepseek-ai/dsh-command-goal disabled: true - id: plan-mode name: deepseek-ai/dsh-plan-mode config: section: You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A users conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode. Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery. The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed only to keep the request shape stable. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode. Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out. Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and>

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询