@zvec/zvec-grep 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -77
- package/README_CN.md +39 -76
- package/dist/daemon/backend.d.ts +1 -0
- package/dist/daemon/backend.js +72 -79
- package/dist/daemon/backend.js.map +1 -1
- package/dist/daemon/change-set.d.ts +5 -0
- package/dist/daemon/change-set.js +75 -13
- package/dist/daemon/change-set.js.map +1 -1
- package/dist/daemon/index-coordinator.d.ts +1 -3
- package/dist/daemon/index-coordinator.js +8 -21
- package/dist/daemon/index-coordinator.js.map +1 -1
- package/dist/daemon/root-runtime.js +2 -2
- package/dist/daemon/root-runtime.js.map +1 -1
- package/dist/daemon/server-controller.js +88 -15
- package/dist/daemon/server-controller.js.map +1 -1
- package/dist/daemon/watch-manager.d.ts +5 -0
- package/dist/daemon/watch-manager.js +68 -21
- package/dist/daemon/watch-manager.js.map +1 -1
- package/dist/engine/extraction/code/extractor.d.ts +5 -0
- package/dist/engine/extraction/code/extractor.js +41 -21
- package/dist/engine/extraction/code/extractor.js.map +1 -1
- package/dist/engine/extraction/index.d.ts +1 -1
- package/dist/engine/extraction/index.js +1 -1
- package/dist/engine/extraction/index.js.map +1 -1
- package/dist/engine/extraction/runtime.d.ts +6 -1
- package/dist/engine/extraction/runtime.js +11 -0
- package/dist/engine/extraction/runtime.js.map +1 -1
- package/dist/engine/extraction/vector-content.d.ts +1 -1
- package/dist/engine/extraction/vector-content.js +7 -19
- package/dist/engine/extraction/vector-content.js.map +1 -1
- package/dist/engine/pipeline/indexing/index.js +40 -21
- package/dist/engine/pipeline/indexing/index.js.map +1 -1
- package/dist/engine/pipeline/indexing/scanner/index.d.ts +2 -0
- package/dist/engine/pipeline/indexing/scanner/index.js +68 -11
- package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -1
- package/dist/engine/storage/index.d.ts +1 -0
- package/dist/engine/storage/index.js.map +1 -1
- package/dist/engine/storage/zvec.js +22 -4
- package/dist/engine/storage/zvec.js.map +1 -1
- package/dist/mcp/stdio-bridge.d.ts +2 -0
- package/dist/mcp/stdio-bridge.js +7 -4
- package/dist/mcp/stdio-bridge.js.map +1 -1
- package/docs/06-server.md +4 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<p>
|
|
7
|
-
<a href="./docs/08-roadmap.md"><img src="https://img.shields.io/badge/status-work%20in%20progress-F59E0B?style=for-the-badge" alt="Work in progress" /></a>
|
|
8
|
-
</p>
|
|
9
6
|
<picture>
|
|
10
7
|
<source media="(prefers-color-scheme: dark)" srcset="./.github/assets/zg-logo-dark.svg">
|
|
11
8
|
<img src="./.github/assets/zg-logo.svg" width="150" alt="zg logo" />
|
|
@@ -42,9 +39,6 @@ terminal, or let your agent use it for you.
|
|
|
42
39
|
<img src="./.github/assets/zvec-grep-tour.gif" width="1000" alt="Install the agent integration, index a workspace, and let the agent search it with zvec-grep" />
|
|
43
40
|
</div>
|
|
44
41
|
|
|
45
|
-
Install the integration once, index the workspace, then search from the terminal
|
|
46
|
-
or ask your agent naturally.
|
|
47
|
-
|
|
48
42
|
<a id="features"></a>
|
|
49
43
|
|
|
50
44
|
## 💫 Why zg?
|
|
@@ -64,47 +58,36 @@ or ask your agent naturally.
|
|
|
64
58
|
|
|
65
59
|
## 🚀 Try it yourself
|
|
66
60
|
|
|
67
|
-
Requires Node.js 22 or newer and a configured
|
|
68
|
-
[OpenCode](https://opencode.ai/) installation. The agent example explicitly
|
|
69
|
-
selects a free model, so it does not depend on your default provider.
|
|
70
|
-
|
|
71
61
|
### 1. Set up a sample bookshelf
|
|
72
62
|
|
|
73
63
|
```bash
|
|
64
|
+
# Requires Node.js 22 or newer.
|
|
74
65
|
npm install -g @zvec/zvec-grep
|
|
75
|
-
zg install --target opencode --yes
|
|
76
66
|
|
|
77
67
|
mkdir zg-mystery && cd zg-mystery
|
|
78
|
-
curl -
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
curl -L https://www.gutenberg.org/files/834/834-0.txt -o sherlock-holmes.txt
|
|
68
|
+
curl --retry 3 --retry-all-errors --progress-bar -fL \
|
|
69
|
+
-o alice-in-wonderland.txt https://raw.githubusercontent.com/GITenberg/Alice-s-Adventures-in-Wonderland_11/master/11.txt \
|
|
70
|
+
-o sherlock-holmes.txt https://raw.githubusercontent.com/GITenberg/The-Memoirs-of-Sherlock-Holmes_834/master/834.txt
|
|
82
71
|
|
|
83
72
|
zg index --embedding local/potion-retrieval-32m
|
|
84
73
|
```
|
|
85
74
|
|
|
86
|
-
This creates a small bookshelf of four public-domain novels and indexes it
|
|
87
|
-
locally with Potion Retrieval.
|
|
88
|
-
|
|
89
|
-
Installation starts the shared daemon immediately. The default MCP transport is
|
|
90
|
-
stdio: the agent runs `zg server --stdio`, which safely starts or reuses it
|
|
91
|
-
again after a reboot. Advanced users can keep direct HTTP configuration with
|
|
92
|
-
`zg install --mcp-transport http` and `zg server on`.
|
|
93
|
-
|
|
94
|
-
Use `zg install --mcp-toolset full` when the installed MCP integration should
|
|
95
|
-
also expose managed rg, index, and status tools. The default is `agent`.
|
|
96
|
-
|
|
97
75
|
### 2. Choose how to search
|
|
98
76
|
|
|
99
77
|
#### For agents: ask with OpenCode
|
|
100
78
|
|
|
79
|
+
With [OpenCode](https://opencode.ai/) configured:
|
|
80
|
+
|
|
101
81
|
```bash
|
|
82
|
+
zg install --target opencode --yes
|
|
102
83
|
opencode run --model opencode/deepseek-v4-flash-free \
|
|
103
84
|
"An unseen creature left a few marks. What did the detective infer? Cite local evidence."
|
|
104
85
|
```
|
|
105
86
|
|
|
106
|
-
OpenCode chooses zg on its own—the prompt does not name a tool.
|
|
107
|
-
|
|
87
|
+
OpenCode chooses zg on its own—the prompt does not name a tool.
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary><strong>See the full agent run and answer</strong></summary>
|
|
108
91
|
|
|
109
92
|
```text
|
|
110
93
|
⚙ zvec_grep_zvec_grep_search {"root":".../zg-mystery","query":"An unseen creature left a few marks. What did the detective infer?","fts":["marks","unseen creature","inferred","detective"],"fuse":true}
|
|
@@ -128,6 +111,8 @@ Holmes also inferred that a man entered through the window with it and later
|
|
|
128
111
|
left with the key (sherlock-holmes.txt:5464-5470, 5527-5528).
|
|
129
112
|
```
|
|
130
113
|
|
|
114
|
+
</details>
|
|
115
|
+
|
|
131
116
|
#### For humans: search directly
|
|
132
117
|
|
|
133
118
|
Search the same bookshelf directly, without an agent:
|
|
@@ -136,60 +121,32 @@ Search the same bookshelf directly, without an agent:
|
|
|
136
121
|
zg query --human "An unseen creature left a few marks. What did the detective infer?" --limit 3
|
|
137
122
|
```
|
|
138
123
|
|
|
139
|
-
zg returns the relevant passages from `sherlock-holmes.txt`, ranked ahead of
|
|
140
|
-
|
|
124
|
+
zg returns the relevant passages from `sherlock-holmes.txt`, ranked ahead of
|
|
125
|
+
`alice-in-wonderland.txt`.
|
|
141
126
|
|
|
142
127
|
<a id="benchmarks"></a>
|
|
143
128
|
|
|
144
129
|
## 📊 Benchmarks
|
|
145
130
|
|
|
146
|
-
zg
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
| Change | **+1.50 pp** | **−47.3%** | **−58.6%** | **−37.5%** | **−38.3%** |
|
|
166
|
-
|
|
167
|
-
Across 20 tasks × 3 runs, zg increased the Judge score by 1.50 points while
|
|
168
|
-
reducing input tokens by 47.3%, tool calls by 58.6%, execution time by 37.5%,
|
|
169
|
-
and cost by 38.3%.
|
|
170
|
-
|
|
171
|
-
### Large-scale text retrieval
|
|
172
|
-
|
|
173
|
-
We use [BrowseComp-Plus](https://github.com/texttron/BrowseComp-Plus), which
|
|
174
|
-
requires multi-document evidence retrieval over a fixed web corpus. The
|
|
175
|
-
evaluation includes 30 queries covering 10 mapped topics and three
|
|
176
|
-
evidence-breadth levels, with 3 runs per profile.
|
|
177
|
-
|
|
178
|
-
| Profile | Accuracy ↑ | Evidence recall ↑ | Search calls ↓ | Input tokens ↓ | Time (s) ↓ | Cost ↓ |
|
|
179
|
-
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
180
|
-
| Agent + BM25 | TBD | TBD | TBD | TBD | TBD | TBD |
|
|
181
|
-
| Agent + zg | TBD | TBD | TBD | TBD | TBD | TBD |
|
|
182
|
-
| Change | TBD | TBD | TBD | TBD | TBD | TBD |
|
|
183
|
-
|
|
184
|
-
Across 30 queries × 3 runs, zg changed accuracy by TBD pp, evidence recall by
|
|
185
|
-
TBD pp, search calls by TBD%, input tokens by TBD%, time by TBD%, and cost by
|
|
186
|
-
TBD%.
|
|
187
|
-
|
|
188
|
-
To reproduce the evaluation, follow the
|
|
189
|
-
[benchmark guide](./benchmarks/README.md) for environment setup, pinned
|
|
190
|
-
revisions, task manifests, baseline configuration, exact commands, and result
|
|
191
|
-
aggregation. All profiles use the same agent, model, prompt, tool budget, and
|
|
192
|
-
three-run protocol.
|
|
131
|
+
zg helps agents find relevant evidence faster by **narrowing the effective
|
|
132
|
+
search space** — using **fewer tokens, tool calls, and less time** while
|
|
133
|
+
preserving answer quality.
|
|
134
|
+
|
|
135
|
+
Each benchmark follows a **controlled, reproducible paired A/B protocol**: the
|
|
136
|
+
same agent runs the same pinned tasks with identical model, prompt, environment,
|
|
137
|
+
and limits; **only `zg` access and usage guidance differ**.
|
|
138
|
+
|
|
139
|
+
<img src="./.github/assets/benchmark-comparison-v2.svg" alt="Paired baseline and zvec-grep comparison for SWE-QA-Bench and BrowseComp-Plus" width="900" />
|
|
140
|
+
|
|
141
|
+
| Benchmark | Answer quality ↑ | Avg. input tokens ↓ | Avg. tool calls ↓ | Avg. agent time ↓ |
|
|
142
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
143
|
+
| [**SWE-QA-Bench**](./benchmarks/swe-qa-bench/README.md)<br>20 tasks · Codebase QA | Judge Score<br>80.42 → 81.92 | 558,651 → 294,262 | 23.42 → 9.70 | 127.5s → 79.7s |
|
|
144
|
+
| [**BrowseComp-Plus**](./benchmarks/browse-comp-plus/README.md)<br>80 cases · Deep-research QA | Accuracy<br>90.00% → 90.00% | 2.04M → 1.19M | 22.70 → 14.24 | 284.8s → 199.3s |
|
|
145
|
+
|
|
146
|
+
Values show **Baseline → zg**.
|
|
147
|
+
|
|
148
|
+
See the [benchmark documentation](./benchmarks/README.md) for full results and
|
|
149
|
+
reproduction details.
|
|
193
150
|
|
|
194
151
|
## 📚 Documentation
|
|
195
152
|
|
package/README_CN.md
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<p>
|
|
7
|
-
<a href="./docs/08-roadmap.md"><img src="https://img.shields.io/badge/status-work%20in%20progress-F59E0B?style=for-the-badge" alt="项目开发中" /></a>
|
|
8
|
-
</p>
|
|
9
6
|
<picture>
|
|
10
7
|
<source media="(prefers-color-scheme: dark)" srcset="./.github/assets/zg-logo-dark.svg">
|
|
11
8
|
<img src="./.github/assets/zg-logo.svg" width="150" alt="zg logo" />
|
|
@@ -42,9 +39,6 @@
|
|
|
42
39
|
<img src="./.github/assets/zvec-grep-tour.gif" width="1000" alt="安装 Agent 集成、为工作区建索引并让 Agent 使用 zvec-grep 检索本地内容" />
|
|
43
40
|
</div>
|
|
44
41
|
|
|
45
|
-
只需安装一次集成并为工作区建索引,之后既可以在终端中搜索,也可以直接向
|
|
46
|
-
Agent 提问。
|
|
47
|
-
|
|
48
42
|
<a id="features"></a>
|
|
49
43
|
|
|
50
44
|
## 💫 为什么选择 zg?
|
|
@@ -64,67 +58,61 @@ Agent 提问。
|
|
|
64
58
|
|
|
65
59
|
## 🚀 动手体验
|
|
66
60
|
|
|
67
|
-
需要 Node.js 22 或更新版本,以及已经完成配置的
|
|
68
|
-
[OpenCode](https://opencode.ai/)。Agent 示例会显式选择免费模型,不依赖你配置的
|
|
69
|
-
默认模型提供方。
|
|
70
|
-
|
|
71
61
|
### 1. 准备示例书架
|
|
72
62
|
|
|
73
63
|
```bash
|
|
64
|
+
# 需要 Node.js 22 或更新版本。
|
|
74
65
|
npm install -g @zvec/zvec-grep
|
|
75
|
-
zg install --target opencode --yes
|
|
76
66
|
|
|
77
67
|
mkdir zg-mystery && cd zg-mystery
|
|
78
|
-
curl -
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
curl -L https://www.gutenberg.org/files/834/834-0.txt -o sherlock-holmes.txt
|
|
68
|
+
curl --retry 3 --retry-all-errors --progress-bar -fL \
|
|
69
|
+
-o alice-in-wonderland.txt https://raw.githubusercontent.com/GITenberg/Alice-s-Adventures-in-Wonderland_11/master/11.txt \
|
|
70
|
+
-o sherlock-holmes.txt https://raw.githubusercontent.com/GITenberg/The-Memoirs-of-Sherlock-Holmes_834/master/834.txt
|
|
82
71
|
|
|
83
72
|
zg index --embedding local/potion-retrieval-32m
|
|
84
73
|
```
|
|
85
74
|
|
|
86
|
-
这会创建一个包含四本公版小说的小书架,并使用 Potion Retrieval 在本地建立索引。
|
|
87
|
-
|
|
88
|
-
安装时会立即启动共享 Daemon。默认 MCP 传输方式为 stdio:Agent 会运行
|
|
89
|
-
`zg server --stdio`,电脑重启后该命令会再次安全启动或复用 Daemon。高级用户
|
|
90
|
-
可以通过 `zg install --mcp-transport http` 保留 HTTP 直连,并自行使用
|
|
91
|
-
`zg server on` 管理后续启动。
|
|
92
|
-
|
|
93
|
-
如需让安装后的 MCP 集成额外暴露 managed rg、index 和 status 工具,可使用
|
|
94
|
-
`zg install --mcp-toolset full`;默认值为 `agent`。
|
|
95
|
-
|
|
96
75
|
### 2. 选择检索方式
|
|
97
76
|
|
|
98
77
|
#### Agent:通过 OpenCode 提问
|
|
99
78
|
|
|
79
|
+
配置好 [OpenCode](https://opencode.ai/) 后:
|
|
80
|
+
|
|
100
81
|
```bash
|
|
82
|
+
zg install --target opencode --yes
|
|
101
83
|
opencode run --model opencode/deepseek-v4-flash-free \
|
|
102
84
|
"An unseen creature left a few marks. What did the detective infer? Cite local evidence."
|
|
103
85
|
```
|
|
104
86
|
|
|
105
|
-
Prompt 中没有指定任何工具,OpenCode 会自主选择 zg
|
|
106
|
-
|
|
87
|
+
Prompt 中没有指定任何工具,OpenCode 会自主选择 zg。
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary><strong>展开查看完整 Agent 调用与回答</strong></summary>
|
|
107
91
|
|
|
108
92
|
```text
|
|
109
93
|
⚙ zvec_grep_zvec_grep_search {"root":".../zg-mystery","query":"An unseen creature left a few marks. What did the detective infer?","fts":["marks","unseen creature","inferred","detective"],"fuse":true}
|
|
110
94
|
→ Read sherlock-holmes.txt [offset=5440, limit=120]
|
|
111
95
|
|
|
112
96
|
Answer:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
97
|
+
The detective is Sherlock Holmes in *The Adventure of the Crooked Man*.
|
|
98
|
+
From the animal's foot-marks, he inferred:
|
|
99
|
+
|
|
100
|
+
- It was not a dog, cat, monkey, or other familiar creature: its print had
|
|
101
|
+
five foot-pads, long nails, and was nearly as large as a dessert-spoon
|
|
102
|
+
(sherlock-holmes.txt:5479-5486).
|
|
103
|
+
- It had a long body and very short legs: nearly two feet long, but with a
|
|
104
|
+
stride of only about three inches (sherlock-holmes.txt:5497-5502).
|
|
105
|
+
- It was carnivorous because it climbed the curtain toward a canary's cage
|
|
106
|
+
(sherlock-holmes.txt:5509-5510).
|
|
107
|
+
- It was probably from the weasel or stoat tribe, though unusually large
|
|
108
|
+
(sherlock-holmes.txt:5514-5516).
|
|
109
|
+
|
|
110
|
+
Holmes also inferred that a man entered through the window with it and later
|
|
111
|
+
left with the key (sherlock-holmes.txt:5464-5470, 5527-5528).
|
|
126
112
|
```
|
|
127
113
|
|
|
114
|
+
</details>
|
|
115
|
+
|
|
128
116
|
#### 用户:直接检索
|
|
129
117
|
|
|
130
118
|
不通过 Agent,直接搜索同一个书架:
|
|
@@ -133,52 +121,27 @@ Answer:
|
|
|
133
121
|
zg query --human "An unseen creature left a few marks. What did the detective infer?" --limit 3
|
|
134
122
|
```
|
|
135
123
|
|
|
136
|
-
zg
|
|
124
|
+
zg 会将 `sherlock-holmes.txt` 中的相关段落排在
|
|
125
|
+
`alice-in-wonderland.txt` 前面。
|
|
137
126
|
|
|
138
127
|
<a id="benchmarks"></a>
|
|
139
128
|
|
|
140
129
|
## 📊 性能测试
|
|
141
130
|
|
|
142
|
-
zg
|
|
143
|
-
|
|
144
|
-
### 代码仓库检索
|
|
145
|
-
|
|
146
|
-
我们使用 [SWE-QA-Bench](https://github.com/peng-weihan/SWE-QA-Bench),它要求
|
|
147
|
-
Agent 在真实代码库中完成跨文件、多跳推理。
|
|
148
|
-
|
|
149
|
-
- **覆盖范围:**20 个检索密集型任务,覆盖论文中的 **What、Where、How、Why**
|
|
150
|
-
四个一级类别、8 种意图和 11 个代码仓库。
|
|
151
|
-
- **Agent:**使用 **Claude Opus 5** 的 Claude Code。
|
|
152
|
-
- **Embedding:**zg 方案使用 **Qwen3.7 Text Embedding**。
|
|
153
|
-
- **测试方式:**每个任务、每种方案运行 3 次。
|
|
154
|
-
|
|
155
|
-
| 方案 | 评审得分 /100 ↑ | 输入 Token ↓ | 工具调用 ↓ | 耗时(秒)↓ | 成本 ↓ |
|
|
156
|
-
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
157
|
-
| 原生工具 | 80.42 | 558,651 | 23.42 | 127.5 | $0.905 |
|
|
158
|
-
| 原生工具 + zg | 81.92 | 294,262 | 9.70 | 79.7 | $0.558 |
|
|
159
|
-
| 变化 | **+1.50 个百分点** | **−47.3%** | **−58.6%** | **−37.5%** | **−38.3%** |
|
|
160
|
-
|
|
161
|
-
在 20 个任务 × 3 次运行中,zg 将评审得分提升了 1.50 分,同时将输入 Token
|
|
162
|
-
减少 47.3%、工具调用减少 58.6%、执行时间减少 37.5%、成本减少 38.3%。
|
|
131
|
+
zg 通过**缩小有效搜索空间**,帮助 Agent 更快找到相关证据 —— 在保持回答质量的同时,使用**更少的 Token、工具调用和时间**。
|
|
163
132
|
|
|
164
|
-
|
|
133
|
+
每项测试均采用**受控、可复现的配对 A/B 评测**:同一个 Agent 在完全相同的模型、Prompt、环境和资源限制下执行同一组预先固定的任务;**实验组仅额外提供 `zg` 工具及其使用说明**。
|
|
165
134
|
|
|
166
|
-
|
|
167
|
-
Agent 从固定网页语料库中检索多文档证据。测试包含 30 个问题,覆盖 10 个映射主题
|
|
168
|
-
和三个证据广度级别,每种方案运行 3 次。
|
|
135
|
+
<img src="./.github/assets/benchmark-comparison-cn-v2.svg" alt="SWE-QA-Bench 和 BrowseComp-Plus 中 Baseline 与 zvec-grep 的配对对比" width="900" />
|
|
169
136
|
|
|
170
|
-
|
|
|
171
|
-
| --- | ---: | ---: | ---: | ---: |
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
| 变化 | TBD | TBD | TBD | TBD | TBD | TBD |
|
|
137
|
+
| 测试 | 回答质量 ↑ | 平均输入 Token ↓ | 平均工具调用 ↓ | 平均 Agent 耗时 ↓ |
|
|
138
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
139
|
+
| [**SWE-QA-Bench**](./benchmarks/swe-qa-bench/README_CN.md)<br>20 个任务 · 代码库 QA | 评审得分<br>80.42 → 81.92 | 558,651 → 294,262 | 23.42 → 9.70 | 127.5s → 79.7s |
|
|
140
|
+
| [**BrowseComp-Plus**](./benchmarks/browse-comp-plus/README_CN.md)<br>80 个样例 · 深度研究 QA | 准确率<br>90.00% → 90.00% | 2.04M → 1.19M | 22.70 → 14.24 | 284.8s → 199.3s |
|
|
175
141
|
|
|
176
|
-
|
|
177
|
-
执行时间和成本的影响尚待测试结果补充。
|
|
142
|
+
表中数值均按 **Baseline → zg** 展示。
|
|
178
143
|
|
|
179
|
-
|
|
180
|
-
任务清单、基线配置、运行命令和结果汇总。所有方案使用相同的 Agent、模型、Prompt、
|
|
181
|
-
工具预算和三次运行协议。
|
|
144
|
+
完整结果和复现细节参见[性能测试文档](./benchmarks/README_CN.md)。
|
|
182
145
|
|
|
183
146
|
## 📚 文档
|
|
184
147
|
|
package/dist/daemon/backend.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export declare class DaemonBackend implements ZvecGrepDaemonBackend {
|
|
|
57
57
|
rg(input: ZvecGrepRgInput): Promise<ZvecGrepRgResult>;
|
|
58
58
|
close(): Promise<void>;
|
|
59
59
|
private runIndex;
|
|
60
|
+
private runIndexOperation;
|
|
60
61
|
private submitIndex;
|
|
61
62
|
private ensureWatcher;
|
|
62
63
|
private settleKnownChanges;
|
package/dist/daemon/backend.js
CHANGED
|
@@ -203,40 +203,26 @@ export class DaemonBackend {
|
|
|
203
203
|
const targetRevision = createsWork
|
|
204
204
|
? runtime.markDirty()
|
|
205
205
|
: runtime.snapshot().dirtyRevision;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
}),
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
runtime.setWriterPending(false);
|
|
227
|
-
throw error;
|
|
228
|
-
}
|
|
229
|
-
if (!submitted.reused) {
|
|
230
|
-
void this.scheduler.waitForRootIdle(runtime.canonicalRoot).finally(() => {
|
|
231
|
-
runtime.setWriterPending(false);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
206
|
+
const submitted = this.scheduler.submit({
|
|
207
|
+
canonicalRoot: runtime.canonicalRoot,
|
|
208
|
+
reason: "manual",
|
|
209
|
+
followupIfRunning: input.rebuild === true || followsNarrowJob,
|
|
210
|
+
run: async (report, signal) => {
|
|
211
|
+
const proof = await this.runIndex(runtime, input, report, options.authorization, signal);
|
|
212
|
+
this.lastScanDiagnostics.set(runtime.canonicalRoot, proof.scanDiagnostics);
|
|
213
|
+
if (proof.reconciled) {
|
|
214
|
+
runtime.markReconciled(targetRevision, proof.reconciliationEpoch);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
runtime.markIndexed(targetRevision);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
});
|
|
234
221
|
const job = input.wait
|
|
235
222
|
? await this.scheduler.wait(submitted.job.id, options.onProgress)
|
|
236
223
|
: submitted.job;
|
|
237
224
|
if (input.wait) {
|
|
238
225
|
await this.settleKnownChanges(runtime);
|
|
239
|
-
runtime.setWriterPending(false);
|
|
240
226
|
}
|
|
241
227
|
this.options.logger?.event("job.submitted", {
|
|
242
228
|
root_id: rootIdentity(runtime.canonicalRoot),
|
|
@@ -378,10 +364,6 @@ export class DaemonBackend {
|
|
|
378
364
|
input.autoUpdate &&
|
|
379
365
|
(runtime.requiresFullReconciliation() ||
|
|
380
366
|
!this.scheduler.hasActiveRoot(runtime.canonicalRoot))) {
|
|
381
|
-
if (!runtime.requiresFullReconciliation() ||
|
|
382
|
-
runtime.canProbeFullReconciliation()) {
|
|
383
|
-
await this.probeCurrentFreshness(runtime);
|
|
384
|
-
}
|
|
385
367
|
const currentJob = this.scheduler.getByRoot(runtime.canonicalRoot);
|
|
386
368
|
const terminalKnownPathJob = !runtime.requiresFullReconciliation() &&
|
|
387
369
|
(currentJob?.state === "failed" || currentJob?.state === "cancelled");
|
|
@@ -535,14 +517,25 @@ export class DaemonBackend {
|
|
|
535
517
|
return this.closePromise;
|
|
536
518
|
}
|
|
537
519
|
async runIndex(runtime, input, report, authorization, signal) {
|
|
520
|
+
const releaseRuntimeActivity = runtime.beginActivity();
|
|
521
|
+
try {
|
|
522
|
+
return await this.runIndexOperation(runtime, input, report, authorization, signal);
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
releaseRuntimeActivity();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async runIndexOperation(runtime, input, report, authorization, signal) {
|
|
538
529
|
const startedAt = Date.now();
|
|
539
|
-
// Watcher path updates already identify their indexing scope
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
const includeInitialStatus = input.
|
|
530
|
+
// Watcher path updates already identify their indexing scope. Uncertain
|
|
531
|
+
// watcher reconciliations are verified by the final status scan, so neither
|
|
532
|
+
// path needs another workspace-wide status scan before indexing starts.
|
|
533
|
+
const includeInitialStatus = input.skipInitialStatus !== true &&
|
|
534
|
+
input.rebuild !== true &&
|
|
535
|
+
input.changedPaths === undefined;
|
|
543
536
|
const includeFinalStatus = input.changedPaths === undefined;
|
|
544
537
|
const before = await this.inspectRoot(runtime.canonicalRoot, includeInitialStatus);
|
|
545
|
-
if (
|
|
538
|
+
if (includeInitialStatus) {
|
|
546
539
|
this.statusCache.set(runtime.canonicalRoot, before);
|
|
547
540
|
}
|
|
548
541
|
const modelLoadRequest = this.indexModelLoadRequest(before, input);
|
|
@@ -573,7 +566,7 @@ export class DaemonBackend {
|
|
|
573
566
|
device: input.runtimeOverridesAreEphemeral ? undefined : input.device,
|
|
574
567
|
daemonInstanceToken: this.runtimeManager.instanceToken,
|
|
575
568
|
});
|
|
576
|
-
const result = await withRemoteEmbeddingOperationPermit(authorization, () => service.index({
|
|
569
|
+
const result = await runtime.withWrite(() => withRemoteEmbeddingOperationPermit(authorization, () => service.index({
|
|
577
570
|
root: runtime.canonicalRoot,
|
|
578
571
|
rebuild: input.rebuild,
|
|
579
572
|
resetPaths: input.resetPaths,
|
|
@@ -592,7 +585,7 @@ export class DaemonBackend {
|
|
|
592
585
|
signal,
|
|
593
586
|
onProgress: report,
|
|
594
587
|
onWriterContext: (context) => runtime.setWriterContext(context, lease.key),
|
|
595
|
-
}));
|
|
588
|
+
})));
|
|
596
589
|
scanDiagnostics = result.scanDiagnostics;
|
|
597
590
|
}
|
|
598
591
|
finally {
|
|
@@ -608,8 +601,10 @@ export class DaemonBackend {
|
|
|
608
601
|
}
|
|
609
602
|
const proofReconciliationEpoch = runtime.reconciliationEpoch();
|
|
610
603
|
const after = await this.inspectRoot(runtime.canonicalRoot, includeFinalStatus);
|
|
611
|
-
if (includeFinalStatus)
|
|
604
|
+
if (includeFinalStatus) {
|
|
612
605
|
this.statusCache.set(runtime.canonicalRoot, after);
|
|
606
|
+
await this.watchers.get(runtime.canonicalRoot)?.refreshPaths?.();
|
|
607
|
+
}
|
|
613
608
|
if (!after.workspaceIndex?.embedding) {
|
|
614
609
|
throw new DaemonError("INDEX_MISSING", "Index completed without an embedding schema.");
|
|
615
610
|
}
|
|
@@ -637,38 +632,29 @@ export class DaemonBackend {
|
|
|
637
632
|
? runtime.markDirty()
|
|
638
633
|
: runtime.snapshot().dirtyRevision;
|
|
639
634
|
const followsNarrowWatch = this.scheduler.getByRoot(runtime.canonicalRoot)?.reason === "watch";
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
reason
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
runtime.setWriterPending(false);
|
|
660
|
-
throw error;
|
|
661
|
-
}
|
|
662
|
-
if (!submitted.reused) {
|
|
663
|
-
void this.scheduler.waitForRootIdle(runtime.canonicalRoot).finally(() => {
|
|
664
|
-
runtime.setWriterPending(false);
|
|
665
|
-
});
|
|
666
|
-
}
|
|
635
|
+
const submitted = this.scheduler.submit({
|
|
636
|
+
canonicalRoot: runtime.canonicalRoot,
|
|
637
|
+
reason,
|
|
638
|
+
followupIfRunning: followsNarrowWatch,
|
|
639
|
+
run: async (report, signal) => {
|
|
640
|
+
if (reason === "background_reconcile" &&
|
|
641
|
+
runtime.canProbeFullReconciliation() &&
|
|
642
|
+
(await this.probeCurrentFreshness(runtime)) === "fresh") {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
const proof = await this.runIndex(runtime, input, report, authorization, signal);
|
|
646
|
+
if (proof.reconciled) {
|
|
647
|
+
runtime.markReconciled(targetRevision, proof.reconciliationEpoch);
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
runtime.markIndexed(targetRevision);
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
});
|
|
667
654
|
if (!wait)
|
|
668
655
|
return submitted.job;
|
|
669
656
|
const job = await this.scheduler.wait(submitted.job.id);
|
|
670
657
|
await this.scheduler.waitForRootIdle(runtime.canonicalRoot);
|
|
671
|
-
runtime.setWriterPending(false);
|
|
672
658
|
return job;
|
|
673
659
|
}
|
|
674
660
|
ensureWatcher(runtime) {
|
|
@@ -678,7 +664,6 @@ export class DaemonBackend {
|
|
|
678
664
|
const coordinator = new IndexCoordinator({
|
|
679
665
|
runtime,
|
|
680
666
|
scheduler: this.scheduler,
|
|
681
|
-
getIndexedFileCount: () => this.statusCache.get(runtime.canonicalRoot)?.status?.filesStored,
|
|
682
667
|
run: async (changes, report, signal) => {
|
|
683
668
|
const changedPaths = [
|
|
684
669
|
...changes.touchedFiles,
|
|
@@ -695,6 +680,7 @@ export class DaemonBackend {
|
|
|
695
680
|
return await this.runIndex(runtime, {
|
|
696
681
|
root: runtime.canonicalRoot,
|
|
697
682
|
changedPaths: changes.forceFullReconcile ? undefined : changedPaths,
|
|
683
|
+
skipInitialStatus: changes.forceFullReconcile,
|
|
698
684
|
}, report, automaticAuthorization.authorization, signal);
|
|
699
685
|
},
|
|
700
686
|
});
|
|
@@ -734,6 +720,7 @@ export class DaemonBackend {
|
|
|
734
720
|
coordinator.enqueue(changes, reason);
|
|
735
721
|
},
|
|
736
722
|
onPendingChange: (pending) => runtime.setWatcherPending(pending),
|
|
723
|
+
getRootPaths: () => this.statusCache.get(runtime.canonicalRoot)?.workspaceIndex?.rootPaths,
|
|
737
724
|
});
|
|
738
725
|
watcher.start();
|
|
739
726
|
this.indexCoordinators.set(runtime.canonicalRoot, coordinator);
|
|
@@ -821,15 +808,21 @@ export class DaemonBackend {
|
|
|
821
808
|
}
|
|
822
809
|
}
|
|
823
810
|
async probeCurrentFreshness(runtime) {
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
811
|
+
const releaseRuntimeActivity = runtime.beginActivity();
|
|
812
|
+
try {
|
|
813
|
+
const freshness = await runtime.probeFreshness(async () => {
|
|
814
|
+
const info = await this.inspectRoot(runtime.canonicalRoot, true);
|
|
815
|
+
this.statusCache.set(runtime.canonicalRoot, info);
|
|
816
|
+
return indexStatusIsFresh(info);
|
|
817
|
+
});
|
|
818
|
+
this.options.logger?.event(`runtime.recovery_probe_${freshness}`, {
|
|
819
|
+
root_id: rootIdentity(runtime.canonicalRoot),
|
|
820
|
+
});
|
|
821
|
+
return freshness;
|
|
822
|
+
}
|
|
823
|
+
finally {
|
|
824
|
+
releaseRuntimeActivity();
|
|
825
|
+
}
|
|
833
826
|
}
|
|
834
827
|
async closeWatcher(canonicalRoot) {
|
|
835
828
|
const watcher = this.watchers.get(canonicalRoot);
|