diff --git a/docs/DSH_ADOPTION_PLAN.md b/docs/DSH_ADOPTION_PLAN.md new file mode 100644 index 0000000..95b6eea --- /dev/null +++ b/docs/DSH_ADOPTION_PLAN.md @@ -0,0 +1,317 @@ +# DeepSeek Harness 采纳方案与决策记录 + +> 基线:DeepCode `main@4d56f44`(0.3.0)· 调研对象 `dsh@47f94385`(0.1.0-rc.5, MIT) +> 前置阅读:[调研报告](research/deepseek-harness.md) +> 体例沿用 [`FLOATBOAT_ADOPTION_PLAN.md`](FLOATBOAT_ADOPTION_PLAN.md):每个候选项走**正方 / 反方 / 裁决**, +> 裁决写进文档,实现 PR 只负责执行,不重开辩论。 + +--- + +## 0. 结论先行 + +本轮采纳的主题是**上下文经济学**:_agent 的注意力预算花在哪、超支时怎么办_。 + +dsh 在这一层领先 DeepCode 一代。它的答案不是"把上下文做大",而是三件具体的事: +**超限输出落盘可取回**(spill)、**无效重复被打断**(护栏)、**历史可检索而非只可回放**(session query)。 + +对照之下查实了 DeepCode 的两处真实缺陷 —— `WebFetch` 无模型可见上限、`Bash` 截断即永久丢失 +(证据见[调研 §2.1](research/deepseek-harness.md))。它们由同一个能力修复,这构成本轮的第一优先级。 + +**同时明确拒绝 Cordis 化重写**(§2.1)。dsh 用 219 个包表达 DeepCode 用 4 个包表达的东西, +这个倍率来自它需要对外分发插件生态的产品前提 —— DeepCode 没有这个前提,抄成本不抄收益。 + +| 裁决 | 项目 | +| -------- | -------------------------------------------------------------------------------------------- | +| **采纳** | spill · 重复调用护栏 · 逐工具超时 · 持久 shell 会话 · session 检索 · 工具渲染意图 · 结果剪枝 | +| **推迟** | 作业注册表统一 · goal 域 · Ralph 循环 · UI 包拆分 | +| **拒绝** | Cordis 化重写 · workflow 引擎 · seam 三包拆分体例 | + +--- + +## 1. 采纳项 + +### 1.1 工具输出溢出(spill)—— P0 + +**做什么**:任何工具的输出超过阈值时,全文写入 session 作用域的文件,模型收到的是 +`预览(头 + 尾)+ 字节数 + 取回路径`。取回沿用现成的 `Read`(带 offset/limit),不新增工具。 + + + + +
+ +**正方** + + + +**反方** + +
+ +- 修复的是已查实的 bug,不是锦上添花:`WebFetch` 现在能把 5 MiB 正文灌进上下文(约 150 万 token), + 一次调用即毁掉整个 session。 +- `Bash` 30 KB 截断后尾部**无处可寻** —— 而失败测试的关键信息通常正在尾部。 +- 成本极低:DeepCode 已有 `sessionDir` 与 `PostToolUse` 后置点,落点现成。 +- 与 DeepCode 已有的 snapshot / ledger 同构 —— 都是"把易失的东西钉在 session 目录里"。 + + + +- 又多一类要清理的 session 产物;`~/.deepcode/sessions` 已经有 snapshots 了。 +- 模型未必会去 `Read` 那个文件,可能拿着预览就瞎猜 —— 那还不如直接截断来得诚实。 +- 阈值定错会有反效果:定低了,本该直接进上下文的中等输出被推到盘上,多花一轮 `Read`。 + +
+ +**裁决:采纳。** 反方第 2、3 点是**设计约束而非否决理由**,按它们收紧设计: + +- 预览必须**同时保留头和尾**(dsh 只要求 bounded preview;这里加严)。测试失败、堆栈、报错 + 几乎总在尾部,只留头是最糟的截断方式 —— 也正是 DeepCode 今天在做的。 +- 取回提示写进结果文本本身("完整输出见 X,用 Read 的 offset/limit 取"),不靠模型自己想到。 +- 阈值可配置,默认取 Bash 现有的 30 KB —— 这样对既有行为**不新增**推到盘上的情况,只是把 + 原本丢掉的部分变成可取回。 +- 反方第 1 点接受为已知代价:spill 文件与 snapshots 同在 session 目录下,共享未来的保留期清理。 + +### 1.2 重复调用护栏 —— P0 + +**做什么**:连续以**完全相同的参数**调用同一工具达到阈值(3/5/8)时,向下一轮注入升级式提醒: +先短提示,再详细提示(点名工具、连续次数、参数摘要)。不拦截、不改写、不进工具列表。 + + + + +
+ +**正方** + + + +**反方** + +
+ +- 卡死循环是 agent 最贵的失败模式:不报错、不停止,安静地烧完预算。 +- DeepCode 已有 `reminders/` 子系统(纯函数 builder + `` 包装),这是**零架构成本**的落点。 +- 纯建议、不否决 —— 合法的重复调用一点不受影响,误判代价是几十 token。 +- 与 DeepCode 的 `/cost` 计价意识天然互补:省下的是真金白银。 + + + +- 启发式,必然误伤:轮询一个文件等它变化,就是合法的同参重复。 +- 状态放内存,session 恢复后清零 —— 恢复到一半的循环要重新数。 +- 阈值是又一个要调的旋钮。 + +
+ +**裁决:采纳。** 反方三点全部接受为**已知且可容忍的代价**,理由是本机制的否决权为零: +误判的唯一后果是模型多读一句提醒。dsh 的取舍相同(其 README 明说 in-memory only、 +"later reminders are the accepted cost"),这里沿用。 + +一处**加严**:dsh 默认排除 `todo_write`,理由是记账工具不该洗白循环。DeepCode 对应的 +`TodoWrite` 同样排除,并追加排除 `AskUserQuestion`(等用户回答期间的重复是正常的)。 + +### 1.3 逐工具超时策略 —— P1 + +**做什么**:在中央调度点为每次工具调用武装一个 deadline,超时则以错误结果收尾并解释原因。 +默认值按工具族给(网络类宽、本地类紧),可配置。 + +**正方**:今天只有 `Bash` 自带 timeout;一个 `Grep` 打到网络挂载盘、一个 `WebFetch` 卡在慢 +TLS 握手,都能让整个 turn 无限期挂起,而**用户在 REPL 里看到的只是光标在闪**。 +**反方**:多数工具本就有自己的超时(fetch 有、ripgrep 会退出),加一层可能与内层超时打架, +出现"两个超时谁先响"的模糊语义;且强行中止的工具可能留下半截副作用。 + +**裁决:采纳,但明确分层。** 外层 deadline 定位为**兜底**,默认值显著大于各工具内层超时, +使内层先响、错误信息更具体;外层只负责"内层根本没响"这一种情况。对有副作用的工具 +(`Edit`/`Write`/`Bash`),超时后的结果文本必须**明说副作用状态未知** —— 不假装什么都没发生。 + +### 1.4 持久 shell 会话 —— P1 + +**做什么**:一组工具开启/发送/读取/关闭长生命周期 shell,跨调用保留 `cwd`、环境变量、 +shell 函数与后台进程。 + + + + +
+ +**正方** + + + +**反方** + +
+ +- 今天每次 `Bash` 都是全新进程:`cd` 白做、`export` 白做、`source venv/bin/activate` 白做。 + 模型的应对是把整串前缀重复拼进每条命令 —— 又长又易错。 +- 起了 dev server 之后想看增量日志,现在只能 `Read` 那个日志文件轮询,而**轮询正是 §1.2 会触发的模式**。 +- 这是 DeepCode 与 dsh 之间**执行形态**上最实的一条差距。 + + + +- 长生命周期进程 = 泄漏源:session 崩了、进程没收,机器上留一堆孤儿 shell。 +- 与沙箱语义纠缠:DeepCode 的沙箱是**每次 spawn 时**包 argv 的,一个持久 shell 在开启时确定 + 沙箱策略,之后策略变了它也不会重新武装。 +- `node-pty` 是原生依赖,要给 Tauri 打包的每个平台各编一份 —— 对一个 Mac 优先的产品是实打实的发布风险。 + +
+ +**裁决:采纳,但不用 PTY。** 反方第 3 点是决定性的:为交互式 TUI(vim、top)付出原生依赖 + +多平台编译的代价,而真实收益 90% 在"保留 cwd/env + 读增量输出"上 —— 这部分**不需要 PTY**。 + +改为 **marker 协议**:持有一个长期 `bash` 进程,命令后追加哨兵 echo,读到哨兵即认为该命令结束 +并取回退出码。纯 Node 管道,零原生依赖,跨平台。代价是不支持全屏 TUI 程序 —— 明确写进工具描述, +让模型知道该退回一次性 `Bash`。 + +反方第 1、2 点转为硬性设计要求: + +- 会话与 session 同生命周期,agent 退出时**全部收割**;再加空闲超时。 +- 沙箱策略在**开启时**固化并记录在案;策略变更不影响已开会话,工具描述明说这一点。 + +### 1.5 session 检索 —— P1 + +**做什么**:让 agent 检索**自己过去的 session**:按文本搜、按 session 取回片段。 + +**正方**:DeepCode 已经把每个 session 以 JSONL 写在 `~/.deepcode/sessions` —— 数据在那儿, +只是没有出口。"上次我们怎么解决这个 CI 报错的" 现在无法回答,而这正是本地 agent 相对云端 +agent 的天然优势(数据全在本机)。 +**反方**:跨 session 检索是**隐私与安全的新面**:A 项目的 session 可能被 B 项目的 agent 搜到, +把凭证片段、别的客户的代码带进当前上下文。dsh 用 SQLite FTS,等于再引一个原生依赖 + +一份要维护的索引与 schema 版本。 + +**裁决:采纳,但按反方收窄两处。** + +- **默认按 workspace 限定**:只搜 `cwd` 相同(或其子目录)的 session。跨 workspace 检索需显式开启。 + dsh 的工具名即 "workspace-authorized session queries",方向一致,这里作为默认而非选项。 +- **不引 SQLite**:流式扫 JSONL + 正则。个人本地 agent 的 session 量级(数百到数千个文件)下, + 一次扫描是几十毫秒量级,不值得为它背一个索引的一致性问题。真到量级不够时再加索引, + 接口不变。 + +### 1.6 工具渲染意图 + 桌面端渲染 —— P1(UI) + +**做什么**:`ToolDefinition` 增加一个**渲染意图**声明(`generic` / `terminal` / `diff` / `locations`), +桌面端 `ToolCard` 按意图选择呈现:`Edit`/`Write` 出真 diff,`Bash` 出终端样式, +`Read`/`Grep`/`Glob` 出可点击的文件位置列表。 + +**正方**:[`THREE_WAY_REVIEW.md`](THREE_WAY_REVIEW.md) 判定"**下一阶段 ROI 几乎全在 UI 出口**", +而这是 UI 出口里最集中的一处:用户 90% 时间盯着工具卡片,今天它们**全长一个样**。 +dsh 把渲染意图定为工具设计的一部分("decided up front",且呈现函数必须是 args 的纯函数), +这条纪律恰好能让 CLI 与桌面端**共用同一份判断**。 +**反方**:把呈现关注点塞进 `ToolDefinition` 会污染内核 —— `packages/core` 一直标榜"无 UI 依赖"。 +而且现有 `ToolCard` 已经有 `diff` 布尔参数,不做这层抽象也能给 Edit 出 diff。 + +**裁决:采纳,按反方保持内核纯净。** 渲染意图是**枚举字符串 + 纯数据**,不含任何 React/DOM 类型, +不引入 UI 依赖 —— 这与 `packages/shared-ui` 只放跨端类型的既有做法一致。反方的替代方案 +(在桌面端硬编码 `name === 'Edit'`)会把同一份知识在 CLI、桌面端、VS Code **各抄一遍**, +下一个新工具就得改三处。声明在工具自己身上,三端各自读。 + +### 1.7 模型无关的结果剪枝 —— P2 + +**做什么**:compaction 触发前,先跑一遍**不花模型调用**的剪枝:丢弃早期已被同路径新结果覆盖的 +文件读取、已失效的目录列表等。 + +**正方**:今天 compaction 一律走 LLM 摘要,**要钱要时间**;而历史里最大的一块往往是同一个文件 +被读了五遍,其中四遍已经过时 —— 这部分丢弃是无损的,不需要模型判断。 +**反方**:判断"已被覆盖"要有语义,判错就是删掉模型还需要的东西,而且**静默** —— 比 LLM 摘要 +更难发现出了问题。 + +**裁决:采纳,但只做能证明无损的一类。** 首版仅剪枝"**同一 `file_path` 的更早 `Read` 结果, +且其后存在同路径的成功 `Read`/`Edit`/`Write`**" —— 这一类可以从工具调用记录本身证明后者取代前者。 +被剪枝的位置留一行占位说明("此处有一次已被后续读取取代的 Read"),使其可见而非静默。 +其余类型不做。 + +--- + +## 2. 拒绝项 + +### 2.1 Cordis 化重写 —— 拒绝 + +即"一切皆插件",把 agent loop、工具注册表、session 日志都变成可从配置替换的插件行。 + + + + +
+ +**正方** + + + +**反方** + +
+ +- 这是 dsh 最核心的主张,也确实解释了它为什么能长出 52 个工具而不失控。 +- 可逆 effect(注册即返回 disposer)是真优雅,能一举解决 DeepCode 现在插件卸载残留的问题。 +- 用户明确授权了"可以整体重构"。 + + + +- **219 包 vs 4 包**。这个倍率的来源是 dsh 要**对外分发插件生态**(`dsh-plugin` topic、 + 第三方 bundle、profile 模板)。DeepCode 没有这个产品前提 —— 抄的是成本,抄不来收益。 +- dsh 自称 developer preview 且**首屏明示会破坏兼容**。DeepCode 已发 0.3.0,有 npm 包、VSIX、 + DMG、update feed 这些下游。拿一个自称会破坏兼容的框架重写已发布产品的地基,风险收益完全不对称。 +- Cordis 是 **vendored** 进 dsh 的 —— 连它自己都不敢直接依赖。DeepCode 采纳意味着要么也 vendor + 一份(多一个上游要跟),要么依赖一个 0.x 外部框架。 +- 机会成本:这次重写会吃掉本轮全部预算,而 §1 的七项**没有一项需要它**。 + +
+ +**裁决:拒绝。** 用户授权了"可以整体重构",但授权是**许可不是要求** —— 判断哪里值得重构正是 +这份方案该给的答案。这里的答案是:**取它的纪律,不取它的框架**。 + +具体地,`ctx.spillStore` 式的三角色 seam 在**真有第二个 provider 时**才建(例如 spill 的 +"本地文件 vs 未来的远程存储"),且在 DeepCode 里表现为**一个模块里的接口 + 实现**, +不拆成三个包。dsh 的三包拆分在 219 包的规模下自洽,在 4 包的仓库里只是目录噪声。 + +### 2.2 workflow 引擎 —— 拒绝(本轮) + +模型编写编排脚本、worker thread 执行。 + +**正方**:表达力远超固定的 sub-agent 派发,能跑出真正的 fan-out/verify 结构。 +**反方**:引入"**模型写代码然后我们直接执行**"这一整个新攻击面 —— 而 worker thread +(dsh 自己也承认)**不是安全边界**。DeepCode 的 `Task` + `TaskCreate` 已覆盖多数编排场景。 + +**裁决:拒绝本轮。** 收益是"更强的编排",而 DeepCode 尚无被现有 sub-agent 卡住的实际用例。 +在没有用例的情况下引入一个明知不是安全边界的代码执行路径,顺序错了。 + +### 2.3 goal 域与 Ralph 循环 —— 推迟 + +**推迟理由**:这两项改变的是"**agent 什么时候停**" —— 是产品取舍,不是能力补齐。 +一个持久目标 + 自动续跑会显著改变 DeepCode 的交互性格(从"回合制"变成"自主推进"), +这该由用户拍板,不该由实现方在一轮技术采纳里顺手决定。列入 backlog。 + +### 2.4 作业注册表统一 —— 推迟 + +DeepCode 今天有两套后台机制:sub-agent 走 `TaskManager`,后台 Bash 走日志文件。dsh 用一个 +`ctx.jobs` 统一。**推迟理由**:这是纯重构(用户可见行为不变),价值在于未来少写一套; +而 §1.4 的持久 shell 会**改变**后台执行的形态。先落 §1.4,等形态稳定后再统一, +否则会统一到一个即将过时的模型上。 + +--- + +## 3. PR 拆分 + +每个 PR 独立可回退,按依赖顺序: + +| # | 内容 | 依赖 | 类型 | +| --- | --------------------------------------- | ---- | ------- | +| 1 | 本文档 + 调研报告 | — | docs | +| 2 | spill:存储 + 策略 + 接入 Bash/WebFetch | — | feature | +| 3 | 重复调用护栏 | — | feature | +| 4 | 逐工具超时兜底 | — | feature | +| 5 | 持久 shell 会话(marker 协议) | — | feature | +| 6 | session 检索(workspace 限定) | — | feature | +| 7 | 工具渲染意图 + 桌面端 diff/终端渲染 | — | feature | +| 8 | 模型无关的结果剪枝 | — | feature | + +PR 2 与 PR 7 各自触及 `ToolResult` / `ToolDefinition`,若并行会在 `types.ts` 冲突 —— +按上表顺序合并,或后者 rebase。 + +## 4. 验证要求 + +每个实现 PR 必须满足: + +- 新增纯函数逻辑有单元测试(阈值边界、预览首尾保留、workspace 过滤等) +- `pnpm typecheck && pnpm lint && pnpm format:check && pnpm test` 全绿 +- 触及桌面端的 PR 需在预览 harness 中实际渲染并截图自验(沿用 `preview-app.html` 的既有做法) +- 不引入原生依赖(§1.4 与 §1.5 的裁决即由此约束推出) diff --git a/docs/research/deepseek-harness.md b/docs/research/deepseek-harness.md new file mode 100644 index 0000000..14370a8 --- /dev/null +++ b/docs/research/deepseek-harness.md @@ -0,0 +1,193 @@ +# DeepSeek Harness (`dsh`) 调研报告 + +> 调研对象:[`deepseek-ai/deepseek-harness`](https://github.com/deepseek-ai/deepseek-harness) +> 基线:`47f94385`(2026-08-13)· 版本 `0.1.0-rc.5` · MIT +> 方式:**一手克隆通读**源码树与 `docs/`,不采信第三方转述 +> 对照基线:DeepCode `main@4d56f44`(0.3.0) +> 配套文档:[采纳方案与辩论](../DSH_ADOPTION_PLAN.md) + +--- + +## 0. 证据分级 + +沿用 [Floatboat 调研](floatboat.md)确立的分级,**只有 A/B 级可作为设计依据**: + +| 级别 | 含义 | +| ---- | ---------------------------------------------------- | +| A | 亲自读过该仓库的源码或规范文本,可指出文件路径 | +| B | 该仓库自己的文档明确声明,但未跑通验证 | +| C | 第三方转述、发布稿、社区讨论 —— **不得作为设计依据** | + +本报告未运行 `dsh`(需要 `DEEPSEEK_API_KEY` 与完整 `pnpm build`),因此**所有关于运行时行为的 +结论均为 B 级**;关于代码组织、接口形状、包边界的结论为 A 级。凡 B 级结论,本文在采纳时 +一律要求 DeepCode 侧**自己重新设计并测试**,而不是照抄实现。 + +--- + +## 1. 它是什么 + +DeepSeek 官方的开源 agent harness,与 DeepCode 属于**同一生态位的直接对照物**:都是驱动 +DeepSeek 模型的 coding agent 运行时。这使它比 Claude Code / Codex 更值得逐项比对 —— 后两者的 +一半功能(云端任务、团队、MDM)在 DeepCode 的定位下没有价值,而 dsh 的取舍面对的是同一组约束。 + +规模(A 级): + +| 维度 | 数字 | +| ------------ | -------------------------------------------- | +| workspace 包 | **219** 个(`packages/<组>/<包>/`) | +| 模型可见工具 | **52** 个(`docs/tool-catalog.md` 逐个列出) | +| 前端 UI 包 | 40 个(`packages/client/ui-*`) | +| 应用 | `apps/cli`、`apps/web` | +| 状态 | developer preview,**明示会破坏兼容** | + +### 1.1 架构:一切皆插件 + +核心主张是 **everything is a plugin**(A 级,`docs/architecture.md`):模型适配器、工具注册表、 +session 日志、**乃至 agent loop 本身**都是插件,全部可从配置替换。没有"特权内核"可打补丁 —— +扩展方式是在插件树旁边挂一个新插件。 + +底座是 [Cordis](https://github.com/cordiverse/cordis)(vendored 进仓库):插件向共享 context +贡献 service、类型化事件与**可逆 effect**;`register()` 返回 disposer,插件卸载时注册自动回滚。 + +组合方式是三层: + +- **profile** —— 一个命名组合(`web` / `headless`),列出它叠的 bundle +- **bundle** —— Cordis 配置行的分发格式 +- **patch** —— 按 id 覆盖某一行的整份 config + +`dsh --profile web --dump-config` 打印实际启动的树,任何一行都能被自己的 patch 换掉。 + +### 1.2 capability seam(能力缝) + +贯穿全仓的组织纪律(A 级,`docs/capability-seams.md`):一条 seam 由**三个角色**构成 —— + +| 角色 | 职责 | 例 | +| ------------------ | ------------------ | ----------------------------------- | +| Service Definition | 声明接口 | `dsh-spill` 定义 `ctx.spillStore` | +| Service Provider | 实现它 | `dsh-spill-local` 存本地文件 | +| Consumer | 使用它(常为工具) | `dsh-spill-policy` 在后置钩子上应用 | + +规则是"**一条 seam 是完整的三者,绝不是其中之一**"。收益不是抽象洁癖:文件系统与子进程 +provider 共享同一个执行世界,于是**把它们指向远程沙箱,Bash / PTY / LSP 会一起搬过去**, +不需要给每个工具各写一份远程分支。这是本次调研里最值得学的一条纪律。 + +### 1.3 turn 流水线 + +```text +turn/start + claim 输入 → 装配 prompt sections + tool schemas + → agent/pre-step (waterfall: 可改写或拒绝这一步要送给模型的消息) + step/start + agent/request → llm/stream → assistant/chunk* → assistant/message + tool/call* → tools/pre-execute → tools/execute → tools/post-execute → tool/result* + step/end + → agent/turn-stopping +turn/end +``` + +**model-visible ⟺ logged**(A 级):任何进入模型请求的东西都必须能从 session 日志重建,且有 +runtime invariant 断言这一点。这条不变量比它听起来重要 —— 它把"注入上下文"从一个随手能加的 +后门,变成一个必须先扩展事件表的动作。 + +--- + +## 2. 能力差异矩阵 + +图例:`✅` 有 · `🟡` 有但形态不同/受限 · `❌` 无 + +| 能力 | dsh | DeepCode 0.3.0 | 差距判定 | +| ------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------- | -------------------- | +| **工具输出溢出(spill)** | ✅ 超限输出落盘,模型拿到预览 + 定位符 | ❌ Bash 30 KB 硬截断,尾部**永久丢失** | **真差距 · 高价值** | +| **重复调用护栏** | ✅ 连续同参调用达阈值注入升级式提醒 | ❌ 无 | **真差距 · 低成本** | +| **逐工具超时** | ✅ 部署策略层统一武装 deadline | 🟡 仅 Bash 自带 timeout | **真差距 · 低成本** | +| **持久 shell / PTY** | ✅ `terminal_open/send/read/signal/close/list` | ❌ 仅一次性 Bash;后台命令写日志文件 | **真差距 · 中成本** | +| **session 检索** | ✅ `session_search` / `session_trace` + SQLite FTS | ❌ JSONL 在盘上,无检索 | **真差距 · 中成本** | +| **后台作业注册表** | ✅ 统一 `ctx.jobs`:list/output/kill + 完成通知 | 🟡 sub-agent 有 TaskManager;后台 Bash 走日志文件(**两套机制**) | 中差距 | +| **持久目标(goal)** | ✅ 事件溯源的 goal + 续跑驱动 | 🟡 TodoWrite(每 session 文件,无续跑) | 中差距 · 需产品取舍 | +| **Ralph 循环** | ✅ 一个不可变目标喂给一串全新子 agent | ❌ 无 | 中差距 · 需产品取舍 | +| **模型无关的结果剪枝** | ✅ compaction-tool-result-pruner | 🟡 只有 LLM 摘要式 compaction | 中差距 · 低成本 | +| **workflow 引擎** | ✅ 模型编写脚本,worker thread 执行 | 🟡 Task/sub-agent 覆盖多数场景 | 弱差距 | +| **工具渲染意图** | ✅ 每个工具声明 `generic`/`terminal`/`diff`+locations | ❌ ToolCard 一律通用卡片 | **真差距 · UI 杠杆** | +| **UI 可扩展性** | ✅ 40 个 `ui-*` 包 + Chat 节点注册表 | 🟡 Repl.tsx 单体 | 中差距 | +| **ACP(编辑器协议)** | ✅ 自动化用 ACP server | 🟡 自有 app-server 协议 + LSP + VS Code | 弱差距 | +| 沙箱 | ✅ landlock(Linux) / seatbelt | ✅ + 选择性网络白名单 | **DeepCode 更强** | +| 文件契约 / 变更账本 | ❌ | ✅ File Contract + Change Ledger + rollback | **DeepCode 更强** | +| 计价意识 | ❌(未见 cache-hit 计价) | ✅ cache-hit 分档 + `/cost` 命中率 | **DeepCode 更强** | +| cron / 定时 | ✅ `schedule_*` | ✅ cron + 日历/文件触发 | 持平 | +| hooks | ✅ 桥接 Claude Code / Codex 钩子协议 | ✅ 原生 hooks | 持平 | +| skills / plugins / MCP | ✅ | ✅ | 持平 | + +### 2.1 顺带查实的两处 DeepCode 缺陷 + +调研过程中对照代码查实(A 级,指向本仓库源码): + +1. **`WebFetch` 把整个响应体灌进模型上下文** + [`web-fetch.ts:149`](../../packages/core/src/tools/web-fetch.ts) 直接 `content: body`, + 上游只有 5 MiB 的**字节**上限(`DEFAULT_MAX_BYTES`),**没有模型可见文本上限**。 + 一个 5 MiB 的 HTML 页面约合 150 万 token —— 远超任何上下文窗口,等于一次调用即毁掉整个 session。 + 这不是"可优化",是 bug。 + +2. **`Bash` 截断即丢失** + [`bash.ts:43-52`](../../packages/core/src/tools/bash.ts) 在 30 KB 处 `slice` 并追加 + `... [stdout truncated]`。被切掉的部分**不写任何地方**,模型没有任何手段取回 —— 一次 + `npm test` 的完整失败输出就这样消失了。 + +两者都由同一个能力修复:spill。 + +--- + +## 3. 值得抄与不值得抄 + +### 3.1 值得抄的:纪律与具体能力 + +- **capability seam 的三角色纪律** —— 但只在真有第二个 provider 的地方用(见方案文档的辩论) +- **spill** —— 直接修复上面两个缺陷 +- **重复调用护栏** —— DeepCode 已有 `reminders/` 子系统,这是现成的落点 +- **逐工具超时策略** +- **持久 shell 会话** +- **session 检索** +- **工具渲染意图** —— 前端最大的单点杠杆 + +### 3.2 不值得抄的:Cordis 化重写 + +**这是本次调研最重要的否定结论。** 详细辩论见[方案文档 §2.1](../DSH_ADOPTION_PLAN.md),此处只记事实: + +- dsh 用 **219 个包**表达 DeepCode 用 **4 个包**表达的东西。这个倍率不是浪费,是它的产品前提 + (对外分发插件生态、`dsh-plugin` topic、第三方 bundle)造成的必要成本。**DeepCode 没有这个 + 产品前提**,抄成本不抄收益。 +- dsh 自己标注 developer preview 且**明示会破坏兼容**;DeepCode 已发 0.3.0 且有下游(npm、VSIX、 + DMG、update feed)。拿一个自称会破坏兼容的框架去重写一个已发布产品的地基,风险与收益完全不对称。 +- Cordis 是 vendored 进 dsh 的;采纳它意味着 DeepCode 要么也 vendor 一份(多一个需要跟进的上游), + 要么依赖一个 0.x 的外部框架。 + +### 3.3 不确定、留待观察 + +- **workflow 引擎**(模型编写编排脚本,worker thread 执行)—— 概念上强,但 DeepCode 的 + Task/sub-agent 已覆盖多数场景,且它引入"模型写代码然后我们执行"的新攻击面。**暂不采纳**, + 等有真实用例再说。 +- **goal + Ralph** —— 是产品取舍而非补齐差距:它们改变的是"agent 什么时候停",属于需要用户 + 拍板的方向,不宜由实现方单方面决定。**列入 backlog,不进本轮。** + +--- + +## 4. 对方的不利事实 + +按调研纪律,同时记录不支持采纳的证据: + +- 版本 `0.1.0-rc.5`,README 首屏即为 **"THERE WILL BE COMPATIBILITY-BREAKING CHANGES"**(A 级)。 +- 219 个包中大量是三角色拆分的产物(如 spill 拆 3 包、goal 拆 4 包、terminal 拆 3 包)。这套 + 纪律在 219 包的规模下自洽,**在 4 包的仓库里照搬会变成纯粹的目录噪声**。 +- 文档密度极高(`docs/config-catalog.md` 3151 行、`tool-catalog.md` 1873 行)且大量为生成物 —— + 说明其可配置面已经大到必须靠生成器维护。这是能力的证据,也是复杂度的证据。 +- 未能验证运行时行为(无 key、未 build),所有行为结论均为 B 级。 + +--- + +## 5. 结论 + +dsh 与 DeepCode 在**内核能力上互有胜负**:DeepCode 在治理(文件契约、变更账本、沙箱网络白名单) +与计价意识上更强;dsh 在**上下文经济学**(spill、剪枝、检索)与**执行形态**(持久终端、作业注册表) +上更强,并且在**前端可扩展性**上领先一代。 + +采纳应当是**能力级的,不是架构级的**:取它的 spill、护栏、持久 shell、session 检索、渲染意图, +拒绝它的 Cordis 化重写。逐项辩论与 PR 拆分见[采纳方案](../DSH_ADOPTION_PLAN.md)。