Mastering RAG Quality: Evaluation-Driven Development with Phoenix and Giskard
Building a Retrieval-Augmented Generation (RAG) system is deceptively simple. With frameworks like LangChain or LlamaIndex, you can have a functional prototype running in an afternoon. However, taking that prototype to production reveals a harsh reality: LLMs are non-deterministic, prone to hallucinations, and notoriously difficult to debug.
In traditional software engineering, we rely on Test-Driven Development (TDD) to ensure reliability. In the world of Generative AI, we need a new paradigm: Evaluation-Driven Development (EDD). This approach shifts the focus from "vibes-based" manual testing to a structured, automated framework for measuring accuracy, context relevance, and faithfulness.
In this guide, we will explore how to implement EDD using two powerful tools in the modern AI stack: Arize Phoenix for observability and evaluation, and Giskard for automated testing and hallucination detection.
The Shift from TDD to EDD
In a standard CRUD application, an input A always produces output B. Unit tests are simple assertions. In a RAG pipeline, the same prompt can yield slightly different answers every time. More importantly, the failure modes are silent. A RAG system might retrieve the wrong document but still generate a professional-sounding, confident, yet entirely incorrect answer.
Evaluation-Driven Development treats evaluation as a first-class citizen in the development lifecycle. Instead of testing for exact string matches, we test for semantic properties:
- Retrieval Quality: Did we find the right context?
- Faithfulness: Is the answer derived solely from the retrieved context?
- Relevancy: Does the answer actually address the user's query?
Setting the Foundation with Arize Phoenix
Arize Phoenix is an open-source observability library that excels at tracing and evaluating LLM applications. It provides a local-first environment to visualize your RAG traces and run Evals (automated evaluations using an LLM-as-a-judge).
Step 1: Instrumenting Your Pipeline
Before you can evaluate, you must observe. Phoenix uses OpenTelemetry under the hood to trace every step of your RAG process—from the initial query to the final response generation.
import phoenix as px from phoenix.trace.langchain import LangChainInstrumentor # Launch the Phoenix server locally session = px.launch_app() # Instrument your LangChain or LlamaIndex app LangChainInstrumentor().instrument()
Once instrumented, every execution of your RAG pipeline is captured. You can see exactly what documents were retrieved, the metadata associated with them, and the latency of each node. This visibility is the prerequisite for EDD.
Step 2: Defining the Evaluation Dataset
To move toward automated unit tests, you need a "Golden Dataset." This is a collection of queries, ground truth answers (if available), and the expected context. Phoenix allows you to export your traces into a dataframe that serves as your testing baseline.
Automated Scanning with Giskard
Giskard takes a different, complementary approach. While Phoenix is excellent for tracing and continuous monitoring, Giskard is designed to "break" your model. It acts as a specialized QA engineer that scans your RAG system for vulnerabilities, including hallucinations, misinformation, and harmful content.
Integrating Giskard for Hallucination Detection
Giskard's Scan feature automatically detects common pitfalls in RAG systems. It generates adversarial inputs to see if your model can be coerced into ignoring its context or hallucinating facts.
import giskard import pandas as pd def model_predict(df: pd.DataFrame): # Your RAG prediction logic here return [rag_pipeline.query(q) for q in df["query"]] # Wrap your model for Giskard giskard_model = giskard.Model( model=model_predict, model_type="text_generation", name="Knowledge_Base_RAG", feature_names=["query"] ) # Run the scan scan_results = giskard.scan(giskard_model, dataset=giskard_dataset)
Giskard will report on specific "vulnerability types." For RAG, the most critical is the Hallucination detector, which checks if the model generates information not present in the provided knowledge base.
Implementing the EDD Loop
With both tools in place, the EDD workflow looks like this:
1. The Retrieval Check (Context Relevance)
Using Phoenix, we apply a Relevance evaluator. We ask an LLM judge (like GPT-4o) to compare the user query against the retrieved chunks. If the relevance score is low, our retrieval logic (embeddings, vector DB parameters, or chunking strategy) is the problem, not the generator.
2. The Generation Check (Faithfulness)
This is where we combat hallucinations. We use Phoenix’s Faithfulness evaluator to ensure the response is grounded in the retrieved context. If the response contains facts not found in the context, the test fails.
3. Automated Unit Tests with Pytest
We can codify these evaluations into standard CI/CD pipelines using pytest. This is the hallmark of a mature RAG implementation.
@pytest.mark.parametrize("query, expected_context", golden_set) def test_rag_faithfulness(query, expected_context): response = rag_pipeline.query(query) # Use Phoenix or Giskard as an assertion engine result = run_faithfulness_eval(query, response, expected_context) assert result.score >= 0.8, f"Hallucination detected! Score: {result.score}"
Real-World Example: Fixing a "Confident Hallucination"
Imagine a RAG system for a legal firm. A user asks: "What is the notice period for contract termination?"
- The Trace: Phoenix shows the system retrieved a document about "Employment Benefits" instead of "Contract Clauses."
- The Response: The LLM, trying to be helpful, synthesizes a general answer: "Usually 30 days."
- The Evaluation: Giskard’s scan flags this as a hallucination because "30 days" appears nowhere in the retrieved "Employment Benefits" text. Phoenix marks it as low
Faithfulness.
The EDD Fix: Instead of tweaking the prompt (a common mistake), the engineer realizes the chunking size was too small, cutting off the termination clauses. They adjust the chunk_size in the vector database, re-run the Phoenix evaluation, and the score improves. The unit test now passes.
Scaling EDD in Production
As your RAG system grows, manual inspection becomes impossible. Here is how to scale this architecture:
- Synthetic Data Generation: Use Giskard to generate hundreds of test cases based on your documents. This creates a robust suite of unit tests before a single user touches the system.
- Shadow Evaluation: Run Phoenix in your production environment. Evaluate a percentage of live traces in real-time. If the
Faithfulnessscore drops below a threshold, trigger an alert. - Regression Testing: Every time you update your embedding model or prompt, run your full Giskard scan. If the new version introduces more hallucinations than the previous one, block the deployment.
Conclusion: Moving Beyond the Hype
The difference between a hobbyist RAG and a production-grade AI system is the rigors of its testing framework. By adopting Evaluation-Driven Development, you treat LLM outputs with the same scrutiny as financial logic or security protocols.
Actionable Next Steps:
- Instrument Today: Add Arize Phoenix to your local development environment to start seeing your traces.
- Scan for Weakness: Run a Giskard scan on your existing RAG to identify where it's most likely to hallucinate.
- Automate Assertions: Transition from manual spot-checks to
pytestassertions using LLM-as-a-judge metrics.
Stop guessing if your RAG is working. Start measuring it.