乐于分享
好东西不私藏

DeepSeek Harness源码深挖:我更在意的不是新Agent,而是模型公司开始争夺运行时

DeepSeek Harness源码深挖:我更在意的不是新Agent,而是模型公司开始争夺运行时

——
DeepSeek Harness源码深挖:我更在意的不是新Agent,而是模型公司开始争夺运行时
——

8月13日,DeepSeek Harness进入公众视野。

从README看,它的使用方式很简单:安装Node.js,执行一条命令,就能启动一个带Web界面的Agent:

npx @deepseek-ai/dsh web

但我看到这个项目后的第一反应,并不是“DeepSeek也做了一个类似Claude Code、Codex的编程Agent”。

做了11年Java开发,我对新框架已经很难只看功能列表。相比它能不能搜索、改文件、执行命令,我更关心另外几件事:状态由谁保存,失败后从哪里恢复,工具调用在哪里被拦住,权限如何落到执行路径,运行中的子任务又由谁负责回收。

所以我把仓库的架构文档、核心源码、默认配置和Python SDK串起来看了一遍。它真正想解决的,并不是“如何再做一个会调用工具的聊天应用”,而是一个更底层的问题:

当模型、工具、会话、权限、沙箱、子智能体和不同界面越来越多时,Agent应该运行在什么样的系统里?

DeepSeek Harness给出的答案是:把Agent运行时拆成一棵可组合、可替换、可回放的插件树。

我认为,这件事比“DeepSeek发布了一个新Agent”更值得关注。它说明模型公司的竞争正在从模型接口向下延伸到运行时:谁来定义工具、状态、权限、协议和插件生态,谁就更有可能控制模型进入真实工作的方式。

这篇文章不做README复述。我更想回答三个问题:DeepSeek为什么此时做Harness?这套设计对做Agent的开发者意味着什么?如果今天要做一个企业知识库产品,我们是否应该直接采用它?

先给结论:我从源码里看到三个信号

第一,Agent竞争正在从“模型能力”转向“运行控制权”。

模型能够推理和调用工具,只解决了“会不会做”。要进入真实业务,还必须有人定义它如何获得上下文、怎样执行工具、何时请求授权、出错后如何恢复。DeepSeek Harness正在争夺的,就是这一层标准。

第二,传统软件工程没有被Agent淘汰,反而重新成为门槛。

事件日志、状态机、依赖注入、权限分层、生命周期、幂等、取消传播和故障恢复,这些都不是新的AI概念。模型越有行动能力,过去在后端系统里积累的工程约束越重要。

第三,这个项目目前更值得学习,不值得盲目押注。

它的架构有野心,源码也不是演示级拼装,但版本仍处于RC和开发者预览阶段。现在最合理的动作,是理解它如何拆分运行时、吸收设计原则,再决定是否做小范围实验,而不是立即迁移业务。

先说清楚:什么是Agent Harness?

今天做一个Agent Demo并不难。

调用模型,给它几个工具,再写一个循环:模型输出工具调用,程序执行工具,把结果交回模型,直到模型给出最终答案。

真正困难的是Demo之后的问题:

  • 对话中断后怎样恢复?
  • 工具执行前怎样判断权限?
  • 文件和Shell怎样限制在工作区?
  • 多个子智能体怎样创建、跟进和退出?
  • Web、CLI和Python程序怎样共用同一套运行能力?
  • 插件升级或卸载时,正在运行的任务怎样收尾?
  • 模型看到的内容,之后还能不能完整重建?

Harness就是承接这些问题的运行层。

它连接模型、工具、状态、权限和交互界面,决定一个Agent如何开始、执行、暂停、恢复、授权和结束。

这也是DeepSeek Harness与普通“Agent应用模板”最本质的区别。

第一处关键设计:连Agent Loop本身都是插件

DeepSeek Harness的架构文档反复强调一句话:一切皆插件。

这里的插件不只是搜索、读文件、执行Shell之类的附加工具。模型适配器、工具注册表、会话日志、Agent Loop、审批服务、沙箱和子智能体,也都是插件。

底层使用的Cordis提供共享上下文、服务注册、类型化事件和可撤销副作用。插件加载时向上下文贡献服务,卸载时撤销自己注册的能力。

这意味着系统没有一个必须不断修改的“超级核心”。增加新能力时,优先选择挂到已有扩展点,而不是继续往Agent循环里写分支。

例如:

  • 新模型通过ctx.llm注册适配器;
  • 新工具通过ctx.tools注册;
  • 新文件系统通过ctx.fs提供能力;
  • 新Shell后端通过ctx.shell替换执行器;
  • 新子智能体后端通过ctx.subagents注册Provider;
  • 新审批方式监听approval/request事件;
  • 新持久化方式消费追加式会话事件。

最终运行起来的dsh,不是一个写死的应用,而是一棵由Profile、Bundle和Patch逐层组装出来的插件树。

webheadless只是两套组合模板。用户还可以继续叠加自己的cordis.patch.yml,替换某个配置,或者插入新的插件。

如果用Java开发者熟悉的语言来理解,它有点像把Spring容器、事件机制、Starter组合和运行时扩展点一起下沉到了Agent基础设施里。

但它比普通依赖注入更激进:默认Agent Loop也不享有不可替换的特权。

· · ·
我的理解:DeepSeek想把模型与Agent产品解耦
· · ·

今天许多Agent产品都把模型、工具和运行逻辑绑在一起。一旦要换模型、换沙箱、接入企业自己的文件系统,或者把同一个Agent同时放进Web、CLI和内部平台,就会碰到大量产品专用代码。

DeepSeek Harness选择把这些能力拆成接口和插件,真正想建立的可能不是一个单点产品,而是一套可以承载不同模型、工具和前端的运行底座。

这也解释了为什么它愿意让Agent Loop本身可替换:如果核心循环不可替换,那么所谓“一切皆插件”最终仍然只是外围扩展。

但可替换不等于低成本。插件化只是把复杂度从核心类搬到了接口设计、配置组装和生命周期管理。对于只想快速做一个业务MVP的团队,这套自由度很可能暂时用不上。

第二处关键设计:Agent运行被拆成Turn和Step

很多Agent实现的核心,是一个看起来很简单的while循环。

DeepSeek Harness当然也需要循环,但它没有把循环当成不可观察的黑盒。

架构中定义了两个层级:

  • 一个Step,是一次模型请求以及随后发生的工具调用;
  • 一个Turn,包含零个或多个Step,从领取输入开始,到不再欠任何工作时结束。

源码里的ReactLoopAgent进一步把运行状态分成idlemaintenancerunning。输入也不是全部塞进同一个消息数组,而是进入Inbox:

  • followup进入下一个Turn;
  • steer进入下一个Step并唤醒执行;
  • inject进入下一个Step,但不会主动唤醒。

取消则通过AbortController向当前活动传播。

这套划分看起来繁琐,但它解决了几个非常现实的问题:

用户在Agent运行中补充一句要求,应该立刻影响下一步,还是等当前轮次结束?外部插件注入一段上下文,是否应该主动触发一次模型调用?取消发生后,新消息应该加入已终止的活动,还是开启下一轮?

如果没有明确的Turn、Step和Inbox语义,这些问题最后通常会散落成一堆条件判断。

更重要的是,模型每一步看到的历史,不是直接读取某个临时内存数组,而是调用:

this.session.deriveMessages()

也就是说,Agent Loop会从会话日志重新投影模型历史。

这就引出了DeepSeek Harness最值得注意的第三处设计。

· · ·
我的理解:Agent工程的核心对象正在从“回答”变成“过程”
· · ·

普通聊天应用最关心最终回答;Agent系统则必须关心答案是怎样形成的。用户中途修改要求、工具执行一半被取消、插件注入新上下文,这些过程状态都会改变下一步行为。

Turn、Step和Inbox的价值,是让这些变化拥有明确语义。它们不会让模型更聪明,却会让系统更容易解释“为什么这一步会发生”。

这也是我认为Java和后端开发者并没有在Agent时代失去优势的原因。线程、队列、事务、状态机和取消传播的经验,正在以新的形式回到Agent运行时里。

第三处关键设计:SessionEvent才是事实源

在DeepSeek Harness里,Session不是简单的聊天消息列表,而是一份追加式事件日志。

源码中的定义非常直接:

An event-sourced session: an append-only log of SessionEvents.

Turn开始、Step开始、用户消息、模型输出块、完整助手消息、工具调用、工具结果、审批问题、审批决定、权限切换,都可以进入同一套事件体系。

架构文档还规定了一条很强的约束:

模型可见,即已记录。

凡是进入模型请求的内容,都应该能从Session日志重建。如果插件增加了一项模型可见信息,就应该有对应的会话事件,而不是只把它临时拼进Prompt。

这条约束的价值,不在于“日志很多”,而在于把一批原本分散的问题收敛到同一个事实源:

  • 恢复会话时,可以从事件重新得到模型历史;
  • UI可以依据事件还原流式输出和工具卡片;
  • fork可以在指定事件边界生成子会话;
  • 持久化后端只需要消费session/event
  • 审批、权限和子智能体状态可以被审计;
  • 遥测与Transcript不需要各自维护另一份真相。

这是一种很典型的事件溯源思路。

它的代价也很明确:事件格式、顺序、不变量和兼容性会变成整个系统的基础协议。项目当前甚至明确写着Session格式仍处于版本0,暂不承诺兼容。

所以这套设计的方向很清楚,但现在还不能把“可回放”自动等同于“已经具备稳定的长期兼容性”。

· · ·
我的理解:事件日志是最有价值、也最危险的选择
· · ·

如果让我从这个仓库里只挑一个最值得企业Agent学习的设计,我会选SessionEvent,而不是插件系统。

企业场景最怕的不是偶尔答得不够漂亮,而是出了问题以后无法还原:模型当时看到了什么、调用了什么工具、用户批准了什么、结果为何进入下一步。

统一事件日志让这些问题有机会被回答。但事件溯源的代价同样很高:事件一旦成为事实源,Schema演进、顺序保证、重复事件、崩溃尾部和隐私清理都会变成长期责任。

所以中小团队不必照搬整个实现,但应该吸收一条原则:所有会改变Agent后续行为的关键事实,都不应该只存在于内存或日志字符串里。

第四处关键设计:工具不是函数表,而是一条策略流水线

很多框架注册工具时,大致是这样:

工具名称 → 参数Schema → 执行函数

DeepSeek Harness在此之上增加了完整的执行流水线:

模型发起调用
  → pre-execute
  → monotonic guard
  → execute
  → post-execute
  → result observation

tools/pre-execute负责在真正执行前允许、拒绝或请求审批。

tools/execute是环绕执行层,可以插入超时、重试、指标和其他运行策略。

tools/post-execute处理已经归一化的执行结果,可以接受、替换、补充或阻止结果进入后续流程。

最后的tools/result用于观察已经冻结的最终结果。

工具还必须声明输入Schema和规范化输出Schema。成功结果会经过JSON校验,模型可见内容则由单独的渲染函数生成。

这说明DeepSeek Harness并不把工具调用理解为“模型给什么参数,我就调用什么函数”。它把工具当成一个需要经过验证、授权、执行、归一化和审计的系统边界。

这一点非常重要。

Agent真正危险的地方往往不是模型说错一句话,而是错误判断被转换成了文件修改、Shell命令、网络请求或外部系统写入。

如果权限和审计只存在于Prompt里,它们只是建议;进入工具执行路径以后,它们才是工程约束。

· · ·
我的理解:Prompt负责表达规则,执行层才负责保证规则
· · ·

这是我最认同DeepSeek Harness的地方。

很多Agent应用会在系统提示词里写“不要删除重要文件”“高风险操作先询问用户”。这种提醒有必要,但它不能承担最终安全责任。模型可能误解、遗忘,也可能在长上下文中降低对规则的关注。

真正可靠的做法,是让高风险动作在工具执行前经过确定性策略,并把审批结果记录下来。换句话说,模型可以提出动作,但系统决定动作是否发生。

这条原则同样适用于企业知识库:模型可以草拟客服答案,但价格、交期、质保承诺和对外发送,必须由规则与人工权限控制。

第五处关键设计:审批和沙箱是两个独立旋钮

DeepSeek Harness没有用一个简单的“安全模式”覆盖所有情况,而是把权限拆成两部分:

  • Sandbox Mode决定进程和文件可以触达什么范围;
  • Approval Policy决定需要审批时是询问用户,还是直接拒绝。

默认组合是workspace-write + ask:允许在工作区写入,高风险动作可以请求审批。

仓库预置了三种组合:

权限预设
沙箱范围
审批策略
read-only
只读
ask
workspace-write
工作区可写
ask
danger-full-access
完全访问
never

这里的never不是“永远允许”,而是“不弹审批”。在完全访问模式下,系统不再通过审批升级权限。

审批服务本身采用fail-closed策略:没有可用回答器、回答器异常或返回非法值时,结果是unavailable,不是默认放行。每次询问和决定还会形成approval/askedapproval/decided事件,进入Session日志。

沙箱后端则根据平台选择执行方式:

  • Linux优先使用Bubblewrap,备用Landlock;
  • macOS使用Seatbelt;
  • Windows使用受限令牌与ACL方案。

源码对能力边界写得相当克制。Windows ACL后端被明确标记为partial,因为ACL无法绝对覆盖外部授权对象和NTFS硬链接等情况。

这比一句“支持跨平台沙箱”更有价值:它不只告诉你系统有没有沙箱,还告诉你不同平台的约束强度并不完全相同。

· · ·
我的理解:安全能力必须允许表达“不完整”
· · ·

工程系统最危险的状态之一,是把“有某项能力”误写成“这项能力在所有环境下都成立”。

Windows后端明确报告partial,说明Harness试图把安全保证的强弱也变成运行时信息。这比一刀切的“沙箱已开启”更诚实,也更方便上层决定是否允许某项操作。

企业部署时,权限设计不能只看开关是否打开,还要知道实际执行环境能保证到什么程度。

第六处关键设计:子智能体不只是Promise.all

现在很多多智能体Demo,本质上是并发调用几个模型,然后汇总结果。

DeepSeek Harness显然想做得更深。

它把子智能体能力拆成Service Definition、Provider和Consumer。Provider可以是进程内新建、会话fork,也可以通过ACP、Codex、Claude Code或其他SDK连接到外部Agent产品。

对于可持续的子智能体,SubagentContinuationManager维护稳定的子Session ID和进程内Activation。

这里的Activation代表某个持久子会话在当前进程中的一次驻留。一个子会话可以执行多个FIFO Turn;如果已经不在内存中,后续消息还能从持久化Session冷恢复一个新Activation。

父子关系也不是只在调用栈里存在。系统会追踪:

  • 谁创建了谁;
  • 子智能体是否仍有未完成输入;
  • 父智能体能否向它追加消息;
  • 谁有权中断它;
  • 子智能体完成后怎样通知父级;
  • 整棵树退出时怎样按child-first顺序释放。

这套代码复杂度不低,但它解决的是多智能体系统一旦长期运行就绕不开的生命周期问题。

多智能体的难点从来不只是“能不能并发”,而是任务完成、失败、恢复和取消时,谁还拥有谁。

· · ·
我的理解:多智能体不是人数问题,而是所有权问题
· · ·

现在不少多智能体演示,把多个模型角色放在一起,就称为“Agent团队”。但如果没有稳定身份、父子关系、消息顺序、取消权限和资源回收,它更像几次并发API调用。

DeepSeek Harness的实现让我更加确认:多智能体是否工程化,不看页面上出现了几个头像,而看系统能否回答“谁创建了这个任务、谁可以继续它、谁可以停止它、父级退出后谁负责收尾”。

不过对大多数业务MVP来说,先把单Agent流程、工具边界和状态恢复做好,通常比尽早引入多个子智能体更重要。

Python SDK说明了它想站在哪一层

仓库里还提供了Python SDK,但它不是把整套Harness重新用Python实现一遍。

HarnessClient会启动打包后的Harness Runtime,通过stdio上的JSON-RPC进行通信。它提供初始化、发送Session Prompt、订阅通知、处理运行时反向请求和关闭进程等能力。

这反而进一步说明了项目的定位:

TypeScript实现运行时,Python只是一个宿主入口。

未来其他语言也可以采用类似方式,把Harness当成独立运行时使用,而不必重新复制Agent Loop、会话事件、审批和子智能体生命周期。

它和LangChain、Spring AI是什么关系?

DeepSeek Harness不是简单替代LangChain、LangGraph或Spring AI。

这些框架更常被用来构建具体AI应用:接模型、做RAG、编排节点、定义业务流程。

DeepSeek Harness更关注Agent的通用运行环境:

  • Agent实例怎样创建和恢复;
  • 工具怎样注册、授权和执行;
  • 状态怎样持久化和回放;
  • 不同界面怎样驱动同一个Session;
  • 子智能体怎样跨进程或跨产品协作;
  • 沙箱、权限和插件生命周期怎样进入统一系统。

两者有重叠,但抽象重心不同。

对Java团队来说,一个现实的组合可能是:业务系统仍然使用Spring Boot、Spring AI或LangChain4j,DeepSeek Harness作为独立Agent Runtime,通过SDK或协议被业务系统调用。

但这只是架构可能性,不代表当前预览版已经提供了成熟的Java集成方案。

对Java开发者来说,真正应该学什么?

看到一个TypeScript项目,Java开发者很容易先问:要不要换语言?

我的答案是:先不要把问题理解成语言迁移。

DeepSeek Harness最值得学习的不是TypeScript语法,而是它如何重新组织我们熟悉的工程能力:

Java开发经验
在Agent运行时中的对应问题
Spring依赖注入与Starter
模型、工具和Provider如何装配与替换
领域事件与事件溯源
Session怎样成为可恢复、可审计的事实源
过滤器、拦截器、AOP
工具调用前后如何加入审批、超时和审计
状态机与任务队列
Turn、Step和Inbox如何处理连续输入
RBAC与安全网关
模型提出的动作怎样被确定性策略约束
线程池与资源生命周期
子智能体怎样创建、取消和child-first释放

过去这些能力经常被看作“传统后端工程”。但模型一旦能够操作文件、调用Shell和驱动外部系统,它们就变成了Agent产品的安全底座。

因此,Java开发者不必急着抛弃原来的技术积累。更需要补的是模型调用、上下文组织和不确定性评估,再把既有的软件工程能力重新映射到Agent运行时。

如果现在做企业知识库,我会不会直接用它?

结合我们正在规划的工业企业客服知识库,我的答案是:第一版不会直接采用DeepSeek Harness作为核心底座。

原因不是它做得不好,而是产品阶段不匹配。

知识库MVP当前最重要的,是验证企业是否愿意为“可靠、带出处的客服答案”付费。技术重点应该放在文档解析、权限隔离、检索质量、引用溯源、拒答和人工审核,而不是先建设一套高度可组合的通用Agent运行时。

如果首版直接引入Harness,我们会同时承担Cordis插件体系、Node运行时、Java跨进程集成和预览版兼容变化,增加的复杂度暂时不能直接改善客户价值。

因此,我会继续使用:

  • Java 21 + Spring Boot 3承载企业账号、知识库、权限和审计;
  • Spring AILangChain4j完成模型接入和RAG流程;
  • PostgreSQL + pgvector保存业务数据和向量;
  • 人工审核后再把答案发送给客户。

但我会从DeepSeek Harness吸收四个设计:

  1. 1关键行为事件化:问题、检索证据、模型草稿、人工修改、最终发送都形成结构化事件。
  2. 2工具调用门控:价格、交期、质保和外部发送不能只靠Prompt约束。
  3. 3沙箱与审批分离:能访问什么资源,和谁可以批准某项动作,使用两套独立规则。
  4. 4先设计恢复,再谈多智能体:任务中断后可以继续,比一开始安排多个Agent角色更重要。

等产品验证付费,开始出现多种Agent、多个前端、跨进程工具或不同沙箱后端时,再评估引入Harness或借鉴它的能力接口,会更符合投入产出。

这也是我对新框架的一贯判断标准:不是看它有多少功能,而是看它此刻能否减少当前产品最重要的不确定性。

为什么DeepSeek要在这个时间点做Harness?

下面这一段是我的推断,不是DeepSeek官方表述。

过去模型厂商主要争夺模型性能和API调用。随着模型差距不断变化,真正形成长期粘性的可能不只是某个模型版本,而是围绕模型形成的运行环境:工具协议、会话格式、权限系统、插件生态和开发者习惯。

一旦开发者围绕某套Harness编写工具和Provider,模型就不再是唯一中心。运行时可以接入不同模型,但所有工作流、权限和状态仍然留在同一生态里。

所以我认为,DeepSeek Harness的战略意义可能有两层:

  • 对内,它提供一个可承载DeepSeek模型能力的工程化实验场;
  • 对外,它试图让DeepSeek不只作为“一个模型API”存在,而是参与定义Agent应用的基础设施。

这条路能否成功,取决于项目能否降低插件开发成本、稳定事件与配置协议,并吸引第三方Provider和工具,而不是取决于首版功能数量。

看完源码以后,我认为DeepSeek Harness最有价值的地方仍然有三点。

第一,它把扩展点当成一等公民。

模型、工具、Shell、文件系统、沙箱和子智能体都有清晰的能力接口,而不是全都耦合到一个Agent类里。

第二,它把模型可见内容与持久日志绑定。

“模型可见即已记录”让恢复、回放、审计和UI投影拥有同一个事实基础。

第三,它把安全策略放进执行路径。

审批失败默认关闭,工具经过执行流水线,沙箱能力还会报告完整或部分执行强度。

这些设计都在回答同一个问题:Agent不仅要“会做”,还要能够被系统控制。

但现在也不适合把它吹成生产级答案

这个项目目前至少有三项现实限制。

第一,官方明确标注为开发者预览,未来会出现破坏性变更。版本仍是0.1.0-rc.5,Session格式也没有兼容承诺。

第二,学习成本很高。要真正扩展它,不只要理解Agent,还要理解Cordis插件树、作用域、Service、Waterfall事件、Profile、Bundle、Patch和事件溯源。

第三,可组合性会把复杂度从代码分支转移到系统组装。插件可以替换,不代表组合天然正确;配置顺序、服务可用性、作用域泄漏、退出顺序和跨平台差异,都需要新的工程约束。

因此,我更愿意把DeepSeek Harness看成一个值得研究的Agent运行时实验,而不是今天就应该全面迁移的稳定底座。

我的最终判断:先学设计,不押框架

DeepSeek Harness重点解决的是中间偏运行时的部分:

  • 模型、工具与权限;
  • 工作流和状态管理;
  • 异常、取消与人工审批;
  • 会话持久化、遥测和长期运行基础。

但它不会自动替你定义业务任务,也不会自动保证数据可信,更不会替你建立具体场景的质量评估标准。

这也是理解所有Agent框架时都应该保持的边界:

一个强大的Harness,可以让Agent更可控、更可扩展;但它不能代替业务定义、数据治理和结果验收。

DeepSeek这次开源的真正信号,不只是“又多了一个Agent产品”。

而是Agent竞争正在继续向下移动:从模型能力、Prompt和工具数量,进入运行时、权限、状态、协议与生态扩展层。

对开发者来说,现阶段最理性的动作不是立刻选边,而是做三件事:

  1. 1跑通官方Web示例,观察Profile、Session和工具调用的真实行为;
  2. 2选一个低风险工具,尝试通过插件和工具流水线接入;
  3. 3用一次中断、恢复和权限拒绝实验,验证它的工程承诺是否符合自己的场景。

至于生产采用,我会等三个信号:稳定版本与兼容承诺、清晰的升级路径、真实第三方插件生态。

过去我们经常把Agent理解成“模型 + 提示词 + 工具”。DeepSeek Harness提醒我们,真正需要被设计的,不只是模型如何回答,还有Agent如何运行、暂停、恢复、授权、组合和退出。

我的结论很简单:现在值得读源码、做实验、借设计,但还不值得让业务押上去。


源码说明

本文主要参考DeepSeek Harness仓库以下部分:

  • README.zh.mddocs/architecture.zh.md
  • packages/core/agent-loop
  • packages/core/session
  • packages/core/tools
  • packages/interaction/user-approval
  • packages/interaction/permission-presets
  • packages/sandbox/sandbox-local
  • packages/subagent/subagent
  • python/sdk

分析快照:47f943859bef60e4160492346772ded9b24f765a

项目采用MIT许可证,但第三方依赖仍需分别遵守对应许可证。