ค้นหาแบบผสานหลายโหมด: เลือกตัวค้นคืนที่ใช่สำหรับทุกคำค้น
> คำค้นอย่าง 'parseConfig ถูกนิยามที่ไหน' ต้องการการค้นหาที่ต่างจาก 'auth ทำงานอย่างไร' Maguyva จำแนกเจตนา ให้น้ำหนักทั้งสี่รูปแบบการค้นคืนตามนั้น แล้วรวมผลลัพธ์ด้วย weighted Reciprocal Rank Fusion
คำค้นหาไม่ใช่สิ่งเดียว
“parseConfig ถูกนิยามที่ไหน” ต้องการ symbol ที่แน่นอน — ตำแหน่งเดียวที่แม่นยำ เร็ว “authentication ทำงานอย่างไร” ต้องการความหมาย — การกระจายของโค้ดที่เกี่ยวข้องซึ่งอธิบายแนวคิด “อะไรจะพังถ้าฉันเปลี่ยนฟังก์ชันนี้” ต้องการ dependency graph “หาสตริง ECONNREFUSED” ต้องการการจับคู่แบบตรงตัว ไม่ต้องมีอะไรฉลาด
Grep ยอดเยี่ยมสำหรับการจับคู่แบบตรงตัวและมีประโยชน์สำหรับการตามหาการอ้างอิงบางอย่าง แต่มันไม่ใช่ dependency graph และมันไม่เข้าใจความหมาย Embedding ครอบคลุมด้าน semantic แต่มันเป็นเครื่องมือที่ผิดสำหรับสตริงตรงตัวและการวิเคราะห์ผลกระทบ เครื่องมือค้นหาโค้ดส่วนใหญ่เลือกเอนจินเดียวและให้ทุกคำค้นอยู่กับตัวเลือกนั้น Maguyva ไม่เลือก มันหาให้ได้ว่าคุณถามคำถามประเภทไหน แล้วผสมตัวค้นคืนสี่ตัวในสัดส่วนที่คำถามนั้นสมควรได้รับ
สี่รูปแบบ
ภายใต้ระบบมีสี่วิธีอิสระในการหาโค้ด:
- semantic — การค้นหาเวกเตอร์บน Voyage binary embedding หาโค้ดตามความหมาย
- text — การจับคู่ trigram หาข้อความตรงตัว, สตริงข้อผิดพลาด, ตัวระบุที่แน่นอน
- structural — AST คิวรี หาคำนิยาม, ซิกเนเจอร์ และโครงสร้างภาษา
- graph — dependency graph หาผู้เรียก, สิ่งที่ถูกเรียก และรัศมีผลกระทบ
แต่ละอย่างแข็งแกร่งในคำถามคนละประเภท เคล็ดลับคือการตัดสินใจว่าจะเชื่อแต่ละอย่างมากแค่ไหนสำหรับคำค้นที่อยู่ตรงหน้าคุณ
การจำแนกเจตนา
ก่อนที่การค้นคืนใดจะรัน ตัวจำแนกแบบเบาจะจัดคำค้นให้อยู่ในหนึ่งในหกเจตนา พร้อมคะแนนความมั่นใจ มันตั้งใจให้ราคาถูก — ฮิวริสติกที่เรียงลำดับ การจับคู่แรกชนะ — เพราะมันรันบนเส้นทางที่ใช้งานบ่อย และเพิ่มเวลาแค่หนึ่งหรือสองมิลลิวินาที:
- เริ่มด้วย
def,class,func,import… → find_definition (ความมั่นใจ 0.95) - “ใครเรียก”, “การใช้งานของ”, “การอ้างอิงถึง” → find_references (0.90)
- “ผลกระทบ”, “รัศมีระเบิด”, “อะไรพึ่งพา” → impact_analysis (0.90)
"string"ที่ใส่เครื่องหมายคำพูด หรือโทเคนข้อผิดพลาดอย่างtraceback→ exact_match (0.85–0.90)- ตัวระบุแบบ
CamelCaseหรือsnake_case→ find_definition (0.60–0.80) - “อย่างไร”, “ทำไม”, “อธิบาย”, “สถาปัตยกรรม” → understand_code (0.75)
- ไม่มีอะไรตรง → understand_code ความมั่นใจต่ำ (0.40)
แต่ละเจตนามีโปรไฟล์น้ำหนักข้ามสี่รูปแบบ นี่คือตัวเลขจริง:
| เจตนา | semantic | text | structural (AST) | graph |
|---|---|---|---|---|
| find_definition | 0.2 | 0.1 | 0.6 | 0.1 |
| find_references | 0.1 | 0.2 | 0.2 | 0.5 |
| understand_code | 0.5 | 0.2 | 0.2 | 0.1 |
| find_similar | 0.4 | 0.3 | 0.2 | 0.1 |
| impact_analysis | 0.1 | 0.1 | 0.1 | 0.7 |
| exact_match | 0.0 | 0.9 | 0.1 | 0.0 |
ดังนั้น “parseConfig ถูกนิยามที่ไหน” จะเอนหนักไปทาง AST (0.6) “auth ทำงานอย่างไร” เอนไปทาง semantic เวกเตอร์ (0.5) “อะไรพึ่งพาสิ่งนี้” แทบทั้งหมดเป็น graph (0.7) “หา ECONNREFUSED” แทบทั้งหมดเป็น trigram (0.9) โดยปิดโมเดล embedding ไปเลย — เพราะความคล้ายคลึงเชิงความหมายเป็นเครื่องมือที่ผิดโดยสิ้นเชิงสำหรับสตริงที่แน่นอน
เส้นทางด่วน และเส้นทางที่ผสมแล้ว
เมื่อตัวจำแนกมั่นใจ — คะแนน ≥ 0.85 — และคำค้นเป็นแบบปกติ Maguyva ข้ามการผสมทั้งหมด แล้วส่งตรงไปยังรูปแบบหลักที่ครองอยู่ “X ถูกนิยามที่ไหน” ไม่ต้องการตัวค้นคืนสี่ตัว มันต้องการดัชนี AST ทันที เส้นทางตรงนั้นถูกรายงานกลับมาเป็น fusion_strategy: "direct"
ทุกอย่างที่คลุมเครือจะผ่านการผสม สี่รูปแบบ (หรือสาม บนพรีเซ็ตเริ่มต้น) รันคู่ขนานกัน แต่ละอย่างคืนรายการจัดอันดับของตัวเอง แล้วเรารวมพวกมัน
Weighted Reciprocal Rank Fusion
การผสมตัวค้นคืนที่แตกต่างกันนั้นยากกว่าที่ฟังดู: ความคล้ายคลึงแบบโคไซน์ 0.82, คะแนน trigram 137 และความเป็นศูนย์กลางของกราฟ 0.004 ไม่ได้อยู่บนมาตราส่วนเดียวกัน ดังนั้นคุณบวกมันตรงๆ ไม่ได้ Reciprocal Rank Fusion หลบเลี่ยงปัญหาด้วยการทิ้งคะแนนดิบไป แล้วเก็บไว้แค่ อันดับ ที่แต่ละเอนจินกำหนดให้ การมีส่วนร่วมของผลลัพธ์หนึ่งจากรูปแบบหนึ่งคือ:
contribution = weight × 1 / (k + rank + 1)
โดยที่ rank คือตำแหน่งของมันในรายการของรูปแบบนั้น และ k คือค่าคงที่ทำให้เรียบ การมีส่วนร่วมจะถูกรวมข้ามรูปแบบสำหรับผลลัพธ์ใดๆ ที่มากกว่าหนึ่งเอนจินพบ — ความสอดคล้องระหว่างตัวค้นคืนลอยขึ้นไปด้านบนตามธรรมชาติ เราใช้ k = 40 บนพรีเซ็ตเริ่มต้น และ 60 บน thorough (quick รันแบบ semantic-only เท่านั้น ดังนั้นการผสมจึงไม่เคยเข้ามาเกี่ยวข้องที่นั่น) งาน RRF ดั้งเดิมลงเอยที่ k = 60 สำหรับการค้นคืนเอนกประสงค์ เราตั้งค่าเริ่มต้นให้คมชัดกว่าเล็กน้อย ซึ่งให้น้ำหนักกับความสอดคล้องระหว่างรูปแบบที่จัดอันดับสูงมากขึ้นเล็กน้อย — และเราไม่แนะนำให้ปรับแต่งด้วยมือ
นอกจากนั้น ผลลัพธ์ยังพก การเพิ่มค่าตามความสำคัญของ graph ฮับ — ฟังก์ชันที่ทั้งโค้ดเบสพึ่งพา — ควรอยู่เหนือโหนดใบที่คลุมเครือแม้ที่ความเกี่ยวข้องเชิงข้อความเท่ากัน ดังนั้นเราคูณการมีส่วนร่วมแต่ละอย่างด้วย:
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
ความเป็นศูนย์กลางมาจากเมตริก PageRank/ดีกรี ที่คำนวณล่วงหน้าของ pipeline และการเพิ่มค่าถูกจำกัดที่ 1.5 เท่า เพื่อไม่ให้ฟังก์ชันยอดนิยม กลบฟังก์ชันที่คลุมเครือแต่เกี่ยวข้องมากกว่าไปเสียหมด สุดท้ายเราลดอันดับผลลัพธ์จาก path ของ vendor, build และ archive และตัดซ้ำเหลือส่วนที่ดีที่สุดต่อไฟล์
สิ่งที่ยังไม่สมบูรณ์แบบ
ตัวจำแนกเจตนาเป็นกองของ regex ไม่ใช่โมเดลที่เรียนรู้ มันครอบคลุมรูปทรงทั่วไปของคำค้นได้ดี — การตัดสินใจที่นำมันเข้ามาบันทึกว่า อัตราผลลัพธ์ศูนย์ลดลงจากราว 15% เหลือต่ำกว่า 5% — แต่มันคือฮิวริสติก และคำค้นที่คลุมเครือจริงๆ จะตกไปที่ understand_code และการผสมที่เอนไป ทาง semantic นั่นคือค่าเริ่มต้นที่ปลอดภัย ไม่ใช่ที่ฉลาด เรายังไม่ได้แทนที่มันด้วยตัวจำแนกที่ฝึกมา เพราะเวอร์ชันราคาถูกนั้นเร็วและดีพอ และเพราะตัวจำแนกที่ผิดแต่มั่นใจนั้นแย่กว่าทางเลือกสำรองที่ซื่อสัตย์ น้ำหนักเองก็เป็น prior ที่เลือกด้วยมือ ไม่ได้เรียนรู้จากข้อมูลคลิก ที่เราไม่ได้เก็บ
ถามคำถาม ไม่ใช่ถามเครื่องมือ
agent ไม่ควรต้องรู้ว่าจะหยิบ grep หรือ embedding หรือ call graph ขึ้นมา — มันควรถามคำถามด้วยคำพูดธรรมดาแล้วได้คำตอบที่ถูกต้อง การผสานหลายโหมดคือสิ่งที่ทำให้ find_symbol, semantic search และการวิเคราะห์ dependency อยู่หลังพื้นผิวคำค้นเดียว: ระบบอ่านรูปทรง ของคำถามแล้วประกอบตัวค้นคืนที่ถูกต้องให้อย่างเงียบๆ โมเดลที่ให้คะแนน embedding สำคัญ แต่การรู้ว่าเมื่อไหร่ ไม่ควร ใช้มันก็สำคัญเช่นกัน การเลือกเครื่องมือที่ถูกต้องสำหรับแต่ละคำค้นเป็นคุณภาพชนิดหนึ่งในตัวมันเอง และเป็นชนิดที่เราอยากรับผิดชอบเองมากกว่าผลักให้ผู้เรียก
// you bring the question. it brings the tools.
อ่านเพิ่มเติมที่เกี่ยวข้อง
เนื้อหาอื่นๆ จากบันทึกการพัฒนา Maguyva
เหตุใดเราจึงอัปเกรดการค้นหาโค้ดเป็น voyage-4-large_
เราย้ายเอ็มเบดดิ้งโค้ดของเราไปที่ voyage-4-large — ปัจจุบันอยู่อันดับหนึ่งของตารางอันดับการค้นคืนโค้ด RTEB สาธารณะ ฉบับตรงไปตรงมา: การแลกเปลี่ยนที่เรายอมรับ สิ่งที่เราทำดัชนีจริง และเหตุผลที่เราจ่ายเงินเพื่อเอ็มเบดดิ้งระดับพรีเมียม
การพัฒนาตนเองเชิงเวียนซ้ำของภาษา: กรินด์ Code Intelligence ครอบคลุมเกือบ 280 ภาษา_
เรารองรับ code intelligence สำหรับเกือบ 280 ภาษา ไม่มีมนุษย์คนไหนตรวจสอบด้วยมือได้ทั้งหมดนั้น เราจึงสร้างลูปการพัฒนาตนเองเชิงเวียนซ้ำของภาษา — สุ่มตรวจ ใช้ LLM เป็นผู้ตัดสิน แก้ไขทีละจุด ตรวจสอบซ้ำ — และรันมันด้วยทีม agent ที่แยกจากกันจนกว่าการแตกโครงสร้างจะถูกต้องจริง ไม่ใช่แค่เขียวผ่าน
การสังเกตการณ์ Agent: Hooks, Alloy และ Grafana_
เราเชื่อมต่อ Claude Code และ Codex เข้ากับ Grafana สแตกเดียวกันด้วย OpenTelemetry และ Alloy จากนั้นใช้ trace และบันทึกเพื่อค้นหาและแก้ไขปัญหาพฤติกรรมของ agent ที่ต้นตอ