← 返回列表
未验证
dsh-rs —— DeepSeek Harness 的进程内搜索
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/6 · 已提供中文文档
DeepSeek Harness 搜索工具的进程内 grep 和 glob,基于 ripgrep 的库 crate——一个 napi-rs 插件,在 harness 中针对打包的 ripgrep 子进程进行测量
综合分
28.5
GitHub 分
28.5
用户评分
—
★ Stars
0
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add Saidoua/dsh-rs该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
信任档位:仅索引本站尚未对其实装验证,仅收录元数据
- 是什么
- dsh 原生插件 · chat
- 装得上吗
- 本站尚未做安装检查
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 19 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
数据截至 2026/9/21(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成dsh-rs —— DeepSeek Harness 的进程内搜索
deepseek-ai/deepseek-harness
的 grep 与 glob 工具每次调用都会 spawn 一个打包好的 ripgrep 二进制,并解析它的
--json stdout。本项目改为在 harness 进程内运行 ripgrep 自己的库 crate,以 napi-rs
addon @saidouahdachi/dsh-native 的形式交付给 Node,并在
fork 中接入、保留 fallback。
实测环境:Apple M2 Pro、macOS 26.6.2、Node v25.9.0、--release,2026-09-05。
它为什么存在
grep "const" 扫 packages/ 是 agent 再普通不过的一次请求。ripgrep 会用 21.1 MB 的
--json 来回答它,超过工具 20 MB 的原始输出上限,于是 spawn 路径失败,模型收到的是
一条让它缩小搜索范围的错误。进程内没有可被撑爆的传输通道:
grep "const" in packages/ — 21 MB of rg --json, over the tool's 20 MB cap:
spawned rg : FAILS SEARCH_RAW_OUTPUT_OVERFLOW (68 ms)
dsh-rs : ok Found 250 of 75108 matches (818 ms)
这是能力上的差别,不是延迟上的差别,也正是这个 addon 的理由。下面的延迟表是这套论证里
较小的一半。
在 harness 中实测
bench/harness-bench.mjs 把同一份负载在真实构建出来的 harness 上跑两遍 —— 每个后端一
遍、各自一个进程 —— 通过 ctx.tools.execute 发起工具调用:
| 工具调用(7 次取中位数) | spawn 的 rg | dsh-rs | 加速比 |
|---|---|---|---|
| grep,整棵 packages/ 树 | 57.7 ms | 52.2 ms | 1.11x |
| grep,packages/session | 11.0 ms | 6.2 ms | 1.77x |
| glob .ts,整棵 packages/ 树 | 69.4 ms | 64.6 ms | 1.07x |
| glob .ts,packages/session | 6.1 ms | 2.8 ms | 2.20x |
消失掉的是固定的 spawn 开销,所以窄范围搜索 —— 也就是典型 agent 调用的形态 —— 收益最
大,而全树遍历几乎不动。并发之下差距是收窄而不是拉大(同时一次搜索时 1.38x,八次时
1.17x):线程和进程一样要排队。
bench/search-bench.mjs 在低一层测同一件事(spawn + --json 解析,对照打包的
@vscode/ripgrep),结论一致:全树 grep 1.5x,窄范围 1.6x,glob 1.1–1.2x。
诚实地说清代价:在动辄花掉数秒模型时间的轮次里,这是每次工具调用省下 2–5 ms。为那个失败
模式而 ship 它,不是为这几毫秒。
范围
搜索是 harness 里唯一一条「换成原生实现会改变 agent 能做什么」的热路径。其余候选都评估过
并留在 TypeScript 里,理由写在 docs/ANALYSIS.md,免得这个问题被问第二
遍:会话读取受限于 JSON.parse,durable append 受限于 fsync,而按轮次计价的 token 成本比
V8 做那点算术更贵。
作为插件安装,不需要 fork
plugin/ 就是 @saidouahdachi/dsh-tool-fs-search-native:一个把 grep 与
glob 注册到本 addon 上的 Cordis 插件,并附带一层禁用内置条目的 patch,于是一个 profile
通过 dsh plugin add 就能用上进程内后端,而不必 fork harness。名称、schema、引导语、渲染
文本、搜索卡片与 spill 恢复都来自内置包自己的导出,因此这次替换对会话不可见;在没有可用
addon 时,插件会自行注册内置的 spawn 版工具。
已接入 harness
fork 的 master 把 @saidouahdachi/dsh-native
作为 packages/fs/tool-fs-search 的依赖加入,并在运行时选用它,以 ripgrep spawn 作为
fallback:DSH_NATIVE=0、没有预编译二进制的平台、或加载不了的 addon,都会原样保持 spawn
的行为。execute 以不设上限的方式调用 grepSearchAsync / globSearchAsync,因此上限与预
览仍归 retention 与 spill 两层所有;工作跑在 libuv 的线程池上 —— 上游是 spawn 一个进程,所
以这次工具调用不能在整棵树遍历期间占住事件循环。
| 用例集 | 结果 |
|---|---|
| packages/fs/tool-fs-search | 163 通过 —— 真实文件系统集成用例每个后端各跑一遍 |
| harness 全量用例(vitest run) | 18 233 通过,0 失败 —— 在上游 0.1.3-alpha.1 的 fork 上 |
| npm run typecheck、oxlint、45 项静态门禁 | 干净 |
tools.spec.ts 为每个用例编排了一个假的 subprocess,因此它固定 DSH_NATIVE=0,负责 spawn
这条传输通道;integration.spec.ts 则在真实文件系统上证明两个后端对模型来说无法区分。
已知的约定差异。 上游的协作式工具超时会终止 rg 的进程树。进行中的进程内搜索没有进程
可以终止,因此 exec.signal 在搜索开始前被遵守,在搜索返回时被观察到。上面那次 818 ms 的
宽搜索就是要盯住的情况。
这里的一致性指什么
ripgrep 用它打印出来的那个路径去匹配每一个 glob —— include 过滤、glob 模式、VCS 排除
—— 也就是 path 参数与遍历得到的后缀拼接出的形式。进程内运行的搜索无法改变进程 cwd,因此
walk 会解析出一个绝对根,并重建那个打印形式用于 glob 匹配与展示。glob 交给 walker 自己的
ignore::overrides 处理,以 workdir 为根,正如 ripgrep 以它的进程 cwd 为根,这样 ripgrep
的优先级规则 —— 显式的正向 --glob 压过隐藏文件过滤 —— 才得以保持。
bench/search-parity.mjs 在相同 argv 下,把这一点钉在打包的 ripgrep 二进制上(从
harness 检出目录解析,而不是 PATH 上碰巧存在的那个 rg):
- fixture 目录树(gitignore、隐藏文件、含 NUL 的二进制、非法 UTF-8、node_modules)与上游源
码树上的匹配集合与总数;
- 每一种 target 形态下的打印路径形式 —— undefined、.、子目录、单个文件;
- 带分隔符、锚定在 workdir 上的 include glob(path: 'src' 下的
include: 'src/.ts');
- 每个文件一个连续块、按路径排序,因此 250 条匹配的头部截断每次都是同一页(ripgrep 自身的
并行文件顺序并非如此);
- target 位于 VCS 目录或其内部时列不出任何东西,正如 --glob=!/.git/ 让 ripgrep 列不
出任何东西;
- 含字面换行的 pattern 被拒绝,正如 ripgrep 在没有 --multiline 时也拒绝它;
- --sort=modified 的顺序,按 mtime 序列比对 —— 相同时间在这里按路径决定先后、在 ripgrep
里按 walk 顺序决定,而两者在 packages/ 下 4 020 个 TypeScript 文件上一致。
打包与发布
addon 以一个包家族发布:@saidouahdachi/dsh-native 承载 loader,并把每个目标平台的预编译包声明
为可选依赖,各自用 os/cpu 收窄,于是 npm 只会装上与宿主匹配的那个二进制。覆盖五个目
标 —— darwin arm64/x64、linux x64/arm64(glibc)与 win32 x64。不在这个集合里的宿主(musl、
32 位,以及其他)解析不到预编译包,就以 spawn ripgrep 的方式搜索,这也正是 addon 加载失败
时消费方已有的行为。
scripts/platforms.mjs 是所有消费方共读的唯一一份矩阵。在那里加一个目标,运行
node scripts/gen-platform-manifests.mjs 写出各平台包的 manifest 与入口包的
optionalDependencies,发布工作流就会带上它;--check 只检查不写入,这是 CI 跑的形式。
.github/workflows/release.yml 在各自的 runner 上构建每个目标,用
scripts/stage-platform.mjs 把 cdylib 就位,然后打包 —— 平台包的 prepack 守卫会拒绝缺失
的、空的或操作系统不对的二进制,因此一个 Mach-O 文件绝不会被塞进 linux 包里。发布是一个显
式的工作流输入,并拒绝在 v tag 之外运行;平台包先于入口包发布,这样入口包落地时它的可
选依赖已经能解析。预演与正式发布消费的是同一批 tarball。
插件包不在那里构建。它要对着 harness 的包编译,而与之匹配的版本还没有发布到 npm,因此在那
之前只能手工发布。
测试
./scripts/build.sh # build addon + smoke test
cargo test -p dsh-core # 15 Rust unit tests
cd bench && DSH_UPSTREAM=… node search-parity.mjs # 13 searcher ≡ real rg checks
cd bench && DSH_UPSTREAM=… node search-bench.mjs # spawn vs in-process
cd bench && DSH_UPSTREAM=… node harness-bench.mjs # in-harness, both backends
用法
const dsh = require('./npm')
const hits = await dsh.grepSearchAsync('SearchError', 'packages', '.ts', cwd)
const files = await dsh.globSearchAsync('.ts', 'src', cwd)
四个导出:grepSearch / globSearch 在调用线程上运行,grepSearchAsync /
globSearchAsync 在 libuv 的线程池上运行。harness 用的是异步这一对。
许可证
MIT,与上游一致。