รีโปมีไฟล์ราว 11,000 ไฟล์ เอเจนต์มีเครื่องมือ bash และคำสั่งคลุมเครือว่า "หาให้เจอว่าเราตรวจสอบลายเซ็นเวบฮุกตรงไหน" มันรัน grep -rn "signature" . ได้ผลลัพธ์ 600 รายการกระจายอยู่ในไฟล์ JS ของเวนเดอร์ ฟิกซ์เจอร์ทดสอบ ไฟล์ CHANGELOG และตัวช่วยคริปโตที่ไม่เกี่ยวข้องอีกสามตัว แล้วก็ชี้ไปยังไฟล์ที่ผิดอย่างมั่นใจ เราเฝ้าดูมันทำแบบนี้ติดต่อกันสี่ครั้งด้วยถ้อยคำต่างกัน ก่อนที่ใครบางคนจะพึมพำสิ่งที่เห็นได้ชัด: grep จับคู่สตริง แต่เอเจนต์กำลังถามคำถามเกี่ยวกับ ความหมาย
นั่นคือปัญหาทั้งหมดในประโยคเดียว grep และ rg ยอดเยี่ยมสำหรับ "โทเค็นนี้ปรากฏตรงไหนพอดี" แต่ไร้ประโยชน์สำหรับ "สิ่งที่ทำ X อยู่ที่ไหน" เพราะโค้ดที่ทำ X แทบไม่เคยมีคำที่คุณจะใช้บรรยาย X อยู่เลย ฟังก์ชันชื่อ verify_hmac คุณค้น "webhook signature" ทับซ้อนกันเป็นศูนย์ และเครื่องมือเดียวที่เอเจนต์เอื้อมหยิบเป็นอย่างแรกก็ไม่คืนอะไรที่มีประโยชน์เลย
ทางแก้คือการค้นหาเชิงความหมาย: เอ็มเบดโค้ด เอ็มเบดคำค้น แล้วหาชังก์ที่ความหมายใกล้เคียงที่สุด ส่วนที่ติดขัดคือคำแนะนำแบบ "ก็แค่ใช้เอ็มเบดดิงสิ" ส่วนใหญ่สันนิษฐานว่าคุณจะส่งซอร์สโค้ดไปยัง API คลาวด์และเช่า GPU สำหรับโมโนรีโปส่วนตัว ทั้งสองอย่างนั้นรับไม่ได้: ฝ่ายกฎหมายจะไม่อนุมัติการอัปโหลดโค้ดกรรมสิทธิ์ให้บุคคลที่สาม และคุณก็ไม่อยากได้บิลคิดต่อโทเค็นที่โตขึ้นตามทุกครั้งที่รีอินเด็กซ์
ดังนั้นนี่คือการตั้งค่าที่เราใช้งานจริง: Octocode ที่จัดทำดัชนีรีโปขนาดใหญ่เพื่อค้นหาเชิงความหมาย แบบโลคัลล้วน — ไม่ใช้ GPU ไม่ใช้เอ็มเบดดิงคลาวด์ ไม่มีอะไรออกจากเครื่อง นี่คือวิธีการทำงานที่แท้จริง คอนฟิกที่สำคัญ และสี่สิ่งที่เล่นงานเรา
ทำไม grep ถึงไม่พอ อย่างเป็นรูปธรรม
ก่อนจะเอื้อมไปหาเอ็มเบดดิง ควรชัดเจนเสียก่อนว่า ทำไม การค้นข้อความถึงล้มเหลวสำหรับเอเจนต์ เพราะมันบอกว่าสิ่งที่มาแทนต้องทำอะไร
- คำศัพท์ไม่ตรงกัน คำค้นคือเจตนา ("rate limiting") แต่โค้ดคือกลไก (
Semaphore,token_bucket,RetryAfter) ไม่มีโทเค็นร่วมกัน - ไม่มีการจัดอันดับ
rgให้ทุกผลลัพธ์ด้วยน้ำหนักเท่ากัน 600 ผลลัพธ์ไม่ใช่คำตอบ แต่เป็นปัญหาการค้นหาอันที่สอง - ไม่มีโครงสร้าง ผลลัพธ์ข้อความในคอมเมนต์ ในเทสต์ และในฟังก์ชันหลัก ดูเหมือนกันหมดสำหรับ
grepมันบอกคุณไม่ได้ว่าอันที่สามคือนิยาม
สิ่งที่คุณต้องการแทนคือ: จัดอันดับตามความใกล้เชิง ความหมาย แต่ไม่ทิ้งสิ่งที่ grep เก่งจริง ๆ — การจับคู่ตัวระบุ (identifier) แบบตรงตัว คำตอบของ Octocode คือทำทั้งสองอย่างแล้วผสานเข้าด้วยกัน ซึ่งผมจะกล่าวถึงต่อไป ก่อนอื่น มาดูว่าโค้ดถูกแปลงให้อยู่ในรูปที่ค้นหาได้อย่างไร
Octocode แบ่งชังก์และจัดทำดัชนีโค้ดอย่างไร
RAG มาตรฐานหั่นไฟล์เป็นหน้าต่างข้อความขนาดคงที่ — ทุก N อักขระ เหลื่อมกันนิดหน่อย แล้วเอ็มเบด สำหรับร้อยแก้วก็โอเค แต่สำหรับโค้ดมันแย่อย่างจริงจัง: มันตัดฟังก์ชันออกเป็นสองชังก์ เอ็มเบดครึ่งหนึ่งของแขน match และสูญเสียข้อเท็จจริงที่ว่าเมธอดเป็นของไทป์
Octocode พาร์สแต่ละไฟล์ด้วย tree-sitter ก่อน เดินผ่าน AST และแบ่งชังก์ตามขอบเขตสัญลักษณ์จริง — ฟังก์ชัน เมธอด คลาส โมดูล — แทนที่จะใช้ตำแหน่งไบต์ที่กำหนดเอง สถาปัตยกรรมคือพาร์สเซอร์หนึ่งตัวต่อภาษาดึงฟังก์ชัน คลาส อิมพอร์ตและเอ็กซ์พอร์ตออกมา แล้วประมวลผลแบบแบ่งชังก์สำหรับอะไรก็ตามที่ใหญ่เกินกว่าจะเอ็มเบดเป็นชิ้นเดียว เมธอดที่ดึงมาจากภายในบล็อก impl หรือคลาสจะพกชื่อไทป์แม่ไว้ในรายการสัญลักษณ์ของมัน ดังนั้นการค้น Suppression.mark_set หรือ Foo.bar จึงเข้าชนชังก์เมธอดได้โดยตรง
การรองรับภาษาเป็นไวยากรณ์ tree-sitter จริง ไม่ใช่ฮิวริสติกแบบ regex ตามรีโป สิ่งที่จัดทำดัชนีเป็น โค้ด ด้วยการพาร์ส AST เต็มรูปแบบ:
| ภาษา | นามสกุลไฟล์ |
|---|---|
| Rust | .rs |
| Python | .py |
| TypeScript / JavaScript | .ts, .tsx, .js, .jsx |
| Go | .go |
| PHP | .php |
| C / C++ | .c, .h, .cpp, .hpp, .cc, .cxx, รวมถึงนามสกุลโมดูล C++20 .cppm, .ixx, .mxx, .ccm, .cxxm |
| Ruby | .rb |
| Java | .java |
| Swift | .swift |
| JSON, Bash, CSS, Lua, Svelte, Markdown | เส้นทาง tree-sitter เฉพาะ |
อย่างอื่นที่มีประโยชน์สำหรับเป็นบริบท (yaml, toml, dockerfile, makefile, ini, conf, env, xml, html, sql, rst และพวกพ้อง) จะจัดทำดัชนีเป็น บล็อกข้อความ จึงยังค้นหาได้ เพียงแต่ไม่มีการดึงสัญลักษณ์เชิงความหมาย
พารามิเตอร์การแบ่งชังก์อยู่ใต้ [index]:
[index]
chunk_size = 2000 # อักขระสูงสุดต่อชังก์โค้ด
chunk_overlap = 100 # การเหลื่อมระหว่างชังก์ที่ติดกัน
quantization = true # การบีบอัดเวกเตอร์ RaBitQ ~32x สูญเสียคุณภาพน้อยมาก
require_git = true # ค่าเริ่มต้นจัดทำดัชนีเฉพาะภายในรีโป git
chunk_size เป็นเพดาน ไม่ใช่เป้าหมาย — ฟังก์ชัน 40 บรรทัดที่เล็กกว่าขีดจำกัดจะอยู่ครบทั้งตัว ขีดจำกัดจะมีผลก็ต่อเมื่อบอดี้ใหญ่จริง ๆ และ Octocode ก็จัดทำดัชนีคลาสขนาดใหญ่ใน Python, TypeScript, C++ และ Ruby แบบทีละเมธอดอยู่แล้ว แทนที่จะเทคลาสทั้งคลาสลงในชังก์เดียว ประเด็นของทั้งหมดนี้: เมื่อคุณค้นหา "ฟังก์ชันที่จัดการการตั้งค่า remote pull" ในภายหลัง หน่วยที่ถูกเอ็มเบดคือ ฟังก์ชันนั้น พร้อมชื่อและไทป์แม่ที่แนบมา ไม่ใช่หน้าต่างที่บังเอิญคร่อมมันอยู่
เอ็มเบดดิงโลคัล: ไม่มี GPU ไม่มีคลาวด์ ไม่มี API key
ส่วนที่ผู้คนเหมาเอาว่าต้องใช้คลาวด์ — การแปลงโค้ดเป็นเวกเตอร์ — จริง ๆ แล้วไม่ต้อง
Octocode มาพร้อม สแตกแบบ local-first ที่สร้างบน fastembed (มี ONNX Runtime อยู่เบื้องหลัง) คุณชี้คอนฟิกไปยังโมเดลโลคัลด้วยพรีฟิกซ์ fastembed: แล้วเอ็มเบดดิงจะรันบน CPU บนเครื่องของคุณ:
[embedding]
code_model = "fastembed:jinaai/jina-embeddings-v2-base-code" # 768 มิติ ออกแบบมาเพื่อโค้ดโดยเฉพาะ
text_model = "fastembed:nomic-ai/nomic-embed-text-v1.5" # ข้อความและเอกสาร
สตริงโมเดลจะเป็น provider:model เสมอ พรีฟิกซ์ผู้ให้บริการคือการตัดสินใจเรื่องการกำหนดเส้นทางทั้งหมด: fastembed: และ huggingface: เป็นโลคัล (ไม่ต้องใช้คีย์ ไม่ต้องใช้เครือข่าย) ส่วน voyage:, jina:, google:, openai:, together:, octohub: เป็นคลาวด์และต้องการ API key ที่สอดคล้องในสภาพแวดล้อม จะผสมก็ได้ (เอ็มเบดโค้ดโลคัล เอ็มเบดข้อความคลาวด์) แต่สำหรับรีโปส่วนตัว เส้นทางโลคัลล้วนคือประเด็นสำคัญ
ที่นี่ไม่มีข้อกำหนดเรื่อง GPU และไม่มีเส้นทางโค้ดสำหรับ GPU: นี่คือการอนุมาน ONNX บน CPU บนเครื่องมัลติคอร์รุ่นใหม่ที่มี AVX2 มันเร็วพอที่ตัวเอ็มเบดเดอร์แทบไม่เคยเป็นคอขวด — โดยปกติคือ disk I/O และการพาร์ส tree-sitter ต่างหากที่เป็น บันทึกประสิทธิภาพของรีโปเองระบุว่าการจัดทำดัชนีด้วย FastEmbed แบบโลคัลอยู่ราว 45 วินาทีต่อ 1,000 ไฟล์ในเชิงลำดับขนาดคร่าว ๆ ผลลัพธ์ของคุณจะต่างกันไปตามขนาดไฟล์และจำนวนคอร์ จึงควรถือว่าเป็นตัวอย่างประกอบ ไม่ใช่คำสัญญา
ข้อควรระวังตรง ๆ สองข้อ:
- การรันครั้งแรกดาวน์โหลดโมเดล
octocode indexครั้งแรกหลังชี้ไปยังโมเดลfastembed:จะดึงน้ำหนัก ONNX (ไม่กี่ร้อย MB) ลงไดเรกทอรีแคช (~/.local/share/octocode/fastembed/บน Linux/macOS) เป็นต้นทุนครั้งเดียว หลังจากนั้นออฟไลน์ตลอดไป - ผู้ให้บริการโลคัลถูกควบคุมด้วยฟีเจอร์แฟล็กและขึ้นกับแพลตฟอร์ม FastEmbed และ HuggingFace ต้องการฟีเจอร์ build
fastembed/huggingfaceบิลด์ค่าเริ่มต้นจะมีให้ในที่ที่รองรับดี ส่วนแพลตฟอร์มที่ไม่รองรับ คุณจะถอยกลับไปใช้ผู้ให้บริการคลาวด์ ตรวจoctocode models list fastembedเพื่อดูว่าไบนารีของคุณเปิดเผยอะไรจริง ๆ งานเรื่องผู้ให้บริการเอ็มเบดดิงโลคัลใน 0.17.0 เมื่อไม่นานนี้คือสิ่งที่ทำให้รายการนี้ซื่อตรงว่าอะไรพร้อมใช้ที่ไหน
ถ้าคุณยังไม่เคยตั้งค่าการค้นหาเชิงความหมายเหนือโค้ดมาก่อน บทแนะนำการค้นหาโค้ดเชิงความหมายของ Octocode ครอบคลุมพื้นฐานเชิงแนวคิด โพสต์นี้สันนิษฐานว่าคุณผ่านขั้นนั้นแล้วและต้องการการตั้งค่าสำหรับโปรดักชัน
จัดทำดัชนีรีโป
เมื่อคอนฟิกพร้อม การจัดทำดัชนีคือคำสั่งเดียวจากรากของรีโป:
cd /path/to/big-monorepo
octocode index
# → Indexed 12,847 blocks across 342 files
สิ่งที่เกิดขึ้นจริงตามลำดับ: ค้นหาไฟล์ (เคารพกฎการเพิกเฉย) พาร์สแต่ละไฟล์ด้วย tree-sitter ดึงสัญลักษณ์ แบ่งชังก์ตามขอบเขต AST เอ็มเบดแต่ละชังก์แบบโลคัล แล้วเขียนทุกอย่างลงสโตร์แบบคอลัมน์ LanceDB ฐานข้อมูล ไม่ได้ อยู่ในรีโปของคุณ — มันถูกคีย์ตามโปรเจกต์ใต้ไดเรกทอรีข้อมูลของระบบ (~/.local/share/octocode/<project-id>/storage บน Linux/macOS) ซึ่งช่วยให้ working tree ของคุณสะอาด และหมายความว่า git clean -fdx จะไม่ล้างดัชนีของคุณ
แฟล็กที่มีประโยชน์:
octocode index --force # เพิกเฉยแคชแบบ incremental สร้างใหม่จากศูนย์
octocode index --verbose # ดูความคืบหน้าทีละไฟล์
octocode stats # มีบล็อกโค้ด/ข้อความ/เอกสารกี่บล็อก โหนดกราฟ ความเก่าของดัชนี
octocode stats คือคำสั่งที่ควรรันเมื่อรู้สึกว่ามีอะไรผิดปกติ — มันบอกว่า HEAD ของคุณตรงกับคอมมิตที่จัดทำดัชนีล่าสุดหรือไม่ คุณจึงเห็นได้ในแวบเดียวว่าดัชนีเก่าแล้วหรือยัง
Incremental กับการรีอินเด็กซ์เต็ม
การรัน octocode index ครั้งต่อ ๆ ไปเป็นแบบ incremental — เฉพาะไฟล์ที่เปลี่ยนแปลงเท่านั้นที่ถูกพาร์สและเอ็มเบดใหม่ ทุกรอบการจัดทำดัชนีจบด้วยรอบ optimize ตาราง เพื่อให้เส้นทางคิวรียังเร็วเมื่อฐานข้อมูลโต (การจัดทำดัชนีแบบ incremental ซ้ำ ๆ เคยทิ้งหางที่ยังไม่ถูกจัดทำดัชนีซึ่งการค้นหาต้องสแกนแบบ brute-force ตอนนี้จัดการอัตโนมัติแล้ว)
เมื่อคุณเปลี่ยน วิธี การจัดทำดัชนี (กฎการแบ่งชังก์ใหม่ โมเดลเอ็มเบดดิงต่างออกไป) แบบ incremental จะไม่ย้อนกลับไปแบ่งชังก์ไฟล์ที่มีอยู่แล้วใหม่ การเปลี่ยนโมเดลเอ็มเบดดิงเป็นเรื่องใหญ่: เวกเตอร์จากโมเดลหนึ่งเทียบกับอีกโมเดลไม่ได้ หลังเปลี่ยน code_model หรือ text_model ให้สร้างใหม่แบบสะอาด:
octocode clear # ทิ้งตารางที่จัดทำดัชนีไว้
octocode index # สร้างใหม่ด้วยโมเดลใหม่
โหมด watch: ทำให้ดัชนีสดอยู่เสมอ
ดัชนีที่ทำครั้งเดียวจะเก่าทันทีที่มีคนคอมมิต สำหรับเอเจนต์ที่ควรตอบคำถามเกี่ยวกับสถานะ ปัจจุบัน ของรีโป ให้รัน watcher:
octocode watch # รีอินเด็กซ์อัตโนมัติเมื่อไฟล์เปลี่ยน
octocode watch --debounce 5 # รอ 5 วินาทีหลังการเปลี่ยนแปลงครั้งสุดท้ายก่อนรีอินเด็กซ์
octocode watch --quiet
watcher ทำ debounce (ปรับได้ 1–30 วินาที) ดังนั้น git checkout ที่แตะ 400 ไฟล์จึงทริกเกอร์การรีอินเด็กซ์หนึ่งครั้ง ไม่ใช่ 400 ครั้ง มันเคารพกฎการเพิกเฉยแบบเดียวกับการจัดทำดัชนีเต็ม ดังนั้นการแก้ไฟล์ใน node_modules จึงไม่ปลุกมัน
ถ้าคุณรันเซิร์ฟเวอร์ MCP (หัวข้อถัดไป) ก็มีทางเลือกที่สะอาดกว่า: ตั้ง mcp_index = true ใต้ [index] แล้วเซิร์ฟเวอร์ MCP จะจัดทำดัชนีและเฝ้าดู ภายในโปรเซสเดียว — มีของที่รันอยู่ชิ้นเดียวแทนที่จะเป็นสองชิ้น ค่าเริ่มต้นคือ false ซึ่งให้บริการดัชนีที่มีอยู่แบบอ่านอย่างเดียวและสันนิษฐานว่าคุณจัดทำดัชนีแยกต่างหาก เปิดมันเมื่อคุณต้องการให้เซิร์ฟเวอร์เป็นเจ้าของวงจรชีวิตทั้งหมด
การค้นหาแบบไฮบริด: เชิงความหมาย และ แบบตรงตัว ผสานกัน
นี่คือส่วนที่แก้ความล้มเหลวของ grep แต่แรกโดยไม่ทิ้งจุดแข็งหนึ่งเดียวของ grep
การค้นหาด้วยเวกเตอร์ล้วนแพ้สำหรับคำค้นที่หนาแน่นด้วยตัวระบุ ค้น parse_remote แล้วเอ็มเบดดิงแบบ dense จะคืนสิ่งที่ ใกล้เชิงความหมาย กับ parse_remote อย่างเริงร่าในขณะที่พลาดฟังก์ชันที่ชื่อ parse_remote ตรง ๆ การค้นหาคีย์เวิร์ด BM25 มีจุดอ่อนตรงกันข้าม: มันจับตัวระบุแบบตรงตัวได้แม่นแต่พลาดเจตนาที่ถูกเรียบเรียงใหม่อย่าง "ฟังก์ชันที่จัดการการตั้งค่า remote pull"
Octocode รันทั้งสองในทุกคำค้นและผสานด้วย Weighted Reciprocal Rank Fusion ภายใน LanceDB น้ำหนักถูกเปิดเผยให้ปรับ:
[search.hybrid]
enabled = true
default_vector_weight = 0.7
default_keyword_weight = 0.3
สำหรับรีโปที่หนาแน่นด้วยโค้ดซึ่งตัวระบุแบบตรงตัวให้สัญญาณมาก ให้เอนไปทางคีย์เวิร์ด เบนช์มาร์กการดึงข้อมูลของรีโปเอง (คำค้นการค้นหาโค้ด 127 รายการพร้อม ground truth แบบช่วงบรรทัด รันแบบโลคัลล้วนด้วย jina-embeddings-v2-base-code และ ไม่ใช้ รีแรงเกอร์) แสดงผลชัดเจน: การเปลี่ยนจากสัดส่วนเริ่มต้น 0.7/0.3 ไปเป็น 0.3/0.7 ที่จูนเข้าหาคีย์เวิร์ด ยก Hit@5 จาก 0.598 เป็น 0.732 และ Recall@10 จาก 0.671 เป็น 0.807 โดยไม่มีต้นทุนเพิ่ม นั่นคือ Hit@5 ที่ +22% จากคอนฟิกบรรทัดเดียว สำหรับเอกสารเนื้อยาว ให้เอนไปอีกทาง (0.8/0.2) เพราะเจตนาเป็นใหญ่ที่นั่น
เหนือการผสาน รีแรงเกอร์ที่เป็นทางเลือกจะจัดอันดับผู้สมัครอันดับต้น ๆ ใหม่:
[search.reranker]
enabled = true
model = "fastembed:jina-reranker-v2-base-multilingual" # รีแรงเกอร์โลคัล ไม่ต้องใช้คีย์
top_k_candidates = 50 # ดึงจำนวนนี้จากการค้นหาเวกเตอร์
final_top_k = 10 # คืนจำนวนนี้หลังการรีแรงก์
รีแรงเกอร์ก็รันแบบโลคัลถ้าคุณชี้ไปยังโมเดล fastembed: มันเป็นพาส cross-encoder บนผู้สมัคร 50 อันดับแรก ซึ่งจับกรณีที่การจัดอันดับขั้นแรกเรียงผิด ต้นทุนมีจริงแต่จำกัด — มันเห็นแค่ top_k_candidates ไม่ใช่ดัชนีทั้งหมด
จาก CLI พื้นผิวการค้นหาตรงไปตรงมา:
octocode search "webhook signature verification"
octocode search "auth" "middleware" "session" # มัลติคำค้น ได้ถึง 10
octocode search "database connection pool" --mode code # จำกัดเฉพาะบล็อกโค้ด
octocode search "auth" --detail-level signatures # เฉพาะลายเซ็น กระชับ
octocode search "authentication refactor" --mode commits # ค้นประวัติ git
เส้นทาง --mode commits ควรรู้ไว้: ประวัติคอมมิตถูกจัดทำดัชนีแบบ lazy เมื่อค้นคอมมิตครั้งแรก (ไม่ใช่ระหว่าง octocode index) คุณจึงถามได้ว่า "เราเปลี่ยนโฟลว์ auth เมื่อไหร่" แล้วได้คอมมิตที่จัดอันดับเชิงความหมาย ไม่ใช่แค่ git log --grep
การค้นหาเชิงโครงสร้าง: เมื่อคุณต้องการแพตเทิร์น AST ไม่ใช่ความหมาย
บางครั้งคุณไม่ต้องการความหมายเลย — คุณต้องการ "ทุก .unwrap() ใน Rust" หรือ "ทุก new Foo() ใน JS" นั่นคือคำค้นเชิงโครงสร้าง และการค้นข้อความก็พลาดอยู่เรื่อย (คอมเมนต์ที่กล่าวถึง .unwrap() ไม่ใช่การเรียก) Octocode ครอบ ast-grep ไว้:
octocode grep '$FUNC.unwrap()' --lang rust
octocode grep 'new $CLASS($$$ARGS)' --lang javascript
octocode grep 'console.log($ARG)' --lang javascript --rewrite 'logger.info($ARG)' --update-all
$VAR จับคู่โหนด AST หนึ่งโหนด $$$ARGS จับคู่อาร์กิวเมนต์ชุดหนึ่ง และ --rewrite ทำรีแฟกเตอร์ที่รู้จัก AST ในที่ ส่วนที่มีประโยชน์จริงสำหรับเอเจนต์: LLM มักผิด kind ของโหนดอย่างสม่ำเสมอ — มันจะเขียน function_declaration สำหรับ Python ทั้งที่จริงคือ function_definition เมื่อแพตเทิร์นไม่จับคู่กับอะไรเลย Octocode จะลองการตีความที่ผ่อนลงเรื่อย ๆ และรู้ kind ที่ถูกต้องของแต่ละภาษา ดังนั้นความพยายามที่เกือบถูกของเอเจนต์จึงยังคืนผลลัพธ์แทนที่จะเป็นศูนย์เงียบ ๆ
GraphRAG: ความสัมพันธ์ ไม่ใช่แค่ความคล้าย
การค้นหาเชิงความหมายหาโค้ดที่ คล้าย กัน มันบอกคุณไม่ได้ว่า auth_middleware.rs อิมพอร์ต jwt.rs เรียก user_store.rs และเชื่อมเข้ากับ router.rs นั่นเป็นคำถามเชิงกราฟ และ Octocode สร้างกราฟระหว่างการจัดทำดัชนี
[graphrag]
enabled = true
use_llm = true
มันดึงเอจ imports, calls, extends และ implements จาก AST ในเก้าภาษา และด้วย use_llm = true ก็เพิ่มความสัมพันธ์เชิงสถาปัตยกรรมระดับสูงกว่า (configures, uses, แพตเทิร์น factory/observer/strategy) ที่ค้นพบโดย LLM จาก CLI:
octocode graphrag get-relationships --node-id "src/auth/middleware.rs"
octocode graphrag find-path --source-id "src/auth/mod.rs" --target-id "src/database/mod.rs"
octocode graphrag overview
นี่คือความต่างระหว่าง "ขอโค้ดที่ดูเหมือน auth" กับ "ขอทุกอย่างที่ ขึ้นอยู่กับ โมดูล auth" สำหรับเอเจนต์ที่กำลังรีแฟกเตอร์ คำถามที่สองคือคำถามที่กันไม่ให้อะไรพัง
มีลูกเล่นใหม่ที่เป็นทางเลือก: การขยายการดึงข้อมูลที่รู้จักกราฟ ด้วย [search] graph_expansion = true (และเปิด GraphRAG) การค้นหาจะดึงบล็อกโค้ดจากไฟล์ที่สัมพันธ์เชิงโครงสร้างกับผลลัพธ์อันดับต้นของคุณเข้ามา ก่อน การรีแรงก์ ดังนั้นคำค้นที่ตกลงบน auth-middleware อาจดึงตัวช่วย JWT ที่มันเรียกขึ้นมาได้ แม้ตัวช่วยนั้นจะไม่จับคู่กับข้อความคำค้นก็ตาม มันปิดไว้ตามค่าเริ่มต้น และคอมเมนต์ในโค้ดก็ตรงไปตรงมา: ทำ A/B บนชุดประเมินของคุณเองก่อนจะไว้ใจ เพราะการขยายอาจเพิ่มสัญญาณรบกวนได้ง่ายพอ ๆ กับสัญญาณ
ต่อเข้ากับเอเจนต์ผ่าน MCP
ทั้งหมดข้างต้นถูกเปิดเผยให้ผู้ช่วย AI ผ่าน เซิร์ฟเวอร์ MCP ในตัว นี่คือวิธีหลักในการใช้ Octocode — เอเจนต์ได้เครื่องมือ ไม่ใช่เชลล์
octocode mcp --path /path/to/your/project
หรือในคอนฟิกของไคลเอนต์ (Claude Desktop, Cursor, Windsurf, Claude Code):
{
"mcpServers": {
"octocode": {
"command": "octocode",
"args": ["mcp", "--path", "/path/to/your/project"]
}
}
}
เครื่องมือที่เอเจนต์เห็น ตรวจสอบกับเซิร์ฟเวอร์แล้ว:
| เครื่องมือ MCP | ทำอะไร |
|---|---|
semantic_search |
การค้นหาไฮบริดเชิงความหมาย + คีย์เวิร์ด มัลติคำค้น ทุกโหมดรวมถึง commits |
view_signatures |
โครงสร้างไฟล์ (ลายเซ็น คลาส อิมพอร์ต) ตาม glob โดยไม่ต้องอ่านไฟล์ทั้งไฟล์ |
structural_search |
การจับคู่แพตเทิร์น AST ผ่าน ast-grep พร้อมการกู้คืนด้วย kind |
graphrag |
คำค้นความสัมพันธ์: search, get-node, get-relationships, find-path, overview |
view_signatures คือฮีโร่ที่ไม่ได้รับการกล่าวถึง แทนที่เอเจนต์จะเผาบริบทด้วยการอ่านไฟล์ 800 บรรทัดสามไฟล์เพื่อหาลายเซ็นฟังก์ชัน มันขอลายเซ็นตาม glob แล้วได้โครงร่างมา (ในเวอร์ชันล่าสุดคุณส่งสตริง glob เดี่ยวได้ ไม่ใช่แค่อาร์เรย์ — เรื่องเล็ก ๆ ที่หยุดการเรียกเครื่องมือที่ผิดรูปแบบไปได้เยอะ)
สำหรับโมโนรีโปที่มีหลายโปรเจกต์ย่อย มีโหมดมัลติรีโป: octocode mcp --multi --path /workspace สแกนไดเรกทอรีย่อยชั้นแรกหารีโป git แล้วให้บริการทั้งหมดจากเอนด์พอยต์เดียว โดยแต่ละเครื่องมือจะได้อาร์กิวเมนต์ project ไว้เลือกเป้าหมาย เซิร์ฟเวอร์ MCP เดียว รีโปทุกตัวในเวิร์กสเปซ
สี่สิ่งที่เล่นงานเรา
เป็นเรื่องจริง ไม่ใช่สมมติ — นี่คือสิ่งที่ทำให้เราเสียเวลา
1. ดัชนีอยู่นอกรีโป และมันทำเอาทุกคนงง ครั้งแรกที่มีคนรันเอเจนต์บนโคลนใหม่เอี่ยม มันไม่มีดัชนี: ฐานข้อมูลอยู่แบบต่อโปรเจกต์ใต้ ~/.local/share/octocode/ ไม่ใช่ใน working tree โคลน ≠ จัดทำดัชนี ทางแก้คือบรรทัดเดียวในการ onboarding: หลังโคลน ให้รัน octocode index หนึ่งครั้ง ชัดเจนเมื่อมองย้อนกลับ แต่ไม่ชัดเลยตอนตีสอง
2. การเปลี่ยนโมเดลเอ็มเบดดิงทำให้ผลลัพธ์แย่ลงอย่างเงียบ ๆ มีคนเปลี่ยน code_model เพื่อลองโมเดลโลคัลตัวอื่น แต่ ไม่ได้ ล้างดัชนี คำค้นใหม่ถูกเอ็มเบดด้วยโมเดลใหม่ ชังก์เก่าถูกเอ็มเบดด้วยตัวเก่า เวกเตอร์เทียบกันไม่ได้ การจัดอันดับเป็นขยะ เวกเตอร์จากต่างโมเดลไม่ได้อยู่ในปริภูมิเดียวกัน เปลี่ยนโมเดลเอ็มเบดดิงเมื่อไหร่ ให้ octocode clear && octocode index เสมอ ไม่มีคำเตือนใดมาช่วยคุณตรงนี้ — มันแค่ค่อย ๆ แย่ลงเงียบ ๆ
3. สัดส่วนเวกเตอร์/คีย์เวิร์ดเริ่มต้นไม่เหมาะกับรีโปของเรา เรารันด้วย 0.7/0.3 (เน้นเวกเตอร์) อยู่หนึ่งสัปดาห์และพลาดคำค้นตัวระบุแบบตรงตัวอยู่เรื่อย โค้ดหนาแน่นด้วยตัวระบุ เบนช์มาร์กข้างบนแสดงว่าน้ำหนักที่จูนเข้าหาคีย์เวิร์ดดีกว่ามากสำหรับการค้นหาโค้ด การเปลี่ยนไปใช้ 0.3/0.7 คือการปรับคอนฟิกที่ให้ผลคุ้มที่สุดที่เราทำ
4. ไฟล์ไบนารีและไฟล์ที่ถูกเจเนอเรตไม่ถูกจัดทำดัชนี — ซึ่งถูกต้องแล้ว แต่ตรวจดูให้แน่ Octocode ข้ามไฟล์ไบนารี (มันตรวจ null byte และอัตราส่วนอักขระที่พิมพ์ได้ก่อนจะถือว่าเนื้อหาเป็นข้อความ) และเคารพ .gitignore, .git/info/exclude และไฟล์ .noindex นั่นคือสิ่งที่คุณต้องการพอดี — คุณไม่อยากได้เอ็มเบดดิงของบันเดิลเวนเดอร์ที่ถูกย่อ แต่ถ้าไดเรกทอรีที่ คุณสนใจ อยู่ใน gitignore (บางทีมใส่ API client ที่ถูกเจเนอเรตไว้ใน gitignore) มันจะไม่ถูกจัดทำดัชนี แล้วคุณจะงงว่าทำไมค้นไม่เจอ วาง .noindex เพื่อ ยกเว้น เส้นทางเพิ่มเติม ตรวจ .gitignore ถ้าบางอย่างที่คาดหวังไว้หายไป octocode stats จะแสดงจำนวนบล็อกให้คุณตรวจสอบความครอบคลุม
การตั้งค่าแบบ end-to-end ขั้นต่ำ
ประกอบรวมกัน นี่คือทั้งหมดสำหรับโมโนรีโปส่วนตัว แบบโลคัลล้วน:
# ~/.local/share/octocode/config.toml (หรือ override แบบโลคัลของโปรเจกต์)
[embedding]
code_model = "fastembed:jinaai/jina-embeddings-v2-base-code"
text_model = "fastembed:nomic-ai/nomic-embed-text-v1.5"
[search.hybrid]
enabled = true
default_vector_weight = 0.3 # โค้ดหนาแน่นด้วยตัวระบุ → เอนเข้าหาคีย์เวิร์ด
default_keyword_weight = 0.7
[search.reranker]
enabled = true
model = "fastembed:jina-reranker-v2-base-multilingual"
[graphrag]
enabled = true
use_llm = false # ความสัมพันธ์จาก AST อย่างเดียว ไม่เรียก LLM ออฟไลน์ล้วน
cd /path/to/monorepo
octocode index # การรันครั้งแรกดาวน์โหลดโมเดล แล้วจัดทำดัชนี
octocode search "webhook signature verification" # ตรวจความสมเหตุสมผล
octocode watch --quiet & # ทำให้สดอยู่เสมอ
claude mcp add octocode -- octocode mcp --path . # ต่อเข้ากับเอเจนต์
ในโฟลว์นั้นไม่มีอะไรแตะเครือข่ายเลยหลังการดาวน์โหลดโมเดลครั้งเดียว ไม่มี GPU ไม่มีบิลคิดต่อโทเค็น เอเจนต์ที่เคย grep หา "signature" แล้วชี้ผิดไฟล์ ตอนนี้เรียก semantic_search ได้ verify_hmac ขึ้นอันดับหนึ่ง และใช้ view_signatures ยืนยันก่อนจะแตะอะไร
คำถามที่พบบ่อย
ต้องใช้ GPU ไหม? ไม่ เอ็มเบดดิงโลคัลรันบน CPU ผ่าน ONNX Runtime CPU มัลติคอร์รุ่นใหม่ก็เพียงพอ ตัวเอ็มเบดเดอร์แทบไม่เคยเป็นคอขวด
มีโค้ดอะไรออกจากเครื่องของผมไหม? ด้วยโมเดล fastembed:/huggingface: และ graphrag.use_llm = false ไม่มี — การจัดทำดัชนี การค้นหา และการรีแรงก์ ล้วนเป็นโลคัล ผู้ให้บริการเอ็มเบดดิงคลาวด์และฟีเจอร์ GraphRAG/ข้อความคอมมิตที่ขับเคลื่อนด้วย LLM เป็นแบบ opt-in และต้องใช้คีย์อย่างชัดเจน
ดัชนีอยู่ที่ไหน และอยู่ในรีโปของผมไหม? อยู่ใต้ไดเรกทอรีข้อมูลของระบบ คีย์ตามโปรเจกต์ (~/.local/share/octocode/<project-id>/ บน Linux/macOS) ไม่ใช่ใน working tree git clean จะไม่แตะมัน โคลนใหม่จะไม่มีมัน
ฐานข้อมูลใหญ่แค่ไหน? ราว ๆ ~10KB ต่อไฟล์ โดยมีการควอนไทซ์ RaBitQ ให้การบีบอัดเวกเตอร์ ~32x รีโป 10k ไฟล์อยู่ที่หลักสิบถึงหลักร้อย MB ไม่ใช่กิกะไบต์
Incremental หรือรีอินเด็กซ์เต็ม? Incremental ตามค่าเริ่มต้น — เฉพาะไฟล์ที่เปลี่ยนถูกเอ็มเบดใหม่ บังคับสร้างใหม่เต็มด้วย octocode index --force หรือ octocode clear && octocode index หลังเปลี่ยนโมเดลเอ็มเบดดิง
ค้นประวัติ git ได้ไหม? ได้ octocode search "..." --mode commits คอมมิตถูกจัดทำดัชนีแบบ lazy เมื่อค้นคอมมิตครั้งแรก octocode index แรกจึงยังเร็วอยู่
— Don
Octocode เป็นโอเพนซอร์สภายใต้ Apache-2.0 สแตกแบบ local-first มาถึงใน 0.15.0 และรายการผู้ให้บริการเอ็มเบดดิงโลคัลใน 0.17.x ถ้าการตั้งค่าของคุณเจอกับดักที่โพสต์นี้ไม่ได้ครอบคลุม เปิด issue — นั่นคือวิธีที่เคล็ดลับเหล่านี้ส่วนใหญ่ถูกบันทึกไว้ตั้งแต่แรก



