ByteNoteByteNote
可见即所属:DeepSeek Harness 作用域注册
字

字节笔记本

2026年10月6日 · 约 5 分钟读完

可见即所属:DeepSeek Harness 作用域注册

API中转
¥120

DeepSeek 开源的 agent 框架 DeepSeek Harness(dsh)采用「一切皆插件」的架构,底座是 Cordis 容器,目前仍处在开发者预览阶段。当多个 agent 并存时,一个绕不开的问题随之出现:同一个注册表里挂进去的东西,哪些 agent 能看见?agent 退场时,又由谁负责拆干净?框架的 dsh-scope 包给出了一套相当克制的答案:让同一个注册上下文,同时表达每个 agent 的可见性与共享的生命周期所有权。

作用域注册:一个上下文,两份职责

一个上下文,两份职责

注册系统里最容易出现的错位,是可见性与生命周期分居两地:一条注册在 A 作用域里可见,销毁时却记在 B 作用域的账上,结果要么留下幽灵注册,要么造成资源泄漏。dsh-scope 的设计契约把这两件事焊死在一起:注册经由哪个上下文发生,就在哪个作用域可见,也在哪个作用域的生命周期里被回收。一条注册,从出生到销毁都归同一把钥匙管。

同时要记住它的边界:作用域只服务于可信的同进程插件路由,既不是沙箱,也不是权限边界。

一把不透明的钥匙

ScopeKey 的类型定义只有一个词:object。它是不透明的对象身份,只做同一性比较,原语本身从不检视对象的内容。框架的 agent 循环直接拿活跃的 Agent 对象充当自己的 key,底层机制对 key 具体是什么毫不知情,因此更低层的包也能复用这套原语,不必依赖任何上层概念。

Scoped<T> 则是一枚编译期的载体徽标。调用 scopeTarget(base, key) 会返回一个只用于路由的接收器;作用域过滤的事件声明要求以它作为 this 类型,拿裸主体去派发会直接得到编译错误。载体不暴露主体的任何属性,真正的事件主体仍作为显式参数随事件负载传递。

ts
type ScopeKey = object

type Scoped<T extends object> = object & { readonly [ScopedBrand]: T }

两条拆卸边界

Scope 对象携带三个成员:ctx、rawDispose 与 dispose()。ctx 是带标签的注册通道;rawDispose 保留 Cordis disposer 的精确函数身份,当作用域需要嵌进一个有序复合 effect 时,必须原样交出这个函数,否则按函数身份去重的 Cordis 会把它当作并发的兄弟 effect 处理;dispose() 面向普通调用方,是幂等的共享停稳边界,多个调用方竞态触发时,等待的是同一次拆卸,连底层 fiber 的惯性收尾也会一并等完。

链条:向下继承,向上放行

作用域 key 之间可以绑出一条父链。bindScopeParent 对同一个 key 只允许绑定一次,重挂必须经由返回的绑定对象完成,而且任何会闭合成环的连接都会被拒绝,无论发生在绑定还是重挂时。

这条链在两个方向上各有一条规则。注册视图沿链向下:子作用域能看到祖先层的贡献,最近的遮蔽最远的。事件准入沿链向上:派发某个 key 的事件时,标签等于该 key 或其祖先的监听者都会收到,未打标签的监听者全局放行,事件永远向上流动,绝不反向。落到实践里,agent 循环为每个活跃 agent 建立一个作用域,而 agent 预设的常驻挂载就是这些 agent 的父作用域,于是常驻组合天然能观察到它麾下每个 agent 的事件。

ScopedLayers:全局层打底,作用域层遮蔽

注册表层:打底与遮蔽

具体注册表通过 ScopeLayer 聚合自己在某一层级的完整贡献,一个层里可以同时容纳多个具名表与匿名表。ScopedLayers 拥有一个构造时立即创建的全局层,以及按 key 惰性创建的作用域层。读取永远不创建层:peek 只查精确作用域,并且刻意做成链盲,查询某个作用域自己的限制和守卫时,不会悄悄捡到祖先的版本;merge 则以全局具名条目打底,再沿链依次叠加遮蔽,最近的作用域赢下重名。

回收的粒度是整层:只有当整个 ScopeLayer 为空时,对应作用域层才会被回收,过程不会丢掉兄弟表。NamedEntries 维护插入顺序,重复名的报错交由调用方处理;AnonymousEntries 给每次追加分配唯一身份,值相等的两条注册依然彼此独立。两者的撤销函数都幂等且精确对应单个条目;迭代在同一张非空表的世代内是活的,表一旦被清空,既有迭代器便与后续插入脱钩。

写在最后

dsh-scope 的全部巧思可以压缩成一句话:注册给谁看,就由谁清理。把可见性与所有权压进同一把钥匙之后,agent 退场时一次 dispose 就能带走它的全部注册,既没有泄漏,也没有越权可见。对正在设计多 agent 框架的团队来说,这是一份值得参考的答卷;同时也别忘了它的自我声明:只有显式按作用域归档的注册表和经由载体派发的事件才真正隔离,普通服务即便经由作用域上下文调用,仍然是全局的。

相关文章

分享: