我克隆了DeepSeek的开源代理框架,并在里面度过了整整一天。我发现这是一款在架构上最具雄心的代理框架。不是最精致的,也不是最流行的,但却是最有原则的。
它的名称是dsh,采用MIT许可协议。当前版本为0.1.0-rc.5 — 开发者预览版,预计有重大更改。它基于一个单一且激进的想法:一切皆插件。不是“我们有一个插件系统。”也不是“你可以扩展某些功能。”而是所有内容。代理循环、模型适配器、工具注册表、会话日志、用户界面 —— 所有这些都是Cordis插件,可以通过配置单独进行替换、修补或替换。
没有特权内核。
规模
让我们先来看看规模:
- 跨40多个组的219个包
- 约94,000行TypeScript代码
- 约1,981个源文件
- 640个测试文件
- 自2026年6月以来约12,293个提交
最后一个数字应该引起你的注意。大约十周内有12,000多个提交。这不是一个副项目。这是一个团队在全速推进。
Cordis 实际是什么
底层框架称为Cordis,描述在一篇关于“时空可组合性”的学术论文中。听起来像是典型的计算机科学术语,但核心思想是实用的:插件贡献服务、类型化事件和可逆效果到共享上下文树中。每次注册都是一个效果 —— 当插件卸载时,它注册的所有内容会自动清理。
这就像依赖注入,但其中的DI容器也是事件总线、生命周期管理器和配置层。插件在ctx上注册服务,其他插件通过ctx.serviceName使用它,当第一个插件卸载时,每个消费者的引用会自动断开。
DeepSeek将整个Cordis框架——九个包——打包进了他们的仓库中的vendor/目录下,重新发布为@deepseek-ai范围,并记录了18个本地修改,包括生命周期强化,修复了上游从未遇到的重新进入释放间隙。这就是他们的承诺。
能力缝合模式
这就是让这一切工作的设计模式。一个“缝合”是一个可替换的能力,具有三个角色:
- 服务定义 — 声明接口
- 服务提供者 — 实现接口
- 消费者 — 通常是面向模型的工具,使用该接口
文件系统缝合、子进程缝合、Shell缝合、LLM缝合 —— 它们都遵循这个模式。而这正是关键所在:将文件系统和进程提供者指向远程沙盒,只需一个配置更改即可将Bash、PTY和LSP移动到那里。没有分叉。没有适配层。提供者共享相同的执行世界。
你想将DeepSeek替换为Claude作为你的模型吗?在ctx.llm上注册一个新的LLM适配器。你想添加持久终端会话吗?注册一个ctx.terminals后端。你想让代理在运行时能够修改自己的插件树吗?有一个extensions/包可以实现。是的,代理可以在运行时检查和挂载/卸载自己的插件。
轮流流程
代理循环本身也是一个插件(dsh-agent-loop)。当代理处理消息时会发生以下情况:
turn/start
claim input + queued message
assemble prompt + tool schemas
-> agent/pre-step (reject or enter)
step/start
derive model history from session log
agent/request -> llm/stream -> assistant/message
tool/call -> tools/pre-execute -> tools/execute -> tools/post-execute
step/end
-> agent/turn-stopping
turn/end
一个步骤是一个模型请求加上其工具调用。一个轮是零或多个步骤。关键事件是瀑布流 —— 监听器必须调用next()来委托,否则会中断链。这意味着钩子可以在管道中的任何点拦截、修改或阻止,而不改变循环本身。
将所有这一切联系在一起的设计规则:模型可见 = 已记录。所有到达模型的内容都必须可以从会话日志中重构。新的模型可见输入?你需要一个新的会话事件。这使得重放、分叉、恢复和遥测都可以从单一数据源推导出来。
工具执行管道
大多数代理框架都有一个简单的“调用工具,返回结果”流程。DeepSeek有10个阶段的管道:
tools/pre-execute瀑布流(钩子、权限检查、沙盒包装)- 单调保护(拒绝或弃权 —— 一旦拒绝,就无法被覆盖)
- 确认提示(可选的一次性用户确认)
tools/execute瀑布流(超时、重试、指标包装)- 工具主体执行
- 文件系统写入意图门(仅针对文件突变)
- 工具拥有的会话事件(待办事项写入、文件观察、钩子调用)
tools/post-execute瀑布流(接受、阻止、替换、添加上下文)- 归一化和
finalizeContent tools/result— 冻结、权威结果
对于玩具来说,这可能有些过度工程化。但对于需要在每个阶段注入策略的生产系统来说,这正是正确的做法,而不必将工具耦合到特定的策略服务上。
安全性
安全模型是认真的:
- 沙盒后端:Landlock(Linux内核)、bwrap(bubblewrap)、Seatbelt(macOS)
- 清理环境:派生命令会获得所有
*KEY*/*SECRET*/*TOKEN*/*PASSWORD*变量被移除的环境 - 临时文件:私有0700目录,随机名称,独占所有者打开
- 符号链接安全性:
lstatSync().isSymbolicLink()然后unlinkSync—— 从不跟随链接进入目标 - 确认工作流:在敏感操作之前的一次性提示
- 单调保护:一旦保护拒绝,下游监听器就无法覆盖它
防御性模式文档读起来像是一本战争日记 —— 每个规则都对应一个实际或几乎实际发货的错误类。“独立报告正交结果”来自于一个超时并退出0的进程,因为它捕获了信号。“销毁必须达到静默”来自于拆解后发出的杀命令但在工作停止前返回,留下了孤儿。
接口
| 接口 | 命令 |
|---|---|
| Web UI | npx @deepseek-ai/dsh web → 本地主机:3080 |
| CLI 一次性 | dsh --profile headless "任务" |
| ACP 服务器 | 代理客户端协议,用于自动化 |
| JSON-RPC | Python SDK 通过标准输入输出驱动代理 |
| 源代码 | pnpm dsh 从代码检出 |
Python SDK 是一个单独的包(deepseek-harness-sdk),通过标准输入输出上的新行分隔的JSON-RPC与捆绑的运行时通信。这是一个干净的分离 —— TypeScript 运行时承担了大部分工作,Python 是一个薄客户端。
实际新颖之处
有三件事我从未在其他地方见过:
-
自我修改。
extensions/组允许代理在运行时检查和修改其自己的Cordis插件树。代理可以挂载新插件、卸载旧插件,并查看其自己的服务图。有一个演示 (pnpm run demo:cordis),其中代理在对话中实际改变了其自身的能力。 -
会话日志作为真相。 只读的
SessionEvent日志不仅仅是一个记录 —— 它是模型历史、分叉、重放、遥测和持久性的来源。deriveMessages()将日志投影到模型上下文中。如果不在日志中,模型就看不到它。 -
一切皆双语。 每个文档、每个README、每个面向用户的字符串都同时提供英文和中文。有翻译验证门,配对冲突解决,以及文档预算系统。这是一个重视国际开发者体验的中国AI实验室。
诚实评估
优点:
- 插件架构是真实的,不是营销
- 工程纪律是生产级的(每个文件100%覆盖率门)
- 多接口设计(Web、CLI、ACP、JSON-RPC、Python)
- 以安全为先的沙盒,有三个OS级后端
- 自我修改能力是真正新颖的
担忧:
- 开发者预览版。承诺有重大更改。
- 219个包的表面积很大。依赖图很复杂。
- Cordis被本地修改了18次。这是一个维护分支,而不是依赖。
- 主要优化是为DeepSeek模型。适配器缝合从架构上是模型通用的,但默认路径假设DeepSeek。
- 还没有标记版本。AGENTS.md 文件中说“在第一个标记版本发布时删除此部分”。
最后总结
DeepSeek Harness 是一个有资金支持的AI实验室决定从第一原理构建代理框架而不是将工具附加到聊天API的结果。Cordis论文不是装饰品 —— 时空可组合性模型实际上驱动了设计。每个组件都是可替换的,因为框架从一开始就设计为可替换的。
它是否过度工程化?对于一个周末项目来说,绝对是的。但对于一个需要支持多个模型后端、多个接口、多个沙盒策略和运行时自我修改的生产代理平台来说,恰恰不是。它正好符合问题所需复杂度。
真正的问题是生态系统是否会采用它。开源代理框架的生死取决于社区,而不是架构。DeepSeek有架构。现在他们需要插件、教程和“从30分钟到生产”的体验,使开发者选择dsh而不是直接调用API。
但如果你正在构建代理基础设施,并且想了解“一切皆插件”在实践中意味着什么 —— 这就是你需要研究的代码库。
完整分析、架构图和源代码引用保存在研究库中。