我从 DeepSeek Harness 学到的

8 月 13 日,DeepSeek 把自己的 agent harness 开源,MIT 协议,和 V4-Pro 转正是同一天。再往前两天,Composio 的实验刚好给这类东西定了调:同一个模型放进八个 harness 跑三十个真实工作流,成功率从 46.7% 摆到 66.7%,每完成一个任务的成本差七倍。
发布后最让我上心的评价,来自本该最挑剔的那个人。Composio 那场测试里成绩最好的 harness 是 Pi(这个名次 Composio 自己也说别当真——Pi 用的推理设置和别家不同),而 Pi 背后公司 Earendil 的联合创始人 Armin Ronacher 看完仓库后说:"我不觉得 DeepSeek Harness 是完美的,但这确实是我第一次看着这个领域里的新东西,很想回头重新审视我们自己的一些选择。"赢家看完对手的作业,说想回去改自己的——这一句比任何发布稿都有分量。
所以我没停在别人的评价上,自己花了几天钻进仓库:十四个分析 agent 扫过源码树,锚定在 47f9438 这个 commit,逐文件核对了几百条论断。六月我写过,AI 工作的单位已经从一次回答变成一次运行,稀缺的技能是给运行立规矩——这个仓库是第一次能把一家厂商的整套规矩从头读到尾。下面是我学到的四个重要的设计。
一、模型只被允许看到日志里的东西
DeepSeek Harness 的每个会话是一条只增不改的事件日志。仓库定义了 44 种事件,其中只有 3 种是模型看得见的:你说的话、它说的话、工具返回的结果。剩下 41 种——审批、轮次边界、配置变更、计费——全是记给人和审计看的账。画出来大概是这样:
seq 101 user/message 你的指令 → 模型可见
seq 102 assistant/message 模型的回复(含工具调用) → 模型可见
seq 103 approval/decision 你点了「允许」 → 只进日志
seq 104 tool/result 工具的返回 → 模型可见
seq 105 turn/end 本轮结账(token 用量) → 只进日志
让我停下来的是这个细节:系统里没有单独存一份"发给模型的对话"。存的只有日志——每次请求之前,那份对话都从日志现算出来。熟悉区块链的人会觉得眼熟:账本是唯一的事实,账户余额从账本推导,没人把"某人有五个币"单独记成一条数据。这里借的正是这一条思想——状态是历史的纯函数——而且不需要共识和加密那些重机器。"模型看到的一切必须能从日志重建"也不是文档承诺,是一个真的会拦你的运行时检查,机制说白了就是:
每次调用模型之前:
期望的上下文 = 从会话日志重新推导出来
if 期望的上下文 != 即将发出的请求:
拒绝发送(报错:日志重建不一致)
换句话说,任何代码想"偷偷"给模型塞一段上下文,都过不了这道闸——不写进日志,就发不出去。

这换来什么?说两个你大概率遇到过的场景。
第一个:agent 突然做了件莫名其妙的事——引用了一段你从没给过它的旧代码,或者放着现成的文件不读、自己编了一个。在大多数工具里,这时候你只能猜:是压缩的时候把关键内容吞了?是哪个插件偷偷注入了什么?在这套设计里不用猜。把日志推导到那一步,模型当时看到的上下文逐字节摆在你面前——是摘要吞了信息,还是注入出了问题,一眼定案。
第二个:跑了两小时的长任务在第 80 步断了,进程崩了,或者你合上了电脑。因为上下文本来就是从日志现算的,重启后从同一份日志接着算,模型看到的和断之前逐字节相同,任务接着跑。想从第 50 步分叉出去试另一条路?复制日志前缀就行。顺带的好处还在往上摞:token 用量盖在每个事件上,月底账单吓到你的时候,日志能直接告诉你钱花在哪个环节;他们的测试系统也骑在同一条性质上——录一次真实会话,日志本身就是回放脚本,CI 里不需要 API key。
代价也是实打实的,而且值得放慢讲,因为 Anthropic 在同一个问题上选了反方向。他们的代码执行接口支持常驻执行环境:模型写的程序跑完一步,变量还留在内存里,下一步接着用——就像一直开着的 Jupyter notebook,上一个单元格算出的变量,下一个单元格直接拿来用。好处很直接:模型在处理一份大数据集时,解析结果留在内存里,后面十步不用重新加载,也不用把中间结果搬进上下文窗口,又快又省。
DeepSeek 的设计笔记里写了他们为什么拒绝这条路:变量留在内存里,就意味着 agent 的一部分状态不在日志里——会话断了接不上,回放对不齐,"模型为什么这么做"又变回悬案。而守住"每个请求都能从日志重建",代价是每个要紧的中间结果都得走一遍日志,多花 token,多花时间。
这笔账两边都摊得开:Anthropic 那个接口面向开发者跑计算,性能优先;DeepSeek 造的是产品级 agent 平台,可恢复、可审计本身就是产品承诺。没有谁对谁错,但你给自己的系统选型时,这就是那条要划的线——省下来的性能,是用"出了事说不清"换的。另外日志只会越来越长:压缩只是把旧内容从模型视野里遮掉,一个字节不删。省的是 token,不是硬盘。
如果只能带走一样,我带那条断言。在自己的系统里加五行:每次请求前,把要发出去的上下文和日志里推导出来的比一遍,不一致就报错。它给你的东西很具体:模型行为不对,你查得到它当时到底看到了什么;长任务断了,能原样接上;哪段代码偷偷改了上下文,当场暴露,不用等上线之后靠猜。
二、Agent 主循环就是一行配置
"一切皆插件"听起来像营销话术,在这里是字面意思。驱动 agent 的主循环——决定什么时候调模型、跑工具、继续还是停下的那个东西——在默认配置里就是普通的一行:
# packages/bundle/base/cordis.patch.yml
- id: agent-loop
name: '@deepseek-ai/dsh-agent-loop'
config:
agents: [] # 预配置的常驻 agent 清单,留空表示按需创建
关掉它不用改代码:disabled: true 是每一行配置都认的开关,加在哪一行,哪个插件就不挂载。这不是理论上的能力,出厂配置自己就在用。同一份文件里,bash 沙箱和 PowerShell 沙箱靠两个相反的平台条件各挂各的,还有一个插件发了货、又在默认配置里关着:
- id: bash-sandbox
name: '@deepseek-ai/dsh-bash-sandbox'
disabled: !!js process.platform === 'win32' # Windows 上不挂载
- id: pwsh-sandbox
name: '@deepseek-ai/dsh-pwsh-sandbox'
disabled: !!js process.platform !== 'win32' # 只在 Windows 挂载
- id: skill-badge
name: '@deepseek-ai/dsh-skill-badge'
disabled: true # 发了这个功能,默认关闭
把同一个开关放到 agent-loop 那一行上,就没有 agent 了。主循环和一个小徽章插件,在配置系统眼里是平级的。
对你来说,这意味着攒一个自己的 agent 是配置活,不是开发活。极简模式整个 agent 只有 62 行,开头长这样:
- id: persona
name: '@deepseek-ai/dsh-persona'
config:
text: You are a helpful software engineer assistant.
一行 persona,一个持久 bash,一个文件编辑器,就是一个能干活的 agent。想换性格、砍工具、改模型路由,动的都是这样的行。想知道自己机器上到底跑着什么,dsh --dump-config 把实际启动的整棵树打出来,每段配置上方注明它来自哪个文件、被哪几层补丁改过,打印出的每一行都能被你自己的补丁覆盖。可换到什么程度?把文件系统和子进程这两行 provider 从本地实现换成 E2B 的远程实现,Bash、终端、LSP 整套工具跟着搬进远程沙箱——工具本身一行代码不用改。
界面上的四种"模式"(标准、代码、极简、创造)就是四份这样的 YAML。最能说明问题的对比是代码模式和标准模式:抛开注释,两份文件的配置一模一样——同样的 bash、文件工具、skill、压缩——差别只有末尾追加的一行:
- id: tool-presentation
name: '@deepseek-ai/dsh-agent-tool-presentation'
config:
mode: code
这一行改的是"工具怎么呈现给模型"。标准模式下,模型看到二十多个工具的说明书,一个一个直接调用;加上 mode: code,模型看到的变成一个 run_code 工具加一份自动生成的 TypeScript 接口——它改成写一段程序来编排这些工具,中间结果留在程序里,不占上下文窗口。整个交互方式翻转了,只动了一行——因为"工具的呈现方式"在这套架构里也是一个可替换的插件。
这种灵活性绕不开一个问题:配置写错了怎么办?代码写错,编译器会拦;YAML 写错,没人拦,而且错得特别安静。DeepSeek 自己翻过两次车。一次是一个条件表达式写错了位置,文件读写工具在所有模式下集体消失——不报错,就是没了。另一次是插件多写了一行 export default,加载器悄悄丢掉了它的依赖声明:178 个测试全绿、覆盖率 100%,产品一连上真实编辑器就崩。他们的对策也直接:每翻一次车,就加一道发布前的静态检查,专门拦那一类错。这样的检查现在有 27 道——等于把编译器的活,用脚本一件一件买回来。想用"一切皆配置"这套思路,就得连这笔账一起接下来。
我的判断:对押注生态的平台,这笔交易是自洽的。对我们大多数人,值得学习的是它的透明度和开关纪律——系统实际跑什么,一条命令可见;每个组件,一行配置可关;每一类踩过的配置错误,配一道机器检查。
三、缓存纪律,用 CI 强制执行
先看钱。按发布那周的定价,DeepSeek 的缓存命中价格大约是未命中的五十分之一(V4-Flash)到一百二十分之一(V4-Pro)——北京时间 8 月 17 日 00:00 起改为分时定价,倍数压到 30 倍左右,但量级摆在那。对一家卖 token 的公司,你的请求有没有命中缓存,不是性能细节,是毛利。
这里得先补一个很多人(包括写这篇之前的我)都模糊的背景:这个缓存到底是谁在管、怎么工作的。打个比方:模型读你的提示词,像一位按小时计费的律师读合同。第一次,他从头读到尾,全价。第二次你拿来同一份合同,只在末尾加了一条新条款——他不用重读前面,从新条款读起,前面的部分按"记忆费"收个零头。但要是你把第一页改了一个字,他就得从那里起整份重读,因为他对每一段的理解都建立在前文之上。LLM 完全一样:服务商把你上次请求里已经"读过"的前缀的计算结果存着,下次请求若以逐字节相同的内容开头,存过的部分按折扣价收,从第一个不同的字节开始才全价重算。

所以分工是这样的:缓存发生在模型服务商那一端,命中与否却由 harness 决定。 各家的开法不太一样——DeepSeek 和 OpenAI 自动缓存前缀,Anthropic 要你显式标记缓存点——但道理相同:存不存是服务商的事,你发过去的内容对不对得上,全看 harness 怎么组装请求。服务商负责存,harness 负责别把钥匙弄丢。
而弄丢钥匙非常容易。最常见的一种:很多框架的默认模板会把当前时间写进系统提示词开头——
坏:系统提示词第一行「当前时间:2026-08-19 09:32:07」
→ 每秒都在变,前缀从第一行起就对不上,每个请求整份全价
好:系统提示词里没有时间
→ 真需要时间时,作为一条普通消息追加到对话末尾
(DSH 默认压根不注入时钟)
→ 前缀逐字节不变,只有新增的尾部按全价算
DSH 就是按"好"的那种做的。第二种丢法:工具说明书的顺序跟着插件加载顺序走,加载顺序一变,说明书重排,前缀就碎了——所以 DSH 不信加载顺序,一律按固定的字典序排。第三种最隐蔽:随便哪个新插件往提示词里塞了点会变的东西,全团队的缓存跟着碎,还没人知道是谁干的——所以他们规定每个包的 README 必须写一节"KV Cache effect",声明自己对前缀的影响,缺了 CI 直接红(只有四个审计过、与模型无关的包豁免)。最狠的一道是连真实 API 的测试,直接断言缓存必须命中:
// packages/core/agent-loop/tests/request-cache.e2e.ts
// 会话里除第一个请求外,每个请求都必须报告缓存命中
for (const usage of usages.slice(1)) {
expect(usage!.cacheReadTokens ?? 0).toBeGreaterThan(0)
}
它敢这么断言,靠的是第一节那套日志设计:每个请求都是上一个请求的延长,前缀天然稳定——两套设计在这里咬合上了。任何打碎前缀的回归,在烧到钱之前,CI 先红。
这套纪律值多少钱,拿一个长会话算一下就明白。一次请求的前缀——系统提示词加工具说明书加全部历史——动辄上万 token;一个几十步的任务,要把这个前缀反复发几十遍。命中,这几十遍按零头计价;不命中,遍遍全价。同一个任务,账单能差出一个量级。大多数框架没有人管这件事,模板里带着时间戳、工具顺序随加载而变,命中与否全凭运气;DSH 把它当成带测试的不变量来管。这也是"谁造的、为什么造"最清晰的指纹——只有模型公司会围绕自家价目表设计 harness。
但这套纪律管的全是字节,管不到时间——而每份缓存都有寿命。DeepSeek 自己的文档写着,不再使用的缓存会被自动清空,"时间一般为几个小时到几天",并且明说整套机制是"尽力而为";Anthropic 那边紧得多:五分钟没被用到就过期,而且计时是从上一次用到它那一刻起算的。钟走的是闲置时长,不是年龄,而且都不归你调。周末搁下,周一回来,第一个请求要为整个前缀重新付一遍——在写缓存要加价的那几家,比全价还贵。不是你哪里拼错了,是保险箱趁你不在的时候自己清空了。
DSH 没有解决这件事,它把边界写了下来。那条管着所有 README 的规矩里写得很清楚:服务商缓存的"可用性与逐出不在包契约范围内"。会话恢复能不能复用缓存,也只看重建出的历史、当前信封和模型路由对不对得上——没有"你离开了多久"这一项。harness 只认字节稳定,时间明确不归它管。这是诚实的分工,也是这几个设计第二次咬合的地方:搁久了,折扣没了,会话照样一字不差地重放——因为留下来的是日志,不是缓存。
这三样:固定排序、易变内容出前缀、缓存命中断言,任何技术栈这周就能用上,跑谁家的模型都省钱。
四、它是"AI 建造复杂系统"迄今最清晰的公开标本
这个仓库用 64 天写了 12,293 次提交。37 位署名作者里,第一名一个人提交了 5,235 次,占全仓 42.6%。仓库里 markdown 文件比 TypeScript 文件还多。我又数了一遍合并记录里的分支名:worktree/ 出现 210 次,codex/ 209 次,agent/ 15 次,claude/ 3 次——codex/ 是编码 agent 建分支的默认命名,两百多次不是手滑。产码的主体不是人。
Sawyer Hood 那句玩笑——"2026 是 harness 自己建造自己的一年"——这个仓库就是实物。而它真正值得学的,是围绕这个事实建起来的整套打法。
开工第二天的一篇流程笔记把理论说破了:相比写在文档里的约定,agent 遵守被强制执行的检查要可靠得多;而当劳动由 agent 完成时,"工作量太大"就不再构成反对理由。往下全是这句话的推论。每个非平凡改动必须附带设计笔记,现在有 683 篇。有一个 rejected/ 目录冻结着被否决的方案和否决理由,只要那份理由还拦得住一个诱人的错误,就一直留着——用过 AI 的人都认得这是什么,一套防止 AI 重提死方案的免疫系统。事故复盘不许以"教训"收尾,必须以机器检查收尾,还得验证过"把 bug 恢复回去,检查真的会红"。
明显的反驳是:文档既然由 agent 来写,数量什么也证明不了——683 篇也可能只是机器吐出来的废纸。这套制度的回答长在自己的规则里:否决记录一旦拦不住错误就删掉,被取代的笔记移进冻结档案,复盘要验证过"检查真的会红"才算数。值钱的是那道过滤,不是那个数字。
我在这上面停的时间最长,因为它回答的问题比 harness 大:让 AI 大规模建造复杂软件,已经从演示变成了常规操作——而这个仓库把操作手册留在了明面上。 展开说是三层。
团队形态变了。37 个人、两个月、一万两千次提交,人的岗位从写代码挪到了设定规则、裁决证据、审批合并。这正是我七月写的 one-person project 在工业尺度上的样子:一个负责人握住上下文,AI 执行,团队管边界和证据——只不过这里的"一个人"带着一整支 agent 车队。

流程的经济学反转了。"每个改动配一篇文档"在人力团队里是官僚主义,会上活不过三分钟;在 agent 团队里是防漂移的护栏,因为写的人不喊累。于是规则可以定得比以前密得多。他们画的那条线也值得直接拿来用:机器能检查的,全部交给机器;需要判断力的,留给人。他们刻意留给人的那条规则,是判断"这个改动重要到需要写一篇设计笔记吗"——这件事机器判不了。
质量的定义也变了。覆盖率和绿色测试在这个仓库里被自己的事故证明过靠不住(上面那个 178 个绿测试的故事),所以他们把质量押在别处:真实加载路径的端到端测试、验证过会变红的闸门、被冻结的否决理由。这些不是工程洁癖,是把"AI 会犯的错"翻译成系统约束的管理结构。
就算 DSH 一直像 Ronacher 说的那样"不完美",最后也没流行起来,这本操作手册已经公开了。对任何在用 AI 做东西的团队,这是仓库里最值钱的部分。
从代码里看 DSH 的战略
三个动作,都能去仓库里验证。第一,它直接采用了对手的标准:skill 用的就是 SKILL.md 格式(设计笔记显示他们把自家字段拼写改成了 Claude 的写法,连兼容旧拼写的别名都拒绝保留),零配置读取 ~/.agents/skills,优先加载 AGENTS.md、兼容 CLAUDE.md——你的 skill 和指令文件第一天就能用。第二,不支持的功能它明说:Claude Code hooks 兼容层的 README 写明 30 种事件有 23 种不支持,不装克隆。第三,它把竞争对手变成了自己的插件:Claude Code 和 Codex 被接成 subagent,任务可以直接委托给对手的产品跑。
有一种更省事的解释:把一个体验还落后的 harness 白送出去,DeepSeek 没什么损失,开源还白赚一波好感。这个解释到此为止都对——但它解释不了上面那三个动作。赚好感犯不着去做那些不露脸的工程活:兼容对手的格式,还把自己不支持什么写成文档。我的读法——也只能是读法,因为意图不写在仓库里:Anthropic 把 harness 闭源、和订阅绑定,是给模型修护城河;DeepSeek 把 harness 白送、让它读所有人的格式,是给自己真正卖的东西修漏斗:token。七月我论证过稀缺的是 deployment 能力,不是模型访问权,当时的证据是各家在部署工程师身上砸几十亿美金,这次是同一个赌注的软件形态。这里面还有一层:当一家厂商采用对手的文件格式,对手用户手里的资产就变成可携带的了。可携带性是把双刃剑,DeepSeek 主动把它磨快了。
最后说说它现在的状态,以下都是仓库自己写在文档里的:防止模型死循环的机制只会发提醒,不能强制打断,提醒发够次数之后就不再管了;文件读、写、编辑三个工具没有任何超时,bash 的超时也不归守卫插件管,只有 shell 执行器自带的默认兜底;Claude Code hooks 兼容层收到"拦截"指令(continue: false)时只记录下来,并不真的拦。一位提前拿到仓库权限的开发者说得很直白:日常体验还落后于 Claude Code 和 Codex。Ronacher 那句"不完美"是准的——这周就要干活,它还不是你的首选。
我学到了什么
三件事我会直接用到自己的系统里。第一,请求前断言:五行代码,每次调模型前核对上下文和日志一致,出问题当场报错。第二,缓存三件套:工具固定排序、时间这类会变的信息不进提示词前缀、加一条测试盯着缓存命中。第三,在自己的仓库里建一个 rejected/ 目录,把否决过的方案和理由存下来——我的 agent 也会把上个月毙掉的想法再提一遍。
看大局,我的结论也更清楚了。模型是租来的,本来就设计成可以换。harness 这一层还会大改——仓库自己都用全大写警告"兼容性说破就破",现在花大力气深度绑定任何一家,很可能白费。真正属于你、而且越攒越值钱的,是每个 harness 都会读的那层:你的 skill、你的指令文件、你记下来的"什么有效、什么被否决"。
所以我给自己定的规则,也推荐给你:模型随时可换,harness 别深度绑定,重点攒每个 harness 都认的那层资产。这周花一小时,把你的 AI 资产列两栏——能带走的、被锁死的。这张清单比任何评测都更能告诉你,接下来该把时间花在哪。

本系列下一篇:第一次请求的账单。在你对 agent 打出第一个字之前,你已经在付一笔固定的入场费——我会在自己的技术栈上复现 token 级的测量,看看我的入场费是多少。订阅邮件列表,第一时间收到。
