RAG System Monitoring
A RAG system answers a question in three steps: embed the question, retrieve matching documents, and generate an answer from them. When an answer is wrong, the cause can be in any step. This guide traces each step as its own observation so you can see what was retrieved, what the model was given and what each step cost. Examples use the Python SDK (ants-platform). For other languages see the JS/TS SDK and Java SDK.
The trace you will build
| Observation | Type | What it records |
|---|---|---|
rag-assistant | agent | The question and the final answer. Root of the trace, carries the agent name. |
embed-query | embedding | The embedding model and its input token count, so the embedding call is costed. |
retrieve | retriever | The query and top_k as input, the retrieved documents with their IDs and similarity scores as output. |
answer | generation | The full prompt including the retrieved context, the answer, the model and token usage. |
Install and configure
API keys and the host URL come from Settings > API Keys in your project. See API Keys.
Write the pipeline
Save this as rag.py. It uses a small in-memory document list in place of a vector database so it runs as is.
Notes:
- The root observation needs
agent_name. Everything inside it inherits the name. - The OpenAI drop-in client records chat completions as generations automatically. It does not record embeddings, so
embed_queryrecords the embedding call itself withas_type="embedding"and reportsusage_details. - The document embeddings computed at startup are not traced. Indexing is usually a separate job; trace it with its own agent observation if you want its cost recorded.
- With a real vector database, keep the same shape: put the search call inside the
retrieverobservation and write the returned IDs, scores and text (or a snippet) to its output.
Run it and open the trace
Open Discover AI > Tracing and select the rag-assistant trace. Select retrieve to see exactly which documents came back and their scores, and answer to see the prompt the model received, its output, token counts and cost. The Timeline view shows how long each step took.
Find retrieval problems
Most wrong answers trace back to retrieval. With the trace above you can check:
- Nothing relevant retrieved. The
retrieveoutput shows low scores or unrelated text. Filter Tracing by levelWARNINGto list traces where the retriever flagged a weak match. - Right documents, wrong answer. The documents are in the
answerprompt but the answer ignores them. That is a prompt or model problem, not retrieval. - Context too long. The
answergeneration shows a large input token count. Lowertop_kor trim documents before building the prompt.
See Debugging AI Agents for recording errors and linking logs to traces.
Track cost per step
Cost is calculated per generation and embedding from the model name and usage, using the prices under Settings > Models. If a model has no price, its cost stays empty. See Models & Pricing.
- Gauge AI > Cost Dashboard shows spend by model, and Top 20 Use Cases (Observation) by Cost splits spend between
embed-queryandanswer. See Cost & Latency Dashboards. - Gauge AI > Latency & Analytics shows p50 to p99 latency per span and generation name, so you can see whether retrieval or generation dominates. See Cost & Performance Metrics.
For ways to reduce cost, see the Cost Optimization guide.
Evaluate answer quality
LLM-as-a-Judge on live traces
Set up LLM-as-a-Judge evaluators that read the trace and write a score between 0 and 1. Managed templates that suit RAG:
| Template | Variables to map |
|---|---|
| Contextrelevance | {{query}} to the trace input, {{context}} to the output of the retrieve observation |
| Hallucination | {{query}} to the trace input, {{generation}} to the trace output |
| Helpfulness, Relevance | The query and the answer |
Other RAG templates (Contextcorrectness, Context Precision, Context Recall, Faithfulness, Answer Correctness) use variables such as {{ground_truth}}, {{answer}} and {{question}}. Open a template to see its variables before mapping them; templates that need a ground truth fit best on experiment runs, where the dataset item's expected output is available.
Use a filter on trace name rag-assistant and a sampling rate to control judge cost. Judge calls run on your own provider key.
Feedback scores from your app
Record user feedback on the trace while the agent observation is open:
If the feedback arrives later, call ants.create_score(name="user-feedback", value=0, trace_id=trace_id, data_type="BOOLEAN") with the trace ID you stored. See Scores.
Regression runs on a dataset
Keep a set of questions with expected answers in a dataset and rerun it after changing the retriever, chunking or prompt:
item.run() must be opened inside an observation that has an agent_name, as shown. Evaluators that target Experiment runs score the new run items automatically, and you can compare runs side by side in the dataset view. See Experiments.
Framework integrations
With LangChain, pass CallbackHandler() in config={"callbacks": [handler]} and set "metadata": {"ants_platform_agent_name": "rag-assistant"}. Retrievers are recorded as retriever observations and LLM calls as generations. See LangChain.
Common issues
| Symptom | Fix |
|---|---|
ValueError: agent_name is required | The pipeline ran outside the rag-assistant agent observation, for example a retriever or OpenAI call made at the top level. |
| Embedding step has tokens but no cost | Add a price for the embedding model under Settings > Models. |
| Retrieved documents missing on the trace | Write them to the retriever's output with retriever.update(output=...). |
| Trace output empty | Call update_trace(output=...) on the root before it closes. |
| No trace after the script ends | Call ants.flush() before the process exits. |