Completed project / research, RAG and agents
RAG: choosing how to search and answer.
I compared retrieval, reranking and generation on financial questions. A decision lab brings together quality, latency and cost, separating project measurements from external data.
The guiding question: which sources actually reach the answer within time and cost constraints?
Finding the right sources
A RAG system searches a corpus for relevant passages and passes them to the model that writes the answer. To assess retrieval, I used the financial subset of MTRAG, which links questions to annotated sources.
Annotations help measure retrieval, but they do not list every passage that could help answer a question.
in the code: recall and nDCG
These are the two measures used throughout the project. Recall counts relevant sources among the top ten results; nDCG also rewards their position, giving more credit to a relevant source near the top.
def recall_at(ranked_ids, relevant, k):
return len(set(ranked_ids[:k]) & relevant) / len(relevant)
def ndcg_at(ranked_ids, relevant, k):
dcg = sum(1 / math.log2(rank + 2)
for rank, pid in enumerate(ranked_ids[:k]) if pid in relevant)
ideal = sum(1 / math.log2(rank + 2) for rank in range(min(k, len(relevant))))
return dcg / ideal if ideal else 0.0
From scripts/run_d1_pilot.py.
A lab for comparing options
The lab compares embedding models, retrieval, reranking and generation. For each option it shows quality, time, cost and data provenance; context constraints filter the alternatives.
Open the lab and try the filters.

How many candidates should be reranked?
Retrieval proposes passages, then a reranker re-examines a subset and moves the most useful ones to the top. I compared 20, 30, 50 and 60 candidates on the same 180 questions, measuring the share of annotated sources that reached the top ten.
I chose 50 candidates: the highest observed result was 48.9%, versus 47.8% at 30. The difference is modest, and the uncertainty intervals overlap.
in the code: passage vectors and search
Each corpus passage is encoded as a vector once. For each question, only the question is encoded and compared with all passage vectors by dot product. The model version is fixed because updated weights would change the results.
self.model = SentenceTransformer(model_name, revision=revision, device="cpu")
# Vettori normalizzati a lunghezza 1: il prodotto scalare è la similarità coseno.
self.embeddings = self.model.encode(
[p.indexed_text for p in passages],
batch_size=batch_size,
normalize_embeddings=True,
)
def search(self, query, top_k=10):
query_vector = self.model.encode(
[self.query_instruction + query], normalize_embeddings=True)[0]
scores = self.embeddings @ query_vector # la domanda contro tutti i brani
order = np.argsort(-scores, kind="stable")[:top_k]
return [RankedPassage(self.passages[i].passage_id, float(scores[i])) for i in order]
From src/retrieval.py.
in the code: the reranker
In the final configuration, I kept models and questions fixed and varied only the number of passages passed to the reranker.
for d in RERANK_DEPTHS: # 20, 30, 50, 60
per_q = []
for i, g in enumerate(gold_ranks):
order = np.argsort(-sc[i][:d], kind="stable")
pos = {int(p): r + 1 for r, p in enumerate(order)}
per_q.append(metrics([pos[r - 1] if r <= d else r for r in g]))
record(f"reranking sui primi {d}", per_q)
Adapted from esperimenti/studio-embedding-retrieval/16_configurazione_finale.py.
From sources to the answer
A source can appear in retrieval results yet be omitted from the context the model reads. I tracked this step and compared generated answers, alongside retrieval metrics.
The configuration with the best average score brought in fewer useful sources for some questions; the resulting answers differed mainly in completeness.
Multi-step search and advance synthesis
In the RAG setup I studied, retrieval precedes the answer. An agent can search again based on what it finds, which can help with multi-hop questions. A WikiLLM prepares cross-document summaries ahead of recurring queries.
The choice depends on the question, the available time and how often sources change.