Agent Surface

Agent Retrieval

Retrieval Evals

Metrics, CI gates, and common failure modes for retrieval quality - condensed current practice

Last verified 2026-10-29

Retrieval quality fails silently - a bad chunking change or a stale doesn't throw an error, it just returns worse results. Measure it explicitly rather than trusting that it still works.

Metrics

MetricWhat it measuresA reasonable first gate
Recall@kWhether known-relevant docs appear in the top krecall_at_10 >= 0.8
nDCG@kWhether more relevant docs rank higherndcg_at_10 >= 0.8
MRRHow quickly the first relevant result appearsmrr >= 0.7
FaithfulnessWhether the generated answer is supported by retrieved contextGate high-risk answers
Latency p95Whether retrieval fits the product's SLASet per endpoint
Cost per queryWhether agentic or multi-hop retrieval stays affordableSet a budget per workflow
function recallAtK(retrievedIds: string[], relevantIds: Set<string>, k = 10) {
  const retrieved = retrievedIds.slice(0, k);
  return retrieved.filter((id) => relevantIds.has(id)).length / Math.max(1, relevantIds.size);
}

Use RAGAS for standardized scoring, plus a domain-specific set of 100–1,000 labelled queries - a 10-query test set produces noisy metrics and false confidence in changes.

CI gate

describe("retrieval quality", () => {
  test("keeps labelled answers retrievable", async () => {
    const cases = loadRetrievalCases();
    const scores = await Promise.all(
      cases.map(async (item) => {
        const results = await retrieveForAnswer(item.query);
        return recallAtK(
          results.map((r) => r.id),
          item.relevantIds,
        );
      }),
    );
    const meanRecall = scores.reduce((sum, s) => sum + s, 0) / scores.length;
    expect(meanRecall).toBeGreaterThanOrEqual(0.8);
  });
});

Drift monitoring

Re-embed a stable document sample monthly or after a model change, and compare nearest-neighbor sets; raw vector similarity can look fine while ranking quietly degrades. Alert when recall drops on labelled queries. Record the embedding model, dimensions, chunker version, and index version with every evaluation run so a regression can be traced to its cause.

Common failure modes

FailureFix
Dense-only retrieval misses exact terms, IDs, filenamesAdd hybrid ( + dense + fusion)
No chunking strategy, anonymous text blobs indexedChunk with source, section, and version metadata
One embedding model for every domainCompare specialized vs. general models with recall@k before committing
No Retrieve broad (50–100), rerank to 5–10
No evaluation at allAdd RAGAS or domain recall@k in CI
Embeddings never refreshed after a model updateVersion embeddings; re-embed on model change; monitor drift
No metadata filtersEnforce tenant and visibility filters before ranking
Naive fixed-size of resultsCap page size; use explicit cursors - see Filters and Pagination
Unbounded agentic retrievalCap sub-queries, tool calls, , and cost per request
No fallback when retrieval returns nothingReturn an explicit "no relevant documents" response rather than generating from empty context

Checklist

  • Hybrid search covered by tests (sparse and dense independently).
  • Reranking measured against a labelled query set.
  • Recall@10 and nDCG@10 gated in CI.
  • Embedding drift detection running on a schedule.
  • Chunk and embedding model versions tracked in metadata.
  • Cost and latency budgets set per retrieval workflow.
  • Reindexing has a rollback path.

See also

On this page