
🚩 2026 年「术哥无界」系列实战文档 X 篇原创计划 第 198 篇,DeepSeek Harness最佳实战「2026」系列第 01 篇
大家好,欢迎来到 术哥无界 | ShugeX | 运维有术。
我是术哥,一名专注于 AI 编程、AI 智能体、Agent Skills、MCP、DeepSeek Harness、AIOps、Milvus 向量数据库的技术实践者与开源布道者!
Talk is cheap, let's explore。无界探索,有术而行。

2026 年 8 月 13 日,DeepSeek 把一个名为 DeepSeek Harness 的仓库设为公开。官方口径是 v0.1 Developer Preview、MIT 协议,npm 包 @deepseek-ai/dsh 当天发布 0.1.0-rc.6。两天后的 GitHub API 快照显示 star 数到了 95,386(2026-08-15,flowtivity 快照)。两天的开源项目拿到这个热度,讨论却没有汇成一个共识,而是劈成两半:一半人骂它没法用,一半人捧它是 Agent 界的操作系统。
骂的和捧的吵的是同一个东西吗?本文把 deepseek-harness 和 opencode 两个仓库的源码放在一起逐层对读,想回答的问题是:两者的差异到底是产品成熟度之差,还是架构范式之差。这个答案决定 DeepSeek Harness(下文简称 DSH,命令行名 dsh)对谁有价值、对谁是负担。
先给本文的统一分析框架:看一套 Harness 把设计自由度放在哪一层。opencode 把脊柱固定、把自由度交给边缘扩展点;DSH 把自由度给到每一层,连 agent loop 本身都是一个可替换的插件。后文的社区分裂、配置体系、机制对照和采纳建议,都沿这条轴展开。
声明:DSH 处于 developer preview 阶段,官方明示会有破坏性变更。本文所有事实来自两个本地源码仓库的走读(DSH HEAD 2026-08-21,v0.1.1-rc.2;opencode HEAD 2026-08-19,v1.18.18)与公开材料,本次未部署、未运行任何 harness,不包含任何性能实测数据,不构成两者优劣排名。
免责声明:本文基于 DeepSeek Harness(v0.1.1-rc.2,2026-08-21 HEAD)与 opencode(v1.18.18,2026-08-19 HEAD)两个开源仓库的本地源码走读及公开网络材料写成,写作时未部署、未运行任何 harness,不包含性能实测数据。文中所有第三方数据(star 数、token 用量、基准成绩等)均标注来源与时点,仅供理解架构差异之用,不构成产品优劣排名或选型唯一依据。DSH 处于 Developer Preview 阶段,官方明示会有破坏性变更,相关机制可能随版本调整。
先对齐发布事实。仓库公开时间为 2026-08-13 11:56 UTC(GitHub API,经 flowtivity 与 VentureBeat 记者 Carl Franzen 的报道交叉核对),官宣渠道是 X 帖、GitHub 与 deepseek.com/harness。发布当天的 git 历史只有 1 个 commit(一个 squash 合并),而截至本文写作的本地 HEAD 已经累积 13,147 个 commit(v0.1.1-rc.2,2026-08-21)。两个事实都真,前者说明它是一次性落地的内部开发成果,后者说明发布后两周开发已经完全铺开。网络上流传的「7 月就已开源」与 GitHub API 的创建时间冲突,本文以 API 为准。
官方对自己的定位写在 README 第一屏:an open-source agent harness developed by DeepSeek AI,架构特点是 everything is a plugin,底座是 Cordis 这个元框架,其设计出自北京大学与 DeepSeek-AI 联合的论文《A Programming Paradigm for Spatiotemporal Composability》(论文仓库 cordiverse/paper;88 页的页数说法来自第三方读后记,仅此单一来源)。官网页面的表述更直接:Agent = Model + Harness,Everything is a plugin. Every run is traceable。
harness 这个词本身就是定位声明。模型之外、承托它运转的部分:工具、会话、权限、循环、界面,都算 harness。仓库形态与之互证:
分组 | 职责(依据 packages/README.md 官方分组表) |
|---|---|
| 产品主干:agent-loop、tools、session、system-prompt 等 |
| 模型能力族:抽象服务 + provider 适配器、token-meter |
| 文件系统能力族、Bash 能力族(接口 + 本地/沙箱实现 + 模型侧工具) |
| 持久会话数据面:persistence seam + JSONL/SQLite 后端、投影 |
| 上下文压缩能力族 |
| 按会话的 agent 组合 |
| 审批、权限预设、命令、ask-user 工具 |
| 进程外 SDK、Automation-only ACP server |
| 可安装的 profile 补丁层:base、web-app、headless |
本地清点(2026-08-26):packages/ 下 50 个分组目录、227 个含 package.json 的实际包,pnpm workspace 管理,npm scope 为 @deepseek-ai/dsh-*。第三方在 2026-08-13 测得的是约 219 个包,两周差出 8 个包,引用这类数字必须带时间点。
更极端的一步在 vendor/ 目录。它是根 package.json 里独立的 workspace,装着 DeepSeek 自己 fork 的 Cordis:本地版本 @deepseek-ai/cordis 4.0.1;发布时 pin 在上游 4.0.0-rc.7 并带 18 个本地补丁(developersdigest 2026-08-13 读码结论,多来源一致)。vendor/README 的说法是让 harness 完全拥有自己的框架层。DSH 不只是用了 Cordis,而是把框架层整个买断进自己仓库,按自己的节奏改。
还有一个值得记住的细节:仓库根目录的 BENCHMARK.md 只有数行,指向 Python SDK 的入门指引,没有任何自带基准分数。一个自称 harness 而非 agent 的项目,不在仓库里自证 agent 能力。这个空缺后面还会回来。
到这里可以立靶了。关键判断:社区的预期是 DeepSeek 版 Codex 或 Claude Code,拿到的是一套可继续构造 Agent 的 Runtime:没有成品 TUI,交互面、工具面、模型路由、执行循环全部以可替换的形态存在。预期错位不是产品失败,是品类不同。
先听原话。负面一端,知乎问题《如何评价在 8 月 13 日发布的 DeepSeek Harness》下 @拜科努 的回答(2026-08-13):
兄弟们,测试了一晚上 harness+v4 pro,感觉完全不能用啊,如果说 codex 是精装房,harness 就是画了一块地,给你一堆水泥沙子。
Reddit r/DeepSeek「My First Impressions」帖的负面要点(大意):非常慢、token 消耗太大,发帖人自述先在 Pi Harness 里做规划、再拿到 DSH 里重做,省了约 20M token(个人说法,无日志佐证,本文只当现象引用);另外插件一大堆却没有说明,看得人发懵。
正面一端,新浪微博一位认证 AI 博主(2026-08-14)的原话:「如果要给它一个历史定位,我愿意给它一个 agent os 层级的评价。」Hacker News 的讨论帖(news.ycombinator.com/item?id=49285244)里有一条被反复引用的评论点破了争议核心:"This is not for community plugins, it's for AI generated plugins"。这句评论的意思是:这套插件体系设想的主要作者不是人类社区,而是模型本身。
把两类反馈按人群拆开,结构就清楚了:
人群 | 核心诉求 | 技术关注点 |
|---|---|---|
终端编码用户 | 开箱即用的成品 | 交互界面、默认工具面、token 成本、长任务完成度 |
平台开发者 | 可重组的底座 | 插件内核、可审计性、Provider 与执行后端的可替换性 |
本文判断:这是一次人群错位,不是产品争议。终端用户拿到的是毛坯,骂毛坯天经地义;平台开发者拿到的是地基,捧地基也天经地义。两组诉求对着的是同一架构的两个面,DSH 当前把完成度压在了 Runtime 层,产品层留白。
插件生态的数据也要按口径分开看,任何一句里混排两个口径都会失真:
口径 | 数字 | 时间与来源 |
|---|---|---|
launch night 插件仓库 | 288 个 | 2026-08 中旬,orcarouter 目录 |
Oh-My-DSH curated 目录 | 1,117 个 | 2026-08-15,同一来源 |
GitHub | 2,600+ 仓库 | 2026-08 中旬自动目录快照 |
dsharness.io 自称验证 | 8,363 个 | 更晚时间点,自称含 GitHub 信号验证 |
明星插件的形态能说明生态在长什么:dsh-market(应用内插件市场)、dsh-find-plugin(让 agent 自己搜索 GitHub 上的插件生态并返回安装命令)、dsh-genui(模型渲染交互组件)。给 agent 装一个替它找插件的插件,这个方向本身就很说明问题。生态质量目前没有可靠抽查,本文不下繁荣或贫瘠的结论。
本章还埋着后文的三条线:token 成本骂声的机制根源在事件日志与上下文工程(五章);插件混乱的骂声指向 Cordis 内核与组合体系(六章);而「到底费不费 token」这个具体问题,公开材料里没有任何 DSH 的可复现数据,只有个人的相对数字与含糊的单任务金额说法,这个缺口本身构成第四章的证据边界。
注:本章是机制验证而非部署实测。本地环境满足运行要求(Node v24.13.0、pnpm 11.24.0,DSH 要求 node ^22.19.0 || >=24.0.0 与 pnpm 11.7.0),但依赖未安装、未构建,本章全部结论以源码为依据。
dsh 命令(apps/cli/,bin 名 dsh)有三种 dispatch 模式:profile 启动、plugin 管理、dump-config。启动器帮助文本(apps/cli/src/args.ts)自己就是形态清单:
dsh --profile web boot the web profile (same as: dsh web)
dsh --profile headless "run the tests" answer one task, print the result, and exit
dsh --profile tui --patch ./extra.yml boot a custom profile with one extra overlay形态 | 源码位置 | 用途 |
|---|---|---|
Web |
|
|
Headless |
| 一次性任务跑完即退出 |
ACP |
| Automation-only,程序化客户端接入 |
Python SDK |
| 子进程驱动,stdio 上的换行分隔 JSON-RPC |
CLI 本体 |
| profile 启动、插件管理、配置审计 |
对大多数读者,npx @deepseek-ai/dsh web 是接触它成本较低的一条路(README 官方路径)。注意这张表里没有 TUI、没有桌面 App、没有 IDE 插件。终端用户的期待落空,在入口这一层就已经注定。
DSH 的配置体系分两级,管的东西不一样。
Profile 是进程级的:$DSH_HOME/profiles/<name> 下的一个目录,含 package.json(插件依赖 + dsh.profile manifest 里有序的 bundles 层列表)和用户自己的 cordis.patch.yml。内置三个 bundle:base、web-app、headless。层的叠加顺序是 bundle patches、profile 自身 patch、home 级 patch、命令行 --patch overlays,从底到顶。Web 与 Headless 的分岔就发生在最底这层:一个在 base 之上叠 web-app,一个叠 headless,装出来的插件组合从根上就是两套。
--dump-config 是这个体系里值得单独讲的机制(apps/cli/src/args.ts 定义,apps/cli/src/dump-config.ts 实现):它不启动运行时,用 include 插件自己的 patch 算法离线组合各层,把组合后的 profile 树渲染成 YAML 打印后退出。配置审计不依赖「跑起来再观察」,启动前就能拿到全貌。--dump-config 与 --dump-default-config(不含用户层)互斥,dump 不接受应用参数,这些都是源码里写死的约束。
Preset 是会话级的:决定这个会话里的 agent 长什么样。apps/cli/config/agent-presets/ 下四个内置预设,官方中文文案直接写在 preset.yml 里:
模式 | 官方文案(preset.yml 原文摘录) | 工具面 |
|---|---|---|
standard(order:1) | 标准模式,功能完整的编码 Agent | 26 个模型可见工具 |
code(order:2) | PTC 模式,通过 Code Mode SDK 呈现工具 | 折叠为 |
minimal(order:3) | 极简模式,仅持久 bash 与 str_replace_editor 双工具 | 2 个工具 |
cordis(order:4) | 创造模式,用于创建自定义 preset | 面向 preset 编写者 |
code 模式在 preset.yml 的中文文案里叫「PTC 模式」,源码与官方英文材料通称 Code Mode,本文统一用后者。

standard preset(非 Windows 平台,逐行清点 agent.cordis.yml 的挂载清单)给模型 26 个可见工具名:
能力 | 模型可见名 | 数量 |
|---|---|---|
Shell |
| 1 |
文件 |
| 4 |
检索 |
| 2 |
后台任务 |
| 3 |
目标与计划 |
| 4 |
子代理 |
| 5 |
编排 |
| 2 |
交互 |
| 3 |
网络 |
| 2 |
26 这个数是挂载结果,不是仓库上限:工具目录里还有数十个未默认挂载的可选工具名(terminal 系、lsp、session 查询系、cordis 动态插件系等),两个数不要混。
Code Mode 是工具面的另一种收敛方向:配置 tools: mode: native | code | both 后,code 模式下 registry 只贡献一个保留的 run_code 传输和系统提示里生成的 TypeScript SDK,模型写一个程序组合多步操作,直接调其它工具名会得到 UNKNOWN_TOOL。code preset 的注释里给了收益表述:原本五次往返的工具调用序列变成一次。设计笔记引用了 Cloudflare 的判断(大意:模型写代码比逐个发工具调用更在行),并把执行后端的信任等级定为与 bash 等同:每次运行新建一个 Node worker thread,空环境变量、堆/输出/时间上限、硬终止(code-runtime-worker-thread/src/index.ts,new Worker(WORKER_PATH, ...))。子调用并没有绕过治理:每个 SDK 调用仍进调度池、仍产生 tool/code-dispatch 事件对。
权限模型是三个命名档位加一个审批旋钮(packages/interaction/permission-presets/src/index.ts):
sandbox: zod.union([
zod.literal('read-only'),
zod.literal('workspace-write'),
zod.literal('danger-full-access'),
]).nullable(),
approval: zod.union([zod.literal('ask'), zod.literal('never')]).nullable(),默认预设表给的是 workspace-write(沙箱同档 + 审批 ask,工作区内的写入放行、越界重试要审批)与 danger-full-access(沙箱同档 + 审批 never,全量文件访问不再询问)。沙箱层有一个值得强调的设计:fail-closed。受限模式下如果找不到可用的沙箱后端(bwrap、Landlock、Seatbelt 三选一),直接抛 SANDBOX_UNAVAILABLE 拒绝执行,而不是降级放行。
社区骂 token 贵,机制上贵在哪。DSH 的默认模型目录在 llm-deepseek 适配器里:deepseek-v4-flash、deepseek-v4-pro、deepseek-v4-flash-vision-exp(实验),适配器默认上下文窗口 1,000,000、默认输出上限 256,000(2026-08-26 本地 HEAD;第三方在 8-13 只读到前两个模型,这个目录还在长)。100 万 token 的窗口给了配置很大的挥霍空间,也让「塞了多少」成为更需要可观测性的问题。
可观测性的落点在第四章细讲,这里只给结论性机制:每次请求发出前,request/header 事件会记录该次请求的完整调用配置、渲染后的系统提示词全文、组装后的工具 schema。也就是说 26 个工具的说明书、系统提示词、仓库规则注入,全部实打实进入每一次请求的上下文。工具越多、preset 越重,首包越大,这不是 bug 是算术。minimal preset 把工具砍到 2 个,从机制上就是把这份固定开销压下来的裁剪手段;压上下文也只是它用途的一面,仓库根的 BENCHMARK.md 指向的 Python SDK 入门示例,跑的同样是 minimal 变体。
对照对象选 opencode,理由是它正好站在 DSH 的对面:README 定位是 The open source AI coding agent,面向终端用户的成品。顺手纠一个流传较广的错误:有第三方文章称 opencode 用 Go 写成。以本地源码为准,packages/opencode/src/ 全部是 TypeScript,跑在 Bun 上(核心包 v1.18.18,packageManager bun@1.3.14)。
两边的体量先摆出来:opencode 在 2026-08-26 的 GitHub 快照约 201.5k star、26.1k fork,一年 862 个 release(mise 版本页数据),出身是 2025 年 Charm 维护版本分裂后的 SST/Anomaly 线。DSH 发布两周,star 十万量级,developer preview。社区侧的比较没有悬念;有信息量的是架构层。
维度 | DeepSeek Harness | opencode |
|---|---|---|
官方定位 | open-source agent harness(README) | The open source AI coding agent(README) |
入口形态 | CLI、Web、Headless、ACP、Python SDK | TUI 为主,Desktop(BETA)、Web、server、SDK、ACP |
扩展范式 | Cordis 插件 + Profile/Patch + Preset | plugin、command、自定义 agent、MCP、skill |
Agent Loop | 具体循环只此一包,以插件注册 | 循环内聚于 src/session 与 src/agent 模块 |
内置 agent 形态 | 四个 preset(standard/code/minimal/cordis) | build(全权限)+ plan(只读)+ general 子代理 |
默认工具集 | 26 个模型可见工具 | 16 个主要内置工具 |
权限模型 | 三档命名预设 × 审批两档,fail-closed | wildcard 规则表,findLast 匹配,无命中默认 ask |
会话存储 | append-only JSONL(可选 zstd) | JSON 文件目录树(按 project/session/message) |
可观测性 | 事件日志 + 请求重建校验 | 存储文件直读 + server 事件流 |
安装与运行时 | npx/npm,Node 22.19+/24+,pnpm 11.7 | curl/npm/brew/scoop/choco/pacman/paru/mise/nix,Bun |
迭代与社区 | 两周、十万 star 量级、dev preview | 一年 862 release、约 20 万 star |
表格里信息量较大的一行是 Agent Loop。DSH 的 packages/core/agent-loop README 开头就说:这是整个 harness 里仅有的包含具体循环逻辑的包,其余一切都是抽象服务或挂在扩展点上的插件。它以 Cordis 插件形态注册,static inject 声明 agents、sessions、llm、tools、systemPrompt 五个接口服务,配置里 maxParallelToolCalls 默认 10。opencode 的正相反:循环逻辑内聚在 src/session(session、processor、llm、compaction、overflow、retry)与 src/agent 的模块里,用 Effect 的 Context.Service 和 Layer 组织,是不可替换的引擎主干。连防死循环的 DOOM_LOOP_THRESHOLD = 3 都是模块内常量。
「harness 到底造成多大差异」这个问题,本文没有自己的实测。目前能引用的外部参照只有 Composio 在 2026-08-10 发布的基准:30 个任务、8 个 harness、统一 DeepSeek V4 Flash 模型,共 240 次运行。官方总结的区间:同一模型在不同 harness 下任务成功率 47% 至 67%,单任务成本 $0.019 至 $0.104,中位耗时 122.7s 至 272.4s。其中 opencode 的单点数据:46.7% 通过率、平均 692,000 tokens、中位 129.7s。
这个参照系的局限必须说全:其一,DSH 未参赛(基准发布比 DSH 开源早两天);其二,单模型口径(只有 V4 Flash);其三,30 个任务的样本量。第三方解读里「harness 选择可造成约 7 倍 token 用量差」的说法是 atlascloud 对这份数据的计算。数据本体多来源一致,7 倍这个倍数是单一来源的推算,引用时要拆开看。
注:本文未运行任何 harness,DSH 侧无同口径公开数据,上述数字不构成两者排名。它证明的只有一件事:固定模型后,harness 这个变量足以把成功率拉开二十个百分点。差异存在,归因需要下一节的机制层。
第一个分异:两边都有 Code Mode,深度不同。opencode 也有一个 code-mode(src/tool/code-mode.ts),工具名 execute,定位是运行一个可访问已连接 MCP 工具的受限编排脚本,只编排 MCP 工具。DSH 的 Code Mode 面对的是全工具注册表:生成的 TypeScript SDK 加 run_code 执行入口,worker thread 后端,子调用走完整调度管线。两者同源于 Cloudflare 的思路,一个是 MCP 编排器,一个是全工具编程面,名字相同、物种不同。
第二个分异:权限哲学。opencode 是规则表:evaluate() 按 wildcard 模式匹配 permission 与 pattern,findLast 取末次匹配,无规则命中时默认 action 是 ask,每个 agent 携带自己的 ruleset。DSH 是命名预设:三档 sandbox mode 与审批策略捆绑成旋钮,fail-closed 到沙箱后端。从机制看,前者把表达力做满,规则可以细到单个工具、单个路径,适合要精细治理的团队;后者把可推理性做满,预设名本身就说完了一个会话的权限姿态。没有对错,是两种治理观。
第三个分异:会话持久化。opencode 按目录树存 JSON 文件(fs.glob("storage/session/message/*/*.json")),核心链路不走 SQLite(仓库里虽有 effect-sqlite 系包,用于别处;「opencode 用 SQLite」是常见误传)。DSH 存 append-only 的事件日志,JSONL 可选 zstd 压缩,每条事件是 { type, seq, time, data } 的信封(seq 为会话内单调递增序号),共 13 种事件类型,从 turn、step 的生命周期到 token 级的流式块、工具调用、请求头全部在册,每次请求的 header 全量入日志。从取舍方向看,一边把存储做成人可以直接翻的文件树,一边把存储做成可逐事件重放的流水。
结论:把两边放在一起看,差异不是「一个成熟一个不成熟」能覆盖的。opencode 把脊柱固定,把自由度给到边缘的扩展点,换来成品体验与迭代速度;DSH 把自由度给到每一层,脊柱本身可替换,代价是任何一层都没有默认的成品形态。这是范式差异,成熟度只是它当前的一个投影。
任务变长、上下文变胖、模型开始打转,这是长任务的三段式死亡。本章看两侧源码在这三个点上的机制设计,以及它们的边界。
DSH 的 compaction 是一个能力族(compaction-basic、command-compact、compaction-tool-result-pruner),压缩行为的参数写在 preset 里。standard preset 的配置:thresholdChars 8192、headChars 4096、tailChars 1024(agent.cordis.yml,可本地核对)。工具结果的裁剪单独成包(tool-result-pruner),把长输出从历史里剪掉而不是整段压缩。opencode 的对应物是 src/session 下的 compaction 与 overflow 模块,与循环逻辑同处一个模块簇。
结构差异在于 DSH 的压缩挂在事件流的投影层:compaction 的 replace 本身是一个新追加进日志的事件,按 SurfaceOp 类型的定义,它替换掉派生历史里一段区间内的旧节点,且必须在 sourceEventSeqs 里引用每一个被遮蔽的节点(packages/core/session/src/types.ts);日志是 append-only 的,旧事件不删,被替换的只是投影。于是模型看到的是压缩后的历史,日志里保留的是全量。压缩是视图变换,不是数据销毁。这个设计换来的是可回溯性:任何时刻的解释口径与当时的真实口径同时存在。

模型打转怎么办,两边思路不同。opencode 在 processor 里放 DOOM_LOOP_THRESHOLD = 3 的硬编码阈值,属于防御性截断。DSH 的 turn/end 事件携带结构化的终止原因(TurnEndReason 联合类型,源码注释明说插件可以通过合并新变体扩展它),aborted、error、max-tokens 等终止原因是一等公民数据,相当于把失控记录成可查询的事件。工程含义的差别:DSH 的事后归因从日志就能读出是哪类终止;至于打转检测这层决策在 DSH 里落在哪、是否可配置,本文没有逐一核实,按它「行为进插件」的架构推断,大概率也在插件侧。
四章讲过它的机制,长任务场景下它的收益点更具体:模型把一串工具调用写成一个 TypeScript 程序交给 run_code,程序里的中间结果只是 worker 里的局部变量,从头到尾不进对话历史;只有程序跑完后的最终结果回到模型上下文。native 模式下同样一段工作,每一步的输入输出都要在模型上下文里进出一次。对 100 万 token 窗口的项目,这不只是省往返,是改变了中间状态的存放位置。代价同样具体:模型生成的代码在执行,隔离边界是 worker thread 加资源上限,不是安全沙箱,这个信任等级官方设计笔记自己定为与 bash 等同。
DSH 差异化卖点是可审计性,机制细节在六章。这里先划它的边界:日志体系兜住的是请求与日志的重建一致,即模型看见的内容事后可以逐字节对上。它管不住模型看的东西是否正确、产出的代码是否正确。工程建议:把 DSH 的日志当作观测与归因工具,而不是验收工具。生成物的验收要留在 Harness 之外:独立测试、外部验收、真实环境。这一点是从 invariant 断言的内容推导出的限定判断,不是实测复盘。
前五章的现象到这里需要一个统一解释:为什么 DSH 敢把脊柱做成插件。
答案的第一半在 vendor/cordis/src/fiber.ts。每个插件运行在一个 Fiber 里,生命周期是六态枚举:
export const enum FiberState {
PENDING,
LOADING,
ACTIVE,
FAILED,
DISPOSED,
UNLOADING,
}PENDING 等待依赖服务、LOADING 执行插件回调、ACTIVE 提供服务、FAILED 回调抛错、UNLOADING 跑清理、DISPOSED 移除后不可重启。不少早期二手分析把 Fiber 生命周期写成两态(PENDING/ACTIVE),那只是六态中的两个。所有副作用要通过 ctx.effect() 提交 disposer,fiber 卸载时按逆注册顺序执行(LIFO),在已 dispose 的 context 上注册 effect 会抛 INACTIVE_EFFECT。用人话说:插件卸载后不留垃圾,后注册的清理先执行,死过的插件不能诈尸。
插件通过 named export 的 inject 声明依赖服务,依赖未满足就停在 PENDING,服务方退出时依赖方按序卸载。这组性质托住了 agent loop 的插件化:一个组件声明过依赖、注册过 disposer,摘掉它就不会在进程里留下半激活的服务——循环逻辑能以普通插件的身份注册进来,靠的就是这个工程条件。
这套清理的管辖范围也要照源码划清:disposer 只回收通过 ctx.effect() 注册的进程内资源,注册过什么才清理什么。文件写了就是写了,业务动作的回滚不归它管;拦恶意代码也不归它管,那是 sandbox 包的事。
答案的第二半在 packages/core/agent-loop/src/invariant.ts。agent loop 组装请求时调用 session.deriveMessages()(按 surfaceOp 标记的节点投影出消息历史,带增量缓存),invariant 在 llm/stream 上挂监听器,对每个即将发出的请求做断言:
const expected = session.deriveMessages()
if (JSON.stringify(options.messages) !== JSON.stringify(expected)) {
fail(`llm request for session "${String(session.id)}" diverges from the dispatch-time durable derivation (log-reconstruction desync)`)
}请求里的消息数组、model、system、temperature、maxTokens、stop、tools,全部要与从日志独立折叠出的 request header 一致,不一致就报 desync。这条不变量把「模型可见即入日志」从口号变成了运行时断言。UI 显示的轨迹、fork 出的新会话、事后回放,与模型实际收到的请求共用同一条日志源,漂移会被当场抓住。
它的代价有第三方评价(developersdigest,多来源一致):每次生产派发都要把全量消息历史序列化两遍,属于热循环上的税。DSH 的取舍是审计性值这个价。这个取舍对不对,取决于你多需要逐字节可回放的会话。
这条不变量还有一个运维侧的推论,作为限定判断记录:系统提示词、工具 schema、消息历史,凡是进过模型上下文的内容都会原样落进 session 日志,所以日志的敏感级要按「模型看过的全部内容」定,脱敏、留存、外发的边界都照此规划。部署 DSH 的团队,session 日志要当敏感数据治理。
packages/fs/ 是能力族分层的样板:fs/ 是 Service Definition(路径规范、containment、原子变更原语),fs-local/ 是本地实现,fs-sandbox/ 是沙箱围栏实现,fs-observation-policy/ 是策略门插件,tool-fs/ 与 tool-fs-search/ 是模型侧工具。README 的原话:沙箱的、远程的、项目级的文件系统后端可以替换 fs-local,而不触碰 Service Definition、策略门和模型侧工具 schema。
写入带乐观并发控制(packages/fs/fs/src/types.ts):
export type FsWriteIntent =
| { kind: 'createIfAbsent' }
| { kind: 'replaceIfVersion'; version: FsVersion }createIfAbsent 对已存在目标拒绝,replaceIfVersion 对版本不匹配拒绝,配合 observation-policy 的 read-before-edit 强制:模型读过文件才能改,读后被外部改过就不能盲写。同族还有 shell、code-runtime、sandbox 的 seam。第二章捧「可重组」的那些人,捧的就是这一层:换执行环境时上层不动。
opencode 的 Effect 与 DSH 的 Cordis 解决相邻但不相同的问题。Effect 提供 Layer 的静态组装:服务定义、依赖图、构建期组合,类型系统参与校验,组装完成后运行。Cordis 提供 Fiber 的动态生命周期:插件可以运行时加载卸载、依赖可以晚到、退出按序清理。换言之,一边的形态在构建期就锁定,一边的可变性保持到运行期。opencode 选 Effect 配固定核心,扩展走边缘点;DSH 选 Cordis 配全插件拓扑,核心自身也是插件。内核选型与自由度分布互为因果,这不是实现细节,是两条路线的岔路口。
没有适用于所有人的答案,按你需要哪种自由度拆:
人群 | 建议 | 理由(回扣本文章节) |
|---|---|---|
Agent 基础设施团队 | 现在读源码,别等稳定版 | loop、session、fs、llm 全部摊在可核对的源码层,连循环都以插件注册(四、六章) |
领域 Agent 构建者 | 从复制 preset 改起 | minimal 与 standard 是现成的裁剪起点;工具增删与 provider 替换在配置体系里有明确落点(三章),但本文未实际跑通这条路 |
评测与复现研究者 | 锁 commit、冻结 preset 再动手 | 事件日志可逐字回放、请求可复核(五、六章);dev preview 期的版本漂移风险自担(一章) |
插件作者 | 先想清楚插件写给谁 | 生态目录口径从数百到数千不等(二章);「插件作者是模型还是人」正是社区争论的核心 |
日常终端编码用户 | 近期选成品 | opencode 一年 862 个 release、安装渠道成体系(四章);DSH 当前没有 TUI(三章) |
准备进生产的团队 | 治理没建好别进场 | 插件从哪来、配置改了什么、日志里躺了多少敏感内容,三件事都得自建规矩(五、六章) |
如果你正在设计或选型自己的 Harness,本文的走读可以折成四个带回去的问题。
自由度放在哪一层:循环写死在核心、扩展留给边缘,还是连 loop 都做成插件——这决定换脊柱时要付的代价,DSH 与 opencode 是两条现成的对照路径。
固定开销能否在请求发出前算清:每个 preset 的工具面与系统提示词在每次请求里占多少、能不能事先列出来——「token 贵」是算术问题还是黑箱问题,分界线在这里。
会话记录做成什么:是人排障时直接翻的文件,还是带一致性断言、可逐字节回放的审计面——后一种的代价是热循环上的序列化税,付不付是明确的取舍。
执行边界的信任等级敢不敢写明:DSH 的做法是把 Code Mode 的信任级明说成与 bash 等同、把沙箱缺失处理成 fail-closed 拒绝——边界写得越明白,使用者越知道自己在担什么。
剩下的三件事没有答案:破坏性变更的窗口何时收窄,口径不一的插件生态何时形成治理,DSH 自己的同口径公开数据什么时候有人拿出来。它们落地之前,本文愿意给出的判断是分层的:源码里那套架构经得起逐层对读,值得学;产品层的留白和生态的混乱也是真实的,再看看不迟。
好啦,谢谢你观看我的文章,如果喜欢可以点赞转发给需要的朋友,我们下一期再见!敬请期待!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。