文章摘要
本文对开源的TypeScript Agent运行时框架DeepSeek Harness进行架构拆解。它聚焦解决生产级问题,以“一切皆插件”为架构核心,基于Cordis构建插件容器。内置两种运行形态,通过Profile、Bundle与Patch组合实现灵活配置。其区分Turn、Step和Tool三层状态,采用事件溯源会话。同时介绍能力接缝、工具执行安全规则等,适配复杂的Agent场景。

DeepSeek Harness 架构拆解:一切皆插件的 Agent 运行时

从 Cordis、事件溯源到安全工具执行链路

一切皆插件,模型所见皆可回放

Cordis Agent Runtime

多数Agent框架的核心目标是快速连接大模型与各类工具,但DeepSeek Harness另辟蹊径,聚焦解决更复杂的生产级问题:当系统中的模型、会话管理、工具调用、安全审批、沙箱环境、子Agent以及交互界面都需要灵活替换时,如何让运行时始终保持可追踪、可组合、可恢复的特性。

DeepSeek Harness 是开源的 TypeScript Agent 运行时框架,命令行工具名为 dsh,当前版本为 0.1.0-rc.5,处于开发者预览阶段,项目明确提示后续版本可能出现破坏性变更。

从表面来看,它集成了Web UI、MCP、Skill、LSP、Plan、Goal、Workflow、Subagent、Sandbox、ACP和Python SDK等多项能力,看起来像是一份功能全面的Agent工具集。但真正将这些能力串联起来的,并非某个“超级Agent”类,而是一个极简却强大的架构判断:一切皆插件。

常规的Agent开发流程通常从一个基础循环开始:组装提示词、调用大模型、识别工具调用请求、执行工具并将结果回传给模型。这个抽象逻辑足以完成简单的Demo演示,但当系统需要承载长期会话、多级审批、隔离沙箱、多端界面以及外部协议接入时,一系列问题会快速暴露:

  • 工具的注册与可用性校验如何统一管理?
  • 模型的上下文能否从持久化存储中完整重建?
  • 如果多个安全插件串联,前一个拒绝了请求,后一个是否会意外放行?
  • Web端、无界面模式和Python SDK是否能共享同一套运行时语义?
  • 插件卸载时,后台任务和子进程是否能被彻底终止?

DeepSeek Harness 将这些问题全部下沉到运行时层面解决。它基于Cordis构建插件容器,通过服务、事件和生命周期来组织所有能力,Agent的基础循环只是其中一个子系统,而非整个架构的核心。

当前检出的代码仓库采用Node.js 22.19+、TypeScript ESM与pnpm workspace;packages/ 目录下包含219个两级包目录。数量本身并非核心卖点,但足以说明项目已经将模型、会话、工具、安全、界面与协议拆分为细粒度的独立模块。

Cordis与“一切皆插件”的架构核心

Cordis提供了依赖注入、服务生命周期管理、事件系统和插件上下文能力。DeepSeek Harness在此基础上,将所有Agent相关能力拆分为独立插件:模型Provider是插件、工具调用是插件、会话存储是插件,MCP、LSP、Skill、审批、沙箱、子Agent和Web UI也均为插件。

这并非简单地将所有代码塞进“插件目录”,而是要求每个插件在自身上下文内注册服务和事件,并在卸载时彻底清理资源;功能消费者仅依赖公开的服务接口,而非直接引用插件的内部实现。

因此,项目的可替换性来自三个核心约束:

  1. 能力通过服务和事件暴露,调用方无需持有具体实现。
  2. 生命周期由插件上下文管理,插件卸载时需释放所有监听、任务和进程。
  3. 跨模块协作走明确接缝,避免单个插件私自读取其他插件的私有状态。

这套设计的核心收益并非“插件数量更多”,而是同一套Agent运行时可以灵活组合出多种产品形态。

运行时组合:Profile、Bundle与Patch

DeepSeek Harness内置了webheadless两个Profile。Profile可以理解为启动时选择的运行形态:Web形态需要界面、HTTP服务和交互配置,而headless模式更适合脚本、服务端任务或被其他进程驱动的场景。

Bundle负责声明一组默认插件与配置,Patch则针对具体部署环境进行增删和覆盖配置。三者组合后,产品入口与核心运行时可以独立演进:

Profile:决定要启动哪种产品形态

Bundle:提供这类形态的默认能力集合

Patch:按部署环境替换或调整局部能力

这种方式比在启动文件中堆砌条件判断更加稳健。Web和headless模式可以共享Agent、Session和Tool的核心语义,仅在入口与交互能力上存在差异;测试环境也可以替换为轻量Provider,无需复制整套应用。

从工程原则来看,这套设计符合SOLID中的依赖倒置原则,也降低了跨产品入口的代码重复。但过细的拆包也会增加理解和调试成本,当前219个包目录说明,这套灵活性需要完善的文档、稳定的命名规范和工具化诊断能力来支撑。

Agent生命周期:Turn、Step与Tool

DeepSeek Harness并未将一次对话简化为“调用模型直到结束”的简单流程,而是区分了Turn、Step和Tool三层状态:

  • Turn:对应一次被Agent领取的用户任务。
  • Step:对应一次模型请求及其产生的消息或工具调用。
  • Tool:对应一次结构化能力执行,并经过调用前、执行中和调用后的完整处理链。

一条典型的执行链路如下:

turn/start

→ 领取输入

→ agent/pre-step

→ step/start

→ 组装系统提示词和工具 schema

→ agent/request

→ llm/stream

→ assistant/message

→ tool/call

→ tools/pre-execute

→ guards / approval / sandbox

→ tools/execute

→ tools/post-execute

→ tool/result

→ step/end

→ turn/end

这种分层设计为插件提供了精确的扩展位置:上下文压缩可以在模型请求前介入,遥测可以观察流式事件,审批可以在工具执行前暂停,结果后处理可以在写回模型前统一格式。

更重要的是,所有状态变化都被显式命名。当遇到“Agent卡住”的问题时,系统可以快速定位到排队、模型流、审批、沙箱、工具进程还是结果写回等具体阶段,而非仅依赖一段难以定位的循环日志。

“模型可见即已记录”的事件溯源会话

DeepSeek Harness的Session并非一份不断被覆盖的messages数组,而是一份仅追加的事件日志。消息历史从事件派生而来,模型看到的上下文也应当能从日志完整重建。

项目将事件大致分为三类:

  • Session Event:持久化事实,例如用户输入、模型消息、工具调用和工具结果,可用于会话回放。
  • Agent Event:实时队列、状态和控制信号,服务于正在运行的Agent。
  • Capability Event:工具、文件、遥测等能力的扩展事件。

这带来了一个实用的验收标准:如果模型曾依据某段内容做出决策,那么这段内容不应仅存在于瞬时内存中。在系统重启、迁移或调试时,需要能够清晰解释“模型当时看到了什么”。

仅追加的日志模式也存在代价:长期会话会持续增长,必须设计快照、压缩、派生视图和归档策略;当事件schema发生变更时,还需要考虑兼容与迁移方案。事件溯源解决了可追踪性问题,但不会自动解决存储成本问题。

Capability Seam:可替换能力的三件套

项目文档用Capability Seam(能力接缝)来描述跨插件契约。一条完整的能力接缝至少包含三部分:

  1. Service Definition:定义服务名称、类型和语义。
  2. Service Provider:插件提供具体的服务实现。
  3. Consumer:Agent、UI或其他插件仅依赖服务定义,而非具体实现。

如果只有Provider而没有稳定的Definition,调用方将被迫依赖具体实现细节;如果只有Definition而没有生命周期和缺失能力处理,系统会在运行时留下隐含假设。只有这三部分齐全,才能通过Profile实现能力替换而无需修改消费者代码。

这种模式同样适用于模型、文件系统、审批器、沙箱或遥测等能力。它本质上是端口与适配器架构在插件系统中的具体落地:Definition是端口,Provider是适配器,Consumer通过端口与服务交互。

工具执行:安全不是一个开关

工具执行是最容易区分Demo与生产级系统的环节。DeepSeek Harness将工具调用分为前置处理、Guard、审批、沙箱、真实执行和后置处理多个阶段,并在文档中强调了多条防御性安全规则:

第一,审批无法明确回答时默认拒绝。 没有审批器、审批器异常或结果不明确的情况,都不能被解释为“继续执行”。

第二,Guard是单调的。 守卫只能执行denyabstain操作。一旦某个守卫拒绝了请求,后续插件无法将其改回允许,这避免了安全策略的执行顺序成为隐蔽漏洞。

第三,沙箱失败要fail closed。 如果策略要求在沙箱中执行工具,而沙箱启动失败,系统不能悄悄退回宿主机裸执行。

第四,子进程环境要净化。 向工具进程传递环境变量时,需要移除名称包含KEYSECRETTOKENPASSWORD的敏感项。

第五,dispose不是“发送终止信号”。 插件释放时必须等待子进程真正退出,否则旧进程可能继续占用端口、文件或凭据资源。

这些规则看起来相对保守,但非常适配Agent场景:模型会尝试组合多种能力,失败路径也比普通表单应用更加复杂。安全控制必须贯穿整个执行链,不能仅依赖单一的工具白名单。

Subagent、Workflow与Jobs:从单次回答到长期任务

当任务无法在一个Turn内完成时,DeepSeek Harness提供了三类不同的抽象来处理:

  • Subagent:将一部分工作交给另一个独立Agent,实现上下文与职责的隔离。
  • Workflow:将步骤、依赖和控制关系组织为可重复执行的流程。
  • Jobs:承载可排队、可观察、可能跨较长时间运行的任务。

这三者不应被混同为“多Agent系统”:Subagent用于解决认知分工问题,Workflow用于解决流程编排问题,Jobs用于解决运行与调度问题。一个复杂的研究任务可以由Workflow拆分为检索、阅读和写作多个阶段,每个阶段启动对应的Subagent,而整个过程由Job记录状态和最终结果。

这种分工让Agent不再局限于请求内的同步执行,但也引入了新的工程问题:任务取消如何传播、重试是否需要幂等、父任务结束时子任务如何处置、结果由谁写入会话。DeepSeek Harness的价值在于提供了这些明确的边界,具体的业务逻辑仍需要团队自行明确策略。

产品入口:同一运行时,多种使用方式

最直接的启动方式是:

npx @deepseek-ai/dsh web

默认访问地址为http://127.0.0.1:3080。Web UI提供了模型Provider配置、会话管理等可视化入口。

除了Web模式,项目还支持headless运行、ACP与JSON-RPC接入。Python SDK并未重新实现一套独立的Python Agent内核,而是通过stdio JSON-RPC驱动内置的TypeScript运行时。这种方式保留了TypeScript运行时中的插件语义,同时让Python应用可以创建会话、发起任务并接收事件。

这是一项务实的取舍:共享一套内核可以减少语义漂移,但跨进程协议会增加启动、错误传播和调试的成本。对于需要使用Python数据生态,又希望复用Harness能力的团队来说,这种边界通常比双语言重写更加可控。

优势、成本与适用场景

主要优势

  • 替换边界清楚:模型、工具、会话、安全和界面都能通过服务契约灵活替换。
  • 调试证据完整:事件日志、生命周期阶段和工具管线提供了可回放的调试线索。
  • 产品形态统一:Web、headless、ACP和Python SDK共享同一套运行时。
  • 安全策略偏保守:审批、Guard、沙箱和进程回收都采用失败关闭的设计思路。

现实成本

  • 插件与包数量较多,初次阅读需要先理解Cordis、Profile、Bundle、事件与服务等核心概念。
  • 生命周期和事件schema会成为长期兼容边界,修改成本高于简单框架。
  • 事件日志需要设计压缩、快照与迁移策略,否则长期会话会持续膨胀。
  • 当前仍是开发者预览版本,不适合将内部API的稳定性作为既定事实。

更适合的场景

它适合需要多种产品入口、严格的工具治理、长期会话管理、可替换模型与沙箱环境,以及希望将Agent能力打造为平台的团队。如果仅需要开发一个短生命周期的单Agent工具,更轻量的循环框架可能更符合KISS原则;过早引入200多个包的认知成本,反而会违背YAGNI原则。

结语

DeepSeek Harness最值得关注的并非“又支持了多少工具”,而是它试图将Agent运行时中最容易失控的部分转化为明确的契约:模型请求有明确阶段,工具执行有完整管线,能力替换有清晰接缝,会话内容可完整回放,安全拒绝不会被后续插件覆盖。

这让它更像是一套Agent操作系统的内核候选,而非简单的聊天机器人脚手架。不过,架构完整并不等于已经成熟。0.1.0-rc.5和开发者预览阶段的定位明确提示:该框架适合学习、试验和参与建设,若用于生产环境,则需要锁定版本、补齐测试,并为后续升级预留迁移成本。

以上内容不代表本平台立场,仅供读者参考