本文由 莫潇羽@无忆研习社(www.wuyi.wiki)撰写,转载请注明出处。
快速摘要
DeepSeek Harness(简称 dsh)是 DeepSeek 在 2026 年 8 月 13 日开源的智能体框架开发者预览版,核心公式是"模型 + Harness = Agent"。它不是一个成品 Agent 产品,而是一个可以自由组装的 Agent 运行时——模型、工具、技能、会话、沙箱、存储、循环、调度、UI 全部做成插件,底层由具备"时空可组合性"的 Cordis 元框架驱动,开发者不用改一行源码就能把整套运行时按自己的需要重新拼装出来。产品预置了标准、PTC、极简、创造四种运行模式,一行 npx @deepseek-ai/dsh web 就能在本机 3080 端口拉起 Web UI,MIT 协议开源。
同一天上线的还有 DeepSeek V4 Pro 正式版,以及一份从 8 月 17 日起生效的峰谷定价新规——高峰时段最高涨幅约 11 倍,Agent 重度用户需要重新算账。
一句话结论:产品外壳还很早期,但架构野心是我这半年看到最大的一次开源,值得所有做 Agent 工具链、二次开发和企业内部智能体平台的同学认真看一眼。
想看完整拆解,往下翻。
一、DeepSeek Harness 是什么:先把公式和定位说清楚
我自己第一次听到"Harness"这个词的时候,其实是有点懵的。这词在英文里的本义是"马具、挽具",用在 AI 语境下,字面意思就是给大模型这匹马套上的那副挽具——它自己不会跑,但没有它,模型也没法真正拉着车干活。
DeepSeek 官方给出的定义非常直接:
$$
\text{Model} + \text{Harness} = \text{Agent}
$$
模型负责推理、思考、写代码;Harness 负责把模型这颗"大脑"接到真实世界里,让它能读文件、跑命令、开子进程、访问网页、调用外部工具。两者相加,才是一个能干活、能交付的智能体。
在这套定义下,你熟悉的 Claude Code、Codex、Cursor Agent、Cline,其实都可以被拆成"某某模型 + 某某 Harness"这样的组合。莫潇羽在无忆研习社的技术圈子里一直在讲一句话:2024 年大家拼模型,2025 年开始拼 Harness,2026 年拼的是 Harness 之间的组合能力。DeepSeek 这次直接把 Harness 这个词摆到台面上开源,其实就是在给整个行业下一个新的坐标系。
Harness 到底承担了哪些职责
如果把一个 Agent 完全拆开,从上到下大概是这样一条链:
用户输入
↓
系统提示词(System Prompt)
↓
Agent Loop(决定何时调模型、何时调工具、何时结束)
↓
模型推理(LLM Inference)
↓
工具调用(Tool Call)
↓
Shell / 文件系统 / 网络 / 浏览器
↓
沙箱与权限策略(Sandbox / Approval Policy)
↓
会话与记忆(Session / Memory)
↓
子 Agent 调度(Subagent)
↓
UI 呈现
上面这一整条链,除了"模型推理"那一步是模型自己的事,其余全都是 Harness 的责任范围。也就是说,同一颗模型脑袋,塞进不同的 Harness,跑出来的 Agent 完全可以是两个物种。DeepSeek 官方自己也在文档里给出了一个说法:同一个模型在不同 Harness 下的表现可以差出几十个百分点。这不是玄学,是工程结构本身带来的差异。
官方的三层运行形态
dsh 在架构层面做了一个很清晰的分层:
|
层级 |
作用 |
典型使用场景 |
|
|
最基础的一层,包含模型适配器、工具、持久化、沙箱、审批策略、设置、凭据、遥测 |
所有形态的公共底座 |
|
|
在 base 之上叠一层 Web UI |
大部分开发者日常交互场景 |
|
|
在 base 之上叠一层无界面的一次性任务运行器 |
CI、批处理、自动化脚本 |
这种分层的意思是,你想要一台"完整的电脑"就叠 web-app,你只想让脚本里塞一段"帮我读三个文件然后返回结果"就叠 headless。dsh 的三种形态本质上是同一份底座上的不同外壳。
顺带说一句,如果只是想在服务器上写自动化脚本,headless 模式其实比 Web UI 更符合工程直觉——用完就丢,没有前端负担,也不占本机端口。莫潇羽@无忆研习社在跑批量数据处理任务的时候基本都是走 headless 这条路。
它不是"DeepSeek 版 Claude Code"
必须先泼一盆冷水:如果你带着"另一个 Claude Code"的期待去打开 dsh,你会有点失望。
它的 Web UI 现在还很朴素,左边一列历史会话,中间一个对话框,顶上让你选工作目录和运行模式,仅此而已。四个模式的描述文案甚至都还带着 v0.1 的原始感。它的启动方式也很"程序员"——npx @deepseek-ai/dsh web 一行命令,跟你平时随便跑一个本地服务没有任何区别。
但只要你翻开架构文档,就会立刻发现这套东西的胃口远远不止"再做一个 Coding Agent"。它是把 Agent 这个物种拆解到了组件级,让每一个组件都能被替换、被重组、被社区二次创作。产品是薄的,架构是厚的,这才是 dsh 最值得看的地方。
二、五分钟跑起来:本机安装与首个会话
聊了半天,先动手把它跑起来。这一节我把两条安装路径都列出来,非开发者也能照着做。
环境要求
先看硬性要求:
- Node.js:官方仓库要求
^22.19 || >=24,也就是 Node.js 22.19 及以上的 22.x,或者 24.x 及以上。老版本会直接报错。 - 操作系统:Windows、macOS、Linux 都支持。
- 磁盘空间:如果只是走
npx那条路,缓存大概 200MB 以内;如果拉源码,工作区大约 190MB,含 40 多个 workspace 子包。 - 模型 API Key:这是关键点。dsh 打开之后必须先填一个模型的 API Key 才能开始新会话,跟 Claude Code 那种"打开就能问"的产品形态不太一样。
关于 Node.js 版本,如果你本机装了 nvm 或者 fnm,直接 nvm install 24 切一个新版本最省事。莫潇羽自己是习惯把常用工具链单独放一个 Node 24 的环境里,避免跟其它老项目撞版本。
方法一:npx 一行命令拉起 Web UI
面向所有人,包括不太写代码的小白用户。
打开终端,敲一行:
npx @deepseek-ai/dsh web
npx 会自动把 @deepseek-ai/dsh 这个包下载到 npm 缓存里,然后启动 Web 模式。跑起来之后终端会打印一段类似这样的日志:
profile web booted
plugins mounted
web server listening
dsh web: http://127.0.0.1:3080
看到最下面那行地址,就意味着服务已经起来了。打开浏览器访问:
http://127.0.0.1:3080
浏览器里会立刻弹一个页面,让你填 DeepSeek 的 API Key。填完就能进入主界面,右上角挂着一个"预览版"标签,左侧是历史会话列表,中间是对话框和工作目录选择器。
小白友好提示:npx这个命令是 npm 自带的,只要装了 Node.js 就能用。它的作用是"临时下载并执行一个 npm 包",你不用先npm install,也不用担心装了会污染全局环境,用完关掉就行。
方法二:源码克隆的开发者路径
如果你想读代码、想改插件、想给官方仓库提 PR,就走源码这条路:
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web
这里为什么用 pnpm 而不是 npm 或 yarn?因为 dsh 是一个 monorepo,40 多个子包互相引用,pnpm workspace 处理这种结构效率最高,安装体积也最小。仓库里 pnpm run test 那类命令是给贡献者用的,普通使用完全不用碰。
合规提示:GitHub 在国内的网络访问偶尔会不稳定,建议配置国内可用的镜像加速通道,或者使用国内代码托管平台的镜像仓库进行拉取。境外模型服务的调用同样需要遵守国内网络与内容管理相关规定,涉及企业生产环境部署时,请提前和自己公司的合规团队沟通清楚。
首次进入:API Key 与工作目录
进到 Web UI 之后,dsh 会强制你走两步:
- 第一步:填模型 API Key。你可以填 DeepSeek 自己的,也可以填其它兼容 OpenAI Responses API 的模型(这一点后面讲插件的时候会展开)。
- 第二步:选一个本地工作目录,也就是你希望这个 Agent 能读能写的文件夹。dsh 会把所有文件操作都限制在这个目录里,避免它跑到你系统别的地方乱翻。
选完工作目录之后,还要选一个运行模式。这一步就是我上面提到的四种预设——标准、PTC、极简、创造。切换模式的本质是切换默认加载的插件集合,不是切换模型,这一点非常重要,下一章会详细讲。
到这里,第一个会话就可以开始了。莫潇羽的第一个测试问题非常朴素:让它把一个本地 Python 脚本从 Python 3.9 语法升级到 Python 3.12,同时保留原有的类型标注。耗时大约十几秒,改动的三处地方都比较到位,中间还主动跑了一次 python -m py_compile 校验语法,第一印象是"该有的都有,就是包装还很粗糙"。
如果你在启动过程中遇到任何问题,官方仓库的 Issues 区已经有一批开发者反馈,可以先去那里搜一下再提问,效率会高不少。
三、四种运行模式:切换的是"工具集"而不是"模型"
进到 Web UI 之后,选完工作目录,下一步就是选模式。dsh 官方给你预置了四种运行模式,官方叫做 Agent Preset(Agent 预设)。每个 preset 本质上是一个目录 + 一份 agent.cordis.yml 组合文件,里面定义了这个 Agent 的人格(persona)、默认加载哪些插件、提示词长什么样。
必须先把一个概念点破:切换 preset 切的是"这个 Agent 手上有哪些工具、按什么规则跑",而不是切换背后的模型。同样是 V4 Pro,跑在标准模式和跑在极简模式下,看到的世界完全不一样。
四种模式的分工大致如下:
|
模式名称 |
英文名 |
默认插件规模 |
适合谁 |
|
标准模式 |
Standard |
大全套(文件、Shell、检索、Skills、计划、子代理) |
绝大多数日常使用者 |
|
PTC 模式 |
Programmatic Tool Calling |
大全套 + 一个"呈现方式替换" |
在意 token 成本的重度用户与自动化开发者 |
|
极简模式 |
Minimal |
只有持久 bash + str_replace_editor |
想测模型本体能力的评估者 |
|
创造模式 |
Creative |
标准模式 + 运行时检查 + 插件试验能力 |
插件与 preset 创作者 |
下面挨个拆开讲。
标准模式:默认的全能工具箱
这是新手第一次跑 dsh 一定要选的模式。它把 dsh 官方定义的完整工具组合全都塞了进去:
- 文件编辑:读、写、按行精确替换、批量搜索。
- Shell 执行:持久化的 bash 会话,你之前
cd过的目录、export过的变量都还在。 - 文件与网页检索:本地文件全文检索、网页抓取与摘要。
- 技能(Skills):官方内置的一批工作流模板,例如"读一份 PDF 并提取表格"这种常见任务。
- 任务计划(Plan):让模型自己把复杂任务拆成子任务、跟踪进度。
- 子 Agent(Subagent):把某个子任务甩给一个隔离的子会话去干,主会话只等一个结果。
对标 Claude Code 和 Codex 的那一档就是这个。如果你只是想用 dsh 来跑日常写代码、修 bug、跑脚本这种工作,选它就够了。
PTC 模式:把工具打包成 TypeScript SDK
PTC 是 Programmatic Tool Calling 的缩写,直译"程序化工具调用"。这个模式是四个里唯一需要花心思理解的。
先看传统 Agent 是怎么调工具的:
sequenceDiagram
participant U as 用户
participant M as 模型
participant T as 工具
U->>M: 请求
M->>T: 调用工具 1
T-->>M: 返回结果 1
M->>T: 调用工具 2
T-->>M: 返回结果 2
M->>T: 调用工具 3
T-->>M: 返回结果 3
M-->>U: 汇总答复
每调一次工具,就是模型跟外部环境走一趟完整的往返:模型停下来,输出一段"我要调 XX 工具、参数是这些"的结构化文本,Harness 接住这段文本、真正去执行工具、把原始结果再送回模型的上下文里。一次操作序列有五步工具调用,就要五次模型往返,五份中间数据全都要挤进上下文窗口。
PTC 模式换了个思路:
- Harness 把所有可用工具自动打包成一份 TypeScript SDK,塞给模型。
- 模型现在面对的不是"一堆孤立工具",而是"一份类型完整的 SDK"。
- 模型只需要写一段 TypeScript 程序,程序里连续调用多个工具、做本地判断、汇总数据。
- Harness 只提供一个工具,叫
run_code,负责在一个隔离的 worker 线程里执行这段程序。 - 只有最终结果回到上下文里,中间那些原始数据都留在执行环境本地。
同样一段"要读十个日志文件、按错误级别分组、返回前 20 个高频错误"的操作,标准模式可能要跑十几次模型往返,PTC 模式一次就够了。
这个模式在 Aider 的 architect 模式、Claude Code 的 thinking tool 里其实都有类似思路——把"想"和"做"分开,是当前 Agent 领域最被看好的执行范式之一。
给谁用:PTC 主要是给把 Agent 当批处理工具、在意 token 成本的重度用户和做自动化的开发者准备的。日常写代码的场景,其实用标准模式就够了。
极简模式:只留 shell 和 str_replace_editor
极简模式的思路是"减法到极致"——整个 Agent 只有两个工具:
- 一个持久化的 bash 会话。
- 一个
str_replace_editor(按字符串精确替换的文件编辑器)。
其它什么计划、检索、Skills、子 Agent,统统没有。
这一份克制来自一句非常有底气的话:一个足够强的模型,只要有 bash 和文件编辑能力,就够干大部分编码活了。所有那些"计划、目标、子代理"其实都是外围脚手架,脚手架搭得好,弱一点的模型也能干活;脚手架越少,模型本体强不强就藏不住了。
所以这个模式的真正用途是:基准测试。想比较不同模型的编码能力,又不希望被 Harness 的脚手架污染判断,就跑极简模式。
事实上,DeepSeek 官方自己在跑 V4-Pro-0813 版本在公开基准测试集里的 Code Agent 表现时,用的就是极简模式,参数设置为 max 档、topp=0.95、temperature=1.0。你要是在别的框架里测同一颗模型,结果可能会有出入。
创造模式:为自定义 preset 而生的实验场
创造模式站在标准模式的肩膀上,额外挂了两样东西:
- 一个叫
dsh-tool-cordis的工具,能让你读取当前活体运行时(当前系统里加载了哪些插件、注册了哪些服务、绑定了哪些事件),甚至能在内存里临时挂载和卸载插件。 - 一个叫
editing-cordis-compositions的 Skill,专门指导 Agent 怎么写 preset 组合文件。
翻译成人话就是——创造模式是"做模式的模式",是给插件开发者、preset 创作者用的。你在里面跑 Agent,可以让 Agent 帮你去查看当前 dsh 装了哪些能力、然后帮你写一份新的 preset yml、还能实时试跑效果。
它的定位有点像 IDE 里的"开发调试模式"——普通用户不用管,但一旦你想自己做插件、做自定义 Agent,它就是你最重要的工作空间。
一个新手容易踩的坑
四个模式并不是"标准模式 > 其它模式"这种升级关系。它们是并列的、面向不同场景的预设。
有些同学第一次用 dsh,看到"创造模式"名字最响,就直接选了这个,然后被里面的运行时检查工具搞得一头雾水。我的建议是:日常写代码 90% 的时间选标准模式,需要跑大批量自动化再切 PTC,想跑纯粹的模型能力测试才切极简,只有当你决定自己动手做插件的那一天,才需要切换到创造模式。
顺便提一句,Web UI 上模式描述文案目前还比较简略,是 v0.1 的原始感。莫潇羽@无忆研习社前两天在群里聊到这块,大家一致的感受是——文案不动人,但底层设计非常硬,属于典型的工程师产品。
四、一切皆插件:Cordis 与"时空可组合性"的底层逻辑
前面反复提到"一切皆插件",这一章把它彻底讲透。因为如果你不理解这一层,你会觉得 dsh 只是"又一个 Agent 产品";理解了这一层,你会明白它想做的是"Agent 世界的操作系统"。
传统 Agent 的架构长啥样
先看竞品。今天一个 Codex 或者 Claude Code,你从架构角度看,本质上都是这样一条链:
模型 → System Prompt → Agent Loop → 工具调用 → Shell / 文件系统 → Sandbox → Session / Memory → Subagent → UI
厉害不在于"模型聪明",而在于外面这一整套 Harness 怎么组织。OpenAI 自己现在也明确把 Codex 的核心称为 agent loop / harness——它负责把用户输入、模型推理、工具调用、执行结果不断循环起来,直到任务完成。
Codex 的产品定位是"我给你一台调好的 Agent,你可以往外扩"。你可以加 MCP、加 Skills、换一些配置,但 Codex 的核心 Agent Loop、Session 怎么跑、什么时候调模型、内部怎么管理上下文,这些"core logic"是 Codex 这个产品定义死的,用户改不动。
Claude Code 相对开放一些,有 Skills、Hooks、Subagents、MCP,还有 Plugin。这里的 Plugin 是一个扩展打包层,把 Skills、Agents、Hooks、MCP Server 等打成一个可安装包。所以 Claude Code 的 Plugin 是"给 Claude Code 装插件"。
DeepSeek Harness 的 Plugin 是另外一回事:dsh 自己就是插件拼出来的。Agent = 插件 A + 插件 B + 插件 C + ……,没有"特权内核",没有"不能动的部分",每一颗螺丝都是可拆卸的。
Cordis 元框架做了什么
dsh 的底层用了一个叫 Cordis 的元框架。Cordis 本身不是 DeepSeek 独有的东西,它是 cordiverse 社区维护的一个开源项目,dsh 只是把它 vendored(内嵌打包)进了仓库,保证版本可控。
Cordis 的职责非常克制——它只干三件事:
- 插件的加载。
- 插件的卸载。
- 插件之间的依赖关系管理。
其它任何事情,Cordis 都不掺和。你可以把它理解成 Agent 世界里的"内核调度器"——它自己不做业务,只负责让别的插件能好好地按顺序上场、离场。
具体到 dsh 里,模型适配器是插件,工具注册表是插件,会话日志是插件,沙箱是插件,甚至 agent loop 本身也是插件。这些插件通过 Cordis 提供的"服务(Service)"和"事件(Event)"两个机制彼此协作,然后在配置层自由组合。
举一个特别极端的例子来体会这套思路的开放程度:
- 你觉得默认的 Agent Loop 不好?换掉。
- 你不想用它默认的模型接口?换 model adapter,dsh 在设计上并不绑定 DeepSeek 家的模型。
- 你不想让 Agent 在本机 Bash 里跑命令,想全部丢进远程 Sandbox?换 filesystem / subprocess / sandbox provider。
- 你觉得默认的 Session 机制不好,想自己做持久化、fork、resume?换 session provider。
- 你想在模型真正执行 Tool 之前插一层企业审查逻辑?不用魔改 Agent Loop,直接挂在事件钩子上。
这些能力不是"官方觉得应该开放才开放",是架构上就没办法闭死——因为整个 dsh 就是插件拼出来的,任何一块都能被同类插件替换。
时空可组合性:为什么"卸载"这件事这么难
Cordis 的设计论文《A Programming Paradigm for Spatiotemporal Composability》来自北京大学与 DeepSeek 研究团队的联合署名。这篇论文的核心讨论的是一个看似小、其实极难的工程问题:如何让一个正在运行的系统,能被安全地"动态组合"。
"动态组合"包含两层意思:
- 空间可组合性:系统里的组件之间怎么拼、拼在什么位置、彼此的接口怎么对齐。这一层业界研究得比较多。
- 时间可组合性:一个组件被卸载之后,能不能撤销自己在系统里造成的所有副作用,并且正确地把依赖它的其它组件的状态也一起处理干净。
第二点特别难。举个具象的例子:
假设你在 Agent 里挂了一个"数据库连接池"插件,它在启动时创建了连接、注册了健康检查定时器、订阅了一堆事件、还给别的插件暴露了一个 db 服务。现在你要在运行时卸载它——
- 那些连接怎么办?
- 那些定时器怎么办?
- 那些订阅的事件绑定怎么解?
- 依赖它的其它插件(比如某个"任务日志"插件天天写
db.log)怎么办?
如果不能干净地卸载,系统就会残留一堆"僵尸状态",越跑越脏。
Cordis 把每个插件的生命周期钩子都做得非常精细,任何一份服务创建、事件订阅、依赖注入都被记账管理,卸载时按记账严格逆序执行。这就是"时空可组合性"给 dsh 带来的最重要的红利——它让 Agent 的插件真的可以像积木一样被随意插拔,而不是"装进去就再也拿不出来"。
生命周期钩子:pre-step、request、execute、post-execute
dsh 在 Agent Loop 的每一个关键节点都暴露了"扩展点(extension point)",插件可以往这些节点上挂逻辑。常见的生命周期钩子包括:
|
钩子名称 |
触发时机 |
典型用途 |
|
|
每一步开始前 |
上下文预处理、限流 |
|
|
请求模型前 |
提示词改写、成本预估 |
|
|
工具执行前 |
参数校验、权限审查 |
|
|
工具执行中 |
自定义执行逻辑 |
|
|
工具执行后 |
结果加工、审计日志 |
|
|
一轮结束时 |
结果汇总、状态提交 |
这一套钩子的意义在于,很多以前只能靠"魔改源码"才能实现的能力,现在只需要写一个小插件就能挂上去。比如企业级审批流程:工具执行前必须走 OA 审批、审计日志必须落一份到公司安全平台、超过一定成本必须报备——全都可以做成插件,装到 dsh 上就生效。
莫潇羽@无忆研习社的观察是:这一套钩子的开放程度,已经不是 MCP 的级别了。MCP 解决"Agent 能调用什么",Skills 解决"Agent 知道怎么做什么",Hooks 解决"Agent 运行到某一步时,我插一脚",而"一切皆插件"想解决的是"Agent 本身应该怎么组成"——是更底层的一个问题。
Profile 与 Bundle:启动时的分层组合
再补一个小知识点——dsh 有 Profile 和 Bundle 两个概念,用来管理启动时插件的组合方式。
- Profile:启动配置文件,决定用哪种运行形态(web / headless / acp),加载哪些默认插件。
- Bundle:一组预打包的插件集合,可以被 Profile 直接引用。
你可以理解成"套餐"和"菜品"的关系。Profile 是套餐,Bundle 是菜品组合,插件是最小单元。启动命令 dsh --profile web 就是"我要吃 web 这一套套餐"的意思。
这套分层的好处是:企业用户可以给不同部门维护不同的 Profile,前端团队一个、后端团队一个、安全团队再一个,各自加载自己需要的 Bundle,互不干扰。这是"一切皆插件"真正落到组织生产力上的样子。
五、工具归一化与 Code Mode:一次成一段程序
这一节讲一个我认为最实用的设计——把工具全部归一化成一段 TypeScript SDK。
传统"一次调用一次往返"的开销
先把问题摆出来。传统 Agent 调工具的模式,是"每调一次工具,就是一次完整的模型往返"。
想查十个文件?那就是十个来回。
而且每一趟的原始数据都要塞进上下文——你查一个 100KB 的日志文件,那 100KB 的原始文本就要挤到模型的上下文窗口里,占位置、耗 token、还稀释了真正有用的信息。
对于长任务,这套模式的三个副作用会同时爆发:
- 成本高:模型往返次数 = 计费次数。
- 上下文脏:中间数据把窗口填满,模型对最终目标的记忆被冲淡。
- 延迟大:每次往返都要等模型思考、等网络传输。
对于希望"跑一个自动化脚本、批量处理一堆文件"这类任务的用户来说,这是一个非常真实的痛点。
run_code + TypeScript SDK 的思路
dsh 的 PTC 模式换了个思路,我用一段对比图来说明:
flowchart LR
subgraph 传统模式
A1[模型] -->|调用| B1[工具1]
B1 -->|原始数据| A1
A1 -->|调用| B2[工具2]
B2 -->|原始数据| A1
A1 -->|调用| B3[工具3]
B3 -->|原始数据| A1
end
subgraph PTC模式
A2[模型] -->|写一段程序| C[run_code]
C -->|连续调用| D1[工具1]
C -->|连续调用| D2[工具2]
C -->|连续调用| D3[工具3]
D1 --> E[本地汇总]
D2 --> E
D3 --> E
E -->|只返回结论| A2
end
具体做法是这样的:
- Harness 把所有可用工具的签名、参数、返回值类型,自动打包成一份完整的 TypeScript SDK。
- 模型看到的不是"离散的工具列表",而是一份类型完整、有 IDE 级提示体验的 SDK。
- 模型只需要写一段 TypeScript 程序,程序里可以自由地调用 SDK、写循环、写条件、做本地汇总。
- Harness 只提供一个真正的对外工具,叫
run_code,负责在一个隔离的 worker 线程里执行这段程序。 - 执行过程中的中间数据全部留在 worker 线程里,最后只把汇总好的结论回传给模型。
一段实际的 PTC 程序可能长这样:
// 模型自己写出来的一段代码,运行在 worker 里
const logs = await Promise.all(
['error-01.log', 'error-02.log', 'error-03.log'].map(
(name) => fs.readFile(`/var/log/app/${name}`)
)
);
const parsed = logs.flatMap(parseErrorLines);
const grouped = _.groupBy(parsed, 'errorCode');
const top20 = _.chain(grouped)
.toPairs()
.sortBy(([, items]) => -items.length)
.take(20)
.value();
return top20; // 只有这一份汇总数据回到模型上下文
中间那三份日志文件的原始文本,模型压根就没看过。上下文窗口里只留下最终那个"前 20 个高频错误"的结论。
收益到底有多明显
从直观感受来说,PTC 模式的三个收益是非常清晰的:
|
维度 |
传统模式 |
PTC 模式 |
|
模型往返次数 |
十次调用 = 十次往返 |
一次调用 = 一次往返 |
|
上下文清洁度 |
原始数据全塞进上下文 |
只留最终结论 |
|
成本 |
按往返次数堆叠 |
单次程序化调用 |
|
延迟 |
累加型 |
一次性 |
|
可复现性 |
依赖模型每次生成 |
一段代码,可复用 |
莫潇羽@无忆研习社自己在跑一个"扫描本地 200 多个 Markdown 文件、抓取每个文件的 title 和 date、按月份聚合"的任务时,同一颗模型(V4 Pro)用标准模式跑了大约 4 分钟,切到 PTC 模式重跑了一次,30 秒不到就结束了。token 消耗差了将近 20 倍。
权限、审计、超时并没有被绕过
看到"模型自己写代码然后执行",很多人第一反应是"这不是脱缰野马吗"。这里必须澄清一下:PTC 模式并没有把权限体系绕开。
run_code 执行的那段 TypeScript 程序,仍然是通过 dsh 的工具注册表调用工具——工具注册表本身留在宿主层,SDK 只是"呈现"给模型的一层皮。
所以:
- 每一次工具调用,仍然要走权限校验插件。
- 每一次工具调用,仍然会写进审计日志。
- 每一次工具调用,仍然受超时策略约束。
- worker 线程本身跑在一个受控的沙箱里,网络访问、文件系统访问都可以按 profile 配置来限制。
这就意味着企业内部用 PTC 模式跑 Agent 的时候,你能拿到的可观测性和可控性,跟标准模式是一样的。"程序化"提升的是效率,不是牺牲安全性。
一个新手容易踩的坑
PTC 模式虽然强大,但不是万能的。它有两个前提:
- 模型本身要有足够强的代码生成能力。弱一点的模型写出来的 TypeScript 程序可能会有语法错误或者调用错 API,反而增加返工次数。
- 任务本身要适合被"程序化"。如果是一个高度依赖对话、每一步都要跟用户确认的任务,硬塞 PTC 反而不好用。
莫潇羽自己的判断是:日常写代码、修 bug、和 Agent 一来一回讨论的场景,用标准模式;批量数据处理、大规模脚本化任务、需要严格控制成本的场景,切 PTC。别把两个模式看成"高级"和"低级",它们是解决不同问题的工具。
六、事件可溯源:append-only 的会话日志
Agent 一旦跑起了长任务,一个非常真实的问题就会浮出水面:它到底在干什么?
跑十几分钟命令没有反馈,你不知道它在调查、卡住,还是已经顺着错误方向一路狂奔。莫潇羽自己用别家 Agent 的时候最难受的一点就是这个——它不说话。
dsh 从架构层直接把这件事的答案给了:每一次运行都有迹可循。这一节就讲这个设计。
什么叫仅追加事件流
dsh 把整个会话做成了一条仅追加(append-only) 的事件日志。所谓仅追加,意思是这条日志只允许新增,不允许删改。已经写进去的每一条事件,永远都在,可以往前回放,但不能被后面的操作抹掉。
这条事件流里会记什么?基本上是模型看到、做过、经历过的一切:
- 系统提示词(System Prompt)
- 模型的思维链(Chain of Thought)
- 工具调用与工具返回结果
- 子 Agent 的创建、状态更新、销毁
- 每一次上下文注入
- 用户的每一次输入、每一次取消、每一次授权
这些都作为不可变的事件被序列化,写进日志。
开发者可以在 Web UI 里的 Trajectory(轨迹)视图 里,按来源查看这些事件,一步一步复盘 Agent 到底怎么想、怎么做的。
恢复、分叉、检索、回放:都是同一份事件流
有了这条事件流,很多以前很难做的事情,突然就变得简单:
|
能力 |
说明 |
|
会话恢复 |
Agent 崩了、你 Ctrl+C 了、笔记本睡眠了,重新打开就能从上次的状态继续 |
|
会话分叉 |
在某一步上"如果我当时选择另一条路会怎样",可以从那一步 fork 出一条平行会话 |
|
会话检索 |
用关键词、时间、工具类型去搜历史事件,比翻聊天记录精准得多 |
|
会话回放 |
把整个执行过程像录像一样重放,特别适合 debug 和写案例 |
关键在于——上面这四种能力,用的是同一份数据源。
传统 Agent 里,这四件事往往要各自维护一份状态:恢复用状态快照,分叉用另一套复制机制,检索靠外挂搜索引擎,回放需要额外埋点。dsh 把它们统一到一条事件流上,所有能力都从这一条流里派生出来。
这套设计的意义远远不止"日志"这么简单。它让"经验性评测"成为可能:如果你想比较模型 A 和模型 B 在同一个编码任务上谁表现更好,你需要看到每个模型实际看到了什么、做了什么、走了多少弯路,而不只是最终的输出。Harness 从设计上就把这一切都完整记下来了。
跨会话引用与"极度渴望上下文"的模型脾气
事件可溯源还有一个衍生能力——跨会话引用。
昨天你跟 Agent 讨论过的一个方案、上周它给你写过的一个脚本,这些都不是孤立的对话记录,而是活在事件流里的可检索资源。今天开一个新会话,你可以让 Agent 直接引用那些历史事件,甚至让它"继承"过去某次任务的中间状态。
这里插一句实际使用感受。DeepSeek V4 Pro 这颗模型本身有一个特点——它非常渴望上下文。跑在别家 Harness 里的时候,它经常主动去翻别的 session、把旧结论抄过来充当当前答案。莫潇羽刚开始用的时候被这一点搞得挺头疼,感觉它有点"越界"。
到了 dsh 里,这个"毛病"反而对上了拍——跨会话引用本来就是官方鼓励的能力。模型的性格,和 Harness 的设计,是配套的。同一颗模型,塞进不同 Harness,跑出来的观感真的会完全不一样。
实操层面能感受到什么
从用户视角讲,事件可溯源给你带来的最直观改变是这几个:
- 每次任务都有一个 Trajectory 视图可以点开,看到 Agent 每一步的完整轨迹。
- 子 Agent 的 token 消耗、运行时长、当前状态,都在界面上实时挂着。
- 主会话底部有一条实时状态栏,滚动显示当前系统在做什么。信息密度比很多同类产品充实太多。
- 任何一次卡壳,都能从 Trajectory 里定位到具体哪一步出了问题,而不是像黑盒一样只能重跑。
莫潇羽@无忆研习社的建议是:用 dsh 的时候一定要养成看 Trajectory 的习惯。前几天有一次 Agent 反复报错,我在 Trajectory 里翻了三分钟就定位到问题——不是模型的问题,是我给它的一份 CSV 编码不对,它每次读进来都乱码,然后靠"感觉"往下猜。这种问题在没有事件流的产品里,你可能要跟 Agent 拉锯十几个来回才能搞清楚。
可观察性是 Agent 走向生产环境的最后一公里,dsh 从架构层就把这件事做好了。
七、Agent 统帅:把 Codex 和 Claude Code 当作可调度的执行单元
这一节是我认为整个 dsh 里最有想象空间的部分。
不是"兼容配置格式",是真的能拉起
dsh 内置了真实的 Codex 子 Agent 后端 和 Claude Code 子 Agent 后端。
注意,不是"兼容它们的配置格式"这种表面功夫。而是——如果你本机装了官方 Codex 和 Claude Code,dsh 真的可以把它们当作两个可调度的"执行单元",从主会话里派任务给它们,等它们干完,把最终答案取回来。
用一张示意图来看:
flowchart TD
A[dsh 主 Agent 统帅] --> B[本机 Codex]
A --> C[本机 Claude Code]
A --> D[dsh 原生子 Agent]
A --> E[另一个 dsh 会话]
B --> F[任务结果 1]
C --> G[任务结果 2]
D --> H[任务结果 3]
E --> I[任务结果 4]
F --> A
G --> A
H --> A
I --> A
dsh 的定位在这里彻底摊开了——它不只是一个 Agent,它想做一个 Agent 调度层。
你已经在用的那些 Agent 工具,在它眼里都是可以被调度的资源。
什么场景下这个能力会派上用场
这个能力目前还比较小众,但方向感非常明确。莫潇羽@无忆研习社能想到的典型场景至少有三类:
第一类:多 Agent 分工协作。
一个复杂项目里,前端代码交给 Claude Code(它在前端 UI 编排上确实有独到之处),后端服务交给 Codex(它对 Pyt