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ố.

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)
  • “ai gọi”, “các lần dùng của”, “tham chiếu đến” → find_references (0,90)
  • “tác động”, “phạm vi ảnh hưởng”, “cái 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ư tracebackexact_match (0,85–0,90)
  • một định danh CamelCase hoặc snake_casefind_definition (0,60–0,80)
  • “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; chúng tôi mặc định sắc hơn một chút, điều này cho sự đồng thuận ở thứ hạng cao giữa các phương thức thêm một chút trọng số — 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 lá cây mù mờ 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 mù mờ 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 chồng regex, không phải một mô hình đã học. Nó bao phủ tốt các hình dạng truy vấn phổ biến — quyết định giới thiệu nó đã ghi nhận tỷ lệ không có kết quả giảm từ khoảng 15% xuống dưới 5% — nhưng nó mang tính heuristic, và một truy vấn thực sự mơ hồ sẽ rơi xuống understand_code và một hỗn hợp nghiêng về ngữ nghĩa. Đó là một mặc định an toàn, không phải một mặc định thông minh. Chúng tôi chưa thay nó bằng một bộ phân loại đã huấn luyện vì phiên bản rẻ này đủ nhanh và đủ tốt, 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 thành thật. Bản thân các trọng số là những giá trị ưu tiên được chọn thủ công, không phải học từ dữ liệu click mà chúng tôi không thu thập.

Hỏi câu hỏi, đừng hỏi 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 bề mặt 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