pi-context-engine 0.1.3 → 0.1.4
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 +10 -0
- package/docs/IMPLEMENTATION-PLAN.md +483 -0
- package/docs/RESEARCH-RETRIEVAL.md +532 -0
- package/package.json +4 -2
- package/src/indexer/embedder.ts +35 -2
package/README.md
CHANGED
|
@@ -130,6 +130,16 @@ Environment overrides:
|
|
|
130
130
|
- **`PCE_EMBED_BACKEND`**: force `local` or `ollama` instead of auto-detect.
|
|
131
131
|
- **`PCE_RECALL_DISPLAY_CAP`**: how many recall hits to show (default 7).
|
|
132
132
|
|
|
133
|
+
**Embedding backends**: two options, picked automatically per project.
|
|
134
|
+
|
|
135
|
+
- **Ollama** (default when reachable): no extra model files on disk beyond the
|
|
136
|
+
Ollama model itself.
|
|
137
|
+
- **Local ONNX** (transformers.js): works with no server. Needs the
|
|
138
|
+
`@huggingface/transformers` optional dependency, which plain `npm` and `pi`
|
|
139
|
+
installs include. If it was stripped (e.g. `--omit=optional`), indexing fails
|
|
140
|
+
with a clear message telling you to reinstall or switch to Ollama, instead of
|
|
141
|
+
a bare module error.
|
|
142
|
+
|
|
133
143
|
## Security
|
|
134
144
|
|
|
135
145
|
Indexing is local, but the index is still a copy of your code. Two guards ship on
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# Implementation plan: retrieval improvements
|
|
2
|
+
|
|
3
|
+
Full build plan for the recommendations in
|
|
4
|
+
[RESEARCH-RETRIEVAL.md](./RESEARCH-RETRIEVAL.md). Nothing here is started yet:
|
|
5
|
+
implementation is deliberately delayed, this document defines exactly what will
|
|
6
|
+
be built, in what order, and how each item is proven before its status changes.
|
|
7
|
+
|
|
8
|
+
**Why a plan first:** every item changes retrieval behavior, and our only
|
|
9
|
+
current metrics are token savings and latency. Building the measurement harness
|
|
10
|
+
before the changes lets us prove each improvement instead of guessing.
|
|
11
|
+
|
|
12
|
+
## How to use this document
|
|
13
|
+
|
|
14
|
+
- **Status legend**
|
|
15
|
+
- **Not started**: planned, nothing built yet. All items are here today.
|
|
16
|
+
- **Ready**: implementation can begin (dependencies met, baseline recorded).
|
|
17
|
+
- **In progress**: work branch open.
|
|
18
|
+
- **Review**: code done, eval deltas attached to the PR.
|
|
19
|
+
- **Done**: merged, released, status table updated.
|
|
20
|
+
- **Dropped**: decided against, with the reason recorded in the item.
|
|
21
|
+
- **Rules for flipping a status**
|
|
22
|
+
1. Typecheck and unit tests pass (`npm run check`).
|
|
23
|
+
2. Eval deltas (before/after) are recorded in the PR description.
|
|
24
|
+
3. README and `src/config.ts` cover any new config keys.
|
|
25
|
+
4. This table is updated in the same PR.
|
|
26
|
+
- **Every item lists**: objective, evidence, files touched, steps, tests,
|
|
27
|
+
acceptance criteria, risks, dependencies, effort (S < half day, M 1-2 days,
|
|
28
|
+
L 3-5 days).
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Status summary
|
|
33
|
+
|
|
34
|
+
| ID | Title | Phase | Priority | Effort | Status |
|
|
35
|
+
|----|-------|-------|----------|--------|--------|
|
|
36
|
+
| R4 | Labeled retrieval eval harness | 0 | P0 | M | Not started |
|
|
37
|
+
| R1 | Contextual chunk headers | 1 | P0 | M | Not started |
|
|
38
|
+
| R2 | BM25 phrases, column weights, symbol matches | 1 | P0 | S | Not started |
|
|
39
|
+
| R3 | Query-aware compression | 1 | P0 | M | Not started |
|
|
40
|
+
| R5 | Multi-path retrieval with symbol path | 2 | P1 | M | Not started |
|
|
41
|
+
| R6 | Small-to-big context expansion | 2 | P1 | L | Not started |
|
|
42
|
+
| R7 | Graph context packs + centrality prior | 2 | P1 | M | Not started |
|
|
43
|
+
| R8 | Query-confidence signal + optional elaboration | 2 | P1 | M | Not started |
|
|
44
|
+
| R9 | Refine suggestions in search results | 2 | P1 | S | Not started |
|
|
45
|
+
| R10 | Opt-in local reranker | 3 | P2 | M | Not started |
|
|
46
|
+
| R11 | Code-specialized embedder option | 3 | P2 | S | Not started |
|
|
47
|
+
| R12 | AST density chunking (split-merge) | 3 | P2 | L | Not started |
|
|
48
|
+
| R13 | Linked memory (A-MEM style) | 3 | P2 | M | Not started |
|
|
49
|
+
|
|
50
|
+
**Blocked by nothing right now:** only R4 (phase 0). Everything else waits on
|
|
51
|
+
R4's baseline so its impact can be measured.
|
|
52
|
+
|
|
53
|
+
**Status history**
|
|
54
|
+
- 2026-10-17: plan created from the research note. Implementation delayed by
|
|
55
|
+
owner decision. All items set to **Not started**.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Phase 0: measurement foundation
|
|
60
|
+
|
|
61
|
+
### R4. Labeled retrieval eval harness
|
|
62
|
+
|
|
63
|
+
- **Objective:** measure retrieval quality (not just tokens) so every later
|
|
64
|
+
item has a before/after number.
|
|
65
|
+
- **Evidence:** CORE-Bench (arXiv:2606.11864) shows code-search scores overstate
|
|
66
|
+
agentic usefulness; UDCG (arXiv:2510.21440) shows distracting chunks must be
|
|
67
|
+
counted; Voyage reports CoSQA has ~51% bad labels, so we label our own.
|
|
68
|
+
- **Files:**
|
|
69
|
+
- new `tests/eval/queries.jsonl` (labels)
|
|
70
|
+
- new `scripts/eval-retrieval.mjs` (runner, tsx)
|
|
71
|
+
- `scripts/smoke-real-world.mjs` (add a `quality` block to its report)
|
|
72
|
+
- new `tests/eval/baseline.json` (recorded thresholds)
|
|
73
|
+
- `package.json` (`eval` script, wire into `check`)
|
|
74
|
+
- **Steps:**
|
|
75
|
+
1. Write 60-100 labeled queries over this repo, four kinds: exact identifier
|
|
76
|
+
(`HybridRetriever`), natural language (`where is the retry logic`),
|
|
77
|
+
bug-report style (paste an error string), file-name style
|
|
78
|
+
(`src/retrieval/retriever.ts`). Each label lists expected file paths and,
|
|
79
|
+
where easy, expected chunk line ranges.
|
|
80
|
+
2. Runner indexes a fixture copy of the repo, runs each query through
|
|
81
|
+
`HybridRetriever`, and computes: Recall@5, Recall@10, nDCG@10, distraction
|
|
82
|
+
ratio (share of served chunks whose file is not in the label set), served
|
|
83
|
+
tokens, p50/p95 latency.
|
|
84
|
+
3. Implement nDCG in the script (no dependency); binary relevance is enough.
|
|
85
|
+
4. Record current numbers into `baseline.json`; the runner exits non-zero on
|
|
86
|
+
regression beyond a small tolerance (start at 2 points on Recall@10).
|
|
87
|
+
5. Add an optional external slice later (CORE-Bench level-2 sample) as a
|
|
88
|
+
separate non-gating script.
|
|
89
|
+
- **Tests:** runner produces stable output twice in a row; a deliberately
|
|
90
|
+
broken query (random words) scores below baseline.
|
|
91
|
+
- **Acceptance:** `npm run eval` reports all metrics, fails on regression, and
|
|
92
|
+
is documented in README's Development section.
|
|
93
|
+
- **Risks:** labels drift when the repo changes. Mitigation: labels point at
|
|
94
|
+
files/paths, not exact line numbers, except where a test pins them.
|
|
95
|
+
- **Dependencies:** none. **Effort:** M.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Phase 1: quick wins (P0)
|
|
100
|
+
|
|
101
|
+
### R1. Contextual chunk headers
|
|
102
|
+
|
|
103
|
+
- **Objective:** give every chunk its surrounding context before embedding and
|
|
104
|
+
indexing, so "the retrieval module" style queries find `src/retrieval/*`
|
|
105
|
+
code.
|
|
106
|
+
- **Evidence:** Anthropic Contextual Retrieval: failed retrievals −49% with
|
|
107
|
+
contextual embeddings + contextual BM25; late chunking (arXiv:2409.04701)
|
|
108
|
+
+2.7-3.6% nDCG from the same missing-context problem.
|
|
109
|
+
- **Files:**
|
|
110
|
+
- `src/indexer/chunker.ts` (`nodeToChunk`, `walk`: collect enclosing class,
|
|
111
|
+
imports, exported symbol names)
|
|
112
|
+
- `src/types.ts` (optional `metadata.contextHeader`)
|
|
113
|
+
- `src/indexer/pipeline.ts` (`embedChunks` currently embeds `c.content`)
|
|
114
|
+
- `src/indexer/manifest.ts` (index format version bump)
|
|
115
|
+
- `src/indexer/embedding-cache.ts` (expect full cache invalidation)
|
|
116
|
+
- **Steps:**
|
|
117
|
+
1. Define header format, deterministic and LLM-free:
|
|
118
|
+
`// file: src/retrieval/retriever.ts | lang: typescript | unit: class HybridRetriever | imports: ./confidence, ./query-parser`
|
|
119
|
+
Header must respect `indexer.redact_secrets` (path only, no file body).
|
|
120
|
+
2. Prepend the header in one place at chunk build time so embed, FTS and
|
|
121
|
+
`expand_chunk` all see the same text. Decision: header goes into
|
|
122
|
+
`content`, single source of truth, no second code path.
|
|
123
|
+
3. Bump `manifest` format version so existing installs reindex automatically
|
|
124
|
+
(content hash change already invalidates the embedding cache).
|
|
125
|
+
4. Keep header length capped (~120 tokens) so small chunks do not become
|
|
126
|
+
header-dominated.
|
|
127
|
+
- **Tests:** header contains file path and unit name; cap respected; redacted
|
|
128
|
+
paths behave; ids stay unique (id hashes `content.slice(0,100)`, so the
|
|
129
|
+
header prefix changes ids: confirm full reindex, no stale rows).
|
|
130
|
+
- **Acceptance:** eval shows Recall@k up or flat, no regression on identifier
|
|
131
|
+
queries, latency within 10% of baseline.
|
|
132
|
+
- **Risks:** header noise on very small chunks; path-only leakage of private
|
|
133
|
+
directory names (accepted, index is local and paths already go to FTS).
|
|
134
|
+
- **Dependencies:** R4 baseline. **Effort:** M.
|
|
135
|
+
|
|
136
|
+
### R2. Strengthen BM25: phrases, column weights, symbol matches
|
|
137
|
+
|
|
138
|
+
- **Objective:** make the strongest code retriever work properly for quoted
|
|
139
|
+
phrases and path queries.
|
|
140
|
+
- **Evidence:** Finding 6 (BM25 strongest for code, arXiv:2605.14503);
|
|
141
|
+
Anthropic reports contextual BM25 as the larger single jump (35% → 49%).
|
|
142
|
+
- **Files:** `src/storage/fts-store.ts` (`escapeFts5`, `search`),
|
|
143
|
+
`src/retrieval/retriever.ts` (existing `FTS_BOOST_CODE_LOOKUP`),
|
|
144
|
+
tests under `tests/`.
|
|
145
|
+
- **Steps:**
|
|
146
|
+
1. Parse quoted spans before tokenizing; emit FTS5 phrase strings
|
|
147
|
+
(`"foo bar"`) for them and keep OR-joining unquoted tokens.
|
|
148
|
+
2. Add explicit `bm25()` column weights in `ORDER BY`: weight `file_path`
|
|
149
|
+
highest, then `content`, then `chunk_type`; leave `language` neutral.
|
|
150
|
+
3. Exact symbol shortcut: if a query token matches a chunk's symbol name
|
|
151
|
+
(metadata from R1/R12, fallback regex on content), give it a deterministic
|
|
152
|
+
score bonus in `retriever.ts`.
|
|
153
|
+
4. Keep stopword fallback and the `code_lookup` FTS weight as they are.
|
|
154
|
+
- **Tests:** phrase query matches only adjacent terms; path query ranks its
|
|
155
|
+
file top-3; OR behavior for unquoted terms unchanged; empty/stopword-only
|
|
156
|
+
queries still return the existing fallback.
|
|
157
|
+
- **Acceptance:** identifier and file-name slices of the eval improve; no
|
|
158
|
+
slice regresses by more than 1 point.
|
|
159
|
+
- **Risks:** FTS5 phrase syntax with punctuation-heavy tokens (code symbols
|
|
160
|
+
contain `_`, `.`); mitigate by tokenizing phrase content the same way FTS5
|
|
161
|
+
tokenizer does for indexed content.
|
|
162
|
+
- **Dependencies:** R4. **Effort:** S.
|
|
163
|
+
|
|
164
|
+
### R3. Query-aware compression
|
|
165
|
+
|
|
166
|
+
- **Objective:** stop cutting the lines that answer the question.
|
|
167
|
+
- **Evidence:** Finding 12 (aggressive compression consistently harmful,
|
|
168
|
+
budget ≥ 8000, arXiv:2605.14503); SWE-Pruner (arXiv:2601.16746) 23-54% token
|
|
169
|
+
cut with task-conditioned selection; LLMLingua budget controller.
|
|
170
|
+
- **Files:** `src/compression/compressor.ts` (`TRUNCATION_LIMITS`, prompts),
|
|
171
|
+
`src/retrieval/retriever.ts` (packing uses
|
|
172
|
+
`chunk.compressedContent` length), `src/config.ts`, README config block.
|
|
173
|
+
- **Steps:**
|
|
174
|
+
1. Add `compress(content, level, query?)`. With a query: keep signature
|
|
175
|
+
lines, import block, any line containing a query token or identifier, then
|
|
176
|
+
apply the cap to the remainder.
|
|
177
|
+
2. Pass query terms into the Ollama summary prompt (`{query}` slot) so
|
|
178
|
+
summaries mention the asked-for symbols; keep the existing
|
|
179
|
+
`QualityChecker` identifier-ratio gate as the safety net.
|
|
180
|
+
3. `full` becomes budget-driven: no fixed cap, only the retrieval token
|
|
181
|
+
budget trims it (aligns with the ≥ 8000 guidance).
|
|
182
|
+
4. New config: `compression.query_aware: true` (default on), so users can
|
|
183
|
+
revert to the old fixed behavior.
|
|
184
|
+
- **Tests:** query tokens always survive truncation; identifier ratio stays
|
|
185
|
+
≥ 0.4; without query, output is byte-identical to today (regression guard).
|
|
186
|
+
- **Acceptance:** at equal or lower served tokens, Recall@k does not drop; on
|
|
187
|
+
the bug-report slice it rises.
|
|
188
|
+
- **Risks:** keeping many matched lines can exceed caps; cap logic must prefer
|
|
189
|
+
matched lines over head lines, with a hard ceiling.
|
|
190
|
+
- **Dependencies:** R4. **Effort:** M.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Phase 2: retrieval depth (P1)
|
|
195
|
+
|
|
196
|
+
### R5. Multi-path retrieval with a structured symbol path
|
|
197
|
+
|
|
198
|
+
- **Objective:** stop relying on similarity alone for "where is X defined".
|
|
199
|
+
- **Evidence:** CodeRAG (arXiv:2509.16112) multi-path retrieval is a core gain;
|
|
200
|
+
the coding-agent preprint found production agents mix lexical, semantic and
|
|
201
|
+
structural signals.
|
|
202
|
+
- **Files:** `src/storage/graph-store.ts` (symbol lookup), `src/retrieval/
|
|
203
|
+
retriever.ts` (third RRF list), `src/retrieval/query-parser.ts` (extract
|
|
204
|
+
symbol candidates), `src/storage/local-backend.ts`.
|
|
205
|
+
- **Steps:**
|
|
206
|
+
1. Add `findSymbol(name)`: exact then case-insensitive then prefix match
|
|
207
|
+
over graph node names (function/class/file nodes).
|
|
208
|
+
2. Add file-path exact match as its own path using existing `fileHints`.
|
|
209
|
+
3. Fuse vector, FTS and symbol results with the existing RRF (k=60); symbol
|
|
210
|
+
and path lists get the same weight class as `code_lookup`'s FTS boost.
|
|
211
|
+
4. Keep `MAX_CHUNKS_PER_FILE` and marginal-ratio logic after fusion.
|
|
212
|
+
- **Tests:** exact symbol query returns its chunk in top-3; unknown symbol
|
|
213
|
+
falls back to current behavior; fusion does not double-count a chunk.
|
|
214
|
+
- **Acceptance:** identifier slice Recall@5 improves; general slice flat.
|
|
215
|
+
- **Risks:** symbol collisions across files (dedupe by file, prefer file hint).
|
|
216
|
+
**Dependencies:** R4, benefits from R1 headers. **Effort:** M.
|
|
217
|
+
|
|
218
|
+
### R6. Small-to-big context expansion
|
|
219
|
+
|
|
220
|
+
- **Objective:** serve enough surrounding context per hit to answer without a
|
|
221
|
+
second tool call.
|
|
222
|
+
- **Evidence:** cross-file context length was the dominant parameter
|
|
223
|
+
(2048 → 8192 tokens = up to +4.2 points, arXiv:2605.04763); small-to-big
|
|
224
|
+
retrieval is the standard fix; cAST metadata retention added +2.7 on SWE-bench.
|
|
225
|
+
- **Files:** `src/indexer/chunker.ts` (emit module/top-level chunks),
|
|
226
|
+
`src/retrieval/retriever.ts` (expansion stage, packing),
|
|
227
|
+
`src/config.ts`.
|
|
228
|
+
- **Steps:**
|
|
229
|
+
1. Emit, for every AST file, a `module` chunk covering top-level statements,
|
|
230
|
+
imports and exported names, so code outside functions is retrievable.
|
|
231
|
+
2. After ranking, expand each top hit: attach the file's module chunk plus
|
|
232
|
+
one neighboring chunk above/below when budget allows. Mark provenance in
|
|
233
|
+
metadata (`served_via: self|module|neighbor`) for eval analysis.
|
|
234
|
+
3. Expansion must run inside the `maxTokens` budget and must not break the
|
|
235
|
+
marginal-ratio stop (expanded context counts as part of the parent hit).
|
|
236
|
+
4. Fix the interaction with `dedupeOverlaps`: a module chunk overlaps every
|
|
237
|
+
function chunk in the file and would be dropped. Exempt parent-tier chunks
|
|
238
|
+
from overlap dedupe, dedupe only within a tier.
|
|
239
|
+
- **Tests:** module chunk survives dedupe; expansion respects budget;
|
|
240
|
+
provenance recorded; concat property of module + function chunks still
|
|
241
|
+
reconstructs the file.
|
|
242
|
+
- **Acceptance:** cross-file and NL slices improve Recall@10 and distraction
|
|
243
|
+
ratio, with served tokens up by no more than the configured budget share.
|
|
244
|
+
- **Risks:** token creep; duplicate content between module and function chunks
|
|
245
|
+
wastes budget (mitigate: serve module chunk in compressed form by default).
|
|
246
|
+
**Dependencies:** R4, R3 (compression runs on expanded content). **Effort:** L.
|
|
247
|
+
|
|
248
|
+
### R7. Graph context packs with a centrality prior
|
|
249
|
+
|
|
250
|
+
- **Objective:** make the repository graph a real retrieval path, not a
|
|
251
|
+
2-chunk bonus.
|
|
252
|
+
- **Evidence:** RepoGraph (ICLR 2025) gained +2.66/+2.34 absolute on
|
|
253
|
+
SWE-bench-Lite across four systems at roughly constant cost; Aider's PageRank
|
|
254
|
+
file ranking was observed working in practice.
|
|
255
|
+
- **Files:** `src/storage/graph-store.ts`, `src/storage/local-backend.ts`,
|
|
256
|
+
`src/indexer/pipeline.ts` (compute prior at index time),
|
|
257
|
+
`src/retrieval/retriever.ts`, `src/extension.ts` (`related_context` output).
|
|
258
|
+
- **Steps:**
|
|
259
|
+
1. At index time, run a small power-iteration PageRank over the file-level
|
|
260
|
+
import graph and store it as node `properties.pagerank`.
|
|
261
|
+
2. In retrieval, build a context pack per top hit: the node's callers,
|
|
262
|
+
callees, importers and importees with edge types and line refs, capped at
|
|
263
|
+
N lines and included in the token budget.
|
|
264
|
+
3. Apply centrality as a bounded prior: score multiplier in
|
|
265
|
+
`[1 - p, 1 + p]` with `retrieval.graph_prior` default `0.05`. Never let it
|
|
266
|
+
outrank a relevance gap larger than the prior.
|
|
267
|
+
4. Extend `related_context` to return the same packs for manual drilling.
|
|
268
|
+
- **Tests:** pack contains at least one caller/importer on a fixture repo;
|
|
269
|
+
prior is clamped to its range; graph failures stay best-effort (existing
|
|
270
|
+
try/catch behavior).
|
|
271
|
+
- **Acceptance:** cross-file slice improves; cost per query stays within
|
|
272
|
+
+20 ms of baseline warm latency.
|
|
273
|
+
- **Risks:** dense graphs flood the pack (cap by degree, prefer higher-ranked
|
|
274
|
+
neighbors); PageRank staleness on incremental reindex (recompute per
|
|
275
|
+
full index, reuse on partial).
|
|
276
|
+
- **Dependencies:** R4, R6 (packs ride the same budget path). **Effort:** M.
|
|
277
|
+
|
|
278
|
+
### R8. Query-confidence signal and optional elaboration
|
|
279
|
+
|
|
280
|
+
- **Objective:** tell the agent when a search was weak, and let it recover,
|
|
281
|
+
without silent query rewriting.
|
|
282
|
+
- **Evidence:** predicted query performance correlates with answer quality
|
|
283
|
+
(arXiv:2507.10411); Findings 1/2/4 (raw query wins, elaborate only when
|
|
284
|
+
ambiguous, LLM rewrites drift); Search-R1 for the loop framing.
|
|
285
|
+
- **Files:** `src/retrieval/confidence.ts`, `src/retrieval/query-parser.ts`,
|
|
286
|
+
`src/guidance.ts`, `src/extension.ts` (tool result shape), `src/config.ts`.
|
|
287
|
+
- **Steps:**
|
|
288
|
+
1. Compute query-level signals: top final score, margin to the next distinct
|
|
289
|
+
file, vector/FTS agreement (Jaccard of their top-10), candidate count.
|
|
290
|
+
2. Map to `query_confidence: high|medium|low` with thresholds calibrated
|
|
291
|
+
against the eval set, stored in config so they are tunable.
|
|
292
|
+
3. Return the signal plus 1-3 deterministic recovery hints (add a file path,
|
|
293
|
+
use an exact symbol, drop vague terms) when confidence is low.
|
|
294
|
+
4. Optional elaboration behind `retrieval.auto_elaborate` (default `false`):
|
|
295
|
+
generate a deterministic variant (stopword-stripped, file hints injected,
|
|
296
|
+
terms from near-miss top hits), run both, fuse with RRF, always keep the
|
|
297
|
+
raw query's results as primary.
|
|
298
|
+
5. Document the signal and the loop in the guidance section so the agent
|
|
299
|
+
knows to refine instead of re-reading files.
|
|
300
|
+
- **Tests:** weak synthetic query gets `low` and hints; strong identifier query
|
|
301
|
+
gets `high`; elaboration never removes raw-query results; no network calls.
|
|
302
|
+
- **Acceptance:** guidance renders, config off switches everything off, eval
|
|
303
|
+
unchanged when elaboration is disabled.
|
|
304
|
+
- **Risks:** noisy confidence on tiny indexes (calibrate on the fixture, floor
|
|
305
|
+
the candidate count). **Dependencies:** R4. **Effort:** M.
|
|
306
|
+
|
|
307
|
+
### R9. Refine suggestions in search results
|
|
308
|
+
|
|
309
|
+
- **Objective:** turn a one-shot tool into a guided loop at near-zero token cost.
|
|
310
|
+
- **Evidence:** every production coding agent in the exploratory preprint
|
|
311
|
+
narrows iteratively; RepoCoder's loop drives its reported gain.
|
|
312
|
+
- **Files:** `src/extension.ts` (result shaping), optionally
|
|
313
|
+
`src/engine.ts`; guidance text in `src/guidance.ts`.
|
|
314
|
+
- **Steps:**
|
|
315
|
+
1. From the top hits, derive up to 3 next queries: a sibling symbol from the
|
|
316
|
+
same file (graph `defines` edges), a near-miss file ranked 11-15 that was
|
|
317
|
+
not served, one alternate term that appears often in top hits.
|
|
318
|
+
2. Emit as a single `refine:` line in the tool result; cap length.
|
|
319
|
+
3. Config `retrieval.refine_hints` (default `true`), documented in README.
|
|
320
|
+
- **Tests:** cap of 3; never suggests a file already served; disabled by config.
|
|
321
|
+
- **Acceptance:** token overhead ≤ 40 tokens per call; no eval regression.
|
|
322
|
+
- **Risks:** hints duplicating what the agent would do anyway (measure usage in
|
|
323
|
+
session telemetry later). **Dependencies:** R5, R7 for better symbol/family
|
|
324
|
+
hints (can ship with weaker heuristics first). **Effort:** S.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Phase 3: gated experiments (P2)
|
|
329
|
+
|
|
330
|
+
Each phase-3 item ships **behind config, default off**, and only graduates to
|
|
331
|
+
default-on if the eval shows a win and latency budget holds.
|
|
332
|
+
|
|
333
|
+
### R10. Opt-in local reranker
|
|
334
|
+
|
|
335
|
+
- **Objective:** test whether cross-encoder reranking helps *our* workloads.
|
|
336
|
+
- **Evidence is split, on purpose:** harmful for SE tasks with 815 ms GPU cost
|
|
337
|
+
(Findings 10/11), a core gain in CodeRAG, −67% failures with Anthropic's
|
|
338
|
+
stack. Only our eval can decide.
|
|
339
|
+
- **Files:** new `src/retrieval/reranker.ts`, `src/config.ts`, README.
|
|
340
|
+
- **Steps:**
|
|
341
|
+
1. Use the already-shipped `@huggingface/transformers` for
|
|
342
|
+
`text-classification` with an ONNX reranker (candidate:
|
|
343
|
+
`Xenova/ms-marco-MiniLM-L-6-v2`), cached in the existing models dir.
|
|
344
|
+
2. Rerank only the fused candidate list (cap 30), then re-apply path penalty,
|
|
345
|
+
file diversity and marginal stop.
|
|
346
|
+
3. Config: `retrieval.rerank: off | local` (default `off`),
|
|
347
|
+
`retrieval.rerank_model`, `retrieval.rerank_topn`.
|
|
348
|
+
4. One-time model download must be explicit (never on by default) because it
|
|
349
|
+
adds ~80 MB to a package that markets "one npm command".
|
|
350
|
+
- **Tests:** mock pipeline ordering (no model in unit tests); opt-in path
|
|
351
|
+
exercised in a smoke test with a stubbed model.
|
|
352
|
+
- **Acceptance:** measured eval delta (up or down) and p95 latency recorded in
|
|
353
|
+
the PR; default stays `off` unless quality wins justify the download.
|
|
354
|
+
- **Risks:** CPU latency; ONNX export availability for the chosen model.
|
|
355
|
+
**Dependencies:** R4 mandatory. **Effort:** M.
|
|
356
|
+
|
|
357
|
+
### R11. Code-specialized embedder option
|
|
358
|
+
|
|
359
|
+
- **Objective:** offer, and only recommend, a code-trained local embedder.
|
|
360
|
+
- **Evidence:** Findings 7/8 say scale and code-specialization did not decide
|
|
361
|
+
outcomes, but CORE-Bench shows agentic retrieval is harder and in-domain
|
|
362
|
+
training helped a lot.
|
|
363
|
+
- **Files:** `src/indexer/embedder.ts` (model list), README embedding section.
|
|
364
|
+
- **Steps:**
|
|
365
|
+
1. Document `embedding.model: <hf-id>` with one tested code-oriented model
|
|
366
|
+
and the existing general defaults.
|
|
367
|
+
2. Run the eval for each backend; publish the table in README.
|
|
368
|
+
3. Dimension change already forces full reindex; verify the migration path.
|
|
369
|
+
- **Tests:** backend selection unit test; reindex-on-dimension-change test
|
|
370
|
+
(logic exists, add a regression test if missing).
|
|
371
|
+
- **Acceptance:** README shows measured numbers per model, not claims.
|
|
372
|
+
- **Dependencies:** R4. **Effort:** S.
|
|
373
|
+
|
|
374
|
+
### R12. AST density chunking (cAST-style split-merge)
|
|
375
|
+
|
|
376
|
+
- **Objective:** replace worst-measured strategy (whole function nodes) with
|
|
377
|
+
budgeted, self-contained chunks.
|
|
378
|
+
- **Evidence:** function chunking −3.57 to −5.64 points and never
|
|
379
|
+
Pareto-optimal (arXiv:2605.04763); cAST +4.3 Recall@5, +2.67 Pass@1
|
|
380
|
+
(arXiv:2506.15655).
|
|
381
|
+
- **Files:** `src/indexer/chunker.ts` (core rework), `src/types.ts`
|
|
382
|
+
(metadata: `symbol`, `parent`, `segment`), `src/config.ts`, chunk-id scheme
|
|
383
|
+
in `makeChunk`.
|
|
384
|
+
- **Steps:**
|
|
385
|
+
1. Top-down fit: a node fits if its non-whitespace chars ≤
|
|
386
|
+
`indexer.chunk_budget_chars` (default ~1200, configurable).
|
|
387
|
+
2. Oversized nodes split at syntactic children; only leaf-level overflow uses
|
|
388
|
+
line windows. Greedy-merge small adjacent siblings up to budget.
|
|
389
|
+
3. Emit module/top-level chunks (shared with R6).
|
|
390
|
+
4. Preserve chunk-id uniqueness for split segments (add segment index; keep
|
|
391
|
+
the upstream column-offset fix that the README documents).
|
|
392
|
+
5. Keep the legacy chunker behind `indexer.chunking: legacy | ast-density`
|
|
393
|
+
(default `legacy`) for A/B on the same index format.
|
|
394
|
+
6. Verify the concat property: chunks concatenated reproduce the file.
|
|
395
|
+
- **Tests:** concat round-trip; id uniqueness on duplicate spans; budget
|
|
396
|
+
respected; legacy mode byte-identical to today.
|
|
397
|
+
- **Acceptance:** `ast-density` wins the eval on at least Recall@10 without
|
|
398
|
+
inflating chunk count beyond +30%; then flip the default in its own release.
|
|
399
|
+
- **Risks:** more chunks = more embeddings = slower first index (content-hash
|
|
400
|
+
cache limits repeat cost); id scheme changes force a full reindex.
|
|
401
|
+
**Dependencies:** R4, R6 (module chunks shared), ideally after R1 so headers
|
|
402
|
+
apply to the new chunk shapes. **Effort:** L.
|
|
403
|
+
|
|
404
|
+
### R13. Linked memory (A-MEM style)
|
|
405
|
+
|
|
406
|
+
- **Objective:** make cross-session recall improve with age instead of
|
|
407
|
+
degrading.
|
|
408
|
+
- **Evidence:** A-MEM (NeurIPS 2025): dynamic indexing, linking between
|
|
409
|
+
memories and memory evolution beat fixed structures on six models.
|
|
410
|
+
- **Files:** `src/memory/db.ts`, `decision-extractor.ts`,
|
|
411
|
+
`session-capture.ts`, `record_decision` tool surface in `extension.ts`.
|
|
412
|
+
- **Steps:**
|
|
413
|
+
1. On `record_decision`: embed it, find top-3 past decisions above a
|
|
414
|
+
similarity threshold, store `related_to` edges (new edge type alongside
|
|
415
|
+
`decided`/`discussed_in`), cap links per memory.
|
|
416
|
+
2. Store structured attributes on write: keywords, tags, affected files
|
|
417
|
+
(files already feed `record_code_area`).
|
|
418
|
+
3. Memory evolution: allow a later decision to annotate an earlier one
|
|
419
|
+
(`relatedTo` tool parameter) instead of only appending rows; keep the
|
|
420
|
+
original text immutable, append annotations with timestamps.
|
|
421
|
+
4. Surface links in `session_recall` output so the agent can walk them.
|
|
422
|
+
- **Tests:** threshold and cap behavior; no self-links; redaction still applies
|
|
423
|
+
before any write (PII guard must run before link text is stored).
|
|
424
|
+
- **Acceptance:** a seeded recall test shows linked neighbours surfacing;
|
|
425
|
+
latency of `record_decision` stays under +100 ms.
|
|
426
|
+
- **Risks:** link noise from loose thresholds (calibrate on a fixture set).
|
|
427
|
+
**Dependencies:** R4-style eval extended to memory recall. **Effort:** M.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Cross-cutting work
|
|
432
|
+
|
|
433
|
+
- **Index format versioning:** R1, R6 and R12 each change on-disk content.
|
|
434
|
+
Add `manifest.formatVersion`; bump it per item and force a full reindex with
|
|
435
|
+
a logged migration line (the dimension-change path in `pipeline.ts` is the
|
|
436
|
+
template).
|
|
437
|
+
- **Config surface:** new keys land in `src/config.ts`, the README
|
|
438
|
+
Configuration block, and the guidance text in one PR per item. Planned keys:
|
|
439
|
+
- `compression.query_aware`, `retrieval.auto_elaborate`,
|
|
440
|
+
`retrieval.graph_prior`, `retrieval.refine_hints`,
|
|
441
|
+
`retrieval.rerank`, `retrieval.rerank_model`, `retrieval.rerank_topn`,
|
|
442
|
+
`indexer.chunk_budget_chars`, `indexer.chunking`.
|
|
443
|
+
- **Guidance updates:** R8 and R9 change how the agent should use the tool, so
|
|
444
|
+
`src/guidance.ts` must document the confidence signal and refine hints in the
|
|
445
|
+
same release, keeping the ~900-token budget.
|
|
446
|
+
- **Docs:** README tables (What you get, How it works, Configuration,
|
|
447
|
+
Differences from upstream) updated per release; research evidence lives in
|
|
448
|
+
`RESEARCH-RETRIEVAL.md`, build detail here.
|
|
449
|
+
- **Releases:** every item ships through a PR to `main` and a `v*.*.*` tag per
|
|
450
|
+
the packages AGENTS.md recipe. Suggested grouping, adjustable once baselines
|
|
451
|
+
exist:
|
|
452
|
+
- **0.2.0**: R4 + R1 + R2 + R3 (measurement + phase 1), format version bump.
|
|
453
|
+
- **0.3.0**: R5 + R6 + R7 (retrieval depth).
|
|
454
|
+
- **0.4.0**: R8 + R9 (agentic loop).
|
|
455
|
+
- **0.5.0+**: whichever of R10-R13 wins its experiment, one per release.
|
|
456
|
+
- **Definition of done (per item):** `npm run check` green, eval deltas in the
|
|
457
|
+
PR, README/config/guidance updated, status table in this file flipped.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Explicit non-goals
|
|
462
|
+
|
|
463
|
+
Recorded so they are not re-litigated later:
|
|
464
|
+
|
|
465
|
+
- **No always-on LLM query rewriting** (Finding 4, +987 tokens per rewrite).
|
|
466
|
+
- **No default reranking** (Findings 10-11; R10 stays opt-in).
|
|
467
|
+
- **No aggressive compression defaults** (Finding 12).
|
|
468
|
+
- **No chunk overlap** by default (two evaluations found no clear benefit).
|
|
469
|
+
- **No bigger-embedder reflex** (Findings 7-8); R11 must win on the eval.
|
|
470
|
+
- **No full-file indexing** as a substitute for retrieval (Anthropic notes it
|
|
471
|
+
only makes sense under ~200k tokens).
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Open questions that gate work
|
|
476
|
+
|
|
477
|
+
1. Does the function-chunking penalty transfer to our question workload?
|
|
478
|
+
R4 then R12 answer this with data.
|
|
479
|
+
2. Which side of the reranking debate applies to pi? R10 answers it.
|
|
480
|
+
3. Should confidence weights (0.5 vector / 0.4 keyword / 0.1 recency) be
|
|
481
|
+
fitted on the eval set instead of fixed? A cheap follow-up after R4.
|
|
482
|
+
4. Do refine suggestions change agent behavior in real sessions? Needs opt-in
|
|
483
|
+
session telemetry after R9.
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# Research: code retrieval literature → improvement backlog
|
|
2
|
+
|
|
3
|
+
This note summarizes what peer-reviewed and preprint research says about code
|
|
4
|
+
retrieval and code RAG, and maps each finding onto pi-context-engine.
|
|
5
|
+
|
|
6
|
+
**Why it exists:** we currently optimize for token savings and latency, both of
|
|
7
|
+
which we measure. Retrieval *quality* we do not measure. The literature shows
|
|
8
|
+
that retriever-side choices dominate final answer quality, so this is the
|
|
9
|
+
highest-leverage area to study before adding features.
|
|
10
|
+
|
|
11
|
+
**How to read it:**
|
|
12
|
+
- **Evidence A**: primary source read in full or abstract (arXiv/ACL/official blog).
|
|
13
|
+
- **Evidence B**: found via search result snippet only, not yet verified in full.
|
|
14
|
+
- **Recommendations** are tagged **P0** (quick win), **P1** (next), **P2**
|
|
15
|
+
(experiment, gate behind config). Every one lists the files to touch and how
|
|
16
|
+
to prove it worked.
|
|
17
|
+
|
|
18
|
+
Research done 2026-10-17. Sources are listed at the end with links.
|
|
19
|
+
|
|
20
|
+
**Implementation:** deferred by owner decision. The build plan, with per-item
|
|
21
|
+
steps, tests, acceptance criteria and a status table, lives in
|
|
22
|
+
[IMPLEMENTATION-PLAN.md](./IMPLEMENTATION-PLAN.md). Nothing is started yet.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1. What the evidence says
|
|
27
|
+
|
|
28
|
+
### 1.1 The retriever matters more than the generator
|
|
29
|
+
|
|
30
|
+
- **Finding**: in a component-wise study of 21+ models across code generation,
|
|
31
|
+
summarization and repair, retriever-side components (query processing,
|
|
32
|
+
retrieval algorithm, context refinement) influenced final performance more
|
|
33
|
+
than the choice of generator model.
|
|
34
|
+
- **Why it matters for us:** pi-context-engine *is* the retriever side. Every
|
|
35
|
+
improvement here lands on every model the user runs, cheap and model-agnostic.
|
|
36
|
+
- **Evidence A**: Not All RAGs Are Created Equal (arXiv:2605.14503).
|
|
37
|
+
|
|
38
|
+
### 1.2 BM25 is remarkably strong on code; hybrid is safe but conservative
|
|
39
|
+
|
|
40
|
+
- **Finding 6**: BM25 was "an exceptionally powerful and versatile retriever"
|
|
41
|
+
across text-heavy, clean and noisy code queries, often beating larger dense
|
|
42
|
+
models. Exact matches on algorithm names and error strings drive this.
|
|
43
|
+
- **Finding 9**: hybrid retrieval was "safe but conservative": in their
|
|
44
|
+
experiments it did **not** beat the single best retriever for a given task.
|
|
45
|
+
- **Finding 7/8**: bigger embedding models did not win, and code-specific
|
|
46
|
+
training did not reliably beat strong general-purpose models.
|
|
47
|
+
- **Why it matters for us:** our RRF hybrid and small general embedders
|
|
48
|
+
(all-MiniLM-L6-v2, nomic-embed-text) are consistent with the evidence. The
|
|
49
|
+
win is not a bigger model, it is better lexical handling and better query
|
|
50
|
+
understanding.
|
|
51
|
+
- **Evidence A**: arXiv:2605.14503, Findings 5-9.
|
|
52
|
+
|
|
53
|
+
### 1.3 Query rewriting: keep the raw query by default
|
|
54
|
+
|
|
55
|
+
- **Finding 1**: the original, unaltered query is a powerful baseline and often
|
|
56
|
+
beats automated transformations when the task input is clear.
|
|
57
|
+
- **Finding 2**: elaboration helps only for ambiguous or noisy inputs.
|
|
58
|
+
- **Finding 4**: every LLM-based rewrite risks changing intent or dropping
|
|
59
|
+
critical details ("semantic drift").
|
|
60
|
+
- **Cost note**: an elaborated query cost +987 input tokens, roughly the price
|
|
61
|
+
of 2-3 extra retrieved documents.
|
|
62
|
+
- **Why it matters for us:** our `QueryParser` does regex intent
|
|
63
|
+
classification and keyword extraction, which is free and drift-free. The
|
|
64
|
+
evidence says do not add LLM query rewriting as a default.
|
|
65
|
+
- **Evidence A**: arXiv:2605.14503, Findings 1-4 and the cost table.
|
|
66
|
+
|
|
67
|
+
### 1.4 Reranking: contested evidence, do not default to it
|
|
68
|
+
|
|
69
|
+
- **Negative (SE tasks)**: Findings 10/11 report reranking as "unreliable" and
|
|
70
|
+
"often detrimental": it disrupted an already good BM25 ordering and failed to
|
|
71
|
+
rescue weak retrievals. Their cost table shows a BGE reranker at 815 ms on a
|
|
72
|
+
12.8 GB GPU, versus <10 ms for BM25.
|
|
73
|
+
- **Positive (repo-level completion)**: CodeRAG's preference-aligned "BestFit"
|
|
74
|
+
reranking (LLM reranker, then distilled to a small model) is a core reason it
|
|
75
|
+
beats SOTA on ReccEval and CCEval.
|
|
76
|
+
- **Positive (general RAG)**: Anthropic reports failed retrievals drop by 67%
|
|
77
|
+
when reranking is added on top of contextual retrieval.
|
|
78
|
+
- **Positive (code search)**: two-stage retrieve-then-rerank work reports
|
|
79
|
+
consistent gains for text-to-code search (arXiv:2208.11274, Evidence B snippet).
|
|
80
|
+
- **Why it matters for us:** the sign of the effect depends on the task. A
|
|
81
|
+
reranker is a reasonable *opt-in experiment*, never a default, until our own
|
|
82
|
+
eval says otherwise.
|
|
83
|
+
- **Evidence A**: arXiv:2605.14503 (Findings 10-11), arXiv:2509.16112,
|
|
84
|
+
Anthropic contextual retrieval blog.
|
|
85
|
+
|
|
86
|
+
### 1.5 Compression: gentle and task-aware helps, aggressive hurts
|
|
87
|
+
|
|
88
|
+
- **Finding 12**: compression utility depends on the token budget and the
|
|
89
|
+
compressor. Code-specialized compressors with a generous budget were robust;
|
|
90
|
+
aggressive compression was consistently harmful. Their advice: if you compress,
|
|
91
|
+
keep a budget of at least 8000 tokens.
|
|
92
|
+
- **SWE-Pruner** (2026): a goal-conditioned 0.6B "skimmer" that keeps task-relevant
|
|
93
|
+
lines achieved 23-54% token reduction on SWE-bench Verified *while improving*
|
|
94
|
+
success rates, and up to 14.84x on single-turn QA. The key idea: pruning is
|
|
95
|
+
conditioned on the current task goal, not a fixed rule.
|
|
96
|
+
- **LLMLingua** (EMNLP 2023): a budget controller plus perplexity-based token
|
|
97
|
+
dropping reaches up to 20x compression with little loss.
|
|
98
|
+
- **Provence** (ICLR 2025): a lightweight trained pruner removes irrelevant
|
|
99
|
+
sentences from retrieved contexts before generation.
|
|
100
|
+
- **Why it matters for us:** our compressor truncates at fixed character caps
|
|
101
|
+
(100/300/800 by level) with no knowledge of the query, and our LLM summarizer
|
|
102
|
+
prompt is query-agnostic. Fixed caps can cut exactly the lines the agent needs.
|
|
103
|
+
- **Evidence A**: arXiv:2605.14503, arXiv:2601.16746, arXiv:2310.05736,
|
|
104
|
+
arXiv:2501.16214.
|
|
105
|
+
|
|
106
|
+
### 1.6 Chunking: pure function-sized AST chunks measured worst
|
|
107
|
+
|
|
108
|
+
- **Controlled study (864 configurations, RepoEval + CrossCodeEval)**:
|
|
109
|
+
function-based chunking underperformed every other strategy by 3.57-5.64
|
|
110
|
+
percentage points (Cliff's delta = -1.0). Sliding window and cAST sat on the
|
|
111
|
+
cost-quality Pareto front; function chunking was never Pareto-optimal.
|
|
112
|
+
- **Dominant parameter**: cross-file context *length*. Doubling from 2048 to
|
|
113
|
+
8192 tokens gave up to +4.2 points, while chunk size had a weaker,
|
|
114
|
+
non-monotonic effect.
|
|
115
|
+
- **cAST** (AST split-then-merge chunking): +4.3 Recall@5 on RepoEval retrieval,
|
|
116
|
+
+2.67 Pass@1 on SWE-bench. It packs AST nodes to a size budget measured in
|
|
117
|
+
non-whitespace characters, merges small siblings, and preserves file/class/
|
|
118
|
+
function metadata so hybrid matching improves.
|
|
119
|
+
- **Overlap**: two sources agree overlap is not clearly helpful (late chunking
|
|
120
|
+
appendix; a 2601 chunking study says "avoid overlap").
|
|
121
|
+
- **Why it matters for us:** `chunker.ts` emits whole functions and classes
|
|
122
|
+
exactly as parsed. There is no size budget, no sibling merge, no parent
|
|
123
|
+
context, and top-level code outside functions is not its own chunk. Our tasks
|
|
124
|
+
are questions rather than completion, so the transfer is partial, but the
|
|
125
|
+
direction is clear: density and surrounding context beat raw node boundaries.
|
|
126
|
+
- **Evidence A**: arXiv:2605.04763, arXiv:2506.15655, arXiv:2409.04701.
|
|
127
|
+
|
|
128
|
+
### 1.7 Contextualized chunks: the biggest cheap win in the literature
|
|
129
|
+
|
|
130
|
+
- **Anthropic Contextual Retrieval**: prepend a short chunk-specific context
|
|
131
|
+
statement before embedding and before building the BM25 index.
|
|
132
|
+
- Contextual embeddings alone: top-20 failure rate 5.7% → 3.7% (−35%).
|
|
133
|
+
- Embeddings + contextual BM25: 5.7% → 2.9% (−49%).
|
|
134
|
+
- Plus reranking: −67%.
|
|
135
|
+
- **Late chunking** (Jina): embed the whole document first, pool per chunk
|
|
136
|
+
afterwards. +2.7% to +3.63% relative nDCG across models and datasets, no extra
|
|
137
|
+
training, and it beats naive chunking in almost every configuration.
|
|
138
|
+
- **Why it matters for us:** `pipeline.ts` embeds `c.content` verbatim. A chunk
|
|
139
|
+
from `src/retrieval/retriever.ts` carries no clue that it lives in the
|
|
140
|
+
retrieval module unless the words appear in the code itself. Deterministic
|
|
141
|
+
context headers (path, enclosing class, sibling names, imports) are free, need
|
|
142
|
+
no LLM, and match what both papers show helps most.
|
|
143
|
+
- **Evidence A**: Anthropic engineering blog, arXiv:2409.04701.
|
|
144
|
+
|
|
145
|
+
### 1.8 Repository structure and graphs pay off
|
|
146
|
+
|
|
147
|
+
- **RepoGraph** (ICLR 2025): an AST-built graph (reference/definition nodes,
|
|
148
|
+
invoke/contain edges) plugged into four systems raised SWE-bench-Lite resolve
|
|
149
|
+
rate by +2.66 and +2.34 absolute, at roughly proportional or lower cost. Best
|
|
150
|
+
combined result was 29.67, state of the art among open-source methods at the time.
|
|
151
|
+
- **Aider** ranks context files with PageRank over the repo graph (cited in the
|
|
152
|
+
RepoGraph related work), which our own preprint source also observed in practice.
|
|
153
|
+
- **Why it matters for us:** we already build `calls`/`imports`/`defines` edges,
|
|
154
|
+
but graph expansion in `retriever.ts` only adds 2 bonus chunks from 2 related
|
|
155
|
+
files with a fixed 0.85 score factor. There is headroom to make the graph a
|
|
156
|
+
first-class retrieval path.
|
|
157
|
+
- **Evidence A**: arXiv:2410.14684.
|
|
158
|
+
|
|
159
|
+
### 1.9 Real coding agents retrieve iteratively and along several paths
|
|
160
|
+
|
|
161
|
+
- **RepoCoder** (EMNLP 2023): iterative retrieval then generation, rerunning
|
|
162
|
+
retrieval with the model's own partial output, improved repository-level
|
|
163
|
+
completion by over 10% (Evidence B for the number, A for the mechanism).
|
|
164
|
+
- **CodeRAG** (EMNLP 2025): attacks three failure modes directly: bad query
|
|
165
|
+
construction (uses log-probability guided probing instead of "last k lines"),
|
|
166
|
+
single-path retrieval (runs several retrieval paths and fuses), and
|
|
167
|
+
retriever/generator mismatch (reranks by generator preference).
|
|
168
|
+
- **Exploratory study of code retrieval in coding agents** (2025 preprint, not
|
|
169
|
+
peer reviewed): across Claude Code, Codex CLI, Gemini CLI, Cursor, Amp, Cline
|
|
170
|
+
and Aider, every agent used progressive narrowing: broad first query, then
|
|
171
|
+
tighter follow-ups. It also found agents mixing lexical, semantic, LSP and
|
|
172
|
+
graph-based signals rather than relying on one.
|
|
173
|
+
- **Query performance prediction in agentic RAG** (2025): predicted quality of
|
|
174
|
+
the generated query correlates positively with final answer quality, and
|
|
175
|
+
better retrievers produce higher quality answers in shorter reasoning chains.
|
|
176
|
+
This is the basis for adaptive retrieval: signal low confidence and retry.
|
|
177
|
+
- **Why it matters for us:** our guidance tells the agent how to phrase one
|
|
178
|
+
query. The literature says the winning pattern is a *loop*: search, judge,
|
|
179
|
+
refine. We can support that with confidence signalling and refine hints.
|
|
180
|
+
- **Evidence A**: arXiv:2303.12570 (mechanism), arXiv:2509.16112,
|
|
181
|
+
arXiv:2507.10411; Evidence B: preprint 10.20944/preprints202510.0924.
|
|
182
|
+
|
|
183
|
+
### 1.10 Evaluation: public code-search benchmarks mislead; measure recall and distraction yourself
|
|
184
|
+
|
|
185
|
+
- **CORE-Bench** (2026): agentic coding retrieval is "requirement-driven
|
|
186
|
+
repository search", not snippet matching. Representative embedding models
|
|
187
|
+
show a *sharp drop* from traditional code search to this setting, because of
|
|
188
|
+
intent-to-implementation gaps, scattered evidence, long chunks that dilute the
|
|
189
|
+
embedding, and dense in-repo distractors.
|
|
190
|
+
- **Voyage AI**: CoSQA has an estimated 51% incorrect labels, CodeSearchNet
|
|
191
|
+
queries are copied from docstrings (too easy), and public sets suffer data
|
|
192
|
+
contamination. They recommend mining eval queries from real SWE-bench-style work.
|
|
193
|
+
- **Metric mismatch**: UDCG (2025) shows classical nDCG/MAP/MRR mispredict RAG
|
|
194
|
+
performance because they ignore *distracting* (related but useless) documents;
|
|
195
|
+
a utility-and-distraction-aware metric improved correlation with end-to-end
|
|
196
|
+
accuracy by up to 36%.
|
|
197
|
+
- **Why it matters for us:** `smoke:real-world` measures tokens and latency only.
|
|
198
|
+
We cannot tell whether a change helps or hurts retrieval quality today, and
|
|
199
|
+
our 97% savings claim has no paired quality number.
|
|
200
|
+
- **Evidence A**: arXiv:2606.11864, arXiv:2510.21440, Voyage AI blog.
|
|
201
|
+
|
|
202
|
+
### 1.11 Memory: link memories, do not just store them
|
|
203
|
+
|
|
204
|
+
- **A-MEM** (NeurIPS 2025): Zettelkasten-style memory with dynamic indexing and
|
|
205
|
+
linking. New memories get structured attributes (context, keywords, tags),
|
|
206
|
+
get linked to related old memories, and can *update* older memories as
|
|
207
|
+
understanding improves. Beats SOTA memory baselines on six foundation models.
|
|
208
|
+
- **MemGPT** (2023): memory as an OS-style paging system, the reference frame
|
|
209
|
+
for tiered agent memory.
|
|
210
|
+
- **Why it matters for us:** `record_decision` stores items that are searchable
|
|
211
|
+
but not linked to each other or to the code areas they concern, and old
|
|
212
|
+
entries are never revised.
|
|
213
|
+
- **Evidence A**: arXiv:2502.12110, arXiv:2310.08560.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 2. Gap analysis: pi-context-engine today vs the evidence
|
|
218
|
+
|
|
219
|
+
| Area | Today | Evidence says | Gap |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| Chunk embedding text | raw `chunk.content` only | contextual headers cut failed retrievals by 49% | no path/class/import context in vectors or FTS |
|
|
222
|
+
| Chunking | whole functions/classes, no size budget | function chunking measured worst; split-merge + density wins | no split-merge, no sibling merge, no top-level code chunks |
|
|
223
|
+
| Served context length | packed to token budget, one chunk at a time | cross-file context length is the dominant quality factor | no parent/neighbor expansion around a hit |
|
|
224
|
+
| Compression | fixed 100/300/800 char caps, query-agnostic summaries | gentle, task-conditioned pruning helps; aggressive hurts | compression ignores the query |
|
|
225
|
+
| FTS/BM25 | OR-joined tokens, quoted phrases flattened, default column weights | BM25 strongest single retriever for code; contextual BM25 gave the bigger jump | no phrase search, no path/file column weighting, no symbol path |
|
|
226
|
+
| Query handling | regex intents + keywords, free and drift-free | raw query best; elaborate only when ambiguous; QPP signals help | no low-confidence feedback loop, no refine hints |
|
|
227
|
+
| Graph | 1-hop file bonus: 2 files × 2 chunks, fixed 0.85 factor | RepoGraph: +2.66/+2.34 absolute SWE-bench resolve | graph is a footnote, not a retrieval path |
|
|
228
|
+
| Reranking | none | contested: negative for SE tasks, positive for repo completion | fine as-is; only revisit as a gated experiment |
|
|
229
|
+
| Evaluation | tokens + latency | recall + distraction metrics needed to trust any change | no labeled query set, no quality metric |
|
|
230
|
+
| Memory | stored + searchable, no links | linking and memory evolution improve recall | no decision-to-decision or decision-to-code links |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 3. Recommendations
|
|
235
|
+
|
|
236
|
+
### P0: quick wins, no new dependencies
|
|
237
|
+
|
|
238
|
+
**R1. Prepend deterministic context headers to chunks (indexing + FTS).**
|
|
239
|
+
- **Do:** build a header per chunk: file path, language, enclosing class or
|
|
240
|
+
module, function signature, imports of the file, and a one-line "part of
|
|
241
|
+
file X" statement. Prepend it before embedding *and* before FTS ingest.
|
|
242
|
+
- **Why:** Anthropic measured a 49% cut in failed retrievals from exactly this,
|
|
243
|
+
and late chunking shows the same "chunks lose surrounding context" effect
|
|
244
|
+
(+2.7-3.6% nDCG). Our headers need no LLM, so there is no per-chunk cost.
|
|
245
|
+
- **Where:** `src/indexer/chunker.ts` (`nodeToChunk`), `src/indexer/pipeline.ts`
|
|
246
|
+
(`embedChunks` embeds `c.content`), `src/storage/fts-store.ts` (`ingest`).
|
|
247
|
+
The embedding cache is keyed by content hash, so the header change triggers a
|
|
248
|
+
clean full re-embed; bump an index-format version to make that explicit.
|
|
249
|
+
- **Verify:** Recall@k on the labeled set (R4) before/after, plus a canary
|
|
250
|
+
query per file that mentions the module name but not the code words.
|
|
251
|
+
|
|
252
|
+
**R2. Strengthen BM25: phrases, column weights, symbol matching.**
|
|
253
|
+
- **Do:** detect quoted input and keep it as an FTS5 phrase; weight `file_path`
|
|
254
|
+
and `chunk_type` columns explicitly in the `bm25()` call; surface exact
|
|
255
|
+
symbol hits (a query token matching a function/class name) as a strong signal.
|
|
256
|
+
- **Why:** BM25 is the most reliable retriever for code in the study, and exact
|
|
257
|
+
identifiers are where dense embeddings are weakest. We already OR-join tokens,
|
|
258
|
+
which is safe, but phrases like `"ENOENT handler"` cannot match today.
|
|
259
|
+
- **Where:** `src/storage/fts-store.ts` (`escapeFts5`, `search`),
|
|
260
|
+
`src/retrieval/retriever.ts` (already boosts FTS for `code_lookup` intent).
|
|
261
|
+
- **Verify:** the identifier-heavy query slice of R4 should improve first.
|
|
262
|
+
|
|
263
|
+
**R3. Make compression query-aware.**
|
|
264
|
+
- **Do:** add a `query` parameter to compression. Keep the fixed caps as the
|
|
265
|
+
fallback, but when a query is present, retain lines containing query tokens,
|
|
266
|
+
identifiers, imports and the signature, then truncate. Treat `full` as the
|
|
267
|
+
budget-generous level (evidence says budgets under ~8k tokens lose quality).
|
|
268
|
+
- **Why:** SWE-Pruner shows task-conditioned pruning both cuts tokens and raises
|
|
269
|
+
success rates, while Finding 12 shows fixed aggressive caps hurt. Our current
|
|
270
|
+
caps can delete the only line that answers the question.
|
|
271
|
+
- **Where:** `src/compression/compressor.ts` (`TRUNCATION_LIMITS`, prompts),
|
|
272
|
+
`src/retrieval/retriever.ts` (packing already estimates compressed size).
|
|
273
|
+
- **Verify:** measure served tokens and answer quality per compression level on
|
|
274
|
+
the labeled set; expect equal or better recall at equal or fewer tokens.
|
|
275
|
+
|
|
276
|
+
**R4. Build a labeled retrieval eval and report quality in the smoke run.**
|
|
277
|
+
- **Do:** a small query set (start with 50-100 queries over this repo: bug-report
|
|
278
|
+
style, identifier style, NL style, file-name style) with expected file or
|
|
279
|
+
chunk labels. Report Recall@5, Recall@10, nDCG@10, a distraction ratio
|
|
280
|
+
(relevant chunks among served), tokens and latency. Add it to
|
|
281
|
+
`npm run smoke:real-world`.
|
|
282
|
+
- **Why:** every other recommendation depends on being able to tell "better"
|
|
283
|
+
from "feels better". CORE-Bench shows code-search scores overstate agentic
|
|
284
|
+
usefulness, and UDCG shows distraction must be counted, not ignored. This also
|
|
285
|
+
gives our 97% savings claim a paired quality number.
|
|
286
|
+
- **Where:** `scripts/smoke-real-world.mjs`, new `tests/eval/` fixture,
|
|
287
|
+
optionally a CORE-Bench sample from Hugging Face for external validity.
|
|
288
|
+
- **Verify:** the harness itself: it must fail loudly when scores drop.
|
|
289
|
+
|
|
290
|
+
### P1: next iteration
|
|
291
|
+
|
|
292
|
+
**R5. Multi-path retrieval with a structured symbol path.**
|
|
293
|
+
- **Do:** keep vector + FTS, and add a third path: exact lookup in the graph node
|
|
294
|
+
index for symbols and file paths named in the query. Fuse all three with the
|
|
295
|
+
existing RRF.
|
|
296
|
+
- **Why:** CodeRAG attributes its gains to multi-path retrieval over a single
|
|
297
|
+
similarity path; the coding-agent study found real agents mix lexical,
|
|
298
|
+
semantic and structural signals. We extract identifiers today but only use
|
|
299
|
+
them for keyword distance.
|
|
300
|
+
- **Where:** `src/retrieval/retriever.ts`, `src/retrieval/query-parser.ts`,
|
|
301
|
+
`src/storage/graph-store.ts`.
|
|
302
|
+
- **Verify:** identifier and "where is X defined" query slices of R4.
|
|
303
|
+
|
|
304
|
+
**R6. Small-to-big serving: expand hits with parent and neighbor context.**
|
|
305
|
+
- **Do:** after ranking, expand the top hits with the enclosing module summary
|
|
306
|
+
and adjacent chunks in the same file, plus the file-level module chunk, until
|
|
307
|
+
the marginal-ratio stop. Keep `expand_chunk` for deeper drilling.
|
|
308
|
+
- **Why:** cross-file context length was the dominant quality parameter in the
|
|
309
|
+
controlled study (+4.2 points from 2048 → 8192 tokens), and small-to-big is
|
|
310
|
+
the standard fix for "the chunk that retrieves well is not the chunk that
|
|
311
|
+
answers well". CORE-Bench also flags long, diluted chunks as a problem, so
|
|
312
|
+
expand selectively rather than making every chunk bigger.
|
|
313
|
+
- **Where:** `src/retrieval/retriever.ts` (diversity/packing stage),
|
|
314
|
+
`src/indexer/chunker.ts` (emit module and top-level chunks for AST files too).
|
|
315
|
+
- **Verify:** recall should rise while token count stays bounded by the budget.
|
|
316
|
+
|
|
317
|
+
**R7. Make the graph a real retrieval path (context packs).**
|
|
318
|
+
- **Do:** for each top hit, build a small context pack: the node, its file, its
|
|
319
|
+
callers/callees or importers/importees with edge types labeled, and a
|
|
320
|
+
centrality-based prior for which files are "important" in this repo.
|
|
321
|
+
- **Why:** RepoGraph gained +2.66/+2.34 absolute on SWE-bench-Lite across four
|
|
322
|
+
systems at no extra cost; Aider's PageRank file ranking was observed working
|
|
323
|
+
in practice. Our current expansion is 2 chunks from 2 files with a magic 0.85.
|
|
324
|
+
- **Where:** `src/storage/local-backend.ts` (`getRelatedFilePaths`),
|
|
325
|
+
`src/storage/graph-store.ts`, `src/retrieval/retriever.ts`,
|
|
326
|
+
`related_context` tool in `src/extension.ts`.
|
|
327
|
+
- **Verify:** cross-file queries in R4; count how often the labeled answer file
|
|
328
|
+
arrives via graph edges instead of similarity.
|
|
329
|
+
|
|
330
|
+
**R8. Adaptive retrieval: score the query, tell the agent, then optionally elaborate.**
|
|
331
|
+
- **Do:** treat the score distribution (top score, margin to the second cluster,
|
|
332
|
+
agreement between vector and FTS) as a query-performance signal. When it is
|
|
333
|
+
low, return it in the tool result: "low confidence, try X". Only when the
|
|
334
|
+
agent opts in (or the query is classified ambiguous), generate an elaborated
|
|
335
|
+
variant and run both, keeping the raw query's results.
|
|
336
|
+
- **Why:** predicted query performance correlates with answer quality in
|
|
337
|
+
agentic RAG, and Findings 1/2/4 say raw queries win by default while
|
|
338
|
+
elaboration helps only ambiguous ones. This gives us the loop real agents use
|
|
339
|
+
(broad first, narrow second) without semantic drift.
|
|
340
|
+
- **Where:** `src/retrieval/confidence.ts`, `src/retrieval/query-parser.ts`,
|
|
341
|
+
`src/guidance.ts` (document the new signal for the agent).
|
|
342
|
+
- **Verify:** measure how often a low-confidence signal is followed by a
|
|
343
|
+
successful second query in real sessions; track recall on ambiguous queries.
|
|
344
|
+
|
|
345
|
+
**R9. Return refine suggestions with every search.**
|
|
346
|
+
- **Do:** in `context_search` results, add 2-3 concrete next queries (sibling
|
|
347
|
+
symbol names, the file that almost matched, an alternate term seen in top
|
|
348
|
+
hits). Keep it to a line or two to stay token-cheap.
|
|
349
|
+
- **Why:** every production coding agent in the exploratory study narrowed
|
|
350
|
+
iteratively, and RepoCoder's loop is the mechanism behind its reported gain.
|
|
351
|
+
Suggestions turn our one-shot tool into a guided loop at near-zero cost.
|
|
352
|
+
- **Where:** tool response shaping in `src/extension.ts` or `engine.ts`.
|
|
353
|
+
- **Verify:** follow-up query rate and whether sessions stop earlier.
|
|
354
|
+
|
|
355
|
+
### P2: experiments, gated behind config, only with R4 in place
|
|
356
|
+
|
|
357
|
+
**R10. Opt-in reranker.**
|
|
358
|
+
- **Do:** `retrieval.rerank: off | cheap` using a small ONNX cross-encoder via
|
|
359
|
+
the transformers.js runtime we already ship (text-classification task).
|
|
360
|
+
Default `off`.
|
|
361
|
+
- **Why:** evidence is genuinely split. The SE study found reranking harmful
|
|
362
|
+
and expensive (815 ms GPU), while CodeRAG and Anthropic found large gains on
|
|
363
|
+
repository tasks. Only our own eval can settle which side applies to pi's
|
|
364
|
+
workloads.
|
|
365
|
+
- **Where:** new `src/retrieval/reranker.ts`, config in `src/config.ts`.
|
|
366
|
+
- **Verify:** R4 with rerank on/off; enable by default only if it wins on
|
|
367
|
+
quality *and* the latency budget holds (warm queries are ~11 ms today).
|
|
368
|
+
|
|
369
|
+
**R11. Optional code-specialized embedder.**
|
|
370
|
+
- **Do:** document an `embedding.model: code` option (a code-oriented ONNX
|
|
371
|
+
model) next to the general default, and evaluate it before recommending it.
|
|
372
|
+
- **Why:** Findings 7/8 say scale and code-specific training did not reliably
|
|
373
|
+
win, but CORE-Bench shows agentic retrieval is a different, harder regime
|
|
374
|
+
where in-domain training helped a lot. So: offer it, measure it, then decide.
|
|
375
|
+
- **Where:** `src/indexer/embedder.ts` (`DEFAULT_LOCAL_MODEL`), README config block.
|
|
376
|
+
- **Verify:** R4 per model; the local backend already reindexes on dimension change.
|
|
377
|
+
|
|
378
|
+
**R12. Rework chunking toward density: split-merge with a budget.**
|
|
379
|
+
- **Do:** adopt cAST's algorithm: top-down fit of AST nodes to a non-whitespace
|
|
380
|
+
character budget, greedy merge of small siblings, and emit module/top-level
|
|
381
|
+
chunks so code outside functions is retrievable. Keep chunks concatenable back
|
|
382
|
+
to the original file.
|
|
383
|
+
- **Why:** function-only chunking was the worst measured strategy (−3.57 to
|
|
384
|
+
−5.64 points) and was never Pareto-optimal; cAST gains 4.3 Recall@5. Our
|
|
385
|
+
current chunker does exactly this (whole function/class nodes, no budget).
|
|
386
|
+
- **Where:** `src/indexer/chunker.ts` (`walk`, `nodeToChunk`), chunk-id scheme in
|
|
387
|
+
`makeChunk` (ids include line spans, so the upstream column-offset fix must
|
|
388
|
+
survive the rework).
|
|
389
|
+
- **Verify:** R4 by query type, plus re-run `smoke:real-world` for chunk counts
|
|
390
|
+
and token deltas; expect more, denser chunks.
|
|
391
|
+
|
|
392
|
+
**R13. Link memories (A-MEM style).**
|
|
393
|
+
- **Do:** when writing a decision or code area, compute links to related past
|
|
394
|
+
entries (embedding similarity + the `decided`/`discussed_in` graph edges) and
|
|
395
|
+
allow a later entry to annotate an earlier one instead of only appending.
|
|
396
|
+
- **Why:** A-MEM's linked, evolving memories beat fixed-structure memory on six
|
|
397
|
+
models. Our memory is flat today, so recall quality degrades as history grows.
|
|
398
|
+
- **Where:** `src/memory/db.ts`, `decision-extractor.ts`, `session-capture.ts`.
|
|
399
|
+
- **Verify:** a small recall test: seed N decisions, query for each by topic,
|
|
400
|
+
measure whether its linked neighbours surface too.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## 4. What the literature says not to do
|
|
405
|
+
|
|
406
|
+
- **No always-on LLM query rewriting**: semantic drift risk plus ~987 tokens per
|
|
407
|
+
rewrite (Finding 4 and the cost table).
|
|
408
|
+
- **No default cross-encoder reranking**: often negative on SE tasks, expensive,
|
|
409
|
+
and our warm-query latency would regress (Findings 10-11).
|
|
410
|
+
- **No aggressive compression**: fixed tight caps and hard truncation lose the
|
|
411
|
+
answering lines (Finding 12, SWE-Pruner).
|
|
412
|
+
- **No chunk overlap by default**: two evaluations found no clear benefit.
|
|
413
|
+
- **No "bigger embedder" reflex**: scale and code-specific training did not
|
|
414
|
+
decide outcomes (Findings 7/8). Measure first with R4.
|
|
415
|
+
- **No full-file indexing as a substitute for retrieval**: useful for repos
|
|
416
|
+
under ~200k tokens, per Anthropic, which is a niche case.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## 5. Measurement plan
|
|
421
|
+
|
|
422
|
+
1. **Baseline first**: run R4's harness on the current release and record
|
|
423
|
+
Recall@5/@10, nDCG@10, distraction ratio, tokens, latency.
|
|
424
|
+
2. **One change at a time**: each Rn lands with its before/after numbers in the
|
|
425
|
+
PR. This mirrors how every paper above isolates one component.
|
|
426
|
+
3. **Report quality in `smoke:real-world`**, next to the existing savings and
|
|
427
|
+
latency numbers, so regressions are visible without a separate tool.
|
|
428
|
+
4. **External check**: optionally run a CORE-Bench level-2 slice to confirm
|
|
429
|
+
gains transfer beyond our own query set.
|
|
430
|
+
5. **Session telemetry (opt-in)**: track low-confidence rate and follow-up
|
|
431
|
+
query rate to validate R8/R9 outside the lab.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## 6. Sources
|
|
436
|
+
|
|
437
|
+
Primary sources read for this note (Evidence A unless marked):
|
|
438
|
+
|
|
439
|
+
- **Not All RAGs Are Created Equal: A Component-Wise Empirical Study for SE Tasks**
|
|
440
|
+
(2026) https://arxiv.org/abs/2605.14503
|
|
441
|
+
Retriever beats generator; BM25 robust; query rewriting and reranking are
|
|
442
|
+
situational, not defaults.
|
|
443
|
+
- **How Does Chunking Affect Retrieval-Augmented Code Completion?** (2026)
|
|
444
|
+
https://arxiv.org/abs/2605.04763
|
|
445
|
+
Function chunking worst; cross-file context length dominant.
|
|
446
|
+
- **cAST: Enhancing Code RAG with Structural Chunking via AST** (2025)
|
|
447
|
+
https://arxiv.org/abs/2506.15655
|
|
448
|
+
Split-merge AST chunking, char budget, metadata retention.
|
|
449
|
+
- **RepoGraph: Enhancing AI Software Engineering with Repository-level Code Graph**
|
|
450
|
+
(ICLR 2025) https://arxiv.org/abs/2410.14684
|
|
451
|
+
Graph plug-in: +2.66/+2.34 absolute SWE-bench-Lite resolve.
|
|
452
|
+
- **CodeRAG: Finding Relevant and Necessary Knowledge for RAG Repository-Level
|
|
453
|
+
Code Completion** (EMNLP 2025) https://arxiv.org/abs/2509.16112
|
|
454
|
+
Log-probability query construction, multi-path retrieval, preference-aligned reranking.
|
|
455
|
+
- **RepoCoder: Repository-Level Code Completion through Iterative Retrieval and
|
|
456
|
+
Generation** (EMNLP 2023) https://arxiv.org/abs/2303.12570
|
|
457
|
+
Iterative retrieve-generate loop for repo context.
|
|
458
|
+
- **CORE-Bench: Code Retrieval in the Era of Agentic Coding** (2026)
|
|
459
|
+
https://arxiv.org/abs/2606.11864
|
|
460
|
+
Agentic retrieval is requirement-driven repo search; standard benchmarks overstate.
|
|
461
|
+
- **SWE-Pruner: Self-Adaptive Context Pruning for Coding Agents** (2026)
|
|
462
|
+
https://arxiv.org/abs/2601.16746
|
|
463
|
+
Task-conditioned pruning: 23-54% fewer tokens, success rates up.
|
|
464
|
+
- **Provence: Efficient and Robust Context Pruning for RAG** (ICLR 2025)
|
|
465
|
+
https://arxiv.org/abs/2501.16214
|
|
466
|
+
Lightweight trained pruner removes irrelevant sentences pre-generation.
|
|
467
|
+
- **LLMLingua: Compressing Prompts for Accelerated Inference** (EMNLP 2023)
|
|
468
|
+
https://arxiv.org/abs/2310.05736
|
|
469
|
+
Budget-controlled compression, up to 20x with little loss.
|
|
470
|
+
- **Late Chunking: Contextual Chunk Embeddings** (2024)
|
|
471
|
+
https://arxiv.org/abs/2409.04701
|
|
472
|
+
+2.7-3.6% relative nDCG; overlap shows no clear benefit.
|
|
473
|
+
- **Anthropic, Contextual Retrieval** (industry, engineering blog, 2024)
|
|
474
|
+
https://www.anthropic.com/engineering/contextual-retrieval
|
|
475
|
+
Failed retrievals −49% (embeddings + BM25), −67% with reranking.
|
|
476
|
+
- **Lost in the Middle: How Language Models Use Long Contexts** (TACL 2024)
|
|
477
|
+
https://arxiv.org/abs/2307.03172
|
|
478
|
+
Result ordering matters: information in the middle of long context is used least.
|
|
479
|
+
- **A-MEM: Agentic Memory for LLM Agents** (NeurIPS 2025)
|
|
480
|
+
https://arxiv.org/abs/2502.12110
|
|
481
|
+
Zettelkasten linking plus memory evolution beats fixed memory structures.
|
|
482
|
+
- **MemGPT: Towards LLMs as Operating Systems** (2023)
|
|
483
|
+
https://arxiv.org/abs/2310.08560
|
|
484
|
+
Tiered, OS-style memory management for agents.
|
|
485
|
+
- **Am I on the Right Track? Predicted Query Performance in Agentic RAG** (2025)
|
|
486
|
+
https://arxiv.org/abs/2507.10411
|
|
487
|
+
Query quality prediction correlates with answer quality: basis for adaptive retrieval.
|
|
488
|
+
- **Search-R1: Training LLMs to Reason and Leverage Search Engines with RL** (2025)
|
|
489
|
+
https://arxiv.org/abs/2503.09516
|
|
490
|
+
Reference design for search-as-a-tool inside reasoning loops.
|
|
491
|
+
- **Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE)** (ACL 2023)
|
|
492
|
+
https://arxiv.org/abs/2212.10496
|
|
493
|
+
Query→hypothetical answer→embedding; costs a generation per query.
|
|
494
|
+
- **Redefining Retrieval Evaluation in the Era of LLMs** (2025)
|
|
495
|
+
https://arxiv.org/abs/2510.21440
|
|
496
|
+
UDCG metric counts distraction, +36% correlation with end-to-end accuracy.
|
|
497
|
+
- **Retrieval-Augmented Code Generation: A Survey (Repository-Level)** (2025)
|
|
498
|
+
https://arxiv.org/abs/2510.04905
|
|
499
|
+
Taxonomy of repo-level retrieval: substrate, control regime, evaluation.
|
|
500
|
+
- **Revisiting Code Search in a Two-Stage Paradigm** (2022) Evidence B
|
|
501
|
+
https://arxiv.org/abs/2208.11274
|
|
502
|
+
Retrieve-then-rerank for code search.
|
|
503
|
+
- **CORNSTACK: High-Quality Contrastive Data for Code Retrieval and Reranking**
|
|
504
|
+
(ICLR 2025) Evidence B https://arxiv.org/abs/2412.01007
|
|
505
|
+
Rerankers matter for code retrieval when trained on good data.
|
|
506
|
+
- **COIR: A Comprehensive Benchmark for Code Information Retrieval** (ACL 2025)
|
|
507
|
+
Evidence B https://aclanthology.org/2025.acl-long.1072.pdf
|
|
508
|
+
Broader code-IR benchmark beyond CodeSearchNet.
|
|
509
|
+
- **CodeSearchNet Challenge** (2019) Evidence B https://arxiv.org/abs/1909.09436
|
|
510
|
+
The classic benchmark, and its limits.
|
|
511
|
+
- **Voyage AI, How do we evaluate vector-based code retrieval?** (industry, 2024)
|
|
512
|
+
https://blog.voyageai.com/2024/12/04/code-retrieval-eval/
|
|
513
|
+
CoSQA ~51% bad labels; CodeSearchNet queries trivial; mine evals from SWE work.
|
|
514
|
+
- **An Exploratory Study of Code Retrieval Techniques in Coding Agents** (2025
|
|
515
|
+
preprint, not peer reviewed) Evidence B
|
|
516
|
+
https://doi.org/10.20944/preprints202510.0924
|
|
517
|
+
Seven production agents all narrow iteratively and mix lexical/semantic/graph/LSP.
|
|
518
|
+
- **ColBERT: Efficient Passage Search via Late Interaction** (SIGIR 2020)
|
|
519
|
+
Evidence B https://arxiv.org/abs/2004.12832
|
|
520
|
+
Late interaction: better than bi-encoders, heavier infrastructure than ours.
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 7. Open questions
|
|
525
|
+
|
|
526
|
+
- Does the function-chunking penalty from the completion study transfer to our
|
|
527
|
+
question-answering workload? R4 + R12 will answer this empirically.
|
|
528
|
+
- Which side of the reranking debate applies to pi: SE tasks or repo tasks?
|
|
529
|
+
- Is a code-specialized embedder worth the extra model weight for local users,
|
|
530
|
+
given Findings 7/8?
|
|
531
|
+
- Should confidence scoring learn weights (vector/keyword/recency) from the
|
|
532
|
+
labeled set instead of the fixed 0.5/0.4/0.1 split?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-context-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Semantic code context for pi: tree-sitter index + hybrid vector/BM25 retrieval + cross-session memory, all local. Port of code-context-engine (MIT, elara-labs) as a native pi extension.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
"web-tree-sitter": "^0.22.6",
|
|
52
52
|
"yaml": "^2.9.1"
|
|
53
53
|
},
|
|
54
|
+
"optionalDependencies": {
|
|
55
|
+
"@huggingface/transformers": "^4.3.1"
|
|
56
|
+
},
|
|
54
57
|
"peerDependencies": {
|
|
55
58
|
"@earendil-works/pi-ai": "*",
|
|
56
59
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -60,7 +63,6 @@
|
|
|
60
63
|
"@earendil-works/pi-ai": ">=0.80.0",
|
|
61
64
|
"@earendil-works/pi-coding-agent": ">=0.99.2",
|
|
62
65
|
"@types/node": "^22.0.0",
|
|
63
|
-
"@huggingface/transformers": "^4.3.1",
|
|
64
66
|
"typebox": "^1.0.0",
|
|
65
67
|
"typescript": "^5.9.0",
|
|
66
68
|
"vitest": "^3.1.0"
|
package/src/indexer/embedder.ts
CHANGED
|
@@ -40,13 +40,37 @@ interface TransformersModule {
|
|
|
40
40
|
env: Record<string, unknown>;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export type TransformersLoader = () => Promise<TransformersModule>;
|
|
44
|
+
|
|
45
|
+
const defaultLoadTransformers: TransformersLoader = async () =>
|
|
46
|
+
(await import("@huggingface/transformers")) as unknown as TransformersModule;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The package ships `@huggingface/transformers` as an optional dependency, so a
|
|
50
|
+
* plain npm install has it, but installs done with `--omit=optional` do not.
|
|
51
|
+
* Detect that case so we can tell the user exactly how to fix it instead of
|
|
52
|
+
* crashing with a bare "Cannot find module".
|
|
53
|
+
*/
|
|
54
|
+
function isModuleNotFoundError(err: unknown): boolean {
|
|
55
|
+
const code = (err as NodeJS.ErrnoException | undefined)?.code;
|
|
56
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
57
|
+
const message = (err as Error | undefined)?.message ?? "";
|
|
58
|
+
return message.includes("Cannot find module") || message.includes("Cannot find package");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const LOCAL_BACKEND_INSTALL_HINT =
|
|
62
|
+
"Local embedding backend unavailable: @huggingface/transformers is not installed. " +
|
|
63
|
+
"It ships as an optional dependency, so a plain install includes it. " +
|
|
64
|
+
"Fix: reinstall pi-context-engine (pi update npm:pi-context-engine), " +
|
|
65
|
+
"or force the Ollama backend with PCE_EMBED_BACKEND=ollama.";
|
|
66
|
+
|
|
43
67
|
export class LocalBackend implements EmbeddingBackend {
|
|
44
68
|
readonly name = "local";
|
|
45
69
|
readonly modelName: string;
|
|
46
70
|
dimension = 0;
|
|
47
71
|
private pipelinePromise: Promise<(inputs: string | string[]) => Promise<number[][]>> | null = null;
|
|
48
72
|
|
|
49
|
-
constructor(modelName: string = DEFAULT_LOCAL_MODEL) {
|
|
73
|
+
constructor(modelName: string = DEFAULT_LOCAL_MODEL, private readonly loadTransformers: TransformersLoader = defaultLoadTransformers) {
|
|
50
74
|
this.modelName = modelName;
|
|
51
75
|
}
|
|
52
76
|
|
|
@@ -55,7 +79,16 @@ export class LocalBackend implements EmbeddingBackend {
|
|
|
55
79
|
this.pipelinePromise = (async () => {
|
|
56
80
|
const cacheDir = resolveModelCacheDir();
|
|
57
81
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
58
|
-
|
|
82
|
+
let transformers: TransformersModule;
|
|
83
|
+
try {
|
|
84
|
+
transformers = await this.loadTransformers();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// Drop the memoized failure so a retry (after installing the
|
|
87
|
+
// package) gets a fresh chance instead of replaying the error.
|
|
88
|
+
this.pipelinePromise = null;
|
|
89
|
+
if (isModuleNotFoundError(err)) throw new Error(LOCAL_BACKEND_INSTALL_HINT);
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
59
92
|
transformers.env.cacheDir = cacheDir;
|
|
60
93
|
const extractor = await transformers.pipeline("feature-extraction", this.modelName, {
|
|
61
94
|
pooling: "mean",
|