Chuyển tới nội dung
cd /blog

Tìm kiếm hợp nhất đa phương thức: Chọn đúng bộ truy xuất cho mọi truy vấn

[Tìm kiếm] [Kiến trúc]

> Một truy vấn như 'parseConfig được định nghĩa ở đâu' cần một kiểu tìm kiếm khác với 'auth hoạt động như thế nào'. Maguyva phân loại ý định, gán trọng số cho bốn phương thức truy xuất tương ứng, và hợp nhất kết quả bằng Reciprocal Rank Fusion có trọng số.

Ghi chú về thiết kế trước đây: bài viết tháng 4 năm 2026 này mô tả thiết kế định tuyến tại thời điểm đó, không phải hành vi mặc định hiện tại. Phần đính chính tháng 9 bên dưới chỉ xử lý một tuyên bố về đo lường. Để biết định tuyến hiện tại, các cấu hình có sẵn và cách điều khiển tường minh từng phương thức truy xuất, xem Cách hoạt động.

Một truy vấn tìm kiếm không phải là một thứ duy nhất.

“parseConfig được định nghĩa ở đâu” muốn một symbol chính xác — một vị trí chuẩn xác, thật nhanh. “authentication hoạt động như thế nào” muốn ý nghĩa — sự trải rộng của mã liên quan giải thích một khái niệm. “điều gì sẽ hỏng nếu tôi thay đổi hàm này” muốn đồ thị phụ thuộc. “tìm chuỗi ECONNREFUSED” muốn một khớp nguyên văn, không cần thông minh gì cả.

Grep rất xuất sắc cho các khớp nguyên văn và hữu ích cho một số cuộc săn tìm tham chiếu, nhưng nó không phải là đồ thị phụ thuộc và không hiểu ý nghĩa. Embedding bao phủ phía ngữ nghĩa, nhưng chúng là công cụ sai cho chuỗi chính xác và phân tích tác động. Hầu hết các công cụ tìm kiếm mã nguồn chọn một engine duy nhất và bắt mọi truy vấn phải sống với lựa chọn đó. Maguyva không chọn một. Nó tìm ra loại câu hỏi bạn vừa đặt, rồi pha trộn bốn bộ truy xuất theo đúng tỷ lệ mà câu hỏi đó xứng đáng.

Bốn phương thức

Bên dưới có bốn cách độc lập để tìm mã nguồn:

  • ngữ nghĩa (semantic) — tìm kiếm vector trên embedding nhị phân của Voyage; tìm mã theo ý nghĩa.
  • văn bản (text) — so khớp trigram; tìm chuỗi nguyên văn, chuỗi lỗi, định danh chính xác.
  • cấu trúc (structural) — truy vấn AST; tìm định nghĩa, chữ ký hàm, và các cấu trúc ngôn ngữ.
  • đồ thị (graph) — đồ thị phụ thuộc; tìm nơi gọi, nơi được gọi, và phạm vi ảnh hưởng.

Mỗi phương thức mạnh ở một loại câu hỏi khác nhau. Bí quyết là quyết định nên tin tưởng mỗi phương thức đến mức nào cho truy vấn đang có trước mặt bạn.

Phân loại ý định

Trước khi bất kỳ lượt truy xuất nào chạy, một bộ phân loại nhẹ sắp xếp truy vấn vào một trong sáu ý định, kèm điểm tin cậy. Nó được thiết kế rẻ một cách có chủ đích — các heuristic có thứ tự, khớp đầu tiên thắng — vì nó chạy trên đường nóng (hot path) và chỉ thêm một hoặc hai mili giây:

  • bắt đầu bằng def , class , func , import … → find_definition (độ tin cậy 0,95)
  • “who calls”, “usages of”, “references to” (ai gọi, các nơi sử dụng, tham chiếu đến) → find_references (0,90)
  • “impact”, “blast radius”, “what depends on” (tác động, phạm vi ảnh hưởng, những gì phụ thuộc vào) → impact_analysis (0,90)
  • một "string" trong dấu ngoặc kép hoặc một token lỗi như traceback → exact_match (0,85–0,90)
  • một định danh CamelCase hoặc snake_case → find_definition (0,60–0,80)
  • “how”, “why”, “explain”, “architecture” (như thế nào, vì sao, giải thích, kiến trúc) → understand_code (0,75)
  • không gì khớp cả → understand_code, độ tin cậy thấp (0,40)

Mỗi ý định mang một hồ sơ trọng số trên bốn phương thức. Đây là những con số thực tế:

Ý định ngữ nghĩa văn bản cấu trúc (AST) đồ thị
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

Vì vậy “parseConfig được định nghĩa ở đâu” nghiêng mạnh về AST (0,6). “auth hoạt động như thế nào” nghiêng về vector ngữ nghĩa (0,5). “cái gì phụ thuộc vào cái này” gần như hoàn toàn là đồ thị (0,7). “tìm ECONNREFUSED” gần như hoàn toàn là trigram (0,9), với mô hình embedding bị tắt hoàn toàn — vì độ tương đồng ngữ nghĩa chính xác là công cụ sai cho một chuỗi chính xác.

Đường nhanh, và đường hợp nhất

Khi bộ phân loại tự tin — điểm số ≥ 0,85 — và truy vấn là một truy vấn bình thường, Maguyva bỏ qua việc hợp nhất hoàn toàn và định tuyến thẳng đến phương thức chiếm ưu thế duy nhất. “X được định nghĩa ở đâu” không cần bốn bộ truy xuất; nó cần chỉ mục AST, ngay lập tức. Đường đi trực tiếp đó được báo cáo lại dưới dạng fusion_strategy: "direct".

Mọi thứ mơ hồ đều đi qua quá trình hợp nhất. Bốn (hoặc ba, với preset mặc định) phương thức chạy song song, mỗi phương thức trả về danh sách xếp hạng riêng, và chúng tôi kết hợp chúng lại.

Reciprocal Rank Fusion có trọng số

Hợp nhất các bộ truy xuất không đồng nhất khó hơn vẻ ngoài của nó: một độ tương đồng cosine 0,82 và một điểm trigram 137 và một độ trung tâm đồ thị 0,004 không nằm trên cùng một thang đo, nên bạn không thể chỉ đơn giản cộng chúng lại. Reciprocal Rank Fusion né tránh vấn đề này bằng cách bỏ đi các điểm số thô và chỉ giữ lại thứ hạng mà mỗi engine gán cho. Đóng góp của một kết quả từ một phương thức là:

contribution = weight × 1 / (k + rank + 1)

trong đó rank là vị trí của nó trong danh sách của phương thức đó và k là một hằng số làm mượt. Các đóng góp được cộng dồn qua các phương thức cho bất kỳ kết quả nào được nhiều hơn một engine tìm thấy — sự đồng thuận giữa các bộ truy xuất tự nhiên sẽ nổi lên trên cùng. Chúng tôi dùng k = 40 trên preset mặc định và 60 trên thorough (quick chỉ chạy ngữ nghĩa thuần túy, nên việc hợp nhất không bao giờ xảy ra ở đó). Nghiên cứu RRF gốc chốt ở k = 60 cho truy xuất đa dụng; giá trị mặc định của chúng tôi nhấn mạnh hơn một chút vào các kết quả được nhiều phương thức cùng xếp hạng cao — và chúng tôi không khuyến nghị tự tay tinh chỉnh nó.

Bên trên đó, các kết quả còn mang một hệ số tăng cường theo tầm quan trọng đồ thị. Một hub — một hàm mà toàn bộ codebase dựa vào — nên được xếp hạng cao hơn một nút lá ít nổi bật ngay cả khi có cùng độ liên quan văn bản, nên chúng tôi nhân mỗi đóng góp với:

boost = min(1 + 0.3 × ln(1 + centrality), 1.5)

Độ trung tâm đến từ các chỉ số PageRank/degree đã được tính toán trước của pipeline, và hệ số tăng cường bị giới hạn ở mức 1,5× để một hàm phổ biến không thể hoàn toàn chôn vùi một hàm ít nổi bật nhưng liên quan hơn. Cuối cùng, chúng tôi hạ thấp thứ hạng các kết quả từ đường dẫn vendor, build, và archive, và loại bỏ trùng lặp để chỉ giữ đoạn tốt nhất cho mỗi file.

Những gì vẫn chưa hoàn hảo

Bộ phân loại ý định là một tập hợp biểu thức chính quy, không phải mô hình đã huấn luyện. Nó xử lý các dạng truy vấn phổ biến bằng heuristic, còn truy vấn mơ hồ sẽ dùng understand_code và một tổ hợp thiên về ngữ nghĩa. Phiên bản trước của bài viết này mô tả việc giảm tỷ lệ truy vấn không có kết quả từ khoảng 15% xuống dưới 5% như một kết quả đã đạt được. Thực ra, đề xuất tháng 12 năm 2025 chỉ nêu mức ban đầu ước tính và một mục tiêu; không có tập truy vấn hay biên bản đo lường nào được cung cấp để chứng minh. Chúng tôi rút lại tuyên bố đã đạt được cải thiện đó; xem bằng chứng nguồn và đính chính (tiếng Anh). Chúng tôi chưa thay bộ phân loại bằng mô hình đã huấn luyện vì phiên bản chi phí thấp đủ nhanh và đủ tốt cho vai trò định tuyến dự kiến, và vì một bộ phân loại sai nhưng tự tin còn tệ hơn một phương án dự phòng trung thực. Các trọng số là những giá trị tiên nghiệm được chọn thủ công, không phải học từ dữ liệu nhấp chuột mà chúng tôi không thu thập.

Đặt câu hỏi, không cần chọn công cụ

Một agent không nên phải biết là nên dùng grep hay embedding hay đồ thị lệnh gọi — nó nên đặt câu hỏi bằng ngôn ngữ đơn giản và nhận được câu trả lời đúng. Hợp nhất đa phương thức là thứ cho phép find_symbol, tìm kiếm ngữ nghĩa, và phân tích phụ thuộc cùng nằm sau một giao diện truy vấn duy nhất: hệ thống đọc hình dạng của câu hỏi và âm thầm ghép lại đúng bộ truy xuất cho nó. Mô hình chấm điểm embedding quan trọng, nhưng biết khi nào không nên dùng chúng cũng quan trọng không kém. Chọn đúng công cụ cho mỗi truy vấn là một loại chất lượng riêng, và đó là thứ chúng tôi muốn tự mình đảm nhận thay vì đẩy sang cho bên gọi.

// you bring the question. it brings the tools.

Đọc thêm liên quan

Thêm từ nhật ký xây dựng Maguyva