
字节笔记本
2026年10月6日 · 约 5 分钟读完
DeepSeek Harness 作用域注册表设计解析
DeepSeek Harness(dsh)是 DeepSeek 开源的 agent 框架,主打「一切皆插件」,底层由 Cordis 驱动,目前处于开发者预览阶段,官方也明确提示后续会有破坏兼容性的变更。支撑这套插件体系的地基里,有一个不起眼但值得细读的包:packages/core/scope。它只用四百多行源码,就同时回答了插件注册里两个绕不开的问题:这份注册对哪个 agent 可见,以及它的生命周期由谁负责。

一把不透明钥匙:ScopeKey
ScopeKey 是一个不透明的对象身份标识,约束只有一条:它必须是对象,比较方式是身份比较。已交付的 agent loop(智能体循环)会把活跃的 Agent 对象直接当作自己的 key,而 scope 原语承诺永不检视这个对象的内容。
/** 不透明的、按身份比较的作用域 key */
type ScopeKey = object这个约束看似朴素,却是整个设计的支点。路由层不关心 key 里有什么字段,只把它当作能放进 Map 的身份令牌,对象结构怎么演进都不会波及路由逻辑。
Scoped:只管路由的编译期载体
scopeTarget(base, key) 返回一个不透明的路由接收器,Scoped<T> 是标在它上面的编译期品牌标记。凡声明为作用域过滤的事件,都要求以这个载体作为 this 类型,而真正的事件主体仍作为显式参数随事件传递。载体上刻意不暴露主体的任何属性,类型参数只记录主体类型供派发检查使用。想拿路由接收器当真实对象用的代码,在编译期就会被拦下。
一个上下文,两条拆卸边界
Scope 把带标签的注册上下文与两个拆卸接口配对。ctx 是作用域名下所有注册的入口;rawDispose 保留 Cordis disposer 的确切身份,用于把这个作用域嵌进有序复合 effect 时维持释放顺序;dispose() 则是面向直接调用方与竞态调用方的公共完全停稳边界,多个调用同时触发时,大家等待的是同一次完成。
/** 一次铸造的注册作用域与它的停稳拆卸边界 */
interface Scope {
/** 作用域名下注册的入口上下文 */
ctx: Context
/** 确切的 Cordis disposer,嵌套进有序复合 effect 时使用 */
rawDispose: () => Promise<void> | void
/** 释放作用域名下的全部注册;竞态调用等待同一次完成 */
dispose(): Promise<void>
}拆成两条边界是刻意的:嵌套场景要的是顺序,直接调用要的是停稳,二者不必互相迁就。
ScopeLayer 与 ScopedLayers:会回收的分层注册表
ScopeLayer 表示某个注册表在全局或确切作用域层级上的完整贡献,一层可以聚合多个具名与匿名 table。ScopedLayers<L> 拥有一个立即创建的全局 layer,以及按需惰性创建的作用域 layer。
几条语义边界值得划出来:读取不建层,peek(undefined) 明确表示不存在作用域覆盖层;merge() 按插入顺序依次物化全局具名条目与带作用域的遮蔽项;只有当某个作用域 layer 的全部 table 都为空时,整层状态才会被回收,且不波及兄弟 table。注册路径上,同一个上下文同时表达可见性与 Cordis effect 所有权,注册方会在可选通知前先拿到一个同步撤销函数,返回的是 Cordis 的原始 disposer。
具名与匿名条目:两种粒度,一致的撤销
NamedEntries<V> 提供按插入顺序的查找与动态迭代,重复项错误交给调用方处理;AnonymousEntries<V> 为每次 append 分配唯一标识,值相等的条目仍彼此独立。同一轮非空 table 生命周期内,迭代器能观察到后续变化;table 被清空后,既有迭代器不再观察新的插入。两者都返回幂等且精确对应各自条目的撤销函数,共享的实现接口 EntryValues 不对外公开。

为什么值得一看
scope 包处理的其实是插件系统里一对正交关切:可见性归路由管,生命周期归所有权管。它用同一个注册上下文把两者捏合,又不让任何一方污染另一方。读操作不建层、空层才回收、撤销幂等、竞态同源,这些边界都写进类型定义与文档,而不是散落在调用方的口头约定里。对正在设计多 agent 插件体系的人来说,这四百多行源码是一门不错的公开课,入口在 packages/core/scope/src/index.ts 与 store.ts。



