How LeanAgent Filters and Selects Relevant Premises During Proof Search

LeanAgent interleaves proof search with dense premise retrieval, using a T5-based encoder to select the top-k most similar premises from the corpus and augmenting the tactic generator's input with these filtered facts.

LeanAgent, developed by the lean-dojo team, implements a retrieval-augmented generation architecture for automated theorem proving in Lean 4. To manage the massive search space of mathematical premises, LeanAgent filters and selects relevant premises during proof search using a learned dense retrieval model that identifies the top-k most relevant facts—typically the top 2% of the corpus—for any given proof state.

The Retrieve-Then-Generate Architecture

LeanAgent's premise selection workflow consists of three tightly coupled components that execute during every step of the proof search:

  1. Context construction – Extracts metadata from the current TacticState to create structured context objects.
  2. Dense retrieval – Encodes the context using a neural model and performs cosine-similarity search against the premise corpus.
  3. Premise filtering and augmentation – Deduplicates and formats the top-k premises for injection into the tactic generator's input.

Step 1: Building Context Objects from Tactic States

The retrieval process begins in prover/proof_search.py, where the proof search algorithm encounters a new tactic state. For each sub-goal in the current state, LeanAgent constructs a Context object containing the theorem's full name, file path, source position (Pos), and the tactic state string.

In retrieval/model.py, the PremiseRetriever.retrieve method (lines 84–114) receives these parameters and instantiates the context:


# From retrieval/model.py, lines 88-95

contexts = [
    Context(file_path, theorem_full_name, theorem_pos, state)
    for state in states
]

Step 2: Dense Retrieval with PremiseRetriever

The PremiseRetriever class in retrieval/model.py implements the core similarity search using a T5 encoder architecture.

Encoding Proof States

The _encode method (lines 68–90) tokenizes the context strings using the T5 tokenizer, passes them through the T5 encoder, and applies L2 normalization to produce dense embeddings:


# From retrieval/model.py, _encode method

def _encode(self, texts: List[str]) -> torch.Tensor:
    tokens = self.tokenizer(
        texts, return_tensors="pt", padding=True, truncation=True
    )
    hidden_states = self.encoder(**tokens.to(self.device)).last_hidden_state
    # Mean pooling and normalization

    embeddings = hidden_states.mean(dim=1)
    embeddings = F.normalize(embeddings, p=2, dim=1)
    return embeddings

Reindexing the Corpus

When the retrieval model is updated during training, the reindex_corpus method (lines 64–78) recomputes embeddings for the entire premise corpus (Corpus) and stores them in self.corpus_embeddings.

Computing Similarity and Selecting Top-k

The retrieval search occurs at line 108 in retrieval/model.py. The PremiseRetriever computes cosine similarity between the encoded context and the corpus embedding matrix, then calls self.corpus.get_nearest_premises to return the k most similar premises:


# From retrieval/model.py, retrieve method (simplified)

context_embeddings = self._encode([str(ctx) for ctx in contexts])

# Cosine similarity via matrix multiplication (embeddings are normalized)

similarities = context_embeddings @ self.corpus_embeddings.T

# Retrieve top-k (default k=100, roughly top 2% of corpus)

retrieved = self.corpus.get_nearest_premises(similarities, k=self.eval_num_retrieved)

The eval_num_retrieved parameter defaults to 100, which represents approximately the top 2% of the premise corpus for typical Mathlib-scale environments.

Step 3: Top-k Filtering and State Augmentation

After retrieval, the RetrievalAugmentedGenerator in generator/model.py processes the premise lists in its batch_generate method (lines 50–63).

Deduplication and Formatting

The generator removes duplicate premises and formats the top-k list using format_augmented_state from common.py. This function inserts the premises as comment blocks into the tactic state string:


# From generator/model.py, batch_generate method

if self.retriever is not None:
    retrieved_premises, _ = self.retriever.retrieve(
        state, file_path, theorem_full_name, theorem_pos,
        self.eval_num_retrieved
    )
    # Augment state with filtered premises

    state = [
        format_augmented_state(s, premises, self.max_inp_seq_len, p_drop=0.0)
        for s, premises in zip_strict(state, retrieved_premises)
    ]

Tactic Generation

With the augmented state containing the filtered premise set, the RetrievalAugmentedGenerator performs beam search (lines 74–84) to generate tactics. The model attends to both the proof state and the retrieved premises during generation, focusing its search on the most relevant mathematical facts.

Code Example: Premise Retrieval in Practice

The following example demonstrates the complete retrieval and filtering pipeline as implemented in RetrievalAugmentedGenerator.batch_generate:


# Inside RetrievalAugmentedGenerator.batch_generate (generator/model.py)

if self.retriever is not None:
    # 1️⃣ Retrieve the most relevant premises for each state

    retrieved_premises, _ = self.retriever.retrieve(
        state,               # list of Lean tactic-state strings

        file_path,
        theorem_full_name,
        theorem_pos,
        self.eval_num_retrieved,   # k = 100 by default (~top 2%)

    )
    # 2️⃣ Augment each state with the top-k premises

    state = [
        format_augmented_state(s, premises, self.max_inp_seq_len, p_drop=0.0)
        for s, premises in zip_strict(state, retrieved_premises)
    ]

The PremiseRetriever.retrieve method in retrieval/model.py (lines 84–114) executes the dense similarity search and returns exactly the top-k premises, implementing the core filtering logic that restricts the tactic generator to the most relevant mathematical facts.

Summary

  • Context Construction: LeanAgent builds Context objects from the current TacticState, capturing the theorem name, file path, source position, and goal state.
  • Dense Encoding: The PremiseRetriever uses a T5 encoder in retrieval/model.py to generate normalized embeddings for proof states and premises.
  • Similarity Search: Cosine similarity against the pre-computed corpus embedding matrix identifies the k-nearest premises (default k=100, approximately top 2% of the corpus).
  • State Augmentation: The format_augmented_state function in common.py injects the filtered top-k premises into the tactic state as comment blocks.
  • Focused Generation: The RetrievalAugmentedGenerator attends to this augmented state during beam search, generating tactics conditioned on the most relevant premises.

Frequently Asked Questions

How many premises does LeanAgent retrieve by default?

LeanAgent retrieves 100 premises by default, controlled by the eval_num_retrieved parameter in PremiseRetriever. For a typical Mathlib-scale corpus, this represents approximately the top 2% of available premises, providing a focused set of relevant facts without overwhelming the tactic generator.

What neural architecture powers LeanAgent's premise retrieval?

LeanAgent uses a T5 encoder architecture implemented in retrieval/model.py. The _encode method tokenizes context strings, processes them through the T5 encoder, applies mean pooling over the hidden states, and L2-normalizes the resulting embeddings to enable efficient cosine similarity search against the premise corpus.

How does LeanAgent prevent duplicate premises from reaching the generator?

After retrieving the top-k premises via get_nearest_premises in retrieval/index.py, the RetrievalAugmentedGenerator.batch_generate method in generator/model.py processes these lists to remove duplicates before calling format_augmented_state. This ensures that the augmented tactic state contains only unique, relevant premises.

Why does LeanAgent use dense retrieval instead of symbolic matching?

LeanAgent uses dense retrieval with learned embeddings rather than symbolic matching because neural encoders capture semantic relationships between proof states and premises that syntactic pattern matching misses. The cosine similarity between T5-encoded vectors identifies premises that are mathematically relevant even when they share no surface-level syntax with the current goal, significantly improving proof search success rates.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →