DeepSeek 官方开源的 Agent 运行时框架。
它的全部叙事只有一句话——Everything is a Plugin:连模型适配器、工具注册表、会话日志、甚至 Agent 主循环本身,都是可替换的插件。
模型负责"想",Harness 负责"做"。DeepSeek Harness(CLI 名 dsh)是把 DeepSeek 模型接入文件系统、终端、网页、沙箱与其他 Agent 的运行时环境——地位相当于 Claude Code 之于 Anthropic,是 DeepSeek 的官方 Vibe Coding 入口。
它不训练、不替代 DeepSeek 模型,而是围绕模型搭建让模型能"动手"的基础设施。
SDK/HTTP 封装只占它极小一角;它提供的是完整的 Agent 能力面:会话、工具、沙箱、审批、持久化。
把模型接入真实世界:文件、终端、代码执行、子 Agent、定时任务、人类协作……让模型成为能规划多步任务的自主智能体。
基于 Cordis 插件元框架(DeepSeek 为其发表论文 A Programming Paradigm for Spatiotemporal Composability)。没有需要打补丁的"特权内核"——扩展 dsh = 在旁边挂一个插件。
一条命令启动完整 Web UI。点击"运行",看模拟终端里会发生什么。
--no-open 可不自动开浏览器
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install && pnpm run build
pnpm dsh web # 使用已构建产物,不重新编译
一个 dsh 启动器,按名字组合不同的插件树:
Python SDK 也会以平台 wheel(deepseek-harness-sdk-runtime-<platform>-<arch>)打包这份 CLI,默认启动 dsh --profile sdk。
读懂 Cordis,就读懂了 dsh 的一切。这五个机制组合出一个结果:任何部件都能被配置替换,注册即效果、卸载即回滚。
插件是一个实现 Service 的对象:可以是带 inject/apply(ctx) 的函数,也可以是 Service 子类,由 Cordis 挂载进当前上下文。
服务认领稳定的 ctx.<key>(如 ctx.tools、ctx.llm),其他插件按键查找而不是 import 具体实现——这就是可替换性的来源。
插件声明需要哪些服务,Cordis 会等它们就绪再激活。加载顺序由"服务需求"表达,不再需要手工排启动序。
加载顺序服务用 TypeScript 声明合并登记事件名,再按五种模式派发。事件是 dsh 的扩展点——拦截几乎一切行为。
扩展点Prompt 区块、工具 schema、适配器、Provider、监听器,全部经 ctx.effect() / ctx.on() 安装;插件卸载时按注册逆序自动拆除。
模型适配器、工具注册表、会话日志、Agent 主循环……产品里的每一部分都是插件。扩展 = 并排挂载;替换 = 改一行配置。
架构哲学一次 agent/pre-step 请求依次流过三个监听器。委托型监听器修改共享请求后调 next() 传递;拥有决策权的监听器可以不调 next() 直接返回,下游全部跳过。
检查输入是否放行;拥有最终决策权,可短路
向请求追加注入的上下文片段
遥测监听器,只看不动
| 模式 | 等待完成? | 派发顺序 | 有返回值? | 语义 |
|---|---|---|---|---|
| emit | 否 | 按注册序观察 | 否 | 广播通知,无人能拦 |
| waterfall | 否 | 按注册序 | 是 | 环绕中间件:next() 委托,不调即短路;prepend: true 抢先注册 |
| parallel | 是 | 全部并行 | 否 | 扇出并发执行 |
| serial | 是 | 按注册序 | 是 | 串行接力,如 agent/turn-stopping 终检 |
| bail | 否 | 按注册序 | 是 | 第一个给出 bail 值的监听器截停 |
ctx.waterfall 本质是 around-middleware:监听器签名收到 (...args, next)。调用 next() 把(可能已包装的)结果交给下一个服务;不调用直接 return 即短路。返回值沿 next() 的返回值向上传播。
next();实战纪律:事件归属要按域划分——工具流水线事件归 ctx.tools,模型流式归 ctx.llm,活跃 Agent 协调归 ctx.agents。策略与拦截优先用事件,直接能力调用优先用服务方法。
一次 dsh 运行 = 一棵在启动时按有序层组装出来的插件树。每一层都只能"替换/插入配置行",于是上游永远可以被下游补丁覆盖。
--patch 临时覆盖dsh --profile web --dump-config 会打印实际启动的每一行配置——打印出来的任何一行,都能被你自己的补丁替换。
自定义 profile 默认 live patch reload;web 开箱即热。而 headless / sdk / acp 在启动时一次性应用所有层——一次性程序跑起来再换依赖会破坏生命周期。
它是唯一不套 dsh-base 的组合:一个 bundle 自拥完整显式 SDK 树,追求最小面。仓库用 verify-application-entrypoints 脚本守住"所有 Node 应用入口必须经过 dsh"。
两个基本单位:Step = 一次模型请求 + 它触发的工具调用;Turn = 零或多个 Step(打开于首个输入被认领,关闭于再无所欠)。下面的时序图逐事件点亮,右侧同步解说。
session/event 广播,重载后仍在)|Agent 事件(agent/*,携带活跃 Agent:收件箱、步骤、状态、请求、校验、继续)|能力事件(fs/*、tools/*、telemetry/*,把策略/适配器挂到接缝上,不碰循环)。{ ...decision, messages })以保留 startsRequestSeries 等声明。dsh-compaction-basic 用 agent/pre-step 查压力、用 agent/request-error 只认上下文溢出;先做免模型的工具结果修剪,再做摘要;修剪/摘要推进了表面替换世代才开新重试回合,否则原始错误仍是权威。一条不变的铁律:"Model-visible means logged"——凡是到达过模型眼前的,必须能从日志重建,且有运行时断言强制执行。Fork、Resume、回放、遥测、UI 渲染,全部从这条追加式日志派生。
原始 assistant/chunk 单独保留,保证回放与 UI 保真;dsh-session-projection 增量折叠已提交事件,宿主用 stateOf() 读类型化状态、用 snapshot() 批量取裁剪后的客户端视图。
ctx.agents.create({ sessionId, seed, meta: { parentSession, seedLength } })),只有 agent-loop 发布的会话才持久化。新增"模型可见输入"的唯一正确姿势 = 扩展 SessionEventMap 加一种新事件、再从日志渲染它。
Seam = 可替换的能力,由三个角色构成完整能力:Service Definition(声明接口、拥有 ctx.<key>)+ Service Provider(实现)+ Consumer(通常是模型可见的工具)。角色可分包也可同包,但单独一个角色不构成 seam。
声明 shell 执行世界的接口,认领 ctx.shell。可以是抽象类,也可以是注册表(如 WebRuntime)——但绝不是 TS interface。
多个实现并存:本地直跑,或经 bwrap / Landlock / Seatbelt 收容。指向哪一个,由配置行决定。
模型看到的 bash 工具只是消费者——它对背后是谁毫无感知,schema 却始终稳定。
fs 与 subprocess 两个 Provider 共享同一个"执行世界"——所以切到远程沙箱时,Bash、PTY、LSP、glob/grep 一起搬走,不需要任何 Provider 分叉。
ctx.sandbox 后端(bwrap / Landlock / Seatbelt)可在 spawn 前包装 argv 收容进程。ctx.<key>Subagent 的 Provider 谱系同样宽广:从"全新子代理"到"另一个产品里的委托回合",都藏在同一个接口后面。这就是"换一个 Provider 改变半个产品"的含义。
Context、Memory、Session、Agent、RAG、Plugin——六个被说烂了的词,在 dsh 里各有精确的技术含义。每个概念按同一把尺子拆:一句话直觉 → 机制 → 源码级事实 → 与其他 harness 对照。
ctx.systemPrompt 瀑布组装)+ 从会话日志派生的历史(deriveMessages())+ agent.inject() 注入的上下文(落入下一次被接纳的请求)。agent-instructions(装载/刷新 AGENTS.md、CLAUDE.md 工作区指令)、file-reference(@file 提及语法)、session-reference(跨会话快照引用)、time-context(每步时钟)、tmux-context(每回合 tmux 位置感知)——上下文的每一部分都是可摘除的插件。dsh-compaction-basic 在 agent/pre-step 查压力 → 免模型的工具结果修剪 → 才轮到摘要);spill(工具结果溢出:超限结果完整存入 ctx.spillStore,模型只拿到可追读的定位器)。SessionEventMap(turn/start、user/message、assistant/chunk、tool/call……)、append-only 不可改写。sourceEventSeqs(指回确切的 chunk 事件)——可溯源到流。dsh-session-projection 增量折叠已提交事件,宿主 stateOf() 读状态、snapshot() 出裁剪视图;投影缓存刚落地跨版本读兼容(升级不炸旧缓存——最近一周的提交主题)。ctx.agents.create({sessionId, seed, meta:{parentSession, seedLength}}) 在回合边界引用同一条日志,零拷贝。ctx.agents;agent/* 事件暴露收件箱拼接、状态机(running/idle)、请求、校验、继续、错误——UI 与 SDK 全靠它协调。agent.ctx 注册即"scope 可见 + scope 生命周期"。子代理谱系(同一个 ctx.subagents 接缝后的四种形态):
预切分 → 向量化 → 相似度召回 → 塞进上下文。
优:海量语料、模糊语义匹配。
代价:索引永远滞后于文件;切分破坏代码结构;召回不可解释;多一套向量基础设施。
模型在循环中自主决定查什么、怎么查、查不到再怎么查——检索变成可审计的多步推理而非一次黑盒相似度。
dsh 提供的检索原语(全部模型可见):
ctx.tools 插件包装你的向量库(如 sqlite-vec / 外部服务),schema 自动进组装——十分钟的事。这正是"一切皆插件"的意义:缺省立场不等于封死道路。(此扩展思路为讲者基于插件体系的推断,官方未内置。)apply(ctx) 的文件 + 在 cordis.yml 加一行。没有框架引导代码,没有插件 API 注册表——Context 本身就是 API。① 最小可用(官方教程原文)
② 认领一个 ctx 键 + 声明依赖
③ 类型化事件(声明合并)
④ 声明式配置(Schemastery,支持热更新)
@deepseek-ai/dsh-*,GitHub topic dsh-plugin 获得可发现性;bundle 即"可安装的 patch 层"(dsh.bundle 字段)。cordis_* 工具让 Agent 在 vm 沙箱里定义/挂载/卸载包(运行中的包还能注册新工具);extensions 组让 Agent 巡检活插件树——插件系统对模型也是可编程的。用户敲入一句话 → Agent 的收件箱认领它 → Context 插件们现场组装视图(agent-instructions 装载指令、time-context 塞入时钟、pre-step 决定准入与压缩)→ Plugin 们注册的 prompt 区块与工具 schema 拼成请求 → 循环驱动 → 模型调工具,Plugin 守卫流水线执行(spill 溢出大结果)→ 每一步作为 Session 事件追加落盘 → Memory 的投影层增量折叠出状态供 UI 与下次派生使用 → 下一步模型用 RAG 原语(grep/FTS5/LSP)现场检索补上下文。六个词不是六个功能,是同一次请求的六个侧面。
把 dsh 放回 harness 谱系,它的选择才有分量。所有 harness 回答的是同一个问题——"如何给模型一个可信赖的行动身体?"——但四家代表给出了四种不同的答案。
Anthropic。harness 即产品:内核闭源、边缘用文件开放——CLAUDE.md 记忆、hooks 脚本、skills 工作流、agents 定义、MCP 服务器,全是"文件即接口"。设计循环四拍:收集上下文 → 行动 → 验证 → 重复。赌的是:把最好的编码伴侣直接交付给你。
OpenAI。Rust 原生单二进制(codex-rs,60+ crates),把"不信任模型输出"做进内核:OS 级沙箱(macOS Seatbelt / Linux Landlock+seccomp)叠 4 档审批策略;扩展走 config.toml、MCP 与生命周期 hooks(企业还有 requirements.toml 受管层)。赌的是:安全与性能的确定性。
Nous Research。harness 的第一性目标是随使用变强:内置学习循环——Agent 自策划记忆(MEMORY.md / SOUL.md / USER.md)、复杂任务后自创技能且"技能在使用中自我改进"、跨会话用户建模(Honcho);七种执行后端(本地/Docker/SSH/Modal…)、全平台消息入口。赌的是:长期陪伴产生复利。
DeepSeek。前三种都保留了"产品内核",dsh 是唯一把内核也交出来的:没有特权核心,产品 = 一份插件配置——连主循环都是插件。它赌的是:harness 领域还没定型,可组合性本身就是产品,让别人能在它上面造出前三种。
agent-loop、tools、session、llm 全是平权插件。改行为 ≠ 改内核,而是在旁边挂插件——升级永不冲突。
web/headless/sdk/acp 四种产品 = 同一基底的不同配置行。造"下一代编码产品"不需要 fork,写一个 profile。
服务经 ctx.<key> 定位而非 import 实现 → 消费者与提供者解耦 → 换 Provider 换掉半个产品(第 6 章的接缝经济学)。
五种类型化派发模式,waterfall = 环绕中间件。拦截点铺满全程(pre-step/request/tools),策略与机制分离。
"到过模型眼前的必须能从日志重建"——有运行时断言执法。回放/fork/UI/压缩全是投影,一个事实源消除一类 bug。
一切注册都是 effect,卸载即逆序回滚 → 热重载、动态插件、scope 生命周期才可能正确而非碰巧正确。
这个仓库本身就是证据:AGENTS.md 规约 Agent 贡献者、.agents/notes 存 Agent 决策记录、工具目录由真实启动插件生成、extensions 组让 Agent 巡检/挂载自己的插件、cordis_* 工具让模型给自己装工具。
hooks-claude-code / hooks-codex 桥直接运行你已有的两家 hooks 配置;ACP 对接自动化;MCP 分组接标准生态。不要求你迁徙,要求你叠加。
@deepseek-ai 域发布)意味着框架演进风险自担。dsh 不是"更好的 Claude Code",它是另一个物种:赌的是 harness 领域需要的是 Linux 内核,而不是又一个发行版。
① "Foundation over blast radius"(根基优先于爆炸半径)——AGENTS.md 原文:首个正式 tag 发布前,"prefer correct foundations to compatibility shims: rename or repackage freely"。后端直接拒绝旧磁盘格式,SQLite 用单调 SCHEMA_VERSION,会话格式版本停在 0、无兼容承诺。这是预发布期把"正确性"排在"兼容性"前面的公开宣言——也解释了为什么 README 敢写"必然破坏性变更"。
② Vendored rescope(框架收编)——Cordis 及其基础库被 vendor 进仓库、以 @deepseek-ai/cordis 等名字发布:因为所有 harness 包都以框架为 peer dependency,"发布 harness 就等于发布这一层",用上游名字会squat 注册表。映射由脚本 rescope-vendor 机械执行、卫生门禁校验,上游快照版本全表留痕。框架从"依赖"变成"自己的地基"——微内核哲学的最终自洽。
每个工具包向 ctx.tools 注册 schema,经系统提示组装进入模型视野。点卡片看细节;官方工具目录由脚本真实启动每个工具插件读取 schema 生成,并有完整性守卫防漏——文档即运行时事实。
维度表格看全景;下面的需求对比器点一个具体需求,看四家分别怎么解。
| 维度 | Claude Code | Codex CLI | Hermes | dsh |
|---|---|---|---|---|
| 形态 | Node.js CLI + IDE/桌面/网页同引擎 | Rust 原生单二进制(codex-rs,60+ crates) | Python 3.11(uv 管理)+ 消息网关 | TS/Node,pnpm monorepo,256 包 / 50 组 |
| 开源 | CLI 闭源(仓库仅 issues);Agent SDK 开源 | Apache-2.0 全开源 | MIT 全开源 | MIT 全开源;Cordis 框架供应商化 |
| 循环归属 | 产品内置(SDK 化后可复用) | 内核内置 | 内置 + 学习循环 | 循环本身是插件(agent-loop 包) |
| 扩展模型 | 文件即接口:CLAUDE.md / hooks / skills / agents + MCP | config.toml + MCP + 生命周期 hooks(企业受管层) | YAML 配置 + MCP + toolsets + 自创技能 | Cordis 插件树:profile / bundle / patch,一切皆插件 |
| 工具体系 | 内置编码工具 + MCP 动态接入 | 内置 + MCP + skills | 40+ 内置 + toolset 分发 + MCP | 24 工具包 / 50+ 工具 + PTC(run_code)+ cordis_* 动态自增 |
| 上下文 | auto-compact + /context 管理 | 内核管理 + 云端任务分工 | 分层记忆 + FTS5 会话检索 + 轨迹压缩 | 追加式日志=真相,派生投影 + 压缩/溢出(spill)策略 |
| 沙箱 | 权限模式 + 审批提示 + 沙箱化 bash | OS 级:Seatbelt / Landlock+seccomp + 4 档审批 | 7 种执行后端(本地/Docker/SSH/Modal/Daytona…) | ctx.sandbox 接缝(bwrap/Landlock/Seatbelt)+ 执行世界整体远程化(E2B) |
| 多智能体 | Task 子代理 + agent teams(实验) | codex exec 编排 + 云端并行任务 | 隔离子代理并行工作流 | ctx.subagents 接缝 + Agent Teams(实验)+ workflow / ralph |
| 记忆成长 | CLAUDE.md + auto memory | AGENTS.md 分层发现 | 学习循环:自策划记忆 + 自创技能 + 用户建模 | 会话持久化 / 标题 / 目标;成长非主打 |
| 一句话 | "最好的编码伴侣,拿来就用" | "最可信的本地内核,安全先行" | "越用越懂你的私人 Agent" | "要不同的 harness?请自便,这里全是零件" |
你要的是今天就能高效干活的编码搭档:产品打磨度、稳定性和安全审计都最好。两者差异主要是生态与审美,而不是架构哲学。
你要的是跨会话成长的个人 Agent:它记得你、自创技能、活在你的 Telegram/Discord 里。harness 在这里被优化成"随时间增值的资产"。
你要改造 Agent 运行时本身:换模型编排策略、定制工具流水线、研究 harness 设计、或在其上长出自己的产品。它还准备了 hooks 桥——你可以带着 Claude Code / Codex 的 hooks 配置直接来用:生态不是敌人,是插件。
第 14 章的 SAFETY.md 摘录只是底线声明。这一章拆开 dsh 真正的防御工事:五层防线如何分工、每层被攻破的后果、以及"失败即关闭"的审批哲学。
服务契约(dsh-sandbox)+ 各平台后端(sandbox-local:Linux bwrap/Landlock、macOS Seatbelt;Windows 走 sandbox-windows-acl 受限令牌)+ sandbox-policy 按调用解析文件副作用策略。消费者在 spawn 前包装 argv——收容是能力,不是补丁。
dsh-user-approval 是渠道无关的一次性审批接缝,明确设计为 fail-closed——审批器缺席/出错时,决策是拒绝而非放行。permission-presets 把"沙箱模式 + 审批策略"打包成用户可选的权限档位。
凭据接缝只流转引用值与记录,secret 值永不进配置文件;credentials-local 做 env/.env 分层;需要人机对话才能取得的凭据走 authorization 流程注册表。子进程环境额外清洗:凡名字含 *KEY*/*SECRET*/*TOKEN*/*PASSWORD* 一律剥离。
这份文档的每个条目都是真实发生过或差点发生的缺陷类,写成防止复发的规则——这是安全工程里最珍贵的一手材料。
进程可能"超时了且 exit 0"(它吞了信号)。timedOut/signal/exitCode 各自独立上报,绝不把一个标志嵌进另一个的分支——否则调用方把截断的运行读成干净成功。
发出 kill/abort 就返回的 teardown 留下孤儿。清理必须 async 并等待子进程退出(kill → await done),且先关监听器注册表再杀——迟到完成保持安静。
spill/临时文件用 0700 私有目录、随机名、独占创建('wx' + 0600)——可预测的世界可读路径就是在邀请符号链接竞态。
可能成为符号链接/junction 的路径:先 lstat 判断再 unlink(只删链接不跟进目标);递归 rmSync 留给确知的真实目录。
dsh 最被低估的部分不是代码,是建造过程本身:1714 条 Agent 决策记录、100% 覆盖率门禁、录制会话回放测试、由真实启动插件生成的文档——一套让"不确定性的系统"保持确定性的工程体系。
| 通道 | 机制 | 独门规则 |
|---|---|---|
| Unit | vitest,测试与被测代码同目录 | 每个注册表必须有 HMR 安全测试:dispose 贡献 fiber,断言清理干净 |
| Coverage 门禁 | packages/*/*/src 逐文件 100% | "未覆盖的行常常是该删的死代码,而非欠的测试"——门禁同时是清理器 |
| Real-API e2e | 带 key 打真实 DeepSeek API,无 key 自跳过 | "我们是 DeepSeek——不做配给制的真实 API 测试";无 key 测试只证明管道通,冒烟测试启动真实 profile 发一条 prompt 检查世界 |
| Snapshot | 录制 session.jsonl 供回放,回放结果即期望 | 改动场景必须同 PR 带录制会话;可变场景独立比对完整工作区树,且 record/refresh 永不改写该树 |
| Web 浏览器快照 | Chromium + ARIA 证据 | CI 强制只读 DSH_SNAPSHOT=replay——期望产物永不被 CI 改写,每个 diff 都人工过目 |
| 构建产物冒烟 | 发布件 bin 在裸 node 下运行 | "真实入口"指发布工件:tsx 会掩盖 settle 竞态与模块解析失败 |
ACP 服务器带着 178 个通过的单测和 100% 行覆盖上线,真实编辑器(Zed)一连上就崩:第一发 session/new 就报 cannot get property "agents" without inject。
export default apply——Loader 把命名导出的 inject 丢弃了。所有测试都是手工挂载插件,从未走过真实 Loader 路径。session/load 暴露第二个独立 bug(shadow 边界上的可选服务查找)——同一个错误串后面藏着两个 bug。投影缓存跨版本读兼容(2026-09-02,本周):session_projcache 磁盘结构三代演进,升级后的 home 出现"首次启动即把坏文档永久写入新树"的投毒。决策:声明式读兼容——
过期的缓存只付出更长的尾部重放,绝不给出错值、绝不拒载。1,714 条这样的记录构成了仓库的制度记忆——决策可追溯、可否决(rejected 目录就放着 20 条)。
贡献者是人类或 Agent(遵循 AGENTS.md)
lefthook + oxlint + 类型检查 + rescope-vendor:check
六通道并行:覆盖率门禁拆分并发分区
工具目录由真实启动插件读取 schema;漏写文档=CI 失败
架构决策落 note,进 implemented/ 或 rejected/
行为变更与记录同步入库——制度记忆持续生长
cordis_* 工具让模型给自己装工具、Agent Notes 由 Agent 撰写、工具目录由 Agent 贡献的插件自动生成文档——dsh 的建造过程就是它运行时能力的最大用户。社区侧的 learn-claude-code(76k stars 的"从零造 harness"课程)用 17 课教人手写这些机制——下一章你会看到,它的课程目录与 dsh 的插件目录几乎一一对应。
dsh 的每个设计都能在计算机科学里找到血统。戴上三副透镜——事件溯源、效果系统、操作系统——那些"为什么"会突然全部对齐。
| 事件溯源概念 | dsh 中的化身 | 备注 |
|---|---|---|
| Event Store | 追加式会话日志(SessionEvent + SessionEventMap 事件目录) | 写路径唯一:到过模型眼前的必须先落日志 |
| 读模型投影 | deriveMessages() / session-projection 增量折叠 | UI、fork、遥测都是只读投影——读写分离 |
| Snapshot | stateOf() / projection-cache 持久缓存 | 缓存过期只损失重放时间,绝不给错值 |
| 事件流分支 | fork(parentSession + seed 引用同一条日志) | 零拷贝分叉 |
| 读模型版本治理 | compatibleVersions 声明式读兼容(本周刚落) | 事件溯源系统最经典的痛,dsh 用一张声明表解决 |
"组件被移除时,其副作用可被完全撤销。"机制 = 可逆效果(revertible effects):每次上下文变换都保留逆操作。你在第 2 章看到的 ctx.effect(() => { … return () => … }) 就是它的运行时形态——热重载、动态插件、scope 生命周期之所以"正确而非碰巧正确",根子在这。
"声明式管理组件间依赖,并对其变化做出响应。"机制 = 响应式协同效果(reactive coeffects):上下文变化对照组件的协同效果规约,触发其激活/停用——你在 inject = ['greeter'] 里见的就是它:依赖即激活条件,加载顺序被声明取代。
论文的雄心在最后一步:一个演算(calculus)把单组件的保证扩展到任意交错的组件系统——"不同组件的效果可交错而不互相干扰"的观察等价性。这正是 256 个包还能保持秩序的理论底座。值得一提的是:论文把自进化 Agent harness 列为该范式的头号动机案例——先有理论,再造 harness,顺序本身就是稀缺品。
工具 schema 就是模型的 ISA(指令集架构)。模型不学"如何 spawn 进程",它只学 bash("ls")——就像程序员写 read() 而不碰磁盘控制器。守卫流水线 = syscall 入口检查;schema 的稳定性承诺 = ABI 兼容承诺("换后端不改指令集",正是设备无关接口的定义)。
ctx.shell / ctx.fs / ctx.llm 是教科书"设备独立性软件"层的翻版:上层(工具消费者)只认接口,下层(Provider)可任意更换。dsh 判定好抽象的三角色判据——Service Definition / Provider / Consumer 缺一不可——与教科书判定设备独立性的判据同构。
"世界上发生过什么"被抽象成类型化事件流(SessionEventMap)——如同 OS 把一切 I/O 抽象成文件读写。模型看见了什么、做了什么、结果是什么,统一成一种可重放的货币。
教科书也提醒:抽象必然泄漏(leaky abstraction)。模型有时确实需要知道真实世界细节(工作目录、平台差异)——dsh 的对策不是假装不泄漏,而是把泄漏面制度化:system-prompt 的环境描述、context 插件(time-context / tmux-context)按需注入真实世界坐标。
fs 与 subprocess 两个 Provider 共享同一个"执行世界"并作为整体切换:本地世界、OS 沙箱世界、E2B 远程世界。Agent 坚信自己在操作一台电脑——但"那台电脑"是配置出来的。这就是虚拟机监控器的定义:客户机(模型的世界观)与宿主机(你的 Mac)彻底解耦。第 6 章的拨动开关演示,本质上是在热迁移整台虚拟机。
上下文窗口 = 稀缺的物理内存。对照精确得可怕:compaction = 页面置换(把冷页压成摘要);spill = swap(工具结果换出到 spillStore,定位器就是 swap handle,可按需换入);scope = 页表(决定这个 Agent 的地址空间里哪些页可见);shadowing = 私有映射(同名工具只在本地改写,如同写时复制);agent.inject() = 动态映射新页进下一次请求。
教科书:一个物理 CPU 时分复用出 N 个虚拟处理器。dsh:一个 agent-loop 驱动器时分复用地驱动注册表里的多个 Agent;jobs 运行时是后台调度器;工具调用的有界滚动池就是并发度控制(= OS 的进程数限制)。
教科书用 SPOOLing 把独占打印机变成共享逻辑设备。dsh:terminal_* 把一次性命令执行虚拟成可复用、可排队的持久设备(PTY);jobs 把"跑个后台任务"虚拟成统一的作业对象;time-context 更妙——每一步都有自己的虚拟时钟视图。
教科书还会问:虚拟化的代价是什么?间接与治理。dsh 的回答也教科书式:用不变量治理(model-visible ⇒ logged)、用断言执法、用投影缓存把间接的代价压到尾部重放。
在 dsh 里加功能,第一步是"选对事件域"。点左边任何目标,右边高亮对应机制——这是官方扩展手册的浓缩版。
hooks 分组内置 Claude Code / Codex 共享线协议库(wire-protocol),acp profile 提供自动化专用 Agent Client Protocol 服务器——可把现有生态工具接进来,也能把 Claude Code / Codex 当子 Agent 调。
Python 侧暴露的是 profile 选择与有序补丁文件(不是完整 Cordis 树);持久外部插件经 dsh plugin 安装。JSON-RPC 协议与 TS 客户端/服务端在 sdk 分组。
插件用 GitHub topic dsh-plugin 获得可发现性;问题走 GitHub Discussions;官方 Discord 社区。文档站:deepseek-harness.github.io。