dsh-goz
Everything 级全盘文件定位插件:让 DeepSeek Harness 的 agent 毫秒级回答「某个文件在哪里」。
dsh-goz 是一个 Cordis 插件,注册 search_file / goz_status 两个面向模型(model-facing)的工具,底层由 goz 引擎支撑。goz 直接读取 NTFS 的 MFT(主文件表) 建立内存文件名索引,完全绕过目录树遍历——检索的是文件名 / 大小 / 时间索引,不含文件内容。
goz 是 Everything 的开源替代品。本机实测:315 万文件(C/D/E 三卷)全盘查询单字符 毫秒级 返回;daemon 空闲工作集约 220-290 MB(进程 private ~520-580 MB,索引结构约 300 MB),索引常驻内存。
设计动机:为什么需要 goz
官方 glob / grep(tool-fs-search)基于 ripgrep 遍历目录树,是沙箱内、项目内检索的正确答案。但「这个文件在哪」的跨工作区问题——例如「我昨晚下载的 PDF 在哪」「D 盘所有 .env 文件」——需要全盘遍历,代价随磁盘规模线性增长:
| 任务 | goz | 常规手段 | 差距 |
|---|---|---|---|
全盘按文件名找 goz.exe |
619 ms | PowerShell Get-ChildItem -Recurse 331,437 ms |
~536x |
全盘通配 *.docx |
756 ms(total 3451) | find 限深 6 耗时 66,383 ms,只找到 355 个 |
~88x,且漏检约 90% |
子树(9,585 文件)*.md |
658 ms(total 3873) | find 完整遍历 4,053 ms |
~6x |
结论:差距最大的是全盘 / 跨盘符、模糊文件名的定位——没有 goz 时这类任务基本做不成(遍历磁盘需要数分钟且易超时);差距最小的是已知路径的小目录内搜索(此时 goz 只是锦上添花,官方 glob 更合适)。基准细节见性能。
架构
┌─────────────┐ spawn (ctx.subprocess seam) ┌─────────────┐ 命名管道 ┌─────────────┐
│ dsh agent │ ──▶ search_file / goz_status ──▶ │ goz.exe │ ──▶ \\.\pipe\goz-v1 ──▶ │ gozd.exe │
│ (模型) │ ◀── 结构化 JSON 值 ◀── │ (CLI 客户端)│ ◀── 查询结果 ◀── │ (系统服务) │
└─────────────┘ └─────────────┘ └──────┬──────┘
│ 读 MFT
┌───────▼───────┐
│ NTFS 卷 (C/D/E) │
└───────────────┘
gozd.exe(daemon):管理员权限启动的系统服务,读取 NTFS MFT 建立内存索引,通过\\.\pipe\goz-v1命名管道服务查询。索引常驻,因此每次查询都是毫秒级。goz.exe(CLI 客户端):每次工具调用由插件 spawn 一次,通过命名管道向 daemon 发查询,以--json输出结构化结果后立即退出。客户端本身是无权限用户态进程。- 插件(本包):负责工具 schema、参数校验、argv 构造、JSON 解析、结果规范化、超时声明和
tools/pre-execute审批门。从不暴露后台任务——只有在goz.exe退出、被协作式超时终止、被中止或失败后,工具调用才返回。
为什么 daemon 需要管理员权限(唯一的人工步骤)
读 MFT 需要管理员权限,而 dsh 是用户态进程弹不了 UAC,所以 daemon 的安装是插件使用前唯一需要人工执行的一步(在管理员终端运行 gozd install)。安装后 daemon 作为 Windows 系统服务常驻,agent 的每一次 search_file 都是毫秒级响应。
安装
1. 安装插件
dsh plugin add dsh-goz
# 或本地目录
dsh plugin add ./dsh-goz
插件包内自带 goz.exe + gozd.exe 二进制(Windows x86_64,位于 vendor/),无需单独下载或编译。daemon 版本可用 .\vendor\gozd.exe --version 验证(当前为 gozd 0.1.1);goz.exe 是无状态 CLI 客户端,刻意不提供 --version 开关(会报 unknown switch),其行为版本跟随同目录的 gozd.exe。
2. 一次性安装 daemon(唯一的人工步骤)
# 开始菜单搜「PowerShell」或「终端」,右键 → 以管理员身份运行
# gozd.exe 不在系统 PATH 里,先进入插件的 vendor 目录(路径按实际安装位置调整)
cd C:\Users\Administrator\Desktop\dsh\dsh-goz\vendor
.\gozd.exe install
关于工作目录:
gozd install内部用自身可执行文件的绝对路径注册服务(安装后服务 PathName 为...\vendor\gozd.exe run --service),与你在哪个目录执行无关,所以不需要特意 cd 到某个"工作目录"。上面的cd只是为了让系统能找到gozd.exe这个命令——它不在 PATH 里,直接敲gozd install会报「无法将 gozd 识别为 cmdlet」。你也可以不 cd,直接写完整路径:& "C:\...\dsh-goz\vendor\gozd.exe" install。
也可以手动前台运行:.\gozd.exe run(调试用,关窗即停)。查看安装后的状态:.\goz.exe --status。
3. 验证
先核对二进制版本(输出应为 gozd 0.1.1):
.\vendor\gozd.exe --version
然后在 dsh 对话里问 agent「找一下 goz.exe 在哪」,或直接跑测试:
node tests/e2e.mjs # 主集成测试:真实启动 web profile,13 项断言(需 daemon 在线)
node tests/verify-syntax.mjs # README 查询语法表逐条核对(CLI 级)
node tests/verify-readme.mjs # README 插件层声明核对(web profile + overlay)
node tests/smoke.mjs # CLI 冒烟测试:直接 spawn vendor/goz.exe 验证查询链路
工具
| 工具 | 参数 | 行为 |
|---|---|---|
search_file |
query(必填)、scope?、max? |
全盘/限定目录按文件名毫秒级定位,返回结构化匹配 |
goz_status |
— | 检查 daemon 是否在线、索引健康状态;离线时返回安装指引 |
search_file(query, scope?, max?)
| 参数 | 类型 | 说明 |
|---|---|---|
query |
string,必填 | goz 查询语法(见查询语法),非空 |
scope |
string,可选 | 限定搜索目录(映射 -path)。缺省为全盘。超出白名单的 scope 会触发用户审批 |
max |
number,可选 | 结果上限(映射 -n),默认取配置 defaultMax(50) |
返回规范 JSON 值(SearchValue):
{
"query": "goz", // 回显原始查询
"scope": null, // 本次搜索根,null 表示全盘
"total": 2, // 完整匹配数(诚实位)
"returned": 2, // 实际返回条数(≤ max)
"more": false, // 是否还有更多(诚实位;注意 goz 在 -n 截断时仍为 false,截断判断用 returned < total)
"volumes_incomplete": false, // 结果可能不完整(诚实位)
"results": [
{
"path": "C:\\dsh\\dsh-goz\\vendor\\goz.exe",
"is_dir": false,
"size": 4617216,
"mtime_iso": "2026-08-17T03:12:44.000Z"
}
]
}
模型看到的是渲染后的文本(output.render),例如:
搜索 "goz" 于 全盘:共 2 个匹配,返回 2 条。
[1.2 MB] C:\dsh\dsh-goz\vendor\goz.exe 修改于 2026-08-17T03:12:44.000Z
[目录] C:\dsh\dsh-goz\vendor 修改于 2026-08-17T03:14:02.000Z
诚实位设计:total / more / volumes_incomplete 三个字段原样透传 goz 的 QueryResults 帧,模型永远知道自己看到的结果是否完整——volumes_incomplete 为真时渲染会加 ⚠ 前缀提示;结果被 max 截断(returned < total)时提示可缩小查询或增大 max。注意 goz 的 more 字段在 -n 截断时仍为 false,截断判断以 returned < total 为准。
goz_status()
返回 { running: boolean, detail: string }。在线时 detail 为 gozd status 输出(卷数、每卷条目数、phase、drift、内存占用);离线时返回完整安装指引。建议模型在首次 search_file 前先确认引擎在线。
查询语法
goz 的查询语法与 Everything 兼容(es-compatible),作用于文件名(不含路径内容,但含路径的 token 按路径子串匹配):
| 语法 | 含义 | 示例 |
|---|---|---|
report |
文件名子串(大小写不敏感) | report 匹配 QuarterlyReport.xlsx |
*.pdf |
通配符(* / ?) |
*.pdf |
ext:pdf;docx |
扩展名过滤(多值用分号;逗号在当前二进制中无效) | ext:docx |
folder: / file: |
仅目录 / 仅文件 | folder: node_modules |
size:>1mb |
大小过滤(< > <= >= =) |
size:>1gb |
path:projects\src |
路径子串匹配 | path:C:\dsh |
"some dir" |
引号内整体匹配(含空格) | "visual studio" |
case: |
大小写敏感开关(当前 v0.1.1 二进制不生效) | case:goz.exe |
| 多个词 | 空格分隔 = AND | annual report |
注意:排除运算符(
!term)在当前 vendor 的 goz 二进制中尚未实现——使用会得到 exit 4 与「operator '!' is not supported yet」错误。需要排除语义时,用多个正向过滤组合(如ext:pdf path:reports)或增大max后在结果中自行筛选。
注意(实测于 v0.1.1):
ext:多扩展名必须用分号分隔(ext:md;png有效),逗号无效(ext:md,png返回 0——逗号被当作扩展名的一部分)。case:大小写开关不生效:任何case:前缀查询都会被当作字面子串解析而返回 0;普通查询本身大小写不敏感,需要精确大小写匹配时目前只能靠增大max后在结果中筛选。
与 ripgrep 语法的区别:goz 查询是Everything 式搜索语言(子串 + 通配符 + 冒号过滤器),不是正则。项目内内容搜索仍用官方
grep。
配置
插件通过 Cordis patch 文件配置,所有字段可选:
- id: goz
name: dsh-goz
config:
defaultMax: 50 # 默认结果上限(模型未传 max 时)
timeoutMs: 15000 # 工具调用协作式超时(毫秒)
binDir: "" # goz.exe 所在目录;留空用插件内 vendor/
allowedRoots: # 白名单目录;超出需用户审批(见安全模型)
- "C:\\Users\\me\\Documents"
- "D:\\projects"
| 配置键 | 默认值 | 含义 |
|---|---|---|
defaultMax |
50 |
模型省略 max 时的结果上限;z.number().step(1).min(1) |
timeoutMs |
15000 |
附加到工具定义的协作式工具调用预算;min(1000)。subprocess seam 在预算之外另有 2s 终止升级宽限 |
binDir |
插件 vendor/ |
自定义 goz.exe / gozd.exe 所在目录(绝对路径);留空用打包二进制 |
allowedRoots |
空(无审批) | 白名单目录数组;相对路径按会话工作目录解析 |
启停
插件列表页是只读投影,启停的唯一事实源是 profile 的 cordis.patch.yml,用同 id 条目覆盖:
- id: goz
disabled: true # 停用;去掉该行或改为 false 重新启用
loader 支持热重载,改完保存即生效,无需重启 dsh。
安全模型
goz 的信任模型与 Everything 一致:任何认证的本地用户可查询文件名/大小/时间索引(不含内容),已在 goz 上游 README 中文档化。索引本身永不触碰文件内容——内容搜索请用 grep。
dsh-goz 在此之上提供两层防护:
- daemon 身份校验(强制):
goz.exe连接命名管道时验证服务器 owner 为 SYSTEM/Administrators,拒绝 pipe squatter(冒充 daemon 的进程,exit code 9)。客户端绝不向不受信任的管道发送查询。 - 白名单审批(可选):配置
allowedRoots后,任何超出白名单的scope都会让插件在tools/pre-execute钩子里返回ask决策——dsh 向用户弹出审批面板,用户可拒绝。全盘可见性由此变成每次可审计、可拒绝的交互,而不是默认放开。
错误处理
goz.exe 的退出码被规范化为模型可见的错误消息:
| 退出码 | 含义 | 模型看到的消息 |
|---|---|---|
8 |
daemon 未运行(\\.\pipe\goz-v1 无服务器) |
完整安装指引(管理员终端 gozd install) |
9 |
管道上有服务器但不是受信任的提权 daemon(owner 非 SYSTEM/Administrators) | 拒绝说明:已拒绝发送查询 |
| 其他非零 | goz 启动失败 / 查询失败 | exit code N + stderr 尾部摘录(上限 16KB) |
参数错误(空 query、非法 max 等)是普通工具参数错误:execute 入口的 parseSearchInput 抛 TypeError,不进入 goz 调用,也不映射任何退出码。
goz_status 对任何 goz 失败都不抛错,统一返回 { running: false, detail: 说明 } 让模型优雅降级:daemon 离线(exit 8)时 detail 为完整安装指引;管道不可信(exit 9)或其他非零退出时 detail 为拒绝说明/错误信息(与上表 search_file 抛出的消息文本一致)。
模型体验
系统提示词
插件在 ctx.systemPrompt 注册一个 tool:search_file 段(order 110),内容大致为:
search_file 是系统级全盘文件定位工具(Everything 级,毫秒响应),用于回答"某个文件在哪里"的跨工作区问题——例如"我昨晚下载的 pdf"、"D 盘所有 .env 文件"。它检索的是文件名/大小/时间索引,不含文件内容;内容搜索请用 grep 类工具。query 支持文件名子串、通配符(*.pdf)、ext:pdf、folder: 等语法;scope 限定目录(超出白名单会请求用户批准)。
工具 schema
search_file描述强调:整台 Windows 机器按文件名毫秒级定位、索引不含内容、适合跨工作区模糊检索、项目内优先用glob;返回含total/more/volumes_incomplete诚实位。goz_status描述建议模型在search_file前先确认引擎在线。
结果与错误
- 结果:见
search_file返回的渲染示例。实际返回条数小于完整匹配数(returned < total,即结果被max截断)时,渲染附「还有更多结果」提示。 - 错误:daemon 离线的
exit 8错误会附带可执行的安装指引,模型可据此告知用户完成一次性安装。
Token / KV Cache 影响
提示词段和工具 schema 是注册期固定的:插件作用域、配置与文本不变时前缀稳定;激活/停用插件会使该段复用失效。结果与错误仅追加在可复用请求前缀之后,不使既有 KV Cache 条目失效。
性能
本机实测环境:Windows 11,NTFS 三卷(C: 1,914,045 + D: 1,202,415 + E: 38,137 ≈ 315 万条目),daemon 索引常驻内存。每次查询是「spawn goz.exe + 管道往返 + JSON 解析」,不含冷启动(索引已在内存)。
复查(2026-08-17):同一环境下全盘查询复测均值为 90-110 ms(此前另一次复测为 423-478 ms),均优于下表记录值——性能随系统负载与 daemon 状态波动,但量级一致(毫秒级),「与磁盘规模无关」的结论不变。
| 任务 | goz | 常规手段 | 差距 |
|---|---|---|---|
全盘按文件名找 goz.exe |
619 ms | PowerShell Get-ChildItem -Recurse 331,437 ms(约 5.5 分钟) |
~536x |
全盘通配 *.docx |
756 ms(total 3451,结果完整) | find 限深 6:66,383 ms,只找到 355 个 |
~88x,且漏检约 90% |
子树(9,585 文件)*.md |
658 ms(total 3873) | find 完整遍历 4,053 ms |
~6x |
全盘 *.tsx |
682 ms(total 524) | — | 常规手段基本不可行 |
要点:
- 全盘/跨盘符、模糊文件名定位是 goz 的质变场景(536x 且完整)。没有它,这类任务要么超时失败、要么深度受限漏检。
- 已知路径的小目录内搜索差距缩小到个位数倍——这种场景官方
glob足够,无需动用 goz。 - goz 的时间开销几乎与磁盘规模无关(索引在内存);常规遍历的时间与文件数线性相关。
已知限制
- 仅 Windows:
os字段限制为win32;NTFS MFT 读取、命名管道服务、gozd服务模型均为 Windows 专属。 - 索引不含内容:只能按文件名/大小/时间检索。内容搜索用
grep,这是设计边界而非缺陷。 - 结果可能不完整:卷仍处于索引(
phase未 live)或不可用时,volumes_incomplete为真,goz 会诚实上报而不是假装完整。 - 无 shell 层:查询词是普通 argv 元素,不存在 shell 引号问题,但也没有 shell 管道/组合能力;复杂过滤请多次调用或在应用侧组合。
- 权限模型放行同名文件:goz 索引的是文件名而非 ACL;检索结果可能包含用户无权读取的路径(读取时才由文件系统拒绝)。
- 没有 UI 卡片定制:工具沿用通用卡片渲染(
presentCall/presentResult未定制),模型可见文本由output.render提供。 - 搜索与文件访问没有共享工作区证明:返回的绝对路径能否继续
read,取决于后续工具对该路径的权限,本插件不做运行时校验。
与官方搜索工具的分工
| 工具 | 场景 | 底层 |
|---|---|---|
glob / grep(官方 tool-fs-search) |
沙箱内项目检索,高频 | ripgrep(遍历目录树) |
search_file(本插件) |
全盘/跨工作区定位,低频,显式授权 | goz(MFT 内存索引,毫秒级) |
开发
npm install # 安装依赖(peer 由 dsh 宿主提供)
npm run build # tsc 编译到 lib/
npm test # 主集成测试 tests/e2e.mjs(需 daemon 在线)
node tests/smoke.mjs # CLI 冒烟测试(需 vendor/goz.exe 且 daemon 在线)
本地调试用 overlay(不修改 profile):
dsh --patch ./cordis.patch.yml
测试覆盖:
tests/smoke.mjs(CLI 冒烟):--json输出可解析且字段与GozQueryResults一致、-pathscope 子树查询、daemon 离线时 exit 8、-n上限生效。tests/e2e.mjs(主集成):真实启动 web profile,覆盖 loader 树、工具注册、goz_status/search_file执行、scope 子树、渲染提示(含returned < total的「还有更多结果」)、参数校验、max 钳制。tests/verify-syntax.mjs/tests/verify-readme.mjs(README 对照):前者逐条实测查询语法表,后者核对插件层声明(schema / timeoutMs / defaultMax / 白名单 / 参数校验矩阵),用于确认 README 与实际行为一致。
调试本机非提权 daemon 时可设 GOZ_INSECURE=1 附加 --insecure-no-server-check 验证数据链路(生产提权 daemon 不需要)。
故障排查
| 症状 | 原因 | 处理 |
|---|---|---|
search_file 报「goz 引擎未运行」 |
daemon 未安装或未启动(exit 8) | 管理员终端执行 gozd install(或 gozd run 前台调试),再重试 |
search_file 报「管道服务器不受信任」(exit 9) |
有进程冒用 \\.\pipe\goz-v1,或 daemon 以非提权方式运行 |
确认 gozd 以管理员身份运行;排查是否有其他进程占用同名管道 |
| 插件列表看不到 goz | 浏览器页面缓存 / 搜索框过滤 | 刷新页面;清空搜索框或搜索「goz」 |
| 结果提示「结果可能不完整」 | 某卷仍在索引或不可用 | 等待索引完成;goz --status 查看各卷 phase |
| 改动 patch 不生效 | loader 未热重载 | 保存后确认无 YAML 语法错误;必要时重启 dsh |
发布与发现
把本插件发布到 GitHub 时,建议给仓库添加 dsh-plugin 话题(Topics),便于被归类进 dsh 插件聚合页、被其他用户搜索发现。添加方式:仓库页 → Settings → Topics → 输入 dsh-plugin 保存。
注:
dsh-plugin仅是 GitHub 仓库的发现话题(topic),不是插件内部的标签字段,也不影响 harness 加载——harness 靠cordis.patch.yml的id/name识别插件。Gitee 等平台有各自独立的话题机制,与 GitHub 不互通。
License
MIT。goz 二进制来自 mustafaahci/goz(MIT)。
还没有评论,来写第一条。