0.17.1 ให้คุณรันทุก embedding ที่ Octocode สร้างบนฮาร์ดแวร์ที่คุณควบคุมเอง — OpenAI-compatible server ใด ๆ, โมเดลใด ๆ และโค้ดของคุณไม่เคยออกจากเครือข่ายของคุณ รีลีสนั้นเกี่ยวกับว่า search รัน ที่ไหน
0.18.0 เกี่ยวกับว่า search ดีจริงแค่ไหน — และพิสูจน์ด้วยตัวเลขที่คุณทำซ้ำได้เอง
เครื่องมือ code-search ส่วนใหญ่อ้างเรื่องคุณภาพ แต่น้อยมากที่เผยแพร่ benchmark ที่คุณรันเองได้ รีลีสนี้ให้มาสองตัว: retrieval benchmark ที่คัดมาอย่างดีรันบน source code ของ Octocode เอง และ harness ของ Loc-Bench บน 560 GitHub issue จริง นอกจากนี้ยังเพิ่ม GraphRAG file-level expansion — ขั้นตอนของ search ที่ดึงไฟล์ที่เกี่ยวข้องเชิงโครงสร้างเข้ามาก่อนที่ reranker จะให้คะแนน
นี่คือสิ่งที่เปลี่ยนไป และสิ่งที่ตัวเลขบอก — รวมถึงส่วนที่ไม่ได้ผลด้วย
ทำไมต้อง benchmark code search ตั้งแต่แรก?
เมื่อคุณเปลี่ยน chunking heuristic หรือ fusion weight คุณอยากรู้ว่า retrieval ดีขึ้นหรือแย่ลง การดูด้วยตาไม่กี่ query บอกไม่ได้ — มันแค่ยืนยันสิ่งที่คุณเชื่ออยู่แล้ว คำตอบที่ซื่อสัตย์อย่างเดียวคือ ชุด query ที่ตายตัว, ground truth ที่ตายตัว, และตัวเลขที่ขยับได้
ดังนั้น 0.18.0 จึงมาพร้อม benchmark ของจริง มันอยู่ใน benchmark/ ในรีโป และวัด pipeline ทั้งหมดของ Octocode แบบ end to end: chunking → embedding → vector search → hybrid fusion → reranking
Ground truth
code.csv มี 127 queries แต่ละ query annotate ด้วย code location ที่ถูกต้อง 1–3 ตำแหน่งในรูปของช่วงบรรทัด:
query,result1,result2,result3
แต่ละ result คือ src/path/file.rs:start-end:relevance โดย relevance 2 คือ primary (ตอบ query โดยตรง) และ 1 คือ secondary (context ที่มีประโยชน์) ผลการ search จะ match ด้วย การซ้อนทับของช่วงบรรทัด (line-range overlap) — hit ที่บรรทัด 40–90 match กับ ground truth 45–92 เพราะช่วงตัดกัน
Corpus ถูก pin ไว้ที่ commit เดียว (b1771ba) ground truth annotate เทียบกับเลขบรรทัดเป๊ะ ๆ เหล่านั้น ดังนั้นการ index checkout อื่นจะทำให้ช่วงบรรทัดเลื่อนและทำให้คะแนนพังเงียบ ๆ pin ก่อน แล้วค่อย index
วิธีสร้าง ground truth สำคัญ เพราะ answer key ที่ลวก ๆ ทำให้ benchmark ไหน ๆ ก็โกหก:
- Candidate ที่สร้างโดย AI ครอบคลุมทุก module หลักและไฟล์เอกสาร
- Source verification — ทุกไฟล์และช่วงบรรทัดที่อ้างถึงถูกอ่านและตรวจสอบเทียบกับโค้ดจริง
- Multi-agent validation — agent หลายตัวขนานกันตรวจซ้ำช่วงบรรทัด, path และระดับ relevance อย่างอิสระ
- การแก้ไขที่อิงผล search — ถ้า search เจอ logic เดียวกันในตำแหน่งทางเลือกที่ถูกต้อง ตำแหน่งนั้นจะถูก เพิ่ม เป็น secondary result ไม่ใช่ลบทิ้ง
Hard queries
27 query สุดท้ายจาก 127 เป็น adversarial โดยตั้งใจ: ภาษาธรรมชาติที่ไม่สะท้อนชื่อ function หรือ comment ใด ๆ มันทดสอบความเข้าใจเชิงความหมาย ไม่ใช่ดวงเรื่อง keyword ตัวอย่าง:
| เจตนาของ query | ทำไมถึงยาก |
|---|---|
| ป้องกัน infinite loop ใน chunk ที่ซ้อนทับ | guard clause 5 บรรทัด ไม่มี keyword โค้ดใน query |
| ข้าม indexing เมื่อรีโปไม่เปลี่ยน | ฝังลึก 100+ บรรทัดในฟังก์ชัน 900 บรรทัด |
| ประมาณ token แบบหยาบโดยไม่มี tokenizer | คำตอบคือบรรทัดเดียว: s.len() / 4 |
| ป้องกัน reindexing พร้อมกันใน MCP server | AtomicBool + compare_exchange ซึ่งเป็น concurrency pattern |
ถ้า standard queries ได้คะแนนใกล้ 1.0 แต่ hard queries ตามหลัง แสดงว่า benchmark กำลังทำหน้าที่: มันเปิดเผยจุดอ่อนของ retrieval จริง ๆ แทนที่จะประจบเครื่องมือ
Metrics
IR metric มาตรฐานสี่ตัว ทั้งหมด "ยิ่งสูงยิ่งดี":
- Hit@k — มีผลที่ถูกต้อง สักตัว อยู่ใน top k ไหม? (coverage)
- MRR — reciprocal rank ของผลที่ถูกต้อง ตัวแรก (hit ตัวแรกอยู่สูงแค่ไหน?)
- NDCG@10 — ให้รางวัลกับการวางผลที่ relevant ที่สุด ไว้บนสุด โดยถ่วงน้ำหนักทั้งระดับ relevance และตำแหน่ง (คุณภาพการจัดอันดับ)
- Recall@k — เจอ ground-truth block ทั้งหมด คิดเป็นสัดส่วนเท่าไร? (ความครบถ้วน)
นิยามและตัวอย่างที่คำนวณให้ดูอยู่ใน benchmark/README.md
ตัวเลข: พื้น (floor) แบบ local ไม่ใช้ key
ผลเหล่านี้ใช้ stack local เต็มรูปแบบ ไม่ใช้ API key — jina-embeddings-v2-base-code ผ่าน fastembed, ไม่มี reranker ทำให้มันเป็น floor ไม่ใช่ ceiling: reranker แบบเสียเงินที่เข้าใจโค้ดจะดันให้สูงขึ้น รันเทียบกับ source code ของ Octocode เอง, 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, RRF weights ค่าเริ่มต้น (0.7/0.3) | 0.598 | 0.717 | 0.485 | 0.528 | 0.671 |
| Hybrid, ปรับเอียงไป keyword (0.3/0.7) | 0.732 | 0.835 | 0.572 | 0.620 | 0.807 |
มีสองอย่างที่สะดุดตา
RRF weights ค่าเริ่มต้นทำตัวเหมือน vector อย่างเดียว เมื่อ fusion เอียง 0.7 ไปทาง dense signal ผล vector จะครอบงำ และ keyword signal แทบไม่นับ — สองแถวบนเหมือนกันเป๊ะ
การเอียงไปทาง keyword คือชัยชนะก้อนใหญ่และฟรีสำหรับโค้ด พลิก weights เป็น 0.3/0.7 — ให้ BM25/keyword signal นำ — แล้ว Hit@5 พุ่ง +22% และ Recall@10 +20% โดยไม่มีต้นทุนเพิ่ม ทำไม? โค้ดเต็มไปด้วย identifier ที่ตรงเป๊ะ — compare_exchange, get_code_blocks_by_path, RelationshipDirection — และการ match token แบบตรงตัวมีน้ำหนักมากเป็นพิเศษเมื่อสิ่งที่คุณค้นหา คือ token พอดี ความคล้ายเชิงความหมายล้วน ๆ ทำให้มันเบลอ แต่ keyword fusion ทำให้มันคม
นี่คือผลแบบที่คุณจะเจอก็ต่อเมื่อวัดเท่านั้น มันไม่ได้ตรงสัญชาตญาณเลยที่สำหรับโค้ดคุณจะอยากให้ keyword มีน้ำหนักมากกว่า embedding
สิ่งที่ไม่ช่วย (และทำไมมันอยู่ใน benchmark)
benchmark ที่รายงานแต่ชัยชนะคือการตลาด ตัวนี้รายงาน regression ด้วย — matrix ครบ 6 variant อยู่ใน benchmark/RESULTS.md
reranker ทั่วไปทำให้แย่ลง การใส่ local cross-encoder แบบ off-the-shelf (bge-reranker-base) ทับบน config ที่ดีที่สุดไม่ได้ช่วย — มัน regress Hit@5 จาก 0.732 กลับลงมา 0.598 ลบล้างชัยชนะจากการปรับ keyword ทั้งหมด และยังช้ากว่าราว ๆ 4 เท่า (≈1900s เทียบกับ ≈490s ต่อรอบ)
บทเรียนไม่ใช่ว่า "reranker ไม่ดี" แต่คือ code retrieval ต้องการ reranker ที่เข้าใจโค้ด reranker ที่เทรนบนงานเขียนทั่วไปให้คะแนน fn verify_hmac เทียบกับ "webhook signature validation" แย่กว่าที่ hybrid fusion ทำได้อยู่แล้วเสียอีก reranker ที่ปรับมาเพื่อโค้ดอย่าง voyage:rerank-2.5 เป็นอีกเรื่อง — แต่คุณต้อง verify บน eval ของคุณเอง ไม่ใช่เดาเอา benchmark นี่แหละที่ทำให้คุณทำได้
GraphRAG File-Level Expansion
ฟีเจอร์เด่นเคียงคู่กับ benchmark ทั้ง dense และ keyword search ให้คะแนน block แบบแยกเดี่ยว — มันไม่รู้ว่าไฟล์ที่คุณต้องการ import ไฟล์ที่มันเจอ หรือ call ฟังก์ชันที่นิยามไว้ข้าง ๆ GraphRAG file-level expansion ปิดช่องว่างนั้น
หลังจาก hybrid search รอบแรกผลิต candidate ที่จัดอันดับแล้ว Octocode จะหยิบตัวที่แข็งแกร่งสุดไม่กี่ตัว เดิน GraphRAG graph เพื่อหาไฟล์ที่ เกี่ยวข้องเชิงโครงสร้างผ่าน imports, calls หรือ extends ดึง block จำนวนหนึ่งจากไฟล์เพื่อนบ้านเหล่านั้น แล้วส่ง candidate set ที่ ขยายแล้ว ให้ reranker:
// 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
เพื่อนบ้านถูกให้คะแนนด้วย weight × confidence ข้ามทุก seed file โดยลิงก์ที่แข็งแกร่งสุดมาก่อน มันเป็นระดับไฟล์เพราะ graph เป็นระดับไฟล์ (CodeNode.id == relative path) และมันเป็น best-effort: error ใด ๆ ของ graph จะ degrade ไปเป็นการส่งผลเดิมผ่านตรง ๆ — ไม่เคยทำให้ search พัง
เปิดใช้ใน config.toml:
[search]
graph_expansion = false # default; requires [graphrag] enabled
มันมาแบบ ปิดเป็นค่าเริ่มต้น โดยตั้งใจ นี่คือส่วนที่ซื่อสัตย์จาก benchmark:
+graph= GraphRAG file-level expansion ซึ่ง ขยับผลก็ต่อเมื่อ reranker ให้คะแนน set ที่ขยายแล้วใหม่เท่านั้น
หากไม่มี reranker การ expansion จะเพิ่ม candidate แต่ไม่เปลี่ยนการจัดอันดับ — นี่คือเหตุที่ hybrid_30_70 และ hybrid_30_70+graph ได้คะแนนเท่ากันใน matrix แบบ local ไม่ใช้ key ข้างบน ผลตอบแทนของ expansion มาเมื่อ reranker (ที่เข้าใจโค้ด) ให้คะแนน pool ที่ใหญ่ขึ้นใหม่และดันไฟล์ที่เกี่ยวข้องเชิงโครงสร้างซึ่ง similarity ดิบจัดอันดับไว้ต่ำเกินไปขึ้นมา การ expansion แบบไร้เดียงสายังอาจ เจือจาง ความแม่นยำได้ด้วย ดังนั้นคำแนะนำที่ฝังใน comment ของ config จึงตรงไปตรงมา: A/B บน eval ของคุณเองก่อนจะไว้ใจมัน benchmark มีอยู่ก็เพื่อสิ่งนี้พอดี
Loc-Bench: Issue → Code โดยไม่มีอะไร self-annotated
benchmark ที่คัดมานั้นเรา annotate เอง คำถามที่ยุติธรรม: query มัน เอื้อ กับเครื่องมือของเราเองเกินไปไหม? 0.18.0 มาพร้อม harness ตัวที่สองที่เอาเราออกจากสมการไปเลย
benchmark/locbench.py ประเมิน Octocode บน Loc-Bench V1 (czlll/Loc-Bench_V1): 560 GitHub issue จริง จากรีโป Python ยอดนิยม query คือ ข้อความ issue ต้นฉบับ ground truth คือชุดไฟล์และฟังก์ชันที่ fix จริงที่ merge แล้ว แก้ไขจริง ๆ (field edit_functions ที่ dataset คัดมา) ไม่มีอะไร self-annotated — เป็นงาน industry-standard แบบ "ให้ bug report มา หาโค้ดที่ต้องแก้"
ต่อหนึ่ง instance harness จะ clone รีโป (blobless, cached) checkout base commit รัน octocode index search ด้วยข้อความ issue และให้คะแนนไฟล์ที่ดึงมาได้เทียบกับ fix จริง — ทั้งระดับ file level (hit@k, recall@k, MRR, accuracy) และ hunk level (ผลที่ดึงมาซ้อนทับกับบรรทัดที่ patch แตะจริงไหม?) แยกตามหมวดของ issue:
python3 benchmark/locbench.py --limit 10 --verbose # smoke test
python3 benchmark/locbench.py # full 560
python3 benchmark/locbench.py --category "Bug Report"
นี่คือเครื่องมือวัด ไม่ใช่การอ้าง leaderboard — มันมีไว้ให้คุณรัน issue localization แบบโลกจริงบนฮาร์ดแวร์และโมเดลของคุณเอง และเพื่อให้ทุกการเปลี่ยนแปลงของ Octocode ในอนาคตวัดได้กับ benchmark ที่เราไม่ได้เขียนเอง
Variant Matrix
score.py วัดได้ทีละ config run_matrix.py sweep หลาย config บน index ที่แชร์ร่วมกันตัวเดียว — ดังนั้นสิ่งเดียวที่เปลี่ยนระหว่างแถวคือ search-time parameter ไม่ใช่ corpus หรือ embedding มันเขียนตารางเปรียบเทียบลง RESULTS.md (และ RESULTS.json ดิบ) และสะสมข้ามรอบรัน ดังนั้นการรันซ้ำแบบเฉพาะจุดจะ merge เข้าตารางเดิม
# pin corpus ไปที่ commit ของ ground truth ก่อน — ไม่งั้นช่วงบรรทัดจะเลื่อน
git worktree add /tmp/corpus b1771ba
# matrix แบบ local เต็ม (fastembed, ไม่ใช้ API key): vector-only เทียบกับ sweep ของ hybrid weight
CORPUS=/tmp/corpus python3 benchmark/run_matrix.py
# เพิ่ม local cross-encoder reranker (ยังไม่ใช้ key) — ใช้ index เดิมซ้ำ
CORPUS=/tmp/corpus SKIP_INDEX=1 ONLY=rerank \
RERANK_MODEL=fastembed:bge-reranker-base python3 benchmark/run_matrix.py
Variant: vector_only, hybrid_70_30 (weight ค่าเริ่มต้น), hybrid_30_70 (เอียงไป keyword), +graph (GraphRAG expansion) และ +rerank (cross-encoder) ถ้าคุณตั้ง VOYAGE_API_KEY variant ของ reranker แบบเสียเงินที่เข้าใจโค้ดจะเปิดใช้อัตโนมัติ — นั่นคือวิธีที่คุณหาเพดาน (ceiling) จริงเหนือ floor แบบ local
อื่น ๆ ทั้งหมด
อัปเดต MCP registry metadata และ server schema ทำให้ descriptor ของ MCP server ที่เผยแพร่ของ Octocode สอดคล้องกับ tool surface จริง เพื่อให้ registry และ client เห็น schema ที่ถูกต้อง บวกกับงานดูแล dependency และ tooling ตามปกติ — ไม่มี behavior change
อัปเกรด
# 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 ของคุณ — อัปเกรดได้อย่างปลอดภัย และ setup เดิมของคุณยังทำงานต่อไป สองอย่างที่ควรลอง:
- เอียง fusion weight ของคุณไปทาง keyword ถ้าคุณ search โค้ดมากกว่างานเขียนทั่วไป benchmark บอกว่า RRF ที่นำด้วย keyword คือชัยชนะด้านความแม่นยำแบบฟรี วัดมันบนรีโปของคุณ
- รัน benchmark clone Octocode, pin corpus และรัน
run_matrix.pyเทียบกับ embedding model และ reranker ของคุณเอง ตัวเลขข้างบนคือ floor แบบ local — ไปหาเพดานของคุณเอง
หากต้องการลอง GraphRAG expansion ให้เปิด [graphrag] ตั้ง search.graph_expansion = true จับคู่กับ reranker ที่เข้าใจโค้ด และ A/B เทียบกับ baseline ของคุณก่อนจะเปิดค้างไว้
Octocode เป็นโอเพนซอร์ส (Apache 2.0) ที่ github.com/Muvon/octocode — มีทั้ง benchmark, ground truth และผลดิบให้ครบ มันคือ code-search engine เบื้องหลัง Octomind และตอนนี้คุณภาพ retrieval ของมันเป็นสิ่งที่คุณวัดได้เอง ไม่ใช่แค่เชื่อคำเรา



