# Octocode 0.18.0: एक रिप्रोड्यूसिबल Retrieval Benchmark और GraphRAG File-Level Expansion

> Octocode 0.18.0 एक रिप्रोड्यूसिबल semantic code search benchmark देता है — line-range ground truth के साथ 127 curated queries, साथ ही 560 असली GitHub issues पर एक Loc-Bench harness — और GraphRAG file-level expansion जोड़ता है जो reranking से पहले structurally related फ़ाइलें search में लाता है। Open source, local-first, Apache-2.0।

[0.17.1 ने आपको Octocode द्वारा जनरेट किया गया हर embedding आपके अपने नियंत्रित हार्डवेयर पर चलाने दिया](/blog/octocode-0-17-1-local-embedding-providers) — कोई भी OpenAI-कम्पैटिबल सर्वर, कोई भी मॉडल, और आपका कोड कभी आपके नेटवर्क से बाहर नहीं जाता। वह रिलीज़ इस बारे में थी कि search _कहाँ_ चलती है।

**0.18.0 इस बारे में है कि search असल में कितनी अच्छी है — और इसे ऐसे numbers से साबित करना जिन्हें आप खुद reproduce कर सकें।**

ज़्यादातर code-search टूल quality का दावा करते हैं। बहुत कम ऐसा benchmark जारी करते हैं जिसे आप खुद चला सकें। यह रिलीज़ दो देती है: Octocode के अपने source code के खिलाफ एक curated retrieval benchmark, और 560 असली GitHub issues पर एक [Loc-Bench](https://github.com/czlll/Loc-Bench_V1) harness। यह **GraphRAG file-level expansion** भी जोड़ती है — एक search step जो reranker के स्कोर करने से पहले structurally related फ़ाइलें खींच लाता है।

यहाँ बताया गया है कि क्या बदला, और numbers क्या कहते हैं — उन हिस्सों समेत जो काम नहीं किए।

---

## आख़िर code search का benchmark क्यों?

जब आप कोई chunking heuristic या fusion weight बदलते हैं, तो आप जानना चाहते हैं कि retrieval बेहतर हुआ या बदतर। चंद queries को आँख से देखना यह नहीं बताता — यह बस वही पुष्ट करता है जो आप पहले से मानते थे। एकमात्र ईमानदार जवाब है: queries का एक fixed सेट, fixed ground truth, और एक number जो हिलता है।

तो 0.18.0 एक असली benchmark देती है। यह repo में [`benchmark/`](https://github.com/Muvon/octocode/tree/master/benchmark) में रहता है, और Octocode की पूरी pipeline को end to end मापता है: chunking → embedding → vector search → hybrid fusion → reranking।

### Ground truth

`code.csv` में **127 queries** हैं, हर एक में 1–3 सही code locations line ranges के रूप में annotate की गई हैं:

```
query,result1,result2,result3
```

हर result `src/path/file.rs:start-end:relevance` है, जहाँ relevance `2` primary है (query का सीधा जवाब देता है) और `1` secondary है (उपयोगी context)। एक search result **line-range overlap** से match होता है — lines 40–90 पर एक hit, ground truth 45–92 से match करता है क्योंकि ranges एक-दूसरे को काटती हैं।

Corpus एक **single commit पर pinned है** (`b1771ba`)। Ground truth उन्हीं exact line numbers के खिलाफ annotate की गई है, इसलिए किसी और checkout को index करने पर ranges खिसक जाएँगी और scores चुपचाप टूट जाएँगे। पहले pin करें, फिर index करें।

Ground truth कैसे बनी, यह मायने रखता है, क्योंकि एक लापरवाह answer key किसी भी benchmark को झूठा बना देती है:

1. **AI-generated candidates** हर बड़े module और doc फ़ाइल में।
2. **Source verification** — हर referenced फ़ाइल और line range को पढ़ा गया और असली code के खिलाफ जाँचा गया।
3. **Multi-agent validation** — parallel agents ने स्वतंत्र रूप से line ranges, paths और relevance grades को फिर से जाँचा।
4. **Search-informed corrections** — अगर search को वही logic किसी valid alternate location में मिली, तो उस location को secondary result के रूप में _जोड़ा_ गया, हटाया नहीं गया।

### कठिन queries

127 में से आख़िरी 27 जानबूझकर adversarial हैं: **natural language जो किसी function name या comment को मिरर नहीं करती।** ये keyword luck नहीं, semantic समझ परखती हैं। कुछ:

| Query का इरादा                              | यह कठिन क्यों है                                          |
| ------------------------------------------- | --------------------------------------------------------- |
| overlapping chunks में infinite loops रोकना | 5-line guard clause, query में कोई code keyword नहीं      |
| repo unchanged होने पर indexing छोड़ना      | 900-line function के अंदर 100+ lines गहराई में दबा हुआ    |
| tokenizer के बिना rough token estimation    | जवाब एक ही line है: `s.len() / 4`                         |
| MCP server में concurrent reindexing रोकना  | `AtomicBool` + `compare_exchange`, एक concurrency pattern |

अगर standard queries ~1.0 के पास स्कोर करती हैं पर कठिन queries पिछड़ती हैं, तो benchmark अपना काम कर रहा है: यह टूल की चापलूसी करने के बजाय असली retrieval कमज़ोरियाँ उजागर करता है।

### Metrics

चार standard IR metrics, सभी में "ज़्यादा बेहतर है":

- **Hit@k** — क्या _कोई भी_ सही result top k में आया? (coverage)
- **MRR** — _पहले_ सही result का reciprocal rank। (पहला hit कितना ऊपर है?)
- **NDCG@10** — _सबसे_ relevant results को सबसे ऊपर रखने पर इनाम देता है, relevance grade और position दोनों को weight करते हुए। (ranking quality)
- **Recall@k** — _सभी_ ground-truth blocks का कितना हिस्सा मिला? (completeness)

Definitions और एक worked example [`benchmark/README.md`](https://github.com/Muvon/octocode/tree/master/benchmark) में हैं।

---

## Numbers: एक local, no-key floor

ये results **पूरी तरह local, no-API-key** stack इस्तेमाल करते हैं — `fastembed` के ज़रिए `jina-embeddings-v2-base-code`, **कोई reranker नहीं**। यह उन्हें एक _floor_ बनाता है, ceiling नहीं: एक paid code-aware reranker इससे ऊपर ले जाता है। Octocode के अपने source code के खिलाफ चलाया गया, 127 code queries:

| Config                                | Hit@5     | Hit@10    | MRR       | NDCG@10   | Recall@10 |
| ------------------------------------- | --------- | --------- | --------- | --------- | --------- |
| सिर्फ़ dense vector                   | 0.598     | 0.717     | 0.485     | 0.528     | 0.671     |
| Hybrid, default RRF weights (0.7/0.3) | 0.598     | 0.717     | 0.485     | 0.528     | 0.671     |
| **Hybrid, keyword-tuned (0.3/0.7)**   | **0.732** | **0.835** | **0.572** | **0.620** | **0.807** |

दो चीज़ें उभरकर आती हैं।

**Default RRF weights सिर्फ़-vector जैसा व्यवहार करते हैं।** जब fusion 0.7 के साथ dense signal की ओर झुका हो, vector results हावी रहते हैं और keyword signal मुश्किल से दर्ज होता है — ऊपर की दो rows समान हैं।

**Keyword की ओर झुकना code के लिए एक बड़ी, मुफ़्त जीत है।** weights को 0.3/0.7 पर पलट दें — BM25/keyword signal को lead करने दें — और Hit@5 **+22%** और Recall@10 **+20%** उछलता है, बिना किसी अतिरिक्त लागत के। क्यों? Code exact identifiers से भरा है — `compare_exchange`, `get_code_blocks_by_path`, `RelationshipDirection` — और exact-token matching का weight असमान रूप से ज़्यादा होता है जब आप जो ढूँढ रहे हैं वह _खुद_ एक token हो। शुद्ध semantic similarity इसे धुंधला कर देती है; keyword fusion इसे तीखा करती है।

यह वैसा result है जो आपको सिर्फ़ मापने से मिलता है। यह intuitive नहीं है कि code के लिए आप चाहेंगे कि keyword embedding से _ज़्यादा weight_ पाए।

---

## जो काम नहीं आया (और यह benchmark में क्यों है)

ऐसा benchmark जो सिर्फ़ जीत बताए, marketing है। यह regressions भी बताता है — पूरी 6-variant matrix [`benchmark/RESULTS.md`](https://github.com/Muvon/octocode/tree/master/benchmark/RESULTS.md) में है।

**एक generic reranker ने चीज़ें बदतर कीं।** सबसे अच्छी config के ऊपर एक local off-the-shelf cross-encoder (`bge-reranker-base`) रखने से मदद नहीं मिली — इसने Hit@5 को **0.732 से वापस 0.598** पर _regress_ कर दिया, पूरी keyword-tuning जीत पलट दी। और यह लगभग **4× धीमा** था (run के लिए ≈1900s बनाम ≈490s)।

सबक यह नहीं है कि "rerankers बुरे हैं"। यह है कि **code retrieval को एक code-aware reranker चाहिए।** prose पर trained reranker `fn verify_hmac` को "webhook signature validation" के खिलाफ उससे भी बदतर स्कोर करता है जितना hybrid fusion पहले ही कर चुका था। `voyage:rerank-2.5` जैसा code-tuned reranker अलग कहानी है — पर आपको इसे अपने eval पर verify करना होगा, मान नहीं लेना। benchmark ही वह चीज़ है जो आपको ऐसा करने देती है।

---

## GraphRAG File-Level Expansion

benchmark के साथ-साथ हेडलाइन फ़ीचर। Dense और keyword search दोनों blocks को अलग-थलग स्कोर करती हैं — उन्हें नहीं पता कि जो फ़ाइल आप चाहते हैं वह उस फ़ाइल को _import_ करती है जो उन्हें मिली, या पास में define किए गए function को _call_ करती है। **GraphRAG file-level expansion** उस अंतर को पाटता है।

जब initial hybrid search ranked candidates बना लेती है, Octocode सबसे मज़बूत कुछ लेता है, **imports, calls या extends से structurally related** फ़ाइलों के लिए GraphRAG graph को walk करता है, उन neighbor फ़ाइलों से मुट्ठी भर blocks खींचता है, और _बढ़ा हुआ_ candidate set reranker को सौंप देता है:

```rust
// Seeds are already RRF-ranked; only expand from the strongest few.
const SEED_LIMIT: usize = 10;        // expand from top 10 hits
const MAX_NEIGHBOR_FILES: usize = 8; // pull at most 8 related files
const BLOCKS_PER_FILE: usize = 3;    // 3 blocks per neighbor
```

Neighbors को सभी seed फ़ाइलों में `weight × confidence` से स्कोर किया जाता है, सबसे मज़बूत links पहले। यह file-granular है क्योंकि graph file-granular है (`CodeNode.id == relative path`)। और यह **best-effort** है: कोई भी graph error original results को सीधे pass करने तक degrade हो जाती है — यह search को कभी नहीं तोड़ता।

इसे `config.toml` में on करें:

```toml
[search]
graph_expansion = false  # default; requires [graphrag] enabled
```

यह जानबूझकर **default में off** आता है। benchmark से ईमानदार हिस्सा यहाँ है:

> `+graph` = GraphRAG file-level expansion, जो **results तभी हिलाता है जब एक reranker बढ़े हुए set को फिर से स्कोर करता है।**

reranker के बिना, expansion candidates जोड़ता है पर ranking नहीं बदलता — इसीलिए ऊपर की local, no-key matrix में `hybrid_30_70` और `hybrid_30_70+graph` समान स्कोर करते हैं। expansion का फ़ायदा तब आता है जब एक (code-aware) reranker बड़े pool को फिर से स्कोर करता है और किसी structurally related फ़ाइल को आगे लाता है जिसे raw similarity ने बहुत नीचे rank किया था। Naive expansion precision को _पतला_ भी कर सकता है। इसलिए config comment में गढ़ी गई सलाह दो टूक है: **इस पर भरोसा करने से पहले अपने eval पर A/B करें।** benchmark ठीक इसीलिए मौजूद है।

---

## Loc-Bench: Issue → Code, कुछ भी self-annotated नहीं

curated benchmark हम annotate करते हैं। एक जायज़ सवाल: क्या queries हमारे अपने टूल के लिए _बहुत ज़्यादा_ मेहरबान हैं? 0.18.0 एक दूसरा harness देती है जो हमें समीकरण से पूरी तरह हटा देता है।

[`benchmark/locbench.py`](https://github.com/Muvon/octocode/tree/master/benchmark) Octocode को **Loc-Bench V1** (`czlll/Loc-Bench_V1`) पर evaluate करता है: लोकप्रिय Python repos से **560 असली GitHub issues**। Query है _original issue text_। Ground truth वह फ़ाइलों और functions का सेट है जिसे _असली merged fix_ ने वास्तव में edit किया (dataset का curated `edit_functions` field)। कुछ भी self-annotated नहीं है — यह "एक bug report दिया हो, बदलने वाला code ढूँढो" वाला industry-standard task है।

हर instance पर harness repo को clone करता है (blobless, cached), base commit checkout करता है, `octocode index` चलाता है, issue text से search करता है, और retrieved फ़ाइलों को असली fix के खिलाफ स्कोर करता है — **file level** (hit@k, recall@k, MRR, accuracy) और **hunk level** दोनों पर (क्या कोई retrieved result उन lines से overlap हुआ जिन्हें patch ने वास्तव में छुआ?), issue category के हिसाब से breakdown के साथ:

```bash
python3 benchmark/locbench.py --limit 10 --verbose   # smoke test
python3 benchmark/locbench.py                         # full 560
python3 benchmark/locbench.py --category "Bug Report"
```

यह एक measurement tool है, कोई leaderboard दावा नहीं — यह इसलिए है ताकि आप अपने हार्डवेयर और मॉडल पर real-world issue localization चला सकें, और ताकि Octocode के हर भविष्य के बदलाव को एक ऐसे benchmark के खिलाफ मापा जा सके जो हमने नहीं लिखा।

---

## Variant Matrix

`score.py` एक config मापता है। [`run_matrix.py`](https://github.com/Muvon/octocode/tree/master/benchmark) कई को **एक shared index** पर sweep करता है — तो rows के बीच बदलने वाली एकमात्र चीज़ search-time parameters हैं, corpus या embeddings नहीं। यह एक comparison table `RESULTS.md` (और raw `RESULTS.json`) में लिखता है, और runs के बीच accumulate करता है, ताकि एक focused re-run मौजूदा table में merge हो जाए।

```bash
# पहले corpus को ground-truth commit पर pin करें — वरना line ranges खिसक जाएँगी
git worktree add /tmp/corpus b1771ba

# पूरी local matrix (fastembed, no API key): vector-only बनाम hybrid weight sweep
CORPUS=/tmp/corpus python3 benchmark/run_matrix.py

# एक local cross-encoder reranker जोड़ें (फिर भी no key) — वही index reuse करता है
CORPUS=/tmp/corpus SKIP_INDEX=1 ONLY=rerank \
  RERANK_MODEL=fastembed:bge-reranker-base python3 benchmark/run_matrix.py
```

Variants: `vector_only`, `hybrid_70_30` (default weights), `hybrid_30_70` (keyword-tilted), `+graph` (GraphRAG expansion), और `+rerank` (cross-encoder)। अगर आप `VOYAGE_API_KEY` सेट करते हैं, तो paid code-aware reranker variants अपने आप enable हो जाते हैं — इसी तरह आप local floor के ऊपर असली ceiling ढूँढते हैं।

---

## बाक़ी सब कुछ

**MCP registry metadata और server schema update हुआ।** यह Octocode के published MCP server descriptor को असली tool surface के साथ कदम मिलाकर रखता है, ताकि registries और clients सही schema देखें। साथ ही सामान्य dependency और tooling housekeeping — कोई behavior change नहीं।

---

## Upgrade

```bash
# Homebrew
brew upgrade muvon/tap/octocode

# Universal installer
curl -fsSL https://raw.githubusercontent.com/Muvon/octocode/master/install.sh | sh

# Cargo
cargo install octocode --version 0.18.0
```

इस रिलीज़ में कुछ भी आपके index या config को नहीं बदलता — upgrade करना safe है, और आपका मौजूदा setup चलता रहता है। आज़माने लायक दो चीज़ें:

1. **अपने fusion weights को keyword की ओर झुकाएँ।** अगर आप prose से ज़्यादा code search करते हैं, तो benchmark कहता है कि keyword-led RRF एक मुफ़्त accuracy जीत है। इसे अपने repo पर मापें।
2. **Benchmark चलाएँ।** Octocode clone करें, corpus pin करें, और अपने embedding model और reranker के खिलाफ `run_matrix.py` चलाएँ। ऊपर के numbers एक local floor हैं — अपना ceiling ढूँढें।

GraphRAG expansion आज़माने के लिए, `[graphrag]` enable करें, `search.graph_expansion = true` सेट करें, इसे एक code-aware reranker के साथ pair करें, और on रखने से पहले अपने baseline के खिलाफ A/B करें।

---

Octocode open source है (Apache 2.0) [github.com/Muvon/octocode](https://github.com/Muvon/octocode) पर — benchmark, ground truth और raw results शामिल हैं। यह [Octomind](https://octomind.run) के पीछे का code-search engine है, और अब इसकी retrieval quality वह चीज़ है जिसे आप खुद माप सकते हैं, सिर्फ़ हमारी बात पर भरोसा नहीं करना पड़ता।
