大規模コードベースの安全なインデックス化
本文の状態
日本語全文を表示中
詳細モードで約3分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
Cursor Blog
チームメイトの既存インデックスを安全に再利用することで、最大規模のリポジトリでも初回クエリ時間を数時間から数秒に短縮。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
大規模コードベースの安全なインデックス化
セマンティック検索は、AIエージェントの性能を大きく左右する要素である。最近の評価では、応答精度が平均12.5%向上し、コード変更がコードベースに残りやすくなり、全体的なリクエスト満足度も上昇したことが確認されている。
Cursorはプロジェクトを開く際、セマンティック検索を可能にするためにコードベースの検索可能なインデックスを構築する。小規模プロジェクトではこれは瞬時に行われるが、数万ファイルを抱える大規模リポジトリでは、単純な方法ではインデックス作成に数時間を要し、作業の80%が完了するまで検索機能は利用できない。
この課題に対し、開発チームは「ほとんどのチームはほぼ同一のコードベースのコピーで作業している」という観察に基づき、インデックス化の高速化を図った。実際、同一組織内のユーザー間では、同じコードベースのクローンは平均92%の類似性を示す。
つまり、新たなメンバーが参加したりマシンを切り替えたりする際に毎回一からインデックスを再構築するのではなく、チームメイトの既存のインデックスを安全に再利用できる。これにより、最大規模のリポジトリでも、初回検索までの待機時間が数時間から数秒に短縮される。
Cursorはコードベースの最初の状態をマークルツリーを用いて把握する。このツリーには各ファイルの暗号学的ハッシュに加え、子要素のハッシュに基づいて計算された各フォルダのハッシュが含まれる。これにより、変更されたファイルやディレクトリを全体を再処理することなく正確に検出できる。
クライアント側での小さな編集が行われた場合、変更されたファイル自体と、ルートに至るまでの親ディレクトリのハッシュのみが更新される。Cursorはこれらのハッシュをサーバー側のバージョンと比較し、両方のマークルツリーがどこで分岐したかを正確に把握する。ハッシュが異なるエントリのみが同期され、一致するものはスキップされる。この同期プロセスでは、クライアント側のファイルが変更されることはない。
マークルツリーのアプローチにより、各同期で転送する必要のあるデータ量が大幅に削減される。5万ファイルのワークスペースでは、ファイル名とSHA-256ハッシュのみで約3.2MBとなるが、ツリー構造を用いることで、ハッシュが異なるブランチのみを走査すれば済む。
ファイルが変更されると、Cursorはそれを構文上のチャンク(塊)に分割する。これらのチャンクはセマンティック検索を可能にする埋め込み表現(エンベディング) に変換される。この埋め込みの作成が計算コストの高いステップであるため、Cursorはこれを非同期でバックグラウンド実行する。
ほとんどの編集では、チャンクの大半は変更されない。Cursorはチャンク内容ごとに埋め込み表現をキャッシュする。変更のないチャンクはキャッシュがヒットするため、推論時に再計算するコストをかけずに、エージェントの応答を高速に保つことができる。結果として
原文を表示
Blog / researchSemantic search is one of the biggest drivers of agent performance. In our recent evaluation, it improved response accuracy by 12.5% on average, produced code changes that were more likely to be retained in codebases, and raised overall request satisfaction.
To power semantic search, Cursor builds a searchable index of your codebase when you open a project. For small projects, this happens almost instantly. But large repositories with tens of thousands of files can take hours to process if indexed naively, and semantic search isn't available until at least 80% of that work is finished.
We looked for ways to speed up indexing based on the simple observation that most teams work from near-identical copies of the same codebase. In fact, clones of the same codebase average 92% similarity across users within an organization.
This means that rather than rebuilding every index from scratch when someone joins or switches machines, we can securely reuse a teammate's existing index. This cuts time-to-first-query from hours to seconds on the largest repos.
Cursor builds its first view of a codebase using a Merkle tree, which lets it detect exactly which files and directories have changed without reprocessing everything. The Merkle tree features a cryptographic hash of every file, along with hashes of each folder that are based on the hashes of its children.
Small client-side edits change only the hashes of the edited file itself and the hashes of the parent directories up to the root of the codebase. Cursor compares those hashes to the server's version to see exactly where the two Merkle trees diverge. Entries whose hashes differ get synced. Entries that match are skipped. Any entry missing on the client is deleted from the server, and any entry missing on the server is added. The sync process never modifies files on the client side.
The Merkle tree approach significantly reduces the amount of data that needs to be transferred on each sync. In a workspace with fifty thousand files, just the filenames and SHA-256 hashes add up to roughly 3.2 MB. Without the tree, you would move that data on every update. With the tree, Cursor walks only the branches where hashes differ.
When a file changes, Cursor splits it into syntactic chunks. These chunks are converted into the embeddings that enable semantic search. Creating embeddings is the expensive step, which is why Cursor does it asynchronously in the background.
Most edits leave most chunks unchanged. Cursor caches embeddings by chunk content. Unchanged chunks hit the cache, and agent responses stay fast without paying that cost again at inference time. The resulting index is fast to update and light to maintain.
#Finding the best index to reuse
The indexing pipeline above uploads every file when a codebase is new to Cursor. New users inside an organization don't need to go through that entire process though.
When a new user joins, the client computes the Merkle tree for a new codebase and derives a value called a similarity hash (simhash) from that tree. This is a single value that acts as a summary of the file content hashes in the codebase.
The client uploads the simhash to the server. The server then uses it as a vector to search in a vector database composed of all the other current simhashes for all other indexes in Cursor in the same team (or from the same user) as the client. For each result returned by the vector database, we check whether it matches the client similarity hash above a threshold value. If it does, we use that index as the initial index for the new codebase.
This copy happens in the background. In the meantime, the client is allowed to make new semantic searches against the original index being copied, resulting in a very quick time-to-first-query for the client.
But this only works if two constraints hold. Results need to reflect the user's local codebase, even when it differs from the copied index. And the client can never see results for code it doesn't already have.
To guarantee that files won't leak across copies of the codebase, we reuse the cryptographic properties of the Merkle tree.
Each node in the tree is a cryptographic hash of the content beneath it. You can only compute that hash if you have the file. When a workspace starts from a copied index, the client uploads its full Merkle tree along with the similarity hash. This associates a hash with each encrypted path in the codebase.
The server stores this tree as a set of content proofs. During search, the server filters results by checking those hashes against the client's tree. If the client can't prove it has a file, the result is dropped.
This allows the client to query immediately and see results only for code it shares with the copied index. The background sync reconciles the remaining differences. Once the client and server Merkle tree roots match, the server deletes the content proofs and future queries r
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み