DeepSeek Harness 的 Cordis scope:术语表说扁平,源码是父子链

2026-08-16

如果你要在 DeepSeek Harness 里给某个 agent 单独挂一批工具,第一个要回答的问题一定是:这批工具,它派生出来的 subagent 能不能看见?

这个问题在仓库里有两处答案,措辞对不上。本文只做一件事:把这两处答案的原文与确切位置摆出来,让你能自己打开文件核对。我们没有安装、也没有运行过这个项目,下面所有内容都是仓库文件的文字与源码结构,不是运行结论。

先交代对象状态:deepseek-ai/deepseek-harness 这个仓库建立于 2026-08-13,我们采集是 2026-08-16,前后差三天;根 package.json 的版本是 0.1.0-rc.5,GitHub 上一个 Release 都没有,README 的 Developer preview 小节(README.md:9-11)用全大写写着 THERE WILL BE COMPATIBILITY-BREAKING CHANGES.。本文提到的所有导出 API 与文件行号,都对应快照 47f9438(默认分支 master),随时可能变。

第一处:docs/glossary.md:13

docs/glossary.md 全文 45 行,是这个仓库给领域词汇定的统一口径。其中关于 scope 的那一行(docs/glossary.md:13)原文写的是:

Two levels, flat: scoped registrations do not inherit down to subagents; subtree behavior is expressed with lineage data, never scope structure.

按字面读:只有两层、是扁平的;scoped 注册不会向下继承给 subagent;子树行为要用 lineage 数据表达,绝不用 scope 结构表达。

这句话的信息量不小。它不只是描述现状,还带着一条设计约束——「never scope structure」是在告诉你别指望靠 scope 的层级去表达父子关系。

第二处:packages/core/scope/src/index.ts:31-38

packages/core/scopepackages/core/ 下 8 个子目录之一(这 8 个目录合计 109 个 .ts 文件,排除 .d.tsnode_modules/lib/dist/)。这个包本身包名是 @deepseek-ai/dsh-scope,版本同样是 0.1.0-rc.5,我们在快照里数到 8 个 .ts 文件、1,207 行——是 packages/core/ 里体量偏小的一个。

它的 src/index.ts:31-38 有一段 JSDoc,挂在一个叫 scopeParents 的表上。这段 JSDoc 说的是:一条父子关系同时驱动 scope 嵌套的两个方向——注册视图沿链向下继承(一个子 scope 能看到它祖先的 layer),事件准入沿链向上延伸(一个打了祖先标签的监听器,会收到派发给后代 key 的事件)。

配套导出的几个 API 都在同一个文件里:

导出位置我们记下的说明
bindScopeParentscope/src/index.ts:72scopeParents 父子关系配套的导出,位置如左
scopeParentOfscope/src/index.ts:89同上,位置如左
scopeChainOfscope/src/index.ts:98返回 [key, parent, grandparent, …]
linkScopeParentscope/src/index.ts:54-59带环检测

最后一行是这篇标题里那半句的来源:linkScopeParent 这一处带环检测。前两个导出我们只核到了它们的名字与所在行号,没有进一步核实各自的完整语义,所以这里不替它们展开——你要用的时候请自己打开这个文件读那几行。

这两处的形状我们照实摆着:一边是 Two levels, flat,一边是 scopeChainOf[key, parent, grandparent, …] 与一处环检测。至于哪一处更准确、为什么会这样,本文不给结论,也不拿它去评价这个项目。

两处口径的差异,说完就停

把两处并排放:

  • docs/glossary.md:13:Two levels, flat;scoped registrations do not inherit down to subagents。
  • packages/core/scope/src/index.ts:31-38:一条 scopeParents 关系驱动两个方向,注册视图沿链向下继承,事件准入沿链向上延伸;并有 scopeChainOf 这样的走链 API 与 linkScopeParent 的环检测。

两者措辞不一致。我们不推断哪一处是「对的」,也不推断为什么会这样,更不拿这个差异去评价这个项目。 以我们实读的仓库快照 47f9438 的状态为准,这两段文字就是这样写着的,你可以自己打开这两个文件的这两处对着看。

还有第三处措辞可以一并看:packages/core/scope/README.md:36(在该文件的 Known Limitations 小节,小节起始于 :33)原文写的是「A context carries one nearest scope key — the hierarchy lives in the key-level parent relation, not in context tags」,即:一个 context 只携带一个最近的 scope key,层级住在 key 级的父子关系里,而不是住在 context 标签里。这句话本身就在说「层级」和「父子关系」的存在,同时也在说明 context 标签这一层确实只有一个 key。三处的文件与行号都给在这里,你可以自己对着看,本文不替它们裁决。

顺带一说,packages/core/ 下每个包的 README 都带一个 ## Known Limitations and Deferred Work 小节,这在 packages/README.md:69 里被写成了一条制度:包 README 要么带这个小节,要么进 allowlist。所以读这个仓库的任何一个 core 包时,先翻到它 README 的这个小节,通常比从头读一遍省事。

怎么自己确认,以及别搞混的四样东西

如果你要在自己那边核这件事,动作是固定的四步,不需要装任何东西:

  1. 打开 docs/glossary.md,翻到第 13 行的 scope 词条,读完整句话,注意 flatnever scope structure 这两个措辞。
  2. 打开 packages/core/scope/src/index.ts,读 31 到 38 行那段 JSDoc,注意它说的是「一条关系、两个方向」。
  3. 在同一个文件里跳到 54-59 行看 linkScopeParent 的环检测,再看 98 行 scopeChainOf 的返回形状。
  4. 打开 packages/core/scope/README.md 的 Known Limitations 小节(第 36 行那条),看它把层级安放在哪一层。

四步做完,你会得到一个和本文一样的结论:这不是「谁写错了」的判断题,而是「同一件事在文档层与源码注释层的表述方式不同」,你在写代码时应当以你实际读到的那份源码为准。

再说四样容易和 scope 混在一起的东西,它们不是一回事:

一是 Cordis 的 ctx.isolate(name, label?)vendor/cordis/src/context.ts:121)。它做的是服务实例的隔离:在返回的 context 之下,某个服务名的读写解析到新的 label;两次 isolate() 传同一个 label 会把两个作用域合并。tutorial 第 6 章(docs/cordis-tutorial/06-composition-and-hmr.md:21)讲的也是这个用法——让一个 group 拥有某个 service 名字的独立实例,两个 group 各自看到不同配置的 shell provider 而互不影响。这是 Cordis 框架层的能力,和 harness 的 agent scope 是两套东西。

二是 ctx.tools.restrict(filter)packages/core/tools/src/index.ts:1071)。它的实现明确要求一个 scoped context(即 agent.ctx),否则直接抛错,index.ts:1072-1074 给出的理由原文是:一个 context-global 的限制会遮蔽每一个 agent。这条约束是硬编码在实现里的,你没法用一个全局 context 去调它。

三是 Cordis 订阅侧的 global 选项。 ctx.on(name, listener, options?) 声明在 vendor/cordis/src/events.ts:97,它接受的 EventOptions 定义在同一个文件的 events.ts:112,只有两个字段:prepend?: boolean(把监听器插到同名事件已有监听器之前)与 global?: boolean(无视 context filter 检查照收)。也就是说,「一个监听器能不能收到某条事件」这件事,除了前面讨论的 scope 关系,还有订阅时传的这个选项这一层。它属于 vendored Cordis 框架层,和 harness 自己的 agent scope 是两回事,讨论过滤规则时别把两层混在一起算。

四是「registry-subject 事件不走 scope 过滤」。 packages/core/tools/src/index.ts:201-204 记录了一个刻意的例外:tools/change 是「UNFILTERED registry-subject 通知,故意不做 scope 过滤派发」。也就是说,即便你在讨论 scope 的过滤规则,也不能默认所有工具相关事件都会被过滤——这一条源码里专门写了注释说明是有意为之。

本文的边界

必须把没做的事说清楚:我们没有跑过这个仓库的任何命令,没有跑 pnpm install、没有构建、没有跑测试;我们没有读完 packages/core/scope 的全部实现,只读了导出 API 与相关 JSDoc;我们也没有去读被这些文档反复引用的 .agents/notes/ 下的设计笔记,因此本文对那些笔记只做转引、不做解读。scope 的运行时行为究竟如何,本文不给任何结论——那需要真正把它跑起来,而我们没有。

最后再提醒一次前面那条限定:这个仓库处于开发者预览阶段,README 自己明写会有破坏兼容性的变更,本文引用的文件路径、行号、导出名与包版本都可能在你读到时已经变了。行号尤其脆弱,改动几行代码就会整体偏移,请按符号名去搜,别按行号硬跳。

延伸阅读


本文依据 DeepSeek Harness 官方仓库(github.com/deepseek-ai/deepseek-harness)的 README、docs/ 下的 架构与子系统文档、以及 packages/ 下的源码整理,核对日 2026-08-16,对应仓库快照 47f9438(版本 0.1.0-rc.5)。 本文内容为仓库源码与文档口径,我们没有安装、也没有运行过这个项目, 因此不涉及界面外观、操作手感与运行速度的任何描述。 该仓库建立于 2026-08-13,README 自述处于开发者预览阶段并明确说明未来会有破坏兼容性的变更, 文中出现的命令、配置与默认值随时可能变动,请以仓库最新内容为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。