乐于分享
好东西不私藏

一切皆插件:DeepSeek 把 Agent 的整个底座拆开,摆在了你面前

一切皆插件:DeepSeek 把 Agent 的整个底座拆开,摆在了你面前

如果你这两年写过 Agent,大概率经历过同一种挫败:框架给你的那套循环、那套工具执行流程、那套会话存储,在 Demo 阶段顺滑得像丝绸,一到真实业务就处处别扭。你想在工具执行前插一道审批,想把 bash 换成公司内网的沙箱,想让某一步走另一家模型——然后你发现,唯一的办法是 fork 源码,从此背上一份永远合不回去的补丁。

2026 年 8 月 13 日,DeepSeek 放出了 DeepSeek Harness(命令行叫 dsh)的开发者预览版,MIT 协议,全球可用[1][2][12]。它给上面这个问题的答案,粗暴得有点可爱:那就别让任何东西是「内核」。

官方那句话是这么写的:模型、工具、技能、会话、沙箱、存储、循环、调度、UI 等所有 Agent 能力均由插件组合而成,可以自由替换和灵活重组[1]。听上去像营销词,但把文档翻完你会发现,它是真的按这个说法把自己拆成了零件——包括那些通常被框架当作命根子藏起来的部分。

这篇文章想聊的不是「又一个 Agent 框架发布了」,而是:当一个团队真的把「一切皆插件」贯彻到底,架构上会长成什么样,代价是什么,以及这件事对正在自建 Agent 的人意味着什么。

先说清楚:它不是给终端用户的产品

很多人第一眼会把 dsh 当成 Claude Code 或 OpenClaw 的对标物。这个类比一半对一半错。

对的部分是形态:npx @deepseek-ai/dsh web 一行命令起一个本地 Web UI(默认在 127.0.0.1:3080),进设置里填 API Key、选一个工作目录、开一个会话,Agent 就能读写文件、执行命令、把活儿再派给子 Agent,需要授权时会停下来问你[2][3]。这套体验和你熟悉的编码 Agent 没有区别。

错的部分是定位。Harness 自己给的四种运行形态已经泄露了意图[1][2]

  • Standard:给编码 Agent 的完整工具集,是大多数人默认用的那套
  • PTC(Programmatic Tool Calling):让模型用 TypeScript 写代码来调工具,而不是一次次发结构化的 tool call
  • Minimal:只留 bash 和 editor 两把工具,专门用来跑基准评测
  • Creative:自己捏 Agent 预设,并在运行时把内部状态翻出来看

一个纯粹面向终端用户的产品,不会专门做一个「只有两把工具、用来刷分」的模式,也不会把「运行时检视」当成卖点。Minimal 模式的存在几乎是一句自白:这东西是给要做对照实验、要研究 Agent 行为的人用的。 它更像一个可组合的 Agent 运行底座,你在配置层重组它,而不是在源码层打补丁[2]

这个区别很重要。因为接下来所有让人觉得「有点复杂」的设计,都只有放在「底座」这个定位下才成立。

写一个插件,是一个 apply 函数的事

好,那么「一切皆插件」在代码上到底长什么样?答案朴素到有点让人失望——一个 Harness 插件,本质就是一个导出了 apply 函数的 TypeScript 模块[4]

importtype { Context } from'@deepseek-ai/cordis'exportconst name = 'hello-plugin'exportfunctionapply(ctx: Context{console.log('[hello-plugin] plugin loaded!')}

框架在加载时调用 apply,把一个上下文对象 ctx 递给你,你所有的能力注册都通过它完成。这里真正值得注意的是它没有的东西:没有 init/destroy 生命周期钩子,没有一堆需要你实现的抽象基类。

因为清理是自动的。所有通过 ctx 注册的资源——事件监听、工具、定时器——在插件卸载时会被框架一并回收,你不需要手写任何 teardown[4]。只有当你持有框架不认识的资源(比如自己开的一条网络连接)时,才需要用 ctx.effect() 显式登记:

exportfunctionapply(ctx: Context{  ctx.effect(() => {const timer = setInterval(() => {console.log('heartbeat')    }, 5000)return() => clearInterval(timer)  })}

依赖用 inject 声明,框架保证依赖服务先于插件初始化,你不用再写那种战战兢兢的 if (!ctx.tools) return[4]

exportconst name = 'my-tool-plugin'exportconst inject = ['tools']exportfunctionapply(ctx: Context{  ctx.tools.register(/* ... */)}

函数式之外还有对象式和类式两种写法,文档自己都说「多数情况函数形式就够了」,类插件只留给那些需要对外提供服务给别的插件用的场景[4]

这套设计的底子是 Cordis——一个把「插件向共享上下文贡献服务与类型化事件」作为核心抽象的插件框架[1][5]。熟悉 Koishi 生态的人会觉得眼熟,这不是巧合。DeepSeek 没有为了 Agent 重新发明一套插件系统,而是拿了一套已经在别处被验证过的、成熟的依赖注入与生命周期机制,直接当地基用。这是个挺克制的选择:Agent 领域已经足够不确定了,底层机制没必要再赌一次。

顺带一个容易踩的坑:注册本地插件时,配置文件里的插件路径必须写绝对路径[4]。这种约束读文档时一带而过,真上手时能耗掉你二十分钟。

Provider:一个不起眼的 ID,藏着对可复现性的执念

模型接入这一块,Harness 给了三条路:内置的 DeepSeek 直接在设置里填 Key;目录里的现成服务(Anthropic、OpenAI、AWS Bedrock、Google Vertex、Azure、Codex 等)选一个填凭据,端点、协议、模型列表会自动带出来;再不行就「添加自定义 Provider」,手工指定 Provider ID、Base URL、API 协议、凭据和至少一个模型,走 OpenAI 兼容接口[6]

这套东西本身没什么可讲的,几乎所有框架都长这样。真正有意思的是藏在细节里的两个设计。

第一个是 Provider ID 的不可变性。 文档明说:Provider ID 是永久标识,会被写进请求、存进已保存的会话、被凭据引用;想改名,只能新建一个再删掉旧的[6]。这是个看着不近人情的限制——为什么不让我改个名字?

因为一旦你的会话日志里记着「这一步用的是 my-gateway 这个 provider」,而 my-gateway 明天被改成了别的含义,那这条历史记录就永久地失去了意义。Harness 把「会话可回放」当成一等公民,而可回放的前提是所有被引用的标识都稳定。它宁可让你多做一次「新建 + 删除」,也不肯让一条历史记录变成谎话。 这个取舍,值得任何做审计、做溯源的人抄走。

第二个是凭据的物理隔离。 API Key 是只写的,存在 $DSH_HOME/.credentials.yaml 里,设置文件本身只保留一个引用[6]。这意味着你的 settings.yaml 可以放心地提交进 Git、分享给同事、贴进 issue,而不用每次都战战兢兢地检查有没有把密钥带出去。这不是什么了不起的创新,但它是那种「做对了没人夸、做错了要出事」的基础功。

还有个小细节暴露了这套系统的诚实:自定义 Provider 需要你在 settings.yaml 的 input 字段里手工声明这个模型支持文本还是图像,理由是「没有任何环境能向端点查询它接受哪些模态」[6]。它没有假装自己能自动探测,而是直接告诉你「这件事做不到,请你告诉我」。在一个到处都是过度承诺的领域,这种坦白反而让人安心。

最后一个实用信息:模型配置的改动在下一次请求时生效,不需要重启服务[6]。调模型的时候这一条能省下大量时间。

事件系统:五种派发语义,决定了你能插到多深

如果说插件是骨架,事件就是关节。Harness 的架构文档里有一句总纲——「事件就是扩展点」[5]

Cordis 把一整套事件 API 混入每个 context,而它给的不是一个 emit,是五个[7]

方法
语义
典型用途
ctx.emit()
同步派发,忽略返回值
纯通知,发完不管
ctx.parallel()
并发执行所有监听器,全部 settle 后 resolve
互不依赖的副作用
ctx.serial()
按顺序 await,直到有人 bail
有先后依赖的处理链
ctx.bail()
顺序调用,遇到非 null / false / undefined 的返回值立即停止
拦截、否决、短路
ctx.waterfall()
最后一个参数是 next 续延,每个监听器包裹剩余链条
改写、包装、加中间件

waterfall 是这五个里最值得琢磨的。每个监听器拿到 next,调用它就继续往下走,不调用就等于否决[7]。这是标准的洋葱模型(Koa 的中间件、Express 的 next 都是这个思路),意味着你可以在一次调用的前后各做一件事,也可以直接把整条链掐掉。

注册侧则是 ctx.on() 和 ctx.once(),都归当前 fiber 所有,都返回一个 disposer;EventOptions 里有两个开关值得记住:prepend 让你插到已有监听器之前,global 让你无视上下文过滤照样收到事件[7]

为什么要为五种派发语义费这么大劲?因为扩展点的表达力,直接由派发语义的丰富度决定。只有 emit 的系统里,你的插件永远只能当观众——你能知道发生了什么,但改变不了任何事。有了 bail 你才能否决,有了 waterfall 你才能改写。Harness 想让你「把整套循环逻辑都换掉」,那它就必须在事件层面先把这些权限准备好。

工具执行:三道 waterfall,一次调用可以被改写三次

事件系统的威力,在工具执行流水线上体现得最直白。

一次工具调用会依次经过:tools/pre-execute waterfall → 单调性守卫(monotonic guards)→ tools/execute waterfall → tools/post-execute waterfall → finalizeContent → tools/result waterfall[8]。文档对这三道 waterfall 的描述是:它们各自可以把一次调用改写一遍[8]

翻译成人话——你想在工具跑之前改参数,pre-execute;你想把整个执行换成自己的实现,execute;你想在结果回给模型之前动手脚,post-execute 和 tools/result。三个位置,三种粒度,都不需要碰工具本身的代码。

审批逻辑的位置也经过设计:上下文审批在单调性守卫之前处理,而所有者策略作为守卫注册、且不可重排序[8]。这句话的意思是——「要不要问用户」这件事可以灵活扩展,但「哪些操作绝对不许做」这道底线,不给你调整顺序的机会。能被插件重排的安全检查,等于没有安全检查,这个边界划得很清醒。

流水线里还散落着几个专用事件:文件系统操作走 fs/*,Code Mode 的分发走 tool/code-dispatch[8]。文件系统那边有个具体例子挺有代表性——「先读后改」的校验(read-before-edit)就是挂在 tool-fs 底下、用文件系统事件实现的[8]。也就是说,连这种听上去应该硬编码在工具里的安全规则,在 Harness 里也是一个可插拔的监听器。

能力接缝:换掉一整个 shell,而调用方毫无察觉

比事件更进一步的抽象,Harness 叫它 capability seams(能力接缝)

架构文档把服务分成三类:固定实现的核心骨干服务、可插拔的能力接缝、以及组合式的包与挂载点[9]。能力接缝是那些「定义了接口、但实现可以整个换掉」的服务,换的时候消费方代码一行都不用改[9]

清单本身就很说明问题[9]

  • 模型与上下文ctx.llm(DeepSeek / Pi-AI / replay 等多种实现)、ctx.attachments(多媒体引用)、ctx.systemPrompt(按步骤构造提示词)
  • 会话与持久化ctx.sessionPersistence(每种后端统一到同一套 SessionEvent 词汇表)、ctx.sessions(append-only 会话实例)、ctx.sessionTitle
  • 执行与工具ctx.subprocess(本地 / E2B / 远程)、ctx.shell(bash / PowerShell)、ctx.fs(含沙箱变体)、ctx.web(搜索与抓取)
  • 存储与配置ctx.storage(后端无关的 KV)、ctx.settings(分层配置)、ctx.credentials(密钥引用)

注意 ctx.llm 里那个 replay 实现[9]。一个「回放」型的模型提供者意味着你可以把真实会话录下来,之后不花一分钱、不等一秒钟地反复重跑,用来测你的插件、你的工具、你的提示词。这是评测和回归测试的刚需,而它在这套架构里不是一个特殊功能,只是 ctx.llm 这个接缝上的又一个实现。 这就是把抽象层次找对之后的红利。

替换的方式简单到无聊:不同实现注册到同一个接缝服务上,在组合(composition)阶段选一个激活,框架负责校验完整性、保证唯一注册[9]。上层的组合单位叫 profiles 和 bundles——分层的插件组合,可以打补丁、可以定制[5]

Trajectory 与 append-only 日志:可回放,才叫可调试

前面反复提到「回放」,这就要说到 Harness 另一条硬约束。

每一次模型交互都被写进一份只追加(append-only)的会话日志——提示词、推理链、工具调用、子 Agent 调度,一个不落;配套的 Trajectory 视图让你能顺着执行路径一路溯源[1]。有分析把这条原则概括得很到位:模型看到的内容,必须能从日志重建[10]

这条原则的价值,任何调过多轮 Agent 的人都懂。Agent 出错的典型场景不是崩溃,而是「它在第 7 步做了个莫名其妙的决定」。你想知道为什么,就得知道它在第 7 步到底看到了什么——上下文被压缩了没?哪些工具结果被裁掉了?系统提示词那时候长什么样?没有可重建的日志,这些问题只能靠猜。

架构上,Harness 把两类状态分得很干净[11]

  • session/event:持久化的、用于回放的事实。做转录、做审计、做数据分析的消费方应该读这里
  • agent/*:实时的控制态。排队、状态管理、提示词拦截、请求构造、引导、续跑、错误处理走这里

工作被切成 turn(一个完整任务)和 step(一次模型请求)两级,事件挂在这两级的各个位置上——turn/startagent/pre-stepllm/streamtool/executeturn/end 等等[10]

上下文压缩的处理位置很能说明这套设计的成熟度:dsh-compaction-basic 插件在 agent/pre-step 阶段、也就是推导请求之前处理上下文压力,而 agent/request-error 专门负责上下文溢出的兜底[11]。恢复流程更精细——发生在失败的 step 之后、但在失败的 turn 结束之前:先可选地裁剪工具结果,再选择摘要,只有当裁剪或摘要真的推进了「替换面」的生成,才会开一个新的重试 turn[11]

最后半句是整段里最有含金量的。它防的是一种非常具体的病:Agent 撞了上下文墙,压缩了一下,发现还是超,再压一次,还是超……于是无限重试烧钱。Harness 的规则是——如果这次压缩没有让状态实质性前进,就不许重试。 这不是什么理论优雅,这是被真实账单教育出来的经验。

几条可以带走的判断

Harness 值不值得你今天就迁过去,取决于你的团队规模和对「开发者预览」这四个字的容忍度。但它摆出来的几条思路,即使你不用它,也值得记下来。

第一,先问「扩展点够不够」,再问「功能全不全」。 选 Agent 框架时,大家习惯对比工具数量、模型支持、UI 好不好看。但决定你两年后是否要 fork 源码的,是它在关键路径上留没留可插入的位置,以及这些位置的语义够不够强。一个只有 emit 的钩子系统和一个有 waterfall 的中间件系统,纸面上都叫「支持插件」,实际能力差着一个数量级。下次做技术选型,把「我能不能否决一次工具调用」「我能不能整个换掉 shell 实现」当成必答题去问。

第二,可回放不是奢侈品,是 Agent 的调试底线。 append-only 日志、Trajectory 视图、ctx.llm 上的 replay 实现、session/event 与 agent/* 的严格分离——这一整套加起来只干一件事:让「第 7 步为什么这么干」变成一个可以被回答的问题[1][9][11]。如果你正在自建 Agent 系统,哪怕别的都先不做,也请先把「模型每一步看到的完整输入」原样存下来。你现在省下的那点存储成本,将来会以通宵排查的形式连本带利还回去。

第三,把「不可变标识」和「不可重排的守卫」当成设计纪律。 Provider ID 不许改名,是为了让历史记录永远说真话[6];所有者策略作为守卫且不可重排序,是为了让安全底线不被插件绕过[8]。这两条都是主动放弃灵活性换取可信度的决定。在一个「什么都可以插拔」的系统里,明确指出哪几样东西绝对不能动,恰恰是这个系统能被信任的前提。 反过来说,如果一个框架宣称什么都能改,那它大概率还没想清楚自己在保护什么。

第四,别为了 Agent 重新发明底层轮子。 Harness 的插件机制直接建在 Cordis 上,没有自研[1]。Agent 这一层的不确定性已经够高了,依赖注入、生命周期、事件总线这些问题在别的领域早就有成熟解法。把创新预算花在真正新的问题上,是个朴素但常被忽略的纪律。

写在最后

Harness 现在还是开发者预览,官方和社区都提醒了会有破坏性变更[10]。它的学习曲线也明显更陡——四种模式、profiles 和 bundles 的分层组合、五种事件派发语义、一长串能力接缝,这些概念不是为了让你十分钟跑通 Hello World 而设计的。

但它的取舍是清楚的:用更高的入门成本,换一个长期不用 fork 的底座。 如果你只是想找个顺手的编码助手,这笔账多半不划算;如果你正在把 Agent 往生产环境里塞,并且已经为了改一行默认行为而维护过一份丑陋的补丁,那么 Harness 提出的问题就值得你认真想一遍。

真正值得追问的,其实不是「DeepSeek 这个框架好不好用」,而是它背后那个更朴素的判断——当 Agent 从 Demo 走向生产,决定成败的可能不是模型多聪明,而是这套运行底座允许你在多少个位置、以多深的方式介入它。 这个问题,每个在做 Agent 的团队迟早都要自己回答一次。DeepSeek 只是先把自己的答案,摊开摆在了桌上。

参考资料

  1. DeepSeek《DeepSeek Harness》官方产品页,2026 年 8 月。https://www.deepseek.com/harness/
  2. AIHub《DeepSeek Harness 开发者预览版发布:MIT 开源,采用"一切皆插件"架构》,2026 年 8 月 13 日。https://www.aihub.cn/news/deepseek-harness-release/
  3. DeepSeek Harness 官方文档《快速开始 / Quickstart》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/guide/quickstart
  4. DeepSeek Harness 官方文档《插件开发基础 / Plugin Development Basics》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/develop/basic/
  5. DeepSeek Harness 官方文档《架构参考 / Architecture Reference》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/reference/
  6. DeepSeek Harness 官方文档《模型提供者配置 / Model Providers》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/guide/providers
  7. DeepSeek Harness 官方文档《Cordis API:事件 / Events》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/reference/cordis-api/events
  8. DeepSeek Harness 官方文档《工具执行流水线 / Tool Execution Pipeline》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/reference/tool-execution-pipeline
  9. DeepSeek Harness 官方文档《能力接缝 / Capability Seams》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/reference/capability-seams
  10. StableLearn《DeepSeek Harness 开源:把 Agent 的整套运行底座拆开了》,2026 年 8 月 13 日。https://stable-learn.com/zh/deepseek-harness-open-source-agent-framework/
  11. DeepSeek Harness 官方文档《Agent 生命周期 / Agent Lifecycle》,2026 年 8 月。https://deepseek-harness.github.io/deepseek-harness/reference/agent-lifecycle
  12. DeepSeek Harness 开源仓库,MIT 协议,2026 年 8 月。https://github.com/deepseek-ai/deepseek-harness