DeepSeek Harness(dsh)从零到全栈【3】核心概念地图:一篇讲透DSH全部术语

核心概念地图:一篇讲透全部术语

本章导读 :这是全系列的"字典章"。dsh 的文档和源码里有大量约定俗成的术语------plugin、bundle、profile、patch、seam、inbox、turn、step、surface......它们在 docs/glossary.zh.md 里有严格定义,但官方术语表是按字母域组织的,不适合第一次系统学习。本章把这些词汇按"七组"重新组织:框架层、组装层、agent 层、会话层、工具层、能力 seam 层、界面层。读完你不必记住每一条,但需要的是遇到陌生词时知道它属于哪一层、去哪里查。后续每一章都会回查本章。
摘要 :dsh 是一个构建在 Cordis 元框架之上的智能体运行时,其核心思想是"一切皆插件"------没有特权内核。本文按七组术语(框架层、组装层、Agent 层、会话层、工具层、能力 seam 层、界面层)系统梳理了 dsh 的完整架构:启动时由 profile 按序叠放 bundle 并应用 patch 组装出插件树;运行时由 Agent 驱动会话,经 inbox 的 next-turn/next-step 两档接收输入;会话层以仅追加的 SessionEvent 日志作为唯一真源,模型历史由 deriveMessages() 现场投影;工具调用经过守卫流水线(pre-execute → ToolGuard → execute → post-execute → result),PTC 模式让模型写程序经 run_code 调工具。事件分三域:会话事件(持久)、agent/*(观察/拦截)、能力事件(挂策略)。循环分三层:step ⊂ turn ⊂ Round。三十多个 ctx.* 键按 seam 三角色组织,替换一个提供方即可改变整个产品。

文章目录

  • 核心概念地图:一篇讲透全部术语
    • 学习目标
    • 正文
      • 一张全景图:一次对话流经了谁
      • [第一组:框架层 ------ Cordis 给了 dsh 什么](#第一组:框架层 —— Cordis 给了 dsh 什么)
      • [第二组:组装层 ------ 一棵插件树是怎么拼出来的](#第二组:组装层 —— 一棵插件树是怎么拼出来的)
      • [第三组:Agent 层 ------ 真正在干活的行为者](#第三组:Agent 层 —— 真正在干活的行为者)
      • [第四组:会话层 ------ 唯一真源](#第四组:会话层 —— 唯一真源)
      • [第五组:工具层 ------ 模型的手](#第五组:工具层 —— 模型的手)
      • [第六组:能力 seam 层 ------ 三十多个 ctx 键总表](#第六组:能力 seam 层 —— 三十多个 ctx 键总表)
      • [第七组:界面层 ------ 从浏览器看这台机器](#第七组:界面层 —— 从浏览器看这台机器)
      • 三大事件域:决定你的代码该写在哪
      • [turn / step / Round:三层循环的精确关系](#turn / step / Round:三层循环的精确关系)
    • 动手实验
      • [实验 1:打印你机器上的组装树(约 5 分钟)](#实验 1:打印你机器上的组装树(约 5 分钟))
      • [实验 2:术语溯源------从本章回到源码(约 5 分钟)](#实验 2:术语溯源——从本章回到源码(约 5 分钟))
    • 常见坑
    • 小结
    • 术语速查表
    • 参考资料

学习目标

  • 能照着本章全景图,向别人讲清"一次对话在 dsh 里流经了哪些组件"。
  • 能用一句话说清任意一条核心术语(如 bundle、inbox、seam、surface、ToolGuard)。
  • 能区分三大事件域(会话事件 / agent 事件 / 能力事件),并为每个域举出两个真实事件名。
  • 能说清 turn、step、Round 三层的包含关系,以及 Ralph 循环和 Goal Round 处在哪一层。
  • 拿到本章没覆盖的术语,知道去仓库哪个文档、哪个源码文件回查。

正文

一张全景图:一次对话流经了谁

  • 先看图。这张图里的每一个名词,本章都会逐条定义,你现在不用全看懂,读完再回来看,应该每个词都认识。
bash 复制代码
你(人类)
  │  提示词 / 斜杠命令 / 审批点击
  ▼
┌─ Client(浏览器 Web UI / CLI / 编辑器 ACP / Python SDK)──────────────────
│  Typert Remote 方法 · $events 事件流 · Conversation 会话视图 · Slots 组件
└────────────────────────────┬─────────────────────────────────────────────
                             │ Connection(RPC + 事件流)
                             ▼
┌─ Host:一棵 Cordis 插件树(没有特权内核)────────────────────────────────
│  (这棵树由 bundle/profile/patch 在启动时组合而成 ------ 第二组组装层)
│
│   ctx.commands ──┐         Agent(agent.ctx 作用域世界)
│   ctx.agents ────┼─ 创建/恢复 ─▶ id · session · inbox · status
│                  │            │  send / followup / steer / inject
│   ctx.agentLoop ─┴─ driver ───┘
│        │
│        │ turn/start
│        │   step 循环(一次模型请求 + 它引发的工具执行)
│        │     ├ ctx.systemPrompt  组装提示词片段 + 工具 schema
│        │     ├ ctx.llm           流式请求模型 → assistant/chunk*
│        │     ├ ctx.tools         守卫流水线 → tool/call → tool/result
│        │     │     ├ ctx.fs / ctx.web          文件、搜索抓取
│        │     │     ├ ctx.shell ─▶ ctx.subprocess(可被 ctx.sandbox 包装)
│        │     │     └ ctx.subagents             派生子代理
│        │     └ 每个模型可见的事实都追加进日志
│        ▼
│   ctx.sessions(Session 日志,唯一真源)
│        │ session/event 广播
│        ├─▶ ctx.sessionPersistence(JSONL / SQLite 落盘)
│        └─▶ Client:deriveMessages 投影历史,渲染回复
│      turn/end
└──────────────────────────────────────────────────────────────────────────

图里的词可以分成七组:框架层(Cordis、插件、Context、Service)是地基;组装层(bundle、profile、patch)决定启动时往树上挂什么;agent 层(Agent、inbox、driver)是干活的行为者;会话层(Session、SessionEvent)是唯一真源;工具层(Tool、ToolGuard、PTC)是模型的手;能力 seam 层(三十多个 ctx.* 键)是可替换的能力插座;界面层(Typert、slots、conversation)是浏览器那一侧。下面逐组讲。

  • ⚠️ 图是运行时快照:这张图只画"对话进行时"流经的组件,并不承载"启动之前"和"抽象原语"两件事。因此组装层(bundle、profile、patch)发生在启动时、在对话之前,不在这张图里,第二组会给它专门的全景;框架层的抽象词汇 Context、Service,以及工具层的 Tool、ToolGuard、PTC,图上也只以它们的实例出现(ctx.* 键、守卫流水线),不会逐一标名。别因为图里没看到某组就觉得它被跳过了,下面七组都会逐条讲到。
  • 先把七组在结构上摆出来,谁垫在谁下面、一次对话又从哪进哪出,先有个空间感------细节全部在后面逐组展开:

#mermaid-svg-SoLv5mv6S4cOQtCx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SoLv5mv6S4cOQtCx .error-icon{fill:#552222;}#mermaid-svg-SoLv5mv6S4cOQtCx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SoLv5mv6S4cOQtCx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx .marker.cross{stroke:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SoLv5mv6S4cOQtCx p{margin:0;}#mermaid-svg-SoLv5mv6S4cOQtCx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster-label text{fill:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster-label span{color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster-label span p{background-color:transparent;}#mermaid-svg-SoLv5mv6S4cOQtCx .label text,#mermaid-svg-SoLv5mv6S4cOQtCx span{fill:#333;color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .node rect,#mermaid-svg-SoLv5mv6S4cOQtCx .node circle,#mermaid-svg-SoLv5mv6S4cOQtCx .node ellipse,#mermaid-svg-SoLv5mv6S4cOQtCx .node polygon,#mermaid-svg-SoLv5mv6S4cOQtCx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .rough-node .label text,#mermaid-svg-SoLv5mv6S4cOQtCx .node .label text,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape .label,#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape .label{text-anchor:middle;}#mermaid-svg-SoLv5mv6S4cOQtCx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .rough-node .label,#mermaid-svg-SoLv5mv6S4cOQtCx .node .label,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape .label,#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape .label{text-align:center;}#mermaid-svg-SoLv5mv6S4cOQtCx .node.clickable{cursor:pointer;}#mermaid-svg-SoLv5mv6S4cOQtCx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx .arrowheadPath{fill:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SoLv5mv6S4cOQtCx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SoLv5mv6S4cOQtCx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SoLv5mv6S4cOQtCx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SoLv5mv6S4cOQtCx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SoLv5mv6S4cOQtCx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster text{fill:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster span{color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SoLv5mv6S4cOQtCx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx rect.text{fill:none;stroke-width:0;}#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape p,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape .label rect,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SoLv5mv6S4cOQtCx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SoLv5mv6S4cOQtCx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SoLv5mv6S4cOQtCx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-SoLv5mv6S4cOQtCx .root>*{fill:#444441!important;stroke:#B4B2A9!important;color:#D3D1C7!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .root span{fill:#444441!important;stroke:#B4B2A9!important;color:#D3D1C7!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .root tspan{fill:#D3D1C7!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .ui>*{fill:#712B13!important;stroke:#F0997B!important;color:#F5C4B3!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .ui span{fill:#712B13!important;stroke:#F0997B!important;color:#F5C4B3!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .ui tspan{fill:#F5C4B3!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .core>*{fill:#085041!important;stroke:#5DCAA5!important;color:#9FE1CB!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .core span{fill:#085041!important;stroke:#5DCAA5!important;color:#9FE1CB!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .core tspan{fill:#9FE1CB!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .seam>*{fill:#633806!important;stroke:#FAC775!important;color:#FAC775!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .seam span{fill:#633806!important;stroke:#FAC775!important;color:#FAC775!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .seam tspan{fill:#FAC775!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .assembly>*{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .assembly span{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .assembly tspan{fill:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .foundation>*{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .foundation span{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .foundation tspan{fill:#B5D4F4!important;} 你(人类)
⑦ 界面层 · 浏览器那一侧

Typert · slots · conversation
Connection

RPC + 事件流
③ agent 层 · 干活的行为者

Agent · inbox · driver
④ 会话层 · 唯一真源

Session · SessionEvent
⑤ 工具层 · 模型的手

Tool · ToolGuard · PTC
⑥ 能力 seam 层 · 可替换插座

30+ 个 ctx.* 键(llm · fs · shell · sandbox · approval · subagents ...)
② 组装层 · 启动时挂树

bundle · profile · patch / cordis.yml / overlay
① 框架层 · 地基

Cordis · plugin · Context · Service

  • 颜色图例:蓝 = 装配 (框架层、组装层------启动时搭起来的结构);绿 = 运行核心 (agent / 会话 / 工具------对话进行时真正在动的三样);橙 = 可替换插座 (能力 seam 层------挂在树上的 ctx.* 键);红 = 界面 (浏览器那一侧);灰 = 入口连接(你与 Connection)。
  • 虚线关系省略:框架层是①到⑥全部的地基,组装层②把整棵树挂好,⑥的接线板就承载③④⑤三种运行服务,而⑦在对话进行时经 Connection 与③联通。

第一组:框架层 ------ Cordis 给了 dsh 什么

这层是干什么的:dsh 没有自己发明插件系统。它站在 Cordis 这个元框架之上,所有上层词汇(服务、事件、生命周期)的语义都来自这里。理解这层,你就理解了为什么 dsh 敢说"一切皆插件"。

  • Cordis --- dsh 底层的元框架:插件向共享上下文贡献服务、类型化事件和可逆的副作用。(docs/architecture.zh.md 第 1 节);系统讲解见 docs/cordis-primer.zh.mddocs/cordis-tutorial/。dsh 以 vendored 方式内置它并更名为 @deepseek-ai/cordis。例:你写的第一个插件,本质就是"向 Cordis 树挂载"。
  • 插件(plugin) --- 产品的最小组成单元。在 dsh 里,模型适配器、工具注册表、会话日志、agent loop(智能体循环)本身全都是插件,因此每一个都可以从配置替换;不存在需要打补丁的特权内核.(docs/architecture.zh.md 第 1 节原话)。例:把 bash-local 换成 bash-sandbox 提供方,整个产品的命令执行行为就变了。
  • Context(上下文) --- 插件共享的服务容器与事件总线,ctx 是它的惯用变量名。出处:docs/cordis-primer.zh.md。dsh 里每个 Agent 还有一个自己的 agent.ctx(见第三组)。例:ctx.tools 就是挂在 Context 上的一个服务键。
  • Service(服务) --- 挂在 Context 上、以 ctx.<key> 为键的可调用对象。声明服务的包是"服务定义方",实现它的包是"提供方"。出处:docs/capability-seams.zh.md 总表(该表由 scripts/gen-doc-graphs.ts 从源码生成)。例:ctx.shellpackages/shell/shell 声明的服务。
  • inject(注入) --- 插件声明"我依赖哪些服务"的方式,框架据此决定装载顺序,并在被依赖插件卸载时先撤走依赖方。(docs/cordis-tutorial/)。例:从能力图能直接看到 tool-bash 直接消费 ctx.shellctx.approvalctx.jobsctx.shellEnv
  • effect(副作用)与 Fiber --- effect 是插件声明"我贡献了什么"的可逆副作用(注册服务、监听事件),插件卸载时按登记顺序自动撤销;fiber 是承载插件生命周期与初始化异步流的执行单元。出处:docs/cordis-primer.zh.md;源码见 packages/core/agent/src/index.tsregister 的 JSDoc------"配置创建的 agent 归 loop fiber 所有"、setFactory 返回"精确的 Cordis effect disposer"。例:插件里 ctx.effect(() => { 注册服务; return 卸载函数 }),卸载函数在插件移除时被自动调用。
  • HMR(hot module reload,热重载) --- 运行中的进程不重启就应用配置变更、替换插件的机制。dsh 里它具体表现为 patch 实时重载:自定义 profile 默认实时重载 patch,随附的 web profile 开启,而 headlesssdksdk-minimalacp 只在启动时应用一次(一次性或 stdio 应用替换依赖会破坏生命周期)。(docs/architecture.zh.md"Profile 与组合包"节)。例:改一行 cordis.patch.yml 保存,运行中的 web 服务随即更新。

⚠️ 常见误区:把 Cordis 理解成"一个普通的依赖注入库"。它对组件有硬约束------副作用必须可逆、组件移除时其效果必须被完全回退(时间维可组合性)、依赖必须结构化声明(空间维可组合性)。dsh 的每个子系统都按这套约束设计,这才是"替换一个提供方就能改变整个产品"能成立的前提。理论出处是设计论文《A Programming Paradigm for Spatiotemporal Composability》。

第二组:组装层 ------ 一棵插件树是怎么拼出来的

这层是干什么的:回答"启动一个 dsh 进程时,到底有哪些插件、按什么顺序、带什么配置被挂进树里"。你以后想替换任何内置能力,都是在这层动手。

  • bundle(组合包) --- Cordis 配置项及其挂载代码的分发格式;一个 bundle 在自己 package.jsondsh.bundle 字段里指向自己的 patch 文件,因此它插入的内容始终可被其上各层 patch。例:dsh-base 就是一个 bundle。
  • profile --- 存放在 Harness home 中的具名组装:它列出自己叠放哪些 bundle、安装了哪些树外插件、并保存用户自己的 cordis.patch.yml。随发行版交付五个:webheadlesssdksdk-minimalacp。各 bundle 详见 packages/bundle/*/README.zh.md。例:dsh --profile headless 启动的是不带服务器的一次性运行器。
  • dsh-base --- webheadlesssdkacp 四个 profile 共享的第一层组合包:模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥。例:sdk profile = dsh-base + dsh-sdk-app(一个 JSON-RPC 服务器),所以 SDK 形态也有完整的工具与审批能力。
  • dsh-sdk-minimal --- 刻意保留的例外:一个拥有完整显式 SDK 配置树的独立组合包,不应用 dsh-base,适合想把依赖面压到最小的嵌入场景。例:Python SDK 的极简示例默认选它。
  • patch(cordis.patch.yml --- 覆盖文件:按 id 定位某个配置条目并替换其整个 config,或插入新条目。各层按顺序应用:profile 列出的每个 bundle → profile 自己的 patch → home 级那份 → 任意 --patch overlay。例:想替换内置某个工具的配置,不改源码,写一份 patch 即可。
  • cordis.yml --- Cordis 的声明式配置文件,bundle 与 profile 用它表达插件树;agent preset 本质上也是一份 cordis.yml------挂载到 agent 作用域之下。docs/capability-seams.zh.mdctx.agentPresets 一行:"把一份 preset cordis.yml 挂载到 agent 作用域之下"。例:你给某个团队写的 agent preset,就是"一个目录 + 一份 cordis.yml"。
  • loader(装载器)与应用启动器 --- 把 bundle/profile/patch 解析成运行时插件树的机制。dsh 规定所有受支持的 Node 应用都从 dsh CLI 与具名 profile 启动:dsh web--profile web 的刻意别名,其余是 dsh --profile headless 等。例:Python SDK 的运行时 wheel 打包的就是这个普通 dsh CLI,客户端默认以 dsh --profile sdk 启动。
  • overlay --- 通过 --patch 命令行参数传入的最后一层覆盖,排在 home 级 patch 之后应用。出处:docs/architecture.zh.md。例:临时实验一个树外插件时用 --patch 指过去,不落盘、不污染 profile。

💡 深挖dsh --profile web --dump-config 会打印机器启动时的完整组装树。官方原话是:"它打印出的任何条目,都可以由你自己的 patch 替换。"这条命令是组装层最好的教具。

第三组:Agent 层 ------ 真正在干活的行为者

这层是干什么的 :定义"谁在替你跑这个循环",输入怎么排队,以及你如何在它跑动时观察、拦截、纠偏。权威文档是 docs/subsystems/core.zh.md(agent / agent-loop 两个包的专属页)。

  • Agent --- 面向编程的活跃智能体句柄。接口定义在 packages/core/agent/src/types.tsid(与其会话共享的 SessionId)、options(provider/model/reasoningEffort/maxTokens)、session(它驱动的会话)、inboxstatusctx(agent 作用域上下文),方法有 sendcancelwhenIdlerunMaintenancefollowupsteerinject。例:Web UI 里每一次对话背后,都是一个 Agent 在驱动。
  • AgentHandle --- 编程式创建/恢复 Agent 时返回的"所有者凭证":{ agent, dispose() }。dispose 是一种能力(capability):在所有消费方中,只有持有者能拆掉这个 agent。例:进程内 subagent 驱动器创建子 agent 后,就持有它的 handle。
  • ctx.agents(AgentRegistry) --- 活跃 Agent 注册表:create()(新建会话 + agent)、resume()(从持久会话恢复)、get(id)list()roots();真正负责"造"的工厂由循环经 setFactory() 注册,所以消费方不依赖具体循环包。例:编辑器集成(ACP)经 ctx.agents 驱动 agent 并从 session/event 渲染(架构文档"新行为的归属位置"表原话)。
  • agent loop(智能体循环)/ driver --- dsh-agent-loop 是公开 Agent 约定的唯一具体实现包:driver 认领一条排队的提示词,在会话日志上开启轮次,经 system-prompt 组装请求前缀并从日志派生历史,经 LLM seam 流式获取响应,经工具注册表分发工具调用,把每个模型可见的事实追加回日志。例:扩展插件依赖 dsh-agent 而绝不直接依赖 dsh-agent-loop------因此循环保持可替换。
  • inbox(收件箱) --- agent 以持久投影形式拥有的两条 有序待处理消息列表。投递目标只有两档:type InboxTarget = 'next-turn' | 'next-step'packages/core/agent/src/types.ts 第 29 行)。认领(claim)时取全部 next-step 输入,外加轮次边界上的一条 next-turn 消息。持久变更事件有 agent/inbox/insertedagent/inbox/claimedagent/inbox/discarded 与整体 splice 记录。
  • followup(后续轮次) --- agent.followup(message):排队一个普通后续轮次并唤醒 driver,该消息成为它自己那一轮的唯一普通消息;不返回句柄,其 MessageId 标识的是持久的 inbox 插入事实。例:你在输入框发出一条新消息、agent 正忙时,走的正是这种排队语义。
  • steer(中途引导) --- agent.steer(message):为最近的步骤提交引导。空闲 driver 会开一个新轮次;运行中的 driver 在下一个 step 边界消费它。被拒绝的步骤会让 steering 留在 inbox 里等下一次唤醒。例:任务跑偏时插话"先别动 tests 目录"。
  • inject(注入上下文) --- agent.inject(message):把模型可见上下文排队到下一次 pre-step,不唤醒 driver;空闲 driver 会留着它,直到 followup 或 steering 把 driver 弄醒。例:agent/session-start 钩子里用它种入项目背景。
  • AgentStatus 与 cancel --- 生命周期状态只有两个值:type AgentStatus = 'idle' | 'running'running 描述整个 driver 的排空区间,可能横跨连续多个轮次,不代表某个轮次还开着。cancel(cause, options?) 的原因有四种:user / parent / hook / disposedAgentCancelCause);keepInbox 选项可保留待办工作。例:Web UI 上的"停止"按钮就是 user 取消。
  • agent/pre-step --- 请求推导前唯一的 waterfall(瀑布式)监听链:每个监听器可以拒绝 本步骤(reject)或改写 进入步骤的消息批次(enter + messages,还可设 startsRequestSeries 开启独立的模型消息序列)。首次领取被拒绝或改写为空时,仍会关闭一个不含步骤的持久轮次------日志会记录这次尝试。例:计划模式(plan mode)的"只规划不动手"就是靠 pre-step 拦截改写实现的。
  • agent/turn-stopping --- 轮次即将关闭时运行的 serial 事件(没有 next());监听器想续命就调用 agent.steer(...),机器会重读 inbox:有新鲜 steering 就再来一步,没有才关轮。数据说了算,监听器顺序无法改变结果。例:goal 领域在轮次末尾判断是否续跑,走的就是这个点。
  • scope 与 agent.ctx --- 按 agent 划分的注册单位:一项贡献(工具、提示词段、变量、监听器)要么全局、要么归属恰好一个 scope;只有两层,扁平结构,不向下继承给 subagent。经 agent.ctx 注册的内容生命周期绑定该 agent,卸载即回滚。同名的带作用域注册会遮蔽(shadowing)全局项------最具体者胜出。例:agent preset 把一份 cordis.yml 挂到 agent.ctx 之下,让这个 agent 拥有专属工具集。

⚠️ 常见误区agent/statusrunning ≠ "当前轮次还开着"。status 描述的是整个 driver 的排空区间;要知道轮次边界,看持久会话事件 turn/start / turn/end,它们是会话事件,不是 agent emit(docs/subsystems/core.zh.md 原话:"轮次和步骤边界是持久会话事件,而不是 agent emit")。

第四组:会话层 ------ 唯一真源

这层是干什么的 :定义"事实记在哪里、以什么形状记"。dsh 的一切可回放、可恢复、可审计,都建立在这层之上。权威文档是 docs/subsystems/session.zh.md

  • Session --- 一份类型化 SessionEvent仅追加日志 (append-only log),模型所见上下文的唯一真源;服务键 ctx.sessionspackages/core/session)。模型历史不是单独存储的------它从日志派生。例:fork、恢复、transcript(文本记录)、遥测和持久化全部派生自这条事件流。
  • SessionEvent(信封) --- 日志条目的形状:单调递增的 seq、时间 time、按 type 判别的 data payload;surface 变体还带两个条件字段------sourceEventSeqs(本事件引用了哪些较早事件)与 surfaceOp。例:一条 assistant/messagesourceEventSeqs 指向它所聚合的每条 assistant/chunksourceEventSeqs: [] 则表示"提供方流已知且完整地为空"。
  • 事件溯源(event sourcing) --- 一条运行时不变量:"模型可见即已记录 "------抵达模型请求的一切都必须能从日志重建;因此新增一项模型可见输入,就必须新增一个会话事件(扩展 SessionEventMap 并从日志渲染)。例:压缩(compaction)子系统加的正是 compaction/start / compaction/summary / compaction/end 三个新事件类型,而不是往消息里塞私货。
  • deriveMessages() --- 从日志投影出模型历史的函数。原始 assistant/chunk 事件则保证回放和 UI 保真。例:恢复一个旧会话时,发给模型的历史是现场从 JSONL 日志投影出来的,没有第二份存储。
  • surface 事件 --- 面向"表层"(用户可见表面)的事件变体,携带 surfaceOp 并用 sourceEventSeqs 引用源事件。三大 surface 事件:user/messageassistant/messagetool/result。例:UI 渲染一条完整回复读的是 assistant/message,而不是逐个 chunk 拼。
  • 十二种核心会话事件 --- turn/startturn/endstep/startstep/enduser/messageassistant/chunkassistant/messagetool/calltool/resultrequest/headerrequest/contextsession/end-seed。例:每次成功的模型调用都会写一条 request/headeragent/pre-step 的 enter 决策设置 startsRequestSeries 开启独立消息序列时,loop 记录的新 header 原因为 series,若封装同时变化则为携带 startsSeries: truechange
  • session/event 与持久化 --- 日志经 session/event 广播给所有消费方;持久化插件订阅它,并在 session/flush 检查点或 dispose 时落盘。后端二选一:session-persistence-jsonlsession-persistence-sqlitectx.sessionPersistence seam),两者持久化同一套 SessionEvent 词汇。例:rc.8 版本把存储换成 SQLite 时发生过不兼容变更------所以升级前备份 $DSH_HOME
  • fork(分叉) --- ctx.sessions.fork(source, boundary?, childSessionId?):从源会话的某个边界派生一个子会话;创建 agent 时可携带可选的 seed 回放前缀。例:从第 10 轮分叉出去试另一个方案,原会话一个字都不动。
  • lineage(谱系) --- 以数据形式携带的父子关系事实:parentSession、持久的 delegationDepth(委派深度)、运行时的 subagentDepth;它从不影响可见性------可见性由 scope 决定。例:子代理会话的元数据里记录着它的父会话与委派深度。
  • 品牌化 ID(Branded ID) --- 在包之间传递的 ID(SessionIdToolCallIdJobId......)结构上是字符串,但类型层面不可互换------不能把 SessionId 传给需要 ToolCallId 的位置。原语 Branded<B> 在独立纯类型包 packages/util/brand/src/index.ts。例:这是全仓通用的两个类型模式之一(另一个是 ...Map → derived-union 声明合并模式,见 docs/subsystems/core.zh.md 末节)。

💡 深挖SessionEventMap 的扩展方式是 TypeScript 声明合并(declaration merging)------插件不改 dsh-session 的源码就能添加新事件类型。docs/subsystems/core.zh.md 给出的五个规范 map:ContentBlockMapMessageSourceMapFinishReasonMap(属 dsh-llm)、TurnEndReasonMapSessionEventMap(属 dsh-session)。这五个 map 是插件作者最常扩展的类型面(第 14 章起会反复用到)。

第五组:工具层 ------ 模型的手

这层是干什么的:定义模型能"做"什么、每一次"做"要过哪些关卡。

  • 权威文档:docs/subsystems/tools.zh.mddocs/tool-execution-pipeline.zh.md;真实工具清单见 docs/tool-catalog.zh.md
  • Tool / ToolDefinition --- 每个已注册工具"是什么":一个面向模型的 ToolSchema + 一个 execute 函数 + 可选的最终内容回调与 UI 回调。工具作者很少手动构造它------defineTool DSL 会用类型化参数构建。例:dsh-tool-fs 注册的 read / write / edit / read_image 四个工具(docs/tool-catalog.zh.md)。
  • 出处:docs/subsystems/core.zh.md ToolDefinition 节 + docs/subsystems/tools.zh.md
  • 工具注册表(ctx.tools --- 作用域化的工具注册表 + 带把关的执行流水线。一次调用依次经过:策略前处理(tools/pre-execute)→ 单调守卫(ToolGuard)→ 环绕分派(tools/execute)→ 策略后处理(tools/post-execute)→ 最终结果观测(tools/result)。
  • 出处:docs/capability-seams.zh.md ctx.tools 行 + docs/tool-execution-pipeline.zh.md
  • restriction(限制) --- tools.restrict 为单个 scope 过滤全局工具集合,多个 restriction 取交集组合;被过滤掉的全局工具既不出现在提示词中、也拒绝执行,与不存在的工具无法区分。例:给子代理加 restriction,只留搜索与读取两类工具。

出处:docs/glossary.zh.md agent-scope 节。

  • approval(审批) --- ctx.approval(审批 seam):一次性权限决策通过 approval/request waterfall 事件分派,回答方是监听器(Web UI、ACP 桥接等);没有回答方时以 unavailable 失败关闭。出处:docs/capability-seams.zh.md ctx.approval 行。例:你在 Web UI 看到的"允许执行这条命令?"弹窗,就是一个回答方在应答 waterfall。
  • ToolGuard(单调守卫) --- 感知作用域的最终预分派策略,在每次 tools/pre-execute 之后求值:type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined。返回类型故意不含"allow"------undefined 表示保留前面的决策,返回 reason 则只能缩减权限,后续监听器无法撤销。例:给某 agent 挂一个 guard,工作区之外的路径一律返回拒绝 reason。
  • 出处:docs/subsystems/tools.zh.md(源码 packages/core/tools/src/index.ts)。
  • sandbox / sandboxPolicy --- ctx.sandbox(进程沙箱 seam):消费方交出即将 spawn 的确切 argv,与宿主共享文件系统和内核的后端按每次调用的策略包装该 argv 并报告强制执行情况。ctx.sandboxPolicy 统一保存部署默认模式与工作区根目录------bash 与 fs 读的是同一份策略,不会限制到不同的根目录 。例:开启沙箱后,bash 走 bash-sandbox、文件变更走 fs-sandbox,共享同一模式与根目录(第 26 章专讲)。
  • 出处:docs/capability-seams.zh.md 两行。
  • PTC(programmatic tool calls,程序化工具调用) --- 工具的三种呈现模式:export type ToolPresentationMode = 'native' | 'ptc' | 'both'docs/config-catalog.zh.md)。native(默认)把每个可见工具的 schema 都发给模型;ptc 只发保留工具 run_code 加一段生成的 SDK 提示词,模型写一段程序、经 binding 调用工具,子调用按并发约定调度并重新进入完整且受守卫保护的工具流水线both 两者都发。例:工具表很大时,ptc 让模型每轮只看"一个工具 + 一份 SDK 文档"。
  • 出处:docs/tool-catalog.zh.md run_code 行。
  • run_code --- PTC 模式下的保留传输工具:由工具注册表所有、处于可过滤能力层之外(你不能用 restriction 把它过滤掉再指望 PTC 还能用);执行时消费 ctx.codeRuntime;每个桥接子调用记录一对 tool/code-dispatch-start + tool/code-dispatch 事件,另有 tools/ptc-dispatch-log waterfall 可改写持久事件所存副本。例:模型在一段 run_code 程序里循环调用 grep 二十次------一次模型请求完成,二十个子调用全走守卫流水线。
  • 出处:docs/tool-catalog.zh.md + docs/subsystems/tools.zh.md
    ⚠️ 常见误区 :PTC 不是"绕过安全"的后门。子调用会重入守卫流水线、记录持久事件、受 maxParallelSubCalls 并发上限约束------以上全部出自 docs/tool-catalog.zh.md run_code 行的原文。改名史:v0.1.2 之前这套机制叫 Code Mode(第 01 章提过更名)。

第六组:能力 seam 层 ------ 三十多个 ctx 键总表

  • 这层是干什么的 :这是 dsh 的"能力插座板"。每一项能力(执行命令、读写文件、接模型、派子代理)都被拆成三角色的 seam(接缝):Service Definition (服务定义,拥有自己的 ctx.<key>)+ 一个或多个 Service Provider (提供方实现)+ 一个或多个 Consumer(消费方)。seam 是完整能力,绝不单指其中一个角色。

  • 规范范例是 packages/shelldsh-shell 是 Service Definition;dsh-bash-local / dsh-bash-sandbox 是提供方;dsh-tool-bash 是消费方。角色需要独立演进时通常放在不同包;同一关注点也可以合并进一个包(dsh-user-approval 在同一个包里既当 approval seam 的定义又当实现)。

  • 出处:docs/glossary.zh.md capability-seam 节 + docs/capability-seams.zh.md

先看主干五个"非 seam"核心服务(角色列标注为 core),再上总表:

服务键 一句话定义 所在包 典型消费方
ctx.sessions 仅追加的 Session 日志与内存 store,唯一真源 core/session agent-loop、agent、持久化、查询
ctx.systemPrompt 每步骤收集提示词片段与工具 schema 的组装注册表 core/system-prompt agent-loop、各 tool-*
ctx.tools 作用域化工具注册表 + 守卫执行流水线 core/tools agent-loop、各 tool-*
ctx.agents 活跃 Agent 注册表 + 创建/恢复工厂 core/agent agent-loop、acp、subagent 驱动器
ctx.agentLoop 唯一的具体循环驱动器(角色标注为 bundle) core/agent-loop 示例包 agent-spine-demo

下面是按字母序排列的常用服务总表(角色:seam 表示可替换接缝)。每条的权威出处都是 docs/capability-seams.zh.md 的总表(由脚本从源码生成,含完整提供方/消费方清单);包路径均相对 packages/

服务键 一句话定义 提供方(实现包) 典型消费方
ctx.llm LLM 适配器注册表:注册提供方,循环与压缩调用提供方无关的流服务 llm/llm-deepseekllm/llm-pi-aillm-replay agent-loop、compaction-basic
ctx.fs 文件系统提供方 seam:读写编辑都经它 fs/fs-localfs/fs-sandboxfs/fs-e2b tool-fs(配套 fs-observation-policy 事件门禁)
ctx.shell Bash 执行器 seam:面向模型的 shell 工具背后的执行世界 shell/bash-localshell/bash-sandboxshell/pwsh-local tool-bash、tool-pwsh、hooks 桥接
ctx.subprocess 子进程 spawn seam:进程坐标、进程树生命周期、kill 升级 subprocess/subprocess-localsubprocess-e2b bash 执行器、PTY、LSP、进程外 subagent 后端
ctx.sandbox 进程沙箱 seam:按每次调用的策略包装即将 spawn 的 argv sandbox/sandbox-local bash-sandbox、terminal-bash
ctx.sandboxPolicy 沙箱策略 home:部署默认模式 + 工作区根目录 sandbox/sandbox-policy bash-sandbox、fs-sandbox、terminal-bash
ctx.approval 审批 seam:一次性权限决策经 approval/request waterfall 分派 interaction/user-approval tools、tool-bash、acp
ctx.codeRuntime 代码执行 seam:用 Host 提供的绑定运行模型写的程序 code-runtime/code-runtime-worker-thread tools(PTC mode 下消费)
ctx.compaction 压缩 seam:上下文压力过大时摘要折叠历史 compaction/compaction-basic ---(无面向模型的压缩工具)
ctx.subagents 子代理提供方与延续服务:同接口后差异极大的委派传输 subagent/spawn-in-processfork-in-processacpcodexclaude-codedsh-sdk tool-subagent、tool-subagent-control、tool-ralph
ctx.skills Skill(技能)目录注册表:合并各提供方的技能目录 skill/skill-filesystemskill-badge tool-skill
ctx.commands 人类命令注册表:注册直接面向人的命令,不发送给模型 interaction/commands 各 UI 适配器与命令插件
ctx.goals 同会话目标领域:从日志折叠带修订版本的目标状态 goal/goal /goal 命令、goal 工具
ctx.jobs 后台任务注册表:登记正在运行的后台工作 jobs/jobs-local tool-bash、tool-terminal、tool-jobs 等
ctx.web Web 访问提供方注册表:搜索与抓取注册到同一 seam web/web-search-exaweb-search-perplexityweb-search-deepseekweb-fetch-http tool-web
ctx.spillStore 溢出存储 seam:保存过大的工具文本,返回定位与取回提示 spill/spill-local spill-policy(tools/post-execute 消费方)
ctx.terminals 持久 PTY 会话注册表:精确到 Agent 的会话身份与清理 terminal/terminal-bash tool-terminal
ctx.lsp 语言服务器导航 seam:恰好四种标准化操作 lsp/lsp-stdio tool-lsp
ctx.storage 非会话存储枢纽:领域数据挂载为类型化持久状态 storage/storage-jsonstorage-sqlite storage-domain
ctx.settings 用户设置 seam:插件注册命名空间 schema,提供方存原始文档 settings/settings-file settings 控制器、LLM 适配器
ctx.credentials 凭据 seam:配置持引用,提供方持实际值,按操作解析 credentials/credentials-local settings 控制器、LLM 适配器
ctx.workspaceRegistry 工作区实体注册表(带 WorkspaceId 品牌类型) workspace/workspace workspace/session 控制器
ctx.sessionPersistence 会话持久化 seam:各后端持久化同一套 SessionEvent 词汇 session/session-persistence-jsonlsession-persistence-sqlite agent-loop、session-query、tool-bash 等
ctx.sessionQuery 会话读取 seam:精确读取、过滤、追踪、搜索 session-query/session-query-sqlite tool-session-query、session-reference
ctx.typert 运行时类型注册表:插件注册实时 zod 贡献 typert/typert-registry typert-loader、api-gateway
ctx.typertGateway Typert Host 调用网关:Remote 描述符 ↔ Cordis 服务 api/api-gateway Client 调用
ctx.webServer HTTP 路由注册:普通 node:http 载体 + 静态回退 host/host-webserver client-connection、client-modules、client-hmr
ctx.planMode 计划协作状态:折叠计划/模式状态,注册 /plan plan/plan-mode UI 与 pre-step 策略
ctx.userQuestions 人类问答 seam:ask() promise 上暂停工具调用等人回答 interaction/user-questions tool-ask-user
ctx.permissionPresets 权限预设表:workspace-write / danger-full-access 组合沙箱与审批选项 interaction/permission-presets permission/preset 事件
ctx.agentDefaultModel 默认模型选择:经 settings 分层默认 ModelSelection core/agent-default-model session 控制器、headless
ctx.webhookRuntime Webhook 规则运行时:分派已认证交付并转为 Workspace Session webhook/webhook webhook-github

三个精讲条目,帮你把"三角色"落到具体:

  1. ctx.shell 是 seam 的教科书 。定义在 packages/shell/shellbash-localbash-sandbox 都实现它;tool-bash 只消费它。所以把 bash-local 换成 bash-sandboxtool-bash 一行不用改------这正是 docs/architecture.zh.md"seam 正是替换一个提供方就能改变整个产品的原因"这句话的出处场景。
  2. ctx.sandbox + ctx.sandboxPolicy 是成对工作的。sandbox 是"怎么做"(包装 argv),sandboxPolicy 是"按什么规则做"(模式与根目录)。两者都只被沙箱执行器和提供方读取,所以 bash 与 fs 永远限制到同一个根目录。
  3. ctx.approval 的回答方是监听器 。审批不是"调用一个函数",而是发一个 approval/request waterfall 事件等监听器应答;ACP 集成为自己的 agent 提供桥接回答方,没有回答方时以 unavailable 失败关闭------这是理解第ACP 集成的钥匙。

第七组:界面层 ------ 从浏览器看这台机器

这层是干什么的 :Web Client 是一个由独立加载插件组装而成的浏览器侧 Cordis 应用(docs/subsystems/web-client.zh.md 原话)。它有四个可复用底座:Client Modules、API Gateway、Slots、Conversation。

  • Typert --- dsh 的运行时类型体系:ctx.typerttypert/typert-registry)是运行时类型注册表,插件直接或经 dsh-typert-loader 注册实时 zod 贡献;构建期则生成 Host/Client 双侧的严格约定。例:Client 调用的参数类型不是手写的,是构建期从 Host 源码生成的。
  • 出处:docs/capability-seams.zh.md + docs/api-gateway.zh.md
  • API Gateway(ctx.typertGateway --- 把生成的 Remote 描述符与实时 Cordis 服务关联、解析已注册身份、并经共享 Connection RPC 载体提供一元调用的网关(api/api-gateway)。例:浏览器里的一次 ctx.remote.goals.create(...) 调用,最终落到 Host 上真正的 Cordis 服务方法。
  • 出处:docs/api-gateway.zh.md
  • @Remote / @RemoteScope --- 标记对 Client 开放的方法的装饰器:@Remote 表示调用根 Host Context 中注册的 Cordis 服务;@RemoteScope(key) 表示先解析到一个作用域 Context 再取服务调用。未标记的方法不会进入生成的 Client 类型,也不能通过 ctx.remote 调用 。例:GoalService 类上的 @Remote('create') 方法。
  • 出处:docs/api-gateway.zh.md
  • ctx.remote.<namespace> --- Client 侧的调用入口:直接调用落在 ctx.remote.<namespace>,作用域调用落在 agentCtx.remote.<namespace>ctx.remote.$on() 接收事件,ctx.remote.$mount() 挂载贡献。例:文档原例 await ctx.remote.goals.create(agentId, { objective: 'ship it' })
  • 出处:docs/api-gateway.zh.md
  • client-modules(ctx.clientModules --- Client 插件图宿主:通过增量 dsh.client 扫描组合出 __DSH_BOOT__ 入口图,提供插件组合包,并通知重建/图变更订阅方(client/client-modules)。例:你的插件带了浏览器侧代码,就是经它进入 Client 的。
  • 出处:docs/capability-seams.zh.md
  • window.__DSH_BOOT__ --- Host 把组合后的 WebBootGraph 写入这个浏览器全局变量,并在 parser-preloaded script 执行前安装浏览器 module-loader facade;模块系统是一张 lazy CommonJS 表------加载 bundle 只注册 factory,materialize entry 时才同步 require 运行。例:第 28 章你会在 DevTools 里亲眼看这个启动图对象。
  • 出处:docs/subsystems/web-client.zh.md
  • slots --- Web Client 的 UI 组合系统:ui-slots 提供类型化 registry 与 lifecycle ledger,声明扩展位置、推导组件 props、把 observable 绑定成 React hook、挂载最终组件树。例:插件往设置页加一张卡片、往会话流加一种消息渲染,走的都是 slot。
  • 出处:docs/subsystems/web-client.zh.md + docs/subsystems/slots.zh.md
  • conversation --- 把 Session 历史窗口变成各 target 自有视图的子系统:event registry 把标准会话事件与 Client-only 的 chunkrow/* 历史 event 关联成稳定的业务 Context,view registry 再 materialize target snapshot;Chat Assistant、Trajectory Assistant 和 Turn Tail 是解释 packed run 的三个内建 Definition。例:你在界面上看到的"一条完整助手回复",其实是 conversation 把几十条 chunk 事件打包解释后的视图。
  • 出处:docs/subsystems/web-client.zh.md + docs/subsystems/conversation.zh.md
  • $events --- API Remotes 的内部逻辑事件流,也是 Connection 的 generation source:开场的 ready frame 携带 Host home 并建立 generation。allowlist 内的普通事件交付给根 Client Context,scoped waterfall 事件交付给已解析的 Session Context(经 ctx.remote.$on();waterfall 监听器可返回结果、调用 next() 或拒绝)。例:Host 上的审批 waterfall 事件一路送到浏览器弹窗,走的就是这条通道------"Host Cordis waterfall → API Remotes $events → Session Context 上的 ctx.remote.$on()"。
  • 出处:docs/subsystems/web-client.zh.md

三大事件域:决定你的代码该写在哪

docs/architecture.zh.md 有一句重要论断:"事件就是扩展点,而选对事件域是大多数改动的第一个决定。"三大事件域各管一件事:

事件域 是什么 用在什么时候 真实事件名举例
会话事件 追加到日志、经 session/event 广播的持久事实 当某个事实必须在重新加载后仍然存在 user/messagetool/result(另有 turn/endassistant/chunk 等)
Agent 事件(agent/* 携带活跃 Agent实时扩展点:inbox、步骤、状态、请求、续跑 观察或拦截进行中的工作 agent/pre-step(waterfall,可拒绝/改写步骤)、agent/turn-stopping(serial,轮次收口前)
能力事件 向某个 seam 附加策略与适配器的事件,无需导入循环 给文件系统、工具执行、遥测挂策略 fs/write-intent(waterfall,写前意图)、tools/pre-execute(waterfall);遥测域如 session-telemetry/record

两个判别技巧:

  1. 要不要"重启后还在"? 要,就是会话事件;不要,才是后两个域。把该持久的东西挂在 agent 事件上,进程一重启就没了。
  2. waterfall 还是 serial? waterfall 事件的监听器必须调用 next() 把控制权交给下一个监听器(可以改写 payload 再交出去);serial 事件没有 next(),只是依次等待。agent/pre-stepagent/requestllm/stream 和三个 tools/* 事件是 waterfall;agent/turn-stopping 是 serial。完整语义(emit/waterfall/parallel/serial/bail 五种分发模式)在第 08 章展开。

turn / step / Round:三层循环的精确关系

  • 这三个词定义了 dsh 的"节奏感",出自 docs/glossary.zh.md"循环层级"节,务必按官方定义理解:
bash 复制代码
Round(外层策略迭代:Goal Round / Ralph Round)
 │  计数器归策略所有,不统计会话中的每个轮次
 ▼
turn(轮次:一次对已接纳输入的排空过程)
 │  在模型及其工具停止工作或终止策略介入后结束;包含 0..n 个 step
 ▼
step(步骤:一次模型请求 + 由模型响应引发的工具执行)
  • 配合 inbox 的两档 target,边界就很清楚了:一条投到 next-turn 的消息要等轮次边界 才被认领;投到 next-step 的消息在步骤边界 就能被认领。turn/end 的原因、工具结果携带 concludesTurn 提前收轮等细节。

在这三层之上,还有一组容易望文生义的 Ralph 词汇,同样以 docs/glossary.zh.md 为准:

  • Ralph 循环(Ralph loop) --- 一次面向不可变目标 的前台全新 agent 工作流运行。它是由工作流和 subagent 原语组合而成的面向模型的工具策略------不是同会话目标、不是 agent loop 模式、不是调度器。docs/tool-catalog.zh.md 里能看到它的模型入口:dsh-tool-ralph 提供的 ralph 工具,每个 Round 启动一个全新的结构化子级。
  • Ralph Round --- Ralph 循环中的一个全新子会话。子会话不接收父会话或此前子会话的对话种子;跨 Round 的状态只靠两样东西:共享工作区,和一份有界的 Ralph 交接。
  • Ralph 交接(Ralph handoff) --- 从一个仍需继续的 Ralph Round 传给下一个 Round 的规范化、有界结构化报告:状态、摘要、证据、后续步骤、阻塞说明。它补充共享工作区,而不取代工作区的权威地位。
  • Goal Round(目标轮) --- 与 Ralph 不同层:同会话的目标(ctx.goals)为当前目标接纳的一次续行周期,具体化为一个由目标触发的轮次,可包含零个或多个步骤。目标有 active / paused / blocked / complete 四个阶段;"目标激活"(续行权限)是进程本地的 armed / disarmed 状态,恢复或 fork 后必须重新经人类授权才能自动续跑。

一句话总结这组关系:step 是模型的一口呼吸,turn 是把一件事干完的一段排空,Round 是策略层的一次"再来一轮"------Goal Round 和 Ralph Round 都属于最外层,但前者在同一会话内续行,后者每次都开全新子会话。

动手实验

实验 1:打印你机器上的组装树(约 5 分钟)

  • 第 02 章你已经跑起来了 dsh。现在看它的"装配清单":
sh 复制代码
# 从源码仓库(REPO):
pnpm dsh --profile web --dump-config
# 若你按第 02 章用 npx 安装过:
npx @deepseek-ai/dsh --profile web --dump-config
  • 预期输出是一份条目列表:每个条目带 id 和 config,覆盖 bundle 各层(dsh-base 及其上的 web app 层)与你 home 目录里的 patch。对照第二组术语逐段解读:哪些条目来自哪个 bundle?你的 cordis.patch.yml 改了哪些 id?官方原话是"它打印出的任何条目,都可以由你自己的 patch 替换"------挑一条你认识的(比如某个工具的配置),试着写一份 patch 替换它,再用同一条命令确认变化。

实验 2:术语溯源------从本章回到源码(约 5 分钟)

  • 本章每条术语都能回源码验证。挑三条走一遍:
sh 复制代码
# 1. inbox 的两档 target(第三组):
grep -n "InboxTarget" REPO/packages/core/agent/src/types.ts
# 预期:export type InboxTarget = 'next-turn' | 'next-step'

# 2. PTC 的三种模式(第五组):
grep -n "ToolPresentationMode" REPO/docs/config-catalog.zh.md
# 预期:export type ToolPresentationMode = 'native' | 'ptc' | 'both'

# 3. 单调守卫的类型(第五组):
grep -n "type ToolGuard" REPO/docs/subsystems/tools.zh.md
# 预期:type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined
  • 这就是本系列反复强调的"给出源码路径"的意义:术语不是背下来的,是查出来的。

常见坑

症状 原因 解法
以为"Agent 和 Session 是一回事" 一个活跃 Agent 与其会话共享同一个 SessionId,但 Agent 是运行中的行为者,Session 是日志 记住:Agent 会 dispose,日志永远在;resume 是"同一日志上再挂一个新 Agent 实例"
想在插件里"修改历史消息" 事件溯源:日志仅追加,历史不可改 改模型看到的内容用 agent/pre-step 改写已领取的批次;改不了已经发生的日志
ctx.shell 当成"启动进程的原语" shell 是面向模型的 Bash 执行器 seam;真正 spawn 进程的是 ctx.subprocess 本地 shell 后端(bash-local)才通过 ctx.subprocess spawn;glob/grep 工具也直接走 ctx.subprocess,不经过 shell 层
waterfall 监听器忘了调用 next() waterfall 语义要求显式委托 拦截型监听器才不调 next();只想"观察"的监听器必须调,否则链条断掉
把 agent 事件当成持久事实来记 agent/* 是实时扩展点,重载后消失 需要持久化的事实,扩展 SessionEventMap 走会话事件
混淆 bundle 与 profile 两者都是"配置载体",但层级不同 bundle 是分发格式 ,profile 是具名组装(列 bundle 清单 + 自己的 patch);一个 profile 叠多个 bundle
以为 sdk-minimal 也有 dsh-base 的全部能力 dsh-sdk-minimal 刻意不应用 dsh-base 需要完整能力用 sdk profile;要最小面用 sdk-minimal,自己显式列出配置树
分不清 turn 和 step 名字相近 step = 一次模型请求 + 它引发的工具执行;turn = 0...n 个 step 的排空过程;Round 是最外层策略迭代

小结

  • dsh = Cordis 框架(可逆副作用 + 结构化依赖)之上的一棵插件树;没有特权内核,模型适配器、工具注册表、会话日志、agent loop 全是插件。
  • 启动 = profile(具名组装)按序叠放 bundle,再依次应用 profile patch、home patch、--patch overlay;dsh --profile web --dump-config 能看到全部可被 patch 的条目。
  • Agent 是活跃行为者(ctx.agents 注册、AgentHandle 归所有者),input 经 inbox 的 next-turn/next-step 两档进入;followup/steer/inject 是三种投递语义。
  • Session 是唯一真源:仅追加的 SessionEvent 日志(信封 {type, seq, time, data},surface 变体带 sourceEventSeqs/surfaceOp),模型历史由 deriveMessages() 从日志派生------"模型可见即已记录"。
  • 工具调用走守卫流水线(pre-execute → 单调 ToolGuard → execute → post-execute → result);PTC 模式('native' | 'ptc' | 'both')让模型写程序经保留工具 run_code 调工具,子调用重入完整流水线。
  • 三十多个 ctx.* 键按 seam(定义/提供方/消费方三角色)或 core 服务组织;权威总表在 docs/capability-seams.zh.md,替换一个提供方就能改变整个产品。
  • 事件分三域:会话事件(持久)、agent/*(观察/拦截)、能力事件(fs/*tools/*telemetry/*,挂策略);waterfall 监听器必须 next()
  • 循环三层:step(一次模型请求+工具)⊂ turn(排空过程)⊂ Round(策略迭代,如 Goal Round / Ralph Round);Ralph 循环 = 面向不可变目标的前台全新 agent 工作流,跨 Round 只靠共享工作区 + Ralph 交接。
  • 界面层五件套:Typert 类型体系 + @Remote/ctx.remote.<namespace> 调用、client-modules 与 window.__DSH_BOOT__ 启动图、slots 组合 UI、conversation 会话视图、$events 事件流。

术语速查表

  • 按七组排列。"详见"列的章号对应总目录(02 环境 / 04 使用指南 / 05 模型 / 07-08 Cordis / 09 架构 / 10 会话 / 11 Agent 生命周期 / 12 工具流水线 / 13 LLM 适配层 / 14-19 插件 / 23 源码 / 24 agent-loop / 25 PTC / 26 沙箱 / 27 子代理 / 28 Web / 29 SDK / 30 集成)。

框架层

术语 一句话 详见
Cordis dsh 底层元框架:插件向共享上下文贡献服务、事件与可逆副作用 第 07 章
plugin(插件) 产品的最小组成单元------dsh 里一切皆插件,无特权内核 第 07、09 章
Context 插件共享的服务容器与事件总线(ctx 第 07 章
Service 挂在 Context 上、以 ctx.<key> 为键的可调用对象 第 07、17 章
inject 插件声明"我依赖哪些服务"的方式 第 07、14 章
effect / Fiber 可逆副作用的注册/撤销机制与插件生命周期载体 第 07 章
HMR 运行中实时应用 patch、替换插件而无需重启 第 08 章

组装层

术语 一句话 详见
bundle(组合包) Cordis 配置项及其挂载代码的分发格式 第 09 章
profile Harness home 中的具名组装:web / headless / sdk / sdk-minimal / acp 第 09 章(sdk 两个形态另见第 29 章)
dsh-base web/headless/sdk/acp 四个 profile 共享的第一层组合包 第 09 章
patch(cordis.patch.yml 按 id 定位并整体替换配置条目、或插入新条目的覆盖文件 第 08、09 章
cordis.yml Cordis 声明式配置文件(agent preset 也是一份 cordis.yml) 第 08 章
loader / 应用启动器 把组装解析成运行时插件树;一切经 dsh CLI + profile 启动 第 09 章
overlay --patch 命令行传入的最后一层覆盖 第 09、14 章

Agent 层

术语 一句话 详见
Agent 活跃智能体句柄:id / options / session / inbox / status / ctx 第 11 章
AgentHandle 创建者持有的所有者凭证 { agent, dispose() } 第 11 章
ctx.agents 活跃 Agent 注册表 + 创建/恢复工厂(工厂由循环注册) 第 11 章
agent loop / driver 具体循环驱动器(dsh-agent-loop,唯一实现) 第 24 章
inbox agent 拥有的两条有序待处理列表,投递目标 next-turn / next-step 第 11 章
followup 排队一个普通后续轮次并唤醒 driver 第 11 章(用法见第 04 章)
steer 为最近的步骤投递引导消息;运行中 driver 在 step 边界消费 第 11 章(实战见第 04 章)
inject(agent 方法) 排队模型可见上下文到下一次 pre-step,不唤醒 driver 第 11 章
agent/pre-step waterfall:拒绝或改写进入本步骤的消息批次(计划模式的机关) 第 11 章
agent/turn-stopping serial:轮次收口前的最后干预点,续命靠 steer 第 11、24 章
scope / agent.ctx 按 agent 划分的注册单位;agent 卸载即回滚其注册 第 11 章(subagent 场景见第 27 章)
shadowing 最具体者胜出:带作用域的注册遮蔽同名全局项 第 17 章

会话层

术语 一句话 详见
Session 仅追加的 SessionEvent 日志,模型所见上下文的唯一真源 第 10 章
SessionEvent 日志条目信封:{type, seq, time, data}(+条件 sourceEventSeqs/surfaceOp 第 10 章
事件溯源 "模型可见即已记录"------抵达模型请求的一切必须能从日志重建 第 10 章
deriveMessages() 从日志投影模型历史(历史不单独存储) 第 10 章
surface 事件 三大表层事件:user/messageassistant/messagetool/result 第 10 章(浏览器消费见第 28 章)
session/event 会话日志的广播通道,持久化插件订阅它落盘 第 10 章
fork ctx.sessions.fork(...):从源会话某边界派生子会话 第 10 章(用法见第 04 章)
lineage 父子事实数据:parentSessiondelegationDepthsubagentDepth 第 27 章
SessionEventMap 经声明合并扩展的会话事件类型 map(插件加事件的地方) 第 10、14 章

工具层

术语 一句话 详见
Tool / ToolDefinition 面向模型的 ToolSchema + execute 函数的注册约定 第 12 章
defineTool 构建工具的类型化 DSL 第 15 章
ctx.tools 作用域化工具注册表 + 带把关执行流水线 第 12 章
restriction 按 scope 过滤全局工具集合(取交集),被滤工具与不存在无法区分 第 27 章
approval 一次性权限决策 seam,经 approval/request waterfall 分派给回答方 第 12 章(体验见第 04 章)
ToolGuard 单调预分派守卫:返回 reason 只能缩减权限、不可撤销 第 12 章
sandbox / sandboxPolicy 包装 argv 的进程沙箱 seam 与统一模式/根目录的策略 home 第 26 章
PTC 程序化工具调用,模式 `'native' 'ptc'
run_code PTC 保留传输工具;子调用重入完整守卫流水线 第 25 章

能力 seam 层

术语 一句话 详见
seam Service Definition + Provider + Consumer 三角色构成的可替换能力 第 09 章
ctx.llm LLM 适配器注册表(llm-deepseek 等注册,循环与压缩消费) 第 13 章
ctx.fs / ctx.shell / ctx.subprocess / ctx.sandbox 文件、Bash 执行器、spawn、argv 包装四个执行世界 seam 第 26 章
ctx.approval / ctx.permissionPresets / ctx.userQuestions 审批、权限预设、人类问答三个交互 seam 第 04 章
ctx.subagents 子代理提供方与延续服务(进程内/ACP/Codex/Claude Code/SDK) 第 27 章
ctx.skills / ctx.commands 技能目录注册表 / 人类命令注册表 第 04 章(写法见第 15 章)
ctx.goals / ctx.jobs / ctx.planMode 同会话目标 / 后台任务 / 计划协作状态 第 04 章(goal 深入见第 27 章)
ctx.compaction 上下文压缩 seam(无面向模型的压缩工具) 第 10 章
ctx.settings / ctx.credentials / ctx.agentDefaultModel 用户设置、凭据引用、默认模型选择 第 05 章
ctx.sessionPersistence / ctx.sessionQuery 会话落盘 seam(JSONL/SQLite)/ 会话读取搜索 seam 第 10 章
ctx.webhookRuntime webhook 规则运行时:交付转 Workspace Session 第 30 章
ctx.web / ctx.spillStore / ctx.terminals / ctx.lsp / ctx.storage 搜索抓取 / 溢出存储 / PTY / 语言服务器 / 非会话存储 第 23 章(逐包地图)

界面层

术语 一句话 详见
Typert / ctx.typertGateway 运行时类型注册表 + Host 调用网关 第 28 章
@Remote / @RemoteScope 标记对 Client 开放的方法(未标记不进生成类型) 第 28 章
ctx.remote.<namespace> Client 侧调用入口($on() 收事件、$mount() 挂贡献) 第 28 章
client-modules / window.__DSH_BOOT__ Client 插件图宿主与其组装出的浏览器启动图 第 28 章
slots Web UI 的类型化扩展位置与组件组合系统 第 28 章(实战见第 22 章)
conversation 把 Session 历史窗口变成各 target 自有视图的子系统 第 28 章
$events API Remotes 内部逻辑事件流 / Connection generation source 第 28 章

循环与流程

术语 一句话 详见
turn / step / Round 排空过程 / 一次模型请求+工具 / 外层策略迭代 第 11 章(状态机见第 24 章)
三大事件域 会话事件(持久)/ agent 事件(观察拦截)/ 能力事件(挂策略) 第 09 章(实战见第 18 章)
waterfall / serial 必须调 next() 委托的瀑布事件 / 无 next() 的串行事件 第 08 章
Ralph 循环 / Ralph Round / Ralph 交接 不可变目标的前台全新 agent 工作流 / 全新子会话 / 有界结构化报告 第 27 章
Goal Round / 目标激活 同会话目标的续行周期 / 进程本地 armed/disarmed 续行权限 第 27 章(用法见第 04 章)

参考资料

官方文档(仓库路径):

  • docs/glossary.zh.md --- 官方术语表,本章第五、三组多条定义的直接出处(capability-seam、agent-scope、目标、循环层级、Ralph 五节)
  • docs/architecture.zh.md --- 事件三域、核心包表、Profile 与组合包、轮次流程、会话日志
  • docs/capability-seams.zh.md --- 全部 ctx.* 服务总表(含提供方/消费方),由 scripts/gen-doc-graphs.ts 生成
  • docs/subsystems/core.zh.md --- Agent/AgentHandle/inbox/拦截决策/类型模式,含 agent/* 事件完整目录
  • docs/subsystems/session.zh.md --- SessionEvent 信封、十二种核心事件、deriveMessages() 投影
  • docs/subsystems/tools.zh.md --- ToolGuardtools/* 事件、PTC 桥接
  • docs/tool-catalog.zh.md --- 真实工具清单(bashrun_codesubagentralph 等)
  • docs/tool-execution-pipeline.zh.md --- 工具流水线全解
  • docs/api-gateway.zh.md --- Typert API Gateway、@Remotectx.remote.<namespace>
  • docs/subsystems/web-client.zh.md --- Web Client 四底座、window.__DSH_BOOT__$events
  • docs/config-catalog.zh.md --- ToolPresentationMode 等配置目录
  • 官方文档站:https://deepseek-harness.github.io/deepseek-harness/

源码

  • packages/core/agent/src/types.ts --- AgentInboxTargetAgentOptionsCancelOptions
  • packages/core/agent/src/runtime-types.ts --- agent/* 事件与 PreStepDecision
  • packages/core/agent/src/index.ts --- AgentRegistryAgentHandle
  • packages/core/tools/src/index.ts --- 工具注册表与 ToolGuard
  • packages/core/agent-loop/src/index.ts --- 具体循环驱动器
  • packages/util/brand/src/index.ts --- Branded<B> 品牌化 ID 原语

外部资料

  • 设计论文:A Programming Paradigm for Spatiotemporal Composability(arXiv:2608.25512)--- Cordis 可组合性理论的形式化出处
相关推荐
风fffff18 小时前
dsh-project-memory v0.3.0到v0.4.0:从项目记忆到开发工作流记忆的演进
ai·agent·插件·dsh·deepseek harness
缘友一世1 天前
DeepSeek Harness(dsh)从零到全栈【1】认识 DeepSeek Harness:Agent、Harness 与“一切皆插件“
dsh·deepseek agent
张忠琳2 天前
【deepseek-harness】Cordis 开源项目深度介绍
ai·agent·deepseek·harness·cordis·dsh
程序员三明治2 天前
【体验毛坯房】Deep Harness 入门教程
java·人工智能·后端·大模型·llm·deepseek·dsh
张忠琳4 天前
【deepseek-harness】Cordis 时空可组合性编程范式 — 三段式精读笔记(四)
ai·agent·deepseek·harness·cordis·dsh
张忠琳4 天前
【deepseek-harness】Cordis 时空可组合性编程范式 — 三段式精读笔记(五)
ai·agent·deepseek·harness·cordis·dsh
张忠琳5 天前
【deepseek-harness】Cordis 时空可组合性编程范式 — 三段式精读笔记(二)
ai·agent·deepseek·harness·cordis·dsh
张忠琳6 天前
【deepseek-harness】Cordis 时空可组合性编程范式 — 三段式精读笔记(一)
ai·agent·deepseek·harness·cordis·dsh
其美杰布-富贵-李7 天前
02. 快速开始:安装、Web UI、Headless 与第一次运行诊断
harness·dsh