Get in touch
or send us a question?
CONTACT

Cách Cursor Tạo Chỉ Mục Codebase Siêu Tốc

Cách Cursor lập chỉ mục Codebase nhanh chóng

Cursor, một AI IDE nổi tiếng vừa công bố đạt doanh thu $300M ARR, sử dụng Merkle Tree để lập chỉ mục code nhanh chóng. Bài viết này sẽ đi sâu vào cách họ thực hiện điều đó.

Trước khi đi sâu vào cách Cursor triển khai, hãy cùng tìm hiểu Merkle Tree là gì nhé.

Merkle Tree – Giải thích đơn giản

Merkle Tree là một cấu trúc cây trong đó mỗi nút “lá” (leaf node) được gán nhãn bằng hàm băm mật mã (cryptographic hash) của một khối dữ liệu, và mỗi nút không phải lá (non-leaf node) được gán nhãn bằng hàm băm mật mã của các nhãn từ các nút con của nó. Cấu trúc phân cấp này cho phép phát hiện thay đổi ở bất kỳ cấp độ nào một cách hiệu quả bằng cách so sánh các giá trị hash.

Hãy hình dung Merkle Tree như một hệ thống “lấy dấu vân tay” cho dữ liệu:

  1. Mỗi phần dữ liệu (ví dụ: một file) sẽ có một “dấu vân tay” (hash) độc nhất.
  2. Các cặp “dấu vân tay” này được kết hợp lại để tạo ra một “dấu vân tay” mới.
  3. Quá trình này tiếp diễn cho đến khi bạn chỉ còn một “dấu vân tay” tổng thể duy nhất (gọi là root hash).

Root hash tóm tắt toàn bộ dữ liệu chứa trong từng phần riêng lẻ, đóng vai trò như một cam kết mật mã (cryptographic commitment) cho toàn bộ tập dữ liệu. Vẻ đẹp của phương pháp này nằm ở chỗ, nếu bất kỳ phần dữ liệu nào thay đổi, nó sẽ làm thay đổi tất cả các “dấu vân tay” phía trên nó, và cuối cùng là thay đổi cả root hash.

Merkle Tree | Suman Kundu

Cách Cursor sử dụng Merkle Tree để lập chỉ mục Codebase

Cursor sử dụng Merkle Tree làm thành phần cốt lõi cho tính năng lập chỉ mục Codebase của mình. Theo một bài đăng của người sáng lập Cursor và tài liệu bảo mật, đây là cách nó hoạt động: 

Bước 1: Chia nhỏ Code (Chunking) và xử lý

Cursor trước tiên sẽ chia nhỏ (chunk) các file trong codebase của bạn ngay trên máy tính cục bộ, phân tách code thành các phần có ý nghĩa ngữ nghĩa trước khi bất kỳ quá trình xử lý nào diễn ra.

Bước 2: Xây dựng và đồng bộ hóa Merkle Tree

Khi tính năng lập chỉ mục Codebase được kích hoạt, Cursor sẽ quét thư mục đang mở trong editor và tính toán một Merkle Tree từ các hash của tất cả các file hợp lệ. Merkle Tree này sau đó được đồng bộ hóa với server của Cursor, như đã được mô tả chi tiết trong tài liệu bảo mật của Cursor.

Bước 3: Tạo Embedding

Sau khi các chunk được gửi đến server của Cursor, các embedding sẽ được tạo ra bằng cách sử dụng API embedding của OpenAI hoặc một mô hình embedding tùy chỉnh (tôi chưa thể xác minh điều này). Các biểu diễn vector này sẽ nắm bắt ý nghĩa ngữ nghĩa của các chunk code.

Bước 4: Lưu trữ và lập chỉ mục

Các embedding, cùng với metadata như số dòng bắt đầu/kết thúc và đường dẫn file, được lưu trữ trong một vector database từ xa (Turbopuffer). Để duy trì quyền riêng tư trong khi vẫn cho phép lọc theo đường dẫn, Cursor lưu trữ một đường dẫn file tương đối đã được làm mờ (obfuscated relative file path) cùng với mỗi vector. Quan trọng hơn, theo người sáng lập Cursor, “Không có đoạn code nào của bạn được lưu trữ trong database của chúng tôi. Nó sẽ biến mất sau khi yêu cầu được xử lý xong.”

Bước 5: Cập nhật định kỳ bằng Merkle Tree

Cứ mỗi 10 phút, Cursor sẽ kiểm tra các hash không khớp (hash mismatches), sử dụng Merkle Tree để xác định những file nào đã thay đổi. Chỉ những file đã thay đổi mới cần được tải lên, giúp giảm đáng kể mức sử dụng băng thông, như đã giải thích trong tài liệu bảo mật của Cursor. Đây chính là nơi cấu trúc Merkle Tree thể hiện giá trị lớn nhất của nó—cho phép cập nhật gia tăng (incremental updates) hiệu quả.

Các chiến lược chia nhỏ Code (Code Chunking Strategies)

Hiệu quả của việc lập chỉ mục codebase phần lớn phụ thuộc vào cách code được chia nhỏ (chunk). Mặc dù giải thích trước đó của tôi chưa đi sâu vào các phương pháp chunking, nhưng bài blog post này về xây dựng tính năng codebase tương tự Cursor có đưa ra một số thông tin chi tiết thú vị:

Trong khi các phương pháp đơn giản chỉ chia code theo ký tự, từ hoặc dòng, chúng thường bỏ qua các ranh giới ngữ nghĩa—dẫn đến chất lượng embedding bị giảm sút.

  • Bạn có thể chia code dựa trên số lượng token cố định, nhưng điều này có thể cắt đứt các khối code như hàm (functions) hoặc lớp (classes) ở giữa chừng.
  • Một phương pháp hiệu quả hơn là sử dụng một bộ chia thông minh (intelligent splitter) có khả năng hiểu cấu trúc code, chẳng hạn như các bộ chia văn bản đệ quy (recursive text splitters) sử dụng các dấu phân cách cấp cao (ví dụ: định nghĩa lớp và hàm) để chia tại các ranh giới ngữ nghĩa phù hợp.
  • Một giải pháp tinh tế hơn nữa là chia code dựa trên cấu trúc Cây cú pháp trừu tượng (Abstract Syntax Tree – AST) của nó. Bằng cách duyệt AST theo chiều sâu (depth-first), nó sẽ chia code thành các cây con (sub-tree) phù hợp với giới hạn token. Để tránh tạo quá nhiều chunk nhỏ, các nút anh em (sibling nodes) sẽ được gộp vào các chunk lớn hơn miễn là chúng vẫn nằm trong giới hạn token. Các công cụ như tree-sitter có thể được sử dụng cho việc phân tích cú pháp AST này, hỗ trợ nhiều loại ngôn ngữ lập trình.

Cách các Embedding được sử dụng tại thời điểm suy luận (Inference Time)

Sau khi đã tìm hiểu cách Cursor tạo và lưu trữ các code embedding, một câu hỏi tự nhiên sẽ nảy sinh: các embedding này thực sự được sử dụng như thế nào sau khi chúng đã được tạo ra? Phần này sẽ giải thích ứng dụng thực tế của các embedding này trong quá trình sử dụng thông thường.

Tìm kiếm ngữ nghĩa và Truy xuất ngữ cảnh (Semantic Search and Context Retrieval)

Khi bạn tương tác với các tính năng AI của Cursor, chẳng hạn như đặt câu hỏi về codebase của mình (sử dụng @Codebase hoặc ⌘ Enter), quá trình sau sẽ diễn ra:

  1. Tạo Embedding truy vấn (Query Embedding): Cursor sẽ tính toán một embedding cho câu hỏi của bạn hoặc cho ngữ cảnh code mà bạn đang làm việc.
  2. Tìm kiếm tương đồng Vector (Vector Similarity Search): Embedding truy vấn này được gửi đến Turbopuffer (vector database của Cursor), nơi thực hiện tìm kiếm hàng xóm gần nhất (nearest-neighbor search) để tìm các chunk code có ý nghĩa ngữ nghĩa tương tự với truy vấn của bạn.
  3. Truy cập File cục bộ: Client của Cursor nhận kết quả, bao gồm các đường dẫn file đã được làm mờ và phạm vi dòng của các chunk code liên quan nhất. Điều quan trọng là, nội dung code thực tế vẫn nằm trên máy của bạn và được truy xuất cục bộ.
  4. Tập hợp ngữ cảnh (Context Assembly): Client đọc các chunk code liên quan này từ các file cục bộ của bạn và gửi chúng dưới dạng ngữ cảnh (context) đến server để LLM xử lý cùng với câu hỏi của bạn.
  5. Phản hồi thông minh: LLM giờ đây đã có ngữ cảnh cần thiết từ codebase của bạn để cung cấp câu trả lời chính xác và phù hợp hơn cho câu hỏi của bạn hoặc để tạo ra các đoạn code hoàn chỉnh thích hợp.

Khả năng truy xuất được hỗ trợ bởi embedding này cho phép:

  1. Tạo code theo ngữ cảnh (Contextual Code Generation): Khi viết code mới, Cursor có thể tham chiếu các cách triển khai tương tự trong codebase hiện có của bạn, giúp duy trì các mẫu và phong cách nhất quán.
  2. Hỏi đáp Codebase (Codebase Q&A): Bạn có thể đặt câu hỏi về codebase của mình và nhận được câu trả lời dựa trên code thực tế của bạn, thay vì các phản hồi chung chung.
  3. Tự động hoàn thành code thông minh (Smart Code Completion): Các tính năng tự động hoàn thành code có thể được nâng cao nhờ khả năng nhận biết các quy ước và mẫu đặc trưng của dự án bạn.
  4. Tái cấu trúc code thông minh (Intelligent Refactoring): Khi tái cấu trúc code, hệ thống có thể xác định tất cả các phần liên quan trong toàn bộ codebase của bạn có thể cần những thay đổi tương tự.