DeepSeek Harness (dsh):用"一切皆插件"重构 Agent 运行时¶
摘要
发布时间:2026年8月15日
项目地址:github.com/deepseek-ai/deepseek-harness
核心定位:DeepSeek 开源的 Agent 运行框架,底层基于独立插件框架 Cordis(cordiverse/cordis),主张"一切皆插件"(Everything is a Plugin)
当前状态:开发者预览(Developer Preview),正在快速迭代,未来可能出现破坏兼容性的变更
引言:Agent 框架的"硬编码之痛"¶
过去两年,我们见证了 AI Agent 从 demo 走向生产。但在工程实践中,一个反复出现的尴尬是:大多数 Agent 框架把"能力"写死在了代码里——你想换一个模型供应商、想让工具在远程沙箱里执行、想给所有文件操作加一层审批,往往要改框架核心、甚至 fork 整个项目。
我们在本系列里讨论过 MCP(标准化的工具协议)、多智能体协作(Agent 间的分工)、AI 安全与对抗(如何约束 Agent 行为)。这些文章解决的是"连接什么""怎么协作""如何约束",却很少有人从"运行时本身该如何组织"这个更底层的角度给出答案。
DeepSeek 于 2026 年 8 月开源的 deepseek-harness(命令行叫 dsh)正是冲着这个问题来的。它的核心理念只有一句话:
一切皆插件(Everything is a Plugin)。
诚实提示:截至本文撰写时
dsh处于开发者预览阶段,官方明确表示"未来将出现破坏兼容性的变更"。本文内容基于其官方架构文档,但请勿把下文任何 API 当作稳定契约;落地前请以仓库最新源码为准。
本文基于 dsh 的官方文档(architecture / cordis-primer / capability-seams / tool-execution-pipeline / defensive-patterns),带你看懂它的设计思想、内核机制,以及它与你现有 Agent 技术栈的关系。
一、dsh 是什么:不是一个聊天客户端¶
首先要澄清一个常见误解:dsh 不是又一个套壳聊天界面,而是一个可组合、可替换的 Agent 运行时(runtime)。
最快的体验方式是:
1 | |
它会启动一个 Web UI(默认地址 http://127.0.0.1:3080)。但这只是"分发形态"之一——dsh 真正的内核是一个插件树。
核心洞察:
- 模型适配器(用哪家 LLM)是一个插件
- 工具注册表(能用哪些工具)是一个插件
- 会话日志(记了什么)是一个插件
- 文件系统、进程、沙箱、子 Agent 调度,全都是插件
运行时本身(Cordis 框架)不持有任何"特权内核"。新增能力的唯一方式,就是"把插件挂载到其他插件旁边"。这种结构让 dsh 既能打包成带浏览器的 web 分发版,也能打包成无服务器的 headless 一次性运行版——两者只是叠加的组合包(bundle)不同。
二、Cordis:dsh 的插件内核¶
dsh 的底层并非 DeepSeek 临时造的轮子,而是基于一个独立的插件应用框架 Cordis(由 cordiverse 组织维护),其设计思想还对应一篇论文《A Programming Paradigm for Spatiotemporal Composability》。理解 Cordis 的五个核心概念,就理解了 dsh 的全部。
1. 插件是实现 Service 的对象¶
一个插件可以是带可选 inject 和 apply(ctx) 的函数,也可以是 Service 的子类。它一旦被 Cordis 挂载到上下文,就"活"了——能注册服务、能监听事件、能安装副作用。
2. 上下文是服务的容器¶
所有能力都通过稳定的 ctx.<key> 暴露,例如 ctx.tools、ctx.llm、ctx.agents。其他插件只通过这个 key 去"查",而不 import 具体实现。这是依赖倒置的关键:消费方永远面向接口,不面向实现。
3. 用 inject 声明依赖,而非手动编排加载顺序¶
插件可以声明"我需要 ctx.llm 就绪后才能启动"。Cordis 会根据依赖图自动决定加载先后,开发者不需要手写启动序列。这避免了传统框架里"初始化顺序地狱"。
4. 类型化事件用于通信¶
服务通过 TypeScript 声明合并注册事件名,并用四种模式分发:
| 模式 | 是否 await | 分发顺序 | 是否有返回值 | 典型用途 |
|---|---|---|---|---|
emit |
否 | 注册顺序观察 | 否 | 广播通知(如 session/event) |
waterfall |
否 | 注册顺序观察 | 是 | 环绕中间件(可改写结果) |
parallel |
是 | 所有监听器并行观察 | 否 | 扇出到多个独立监听者 |
serial |
是 | 注册顺序观察 | 是 | 按序执行、可短路决策 |
其中 waterfall 最值得玩味:监听器签名是 (...args, next),调用 next() 执行下游并拿到返回值,再包一层返回;如果不调 next() 直接返回,就短路了整个链路。这正是"加策略/加拦截"的标准钩子。
5. 注册是可逆的副作用¶
一个插件安装提示词片段、工具 schema、LLM 适配器,都是通过 ctx.effect() 或 ctx.on() 完成。当插件被卸载或 reload 时,这些注册会被自动撤销(dispose)。这意味着"换插件 = 干净地换行为",不会留下脏状态。
一句话总结 Cordis:
插件向共享上下文贡献服务、类型化事件和可逆副作用;整个产品没有特权内核,扩展方式只有"在插件旁边再挂一个插件"。
三、能力 Seam:解耦的扩展点¶
如果说 Cordis 是"胶水",那 Seam(接缝) 就是 dsh 留给生态的"标准插座"。
一个 Seam 是一个标记为 seam 角色的 ctx 服务,它由某个包定义接口,由若干实现包提供适配器,被消费方零改动消费。dsh 的文档把服务明确分为三类:core(核心,无外部替换实现)、seam(可替换的能力接缝)、bundle(组合点)。这里挑几个最具代表性的 seam:
| ctx 键 | 角色 | 实现包举例 | 消费方 |
|---|---|---|---|
ctx.llm |
seam | llm-deepseek、llm-pi-ai、llm-replay | agent-loop、compaction-basic |
ctx.fs |
seam | fs-local、fs-sandbox、fs-e2b | tool-fs |
ctx.subprocess |
seam | subprocess-local、subprocess-e2b | bash-*、terminal-bash、lsp-stdio |
ctx.subagents |
seam | subagent-spawn-in-process、subagent-acp、subagent-claude-code | tool-subagent、tool-ralph |
ctx.web |
seam | web-search-exa、web-fetch-http | tool-web |
ctx.skills |
seam | skill-badge、skill-filesystem | tool-skill |
ctx.approval |
seam | acp | tools、tool-bash |
关键价值在于"消费方零改动":
- 把
ctx.fs从fs-local换成fs-sandbox,tool-fs的读写代码一行都不用动 - 把
ctx.subprocess从本地换成subprocess-e2b远程沙箱,所有依赖子进程的命令工具自动获得隔离执行能力 - 把
ctx.llm从 DeepSeek 换成llm-replay(位于packages/test-support/llm-replay),就能在不调用真实模型的情况下做可复现的评测
这正是我们在 AI 安全与对抗 里强调的"最小权限/隔离执行"的工程落地方式:不是靠开发者自律,而是靠架构把危险能力换成受限实现。
四、Turn Flow:一次交互的精确事件链¶
理解 Agent "一轮(turn)"内部发生了什么,是读懂 dsh 扩展机制的钥匙。dsh 把一次轮次定义为:
- 步骤(step) = 一次模型请求 + 可能的工具调用
- 轮次(turn) = 零个或多个步骤
一个 turn 会按固定顺序触发以下事件(括号内为关键节点):
1 2 3 4 5 6 7 8 9 10 11 12 | |
这对扩展意味着什么? 几乎所有"观察/拦截/增强"行为,都能挂在这些事件上。工具执行本身由一条更细的流水线承载(tools/pre-execute → 单调守卫 → tools/execute → tools/post-execute),这条流水线正是策略/钩子/沙箱的挂点。
文档还区分了两类事件:持久会话事件(追加日志、reload 后仍在)与实时扩展点(运行中观察/拦截)。一句话铁律是——
"模型可见即已记录"(what the model sees is what's logged)。
新增任何"模型可见的输入",都必须新增对应的会话事件。这条约束直接保证了 Agent 行为的可审计、可回放,与 AI 安全与对抗 中"记录一切、可事后溯源"的原则完全契合。
五、组合与分层:Profile + Bundle¶
dsh 不是把所有插件平铺,而是用一套层叠(layering)模型组装:
分层结构:
- 基础层
dsh-base:每个 profile 的第一层,含模型适配器、工具、持久化、沙箱、审批、设置、凭据、遥测 - 上层组合包:
dsh-web-app(增加浏览器应用)、dsh-headless(增加一次性运行器,无服务器)
运行中的 dsh = 一棵插件树,由启动期按序叠加的层组成:
- Profile 列出的各组合包(按顺序)
- Profile 的
cordis.patch.yml - Home 级的
cordis.patch.yml - 任意
--patchoverlay
patch 按 id 定位条目,替换整块 config 或插入新条目。你可以用 dsh --profile web --dump-config 打印出完整的启动树,任意条目都能被用户 patch 掉。
作用域隔离:
Agent preset 里的服务行可以标 isolate realm,把单个 Agent 的注册限定在它自己的 agent.ctx 里。这让多 Agent 之间既能共享基础设施,又能各自拥有私有工具集——这正是 多智能体协作 架构在运行时层面的支撑。
六、一个真实可感的扩展示例:给 bash 工具加审批¶
为了把机制落到能感知的例子,下面演示"如何给所有 bash 工具调用加一层审批/白名单"。这里用的是 dsh 真实的扩展点,而非虚构 API:
机制说明:
- 工具执行走
tools/pre-execute这条 waterfall 事件——监听器可改写或阻断一次调用 - 审批本身由
ctx.approval这个 seam 提供(acp实现),它在单调守卫之前处理"一次性询问" - 单调守卫(Registered monotonic guards)在
pre-execute允许后运行,可"拒绝"或"弃权"
用 Cordis 语义表达为(伪代码,仅说明结构,真实 API 以仓库源码为准):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
这个例子的关键点:
- 我们没有改
tool-bash、没有改agent-loop - 我们只是在
tools/pre-execute的流水线上"挂了一个守卫",并复用ctx.approvalseam - 卸载插件时,注册被自动撤销,系统回到无审批状态
这就是"一切皆插件"的威力:行为改变 = 挂载/卸载插件,而非修改核心。值得强调的是,dsh 的作者们在 defensive-patterns 文档里专门总结了安全相关的工程纪律,例如:
真实的安全防御机制:
- 凭证不泄漏:启动命令使用清理后的环境变量,自动移除名称匹配
*KEY*、*SECRET*、*TOKEN*、*PASSWORD*的项,防止 harness 凭证通过输出或 spill 文件泄漏;临时/spill 文件存于0700权限的私有目录、随机文件名、以0o600独占方式打开 - 符号链接安全删除:删除路径前先
lstatSync().isSymbolicLink()判断,对符号链接用unlinkSync仅删链接本身(不跟随目标),规避递归删除误入链接目标导致的越权或数据破坏
这些正是 AI 安全与对抗 所倡导原则在运行时层面的具体落地。
七、它和你现有技术栈的关系¶
很多人会问:dsh 和 MCP 是什么关系?是竞争还是互补?
核心区别:
- MCP 解决的是"Agent 如何标准化地连接外部工具/资源"——它是一个协议
- dsh 解决的是"Agent 运行时本身如何组织与替换能力"——它是一个运行时框架
它们不冲突,反而层次不同:MCP 定义的工具,可以在 dsh 里作为一个 ctx.tools 的实现被挂进来;dsh 的 Seam 机制,则负责在"用哪家 MCP server、跑在本地还是沙箱、是否加审批"之间做组合。
与其它文章的衔接:
- 与 多智能体协作:
ctx.subagents的 seam 与isolaterealm,提供了多 Agent 隔离运行的原生支撑 - 与 AI 安全与对抗:
fs-sandbox/subprocess-e2b等 seam 实现,以及 defensive-patterns 的凭证/路径保护,是"最小权限、隔离执行"的架构级落地 - 与 上下文工程:
ctx.systemPrompt把提示词拆成可组合的片段,多个插件各自贡献一块 - 与 本地 Agent 工具 / nanobot:dsh 的
headlessbundle 同样面向"轻量、本地优先"的场景
八、小结与选型建议¶
dsh 的核心贡献,不是某个惊艳的算法,而是一种工程纪律:
- 用 Cordis 的
ctx+inject+ 可逆副作用,把"依赖管理"和"状态清理"交给框架 - 用 Seam 把"能力接口"和"能力实现"彻底解耦,换后端零改动消费方
- 用类型化事件 + Turn Flow,把"扩展点"变成一串明确定义的观察/拦截钩子
- 用"模型可见即已记录"的铁律,保证 Agent 行为天然可审计、可回放
什么时候值得用 dsh?
- 你需要在同一套 Agent 逻辑下,频繁切换模型、执行环境(本地/沙箱)、或加各种策略(审批、日志、压缩)
- 你在做可复现评测(
llm-replayseam 能离线回放,不调真实模型) - 你希望 Agent 的每一条行为都可审计、可溯源
什么时候它可能不是最优选?
- 你只想要一个"调通 API 就能跑"的轻量助手,Cordis 的插件树对你来说是过度设计
- 你的工具生态已经深度绑定某个特定框架(如 LangChain),迁移成本高于收益
- 你需要生产稳定 API:dsh 目前是开发者预览,官方已预警未来会有破坏性变更
- 对于普罗大众来说,这种皆可插件方式或许是用力过猛。对于专业人员来说,又可能懒于配置。
无论如何,dsh 把"Agent 运行时该如何组织"这个问题,给出了一个清晰、克制、可组合的参考答案。对正在构建生产级 Agent 的工程师来说,它的架构文档本身,就是一份值得反复咀嚼的设计教材。
延伸阅读(本系列):
- What is MCP:模型上下文协议
- 多智能体协作入门
- AI 安全与对抗
- 上下文工程(Context Engineering)
- Agentic AI:从 Chatbot 到可行动的智能体
- 本地开源 AI 助手 OpenClaw