n4nAI

Context relevance scoring for RAG pipeline monitoring

Context relevance scoring RAG measures whether retrieved documents actually answer the query. This guide explains the metric, implementation, and pitfalls.

n4n Team4 min read971 words

Audio narration

Coming soon — every post will get a voice note here.

Context relevance scoring RAG is the process of quantifying how well retrieved passages align with the user’s query before those passages are fed to a generator. It produces a numeric or categorical signal that tells you whether your retriever surfaced useful context, independent of the final answer quality.

What context relevance scoring actually measures

Retrieval-augmented generation splits into two failure domains: retrieval and generation. Context relevance scoring RAG isolates the first. It asks a single question: given the query, would a human judge consider this chunk worth reading to answer it?

The signal is not answer correctness. A passage can be perfectly relevant and still lead to a wrong final answer because the generator misread it. Conversely, a generated answer can be correct while citing irrelevant context through lucky reasoning. Scoring relevance decouples these concerns so you can localize regressions.

In practice the score is either a float in [0,1], a 1–5 rating, or a binary label. The granularity depends on how you compute it and what your monitoring dashboard can display without lying to viewers.

How it works in practice

There are two families of scorers that ship in real systems: overlap-based and model-based. Most teams start with the former and migrate to the latter once volume justifies the cost.

Lexical and embedding overlap

The cheapest method computes similarity between the query embedding and each retrieved chunk embedding. Cosine similarity above a threshold is “relevant.” This catches obvious mismatches but fails on paraphrases where vocabulary diverges.

import numpy as np
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")

def relevance_score(query: str, chunk: str) -> float:
    q_emb = model.encode(query, normalize_embeddings=True)
    c_emb = model.encode(chunk, normalize_embeddings=True)
    return float(np.dot(q_emb, c_emb))

A similarity of 0.82 might be relevant for factual queries; 0.31 is almost certainly noise. The threshold is domain-specific. Set it from a labeled sample, not intuition.

Lexical overlap (token Jaccard, BM25 match) is even noisier but requires no model. It is useful as a sanity check that your vector store isn’t returning empty or duplicated rows.

LLM-as-judge scoring

For nuanced queries, an LLM grades the pair. You pass the query and the chunk, ask for a strict relevance verdict, and parse the response. This is slower and costs tokens, but it captures semantic fit that embeddings miss.

from openai import OpenAI

client = OpenAI(base_url="https://api.n4n.ai/v1", api_key="KEY")  # OpenAI-compatible, fallback built in

PROMPT = """You are a strict relevance grader.
Query: {query}
Passage: {passage}
Rate relevance 1-5 where 1 is unrelated and 5 is directly answers query.
Return only the integer."""

def llm_relevance(query: str, passage: str) -> int:
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": PROMPT.format(query=query, passage=passage)}],
        temperature=0,
    )
    return int(resp.choices[0].message.content.strip())

When you call an LLM to compute context relevance scoring RAG metrics, routing through an OpenAI-compatible gateway such as n4n.ai gives you automatic fallback across providers so a rate limit doesn’t stall your nightly eval job. The per-token metering also keeps the cost of grading visible per pipeline run.

Why it matters for pipeline monitoring

Without context relevance scoring RAG, you are blind to retrieval failures until users complain. Retrieval quality drifts constantly: embedding model updates, chunk-size tweaks, vector index rebalancing, or upstream document changes all shift what surfaces.

A monitoring graph of mean relevance per query type catches:

  • Index corruption (sudden drop to near zero)
  • Embedding drift after a model swap
  • Partial coverage gaps (low scores only on “billing” queries)

You can alert on the 5th percentile rather than the mean. If even 5% of retrieved contexts score below 0.2, certain user segments are getting garbage context and will churn.

Context relevance also gates cost. If 30% of retrieved chunks are irrelevant, you are paying to embed, store, and generate against tokens that add latency and hallucination risk. Cutting them improves both margin and accuracy.

A concrete implementation example

Assume a support bot over a documentation corpus. We evaluate the retriever nightly on 200 held-out queries with known relevant doc IDs.

Dataset and retriever setup

{
  "query": "How do I rotate API keys?",
  "retrieved": [
    {"id": "doc-882", "text": "To rotate keys, open settings > security > rotate..."},
    {"id": "doc-101", "text": "Billing cycles close on the last day of month."}
  ],
  "expected_ids": ["doc-882"]
}

Scoring code

We blend embedding similarity with an LLM judge for the top-3 hits, then log the fraction of queries where at least one relevant chunk appears in the top-3 (recall@3) and the average judge score.

import json, numpy as np
from sklearn.metrics import recall_score

def evaluate(run_path: str):
    scores, y_true, y_pred = [], [], []
    for line in open(run_path):
        item = json.loads(line)
        q = item["query"]
        for doc in item["retrieved"][:3]:
            s = llm_relevance(q, doc["text"])
            scores.append(s)
            rel = int(doc["id"] in item["expected_ids"])
            y_true.append(1 if rel else 0)
            y_pred.append(1 if s >= 3 else 0)
    print("mean_judge_score", np.mean(scores))
    print("recall@3", recall_score(y_true, y_pred, zero_division=0))

evaluate("nightly_retrieval.jsonl")

The output tells you two things: the average quality of what you retrieve, and whether the right doc is present at all. Both are needed; a high average score with low recall means you retrieve four good chunks but never the one the query needed.

This implements a minimal context relevance scoring RAG evaluator that fits in a single cron job. Expand it with per-topic breakdowns once the baseline is stable.

Common misconceptions

Relevance equals answer correctness

Engineers new to RAG pipeline observability often treat a high relevance score as proof the bot works. It is not. The generator can ignore the context, contradict it, or cite it wrongly. Relevance scoring monitors the retriever only; pair it with answer-faithfulness eval to cover the full path.

High recall is enough

Recall@k looks reassuring until you notice the generator picks the wrong chunk. If you retrieve ten passages and one is relevant, recall@10 is 100% but the signal-to-noise ratio is terrible. Always track precision or mean judge score alongside recall, or you will mask cost and latency bloat.

The score is stable across models

Embedding-based relevance thresholds calibrated on all-MiniLM-L6-v2 do not transfer to text-embedding-3-large. LLM judges change behavior between model versions and temperature settings. Re-baseline your thresholds whenever you swap a scorer. Treat the number as relative to the scoring method, not an absolute ground truth.

Binary labels are sufficient for tuning

A binary “relevant / not” hides gradations that matter for ranking. A chunk that partially answers the query should outrank one that merely shares keywords. Use ordinal or continuous scores when optimizing the retriever; reserve binary for hard alerting rules.

Operational notes

Store the raw scores, not just aggregates. When a query cluster degrades, you need the per-chunk distribution to see whether it is a single bad embedding or a systemic miss. Pipe the scores into the same tracing system as your generation logs so a support ticket can be traced from answer back to retrieved context.

Context relevance scoring RAG is not a one-time benchmark. It is a continuous sensor. The teams that get value from it wire it into the same CI that deploys retriever changes, so a relevance regression blocks the rollout exactly like a unit test failure.

Tagsragevaluationcontext-relevanceobservability

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All rag pipeline observability posts →