From 3489b8ed46e5363dd69d77abb4edeb387c56f3bf Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 23 Oct 2025 14:01:10 +0530 Subject: [PATCH 1/9] update: example to use collections --- README.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e13b5bdc4..bcf8329f7 100644 --- a/README.md +++ b/README.md @@ -97,21 +97,39 @@ Available templates: ### Evaluate your LLM App -This is 5 main lines: +This is a simple example evaluating a summary for accuracy: ```python -from ragas import SingleTurnSample -from ragas.metrics import AspectCritic +import asyncio +from ragas.metrics.collections import AspectCritic +from ragas.llms import instructor_llm_factory +# Setup your LLM +llm = instructor_llm_factory("openai", model="gpt-4o") + +# Create a metric +metric = AspectCritic( + name="summary_accuracy", + definition="Verify if the summary is accurate and captures key information.", + llm=llm +) + +# Evaluate test_data = { "user_input": "summarise given text\nThe company reported an 8% rise in Q3 2024, driven by strong performance in the Asian market. Sales in this region have significantly contributed to the overall growth. Analysts attribute this success to strategic marketing and product localization. The positive trend in the Asian market is expected to continue into the next quarter.", "response": "The company experienced an 8% increase in Q3 2024, largely due to effective marketing strategies and product adaptation, with expectations of continued growth in the coming quarter.", } -evaluator_llm = LangchainLLMWrapper(ChatOpenAI(model="gpt-4o")) -metric = AspectCritic(name="summary_accuracy",llm=evaluator_llm, definition="Verify if the summary is accurate.") -await metric.single_turn_ascore(SingleTurnSample(**test_data)) + +score = await metric.ascore( + user_input=test_data["user_input"], + response=test_data["response"] +) +print(f"Score: {score.value}") +print(f"Reason: {score.reason}") ``` +> **Note**: Make sure your `OPENAI_API_KEY` environment variable is set. + Find the complete [Quickstart Guide](https://docs.ragas.io/en/latest/getstarted/evals) ## Want help in improving your AI application using evals? From 10df27b9f450d4feec354a5d6dc47f8527111787 Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 23 Oct 2025 15:19:12 +0530 Subject: [PATCH 2/9] docs: quickstart guide with interactive LLM and project structure --- Makefile | 4 +- docs/getstarted/evals.md | 288 ++++++++++++++----------- docs/howtos/integrations/_haystack.md | 6 +- docs/howtos/integrations/_helicone.md | 8 +- docs/howtos/integrations/_langchain.md | 9 +- docs/howtos/integrations/_langsmith.md | 4 +- docs/howtos/integrations/_zeno.md | 5 +- mkdocs.yml | 3 +- 8 files changed, 189 insertions(+), 138 deletions(-) diff --git a/Makefile b/Makefile index 3e4b4fb19..f00c9a6be 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ build-docs: ## Build all documentation @echo "Converting ipynb notebooks to md files..." $(Q)MKDOCS_CI=true uv run python $(GIT_ROOT)/docs/ipynb_to_md.py @echo "Building ragas documentation..." - $(Q)uv run --group docs mkdocs build + $(Q)MKDOCS_CI=false uv run --group docs mkdocs build serve-docs: ## Build and serve documentation locally - $(Q)uv run --group docs mkdocs serve --dirtyreload + $(Q)MKDOCS_CI=false uv run --group docs mkdocs serve --dirtyreload diff --git a/docs/getstarted/evals.md b/docs/getstarted/evals.md index 08de6b4cc..739a64233 100644 --- a/docs/getstarted/evals.md +++ b/docs/getstarted/evals.md @@ -2,183 +2,229 @@ The purpose of this guide is to illustrate a simple workflow for testing and evaluating an LLM application with `ragas`. It assumes minimum knowledge in AI application building and evaluation. Please refer to our [installation instruction](./install.md) for installing `ragas` +!!! tip "Get a Working Example" + The fastest way to see these concepts in action is to create a project using the quickstart command: -## Evaluation + ```sh + ragas quickstart rag_eval + ``` -In this guide, you will evaluate a **text summarization pipeline**. The goal is to ensure that the output summary accurately captures all the key details specified in the text, such as growth figures, market insights, and other essential information. + This generates a complete project with sample code. Follow along with this guide to understand what's happening in your generated code. -`ragas` offers a variety of methods for analyzing the performance of LLM applications, referred to as [metrics](../concepts/metrics/available_metrics/index.md). Each metric requires a predefined set of data points, which it uses to calculate scores that indicate performance. + ```sh + cd rag_eval + ``` -### Evaluating using a Non-LLM Metric + Let's get started -Here is a simple example that uses `BleuScore` to score a summary: +## Project Structure -```python -from ragas import SingleTurnSample -from ragas.metrics import BleuScore - -test_data = { - "user_input": "summarise given text\nThe company reported an 8% rise in Q3 2024, driven by strong performance in the Asian market. Sales in this region have significantly contributed to the overall growth. Analysts attribute this success to strategic marketing and product localization. The positive trend in the Asian market is expected to continue into the next quarter.", - "response": "The company experienced an 8% increase in Q3 2024, largely due to effective marketing strategies and product adaptation, with expectations of continued growth in the coming quarter.", - "reference": "The company reported an 8% growth in Q3 2024, primarily driven by strong sales in the Asian market, attributed to strategic marketing and localized products, with continued growth anticipated in the next quarter." -} -metric = BleuScore() -test_data = SingleTurnSample(**test_data) -metric.single_turn_score(test_data) -``` +Here's what gets created for you: -Output -``` -0.137 +```sh +rag_eval/ +├── README.md # Quick start guide for your project +├── evals.py # Your evaluation code (metrics + datasets) +├── rag.py # Your RAG/LLM application +└── evals/ # Evaluation artifacts + ├── datasets/ # Test data files (edit these to add more test cases) + ├── experiments/ # Results from running evaluations + └── logs/ # Evaluation execution logs ``` -Here we used: +**Key files to focus on:** -- A test sample containing `user_input`, `response` (the output from the LLM), and `reference` (the expected output from the LLM) as data points to evaluate the summary. -- A non-LLM metric called [BleuScore](../concepts/metrics/available_metrics/traditional.md#bleu-score) +- **`evals.py`** - Where you define metrics and load test data (we'll explore this next) +- **`rag.py`** - Your application code (query engine, retrieval, etc.) +- **`evals/datasets/`** - Add your test cases here as CSV or JSON files +## Understanding the Code -As you may observe, this approach has two key limitations: +In your generated project's `evals.py` file, you'll see two key patterns for evaluation: -- **Time-consuming preparation:** Evaluating the application requires preparing the expected output (`reference`) for each input, which can be both time-consuming and challenging. +1. **Metrics** - Functions that score your application's output +2. **Datasets** - Test data that your application is evaluated against -- **Inaccurate scoring:** Even though the `response` and `reference` are similar, the output score was low. This is a known limitation of non-LLM metrics like `BleuScore`. +`ragas` offers a variety of evaluation methods, referred to as [metrics](../concepts/metrics/available_metrics/index.md). Let's walk through the most common ones you'll encounter. +### Custom Evaluation with LLMs -!!! info - A **non-LLM metric** refers to a metric that does not rely on an LLM for evaluation. +In your generated project, you'll see the `DiscreteMetric` - a flexible metric that uses an LLM to evaluate based on any criteria you define: -To address these issues, let's try an LLM-based metric. +```python +from ragas.metrics import DiscreteMetric +from ragas.llms import instructor_llm_factory +# Create your evaluator LLM +evaluator_llm = instructor_llm_factory("openai", model="gpt-4o") -### Evaluating using a LLM-based Metric +# Define a custom metric +my_metric = DiscreteMetric( + name="correctness", + prompt="Check if the response is correct. Return 'pass' or 'fail'.\nResponse: {response}\nExpected: {expected}", + allowed_values=["pass", "fail"], +) +# Use it to score +score = my_metric.score( + llm=evaluator_llm, + response="The capital of France is Paris", + expected="Paris" +) +print(f"Score: {score.value}") # Output: 'pass' +``` -**Choose your LLM** ---8<-- -choose_evaluator_llm.md ---8<-- +What you see in your generated `evals.py` lets you define evaluation logic that matters for your application. Learn more about [custom metrics](../concepts/metrics/index.md). -**Evaluation** +### Choosing Your Evaluator LLM -Here we will use [AspectCritic](../concepts/metrics/available_metrics/aspect_critic.md), which is an LLM based metric that outputs pass/fail given the evaluation criteria. +Your evaluation metrics need an LLM to score your application. Ragas works with **any LLM provider** through the `instructor_llm_factory`. Your quickstart project uses OpenAI by default, but you can easily swap to any provider by updating the LLM creation in your `evals.py`: -```python -from ragas import SingleTurnSample -from ragas.metrics import AspectCritic +=== "OpenAI" + Set your OpenAI API key: -test_data = { - "user_input": "summarise given text\nThe company reported an 8% rise in Q3 2024, driven by strong performance in the Asian market. Sales in this region have significantly contributed to the overall growth. Analysts attribute this success to strategic marketing and product localization. The positive trend in the Asian market is expected to continue into the next quarter.", - "response": "The company experienced an 8% increase in Q3 2024, largely due to effective marketing strategies and product adaptation, with expectations of continued growth in the coming quarter.", -} + ```sh + export OPENAI_API_KEY="your-openai-key" + ``` -metric = AspectCritic(name="summary_accuracy",llm=evaluator_llm, definition="Verify if the summary is accurate.") -test_data = SingleTurnSample(**test_data) -await metric.single_turn_ascore(test_data) + In your `evals.py`: -``` + ```python + from ragas.llms import instructor_llm_factory -Output -``` -1 -``` + llm = instructor_llm_factory("openai", model="gpt-4o") + ``` -Success! Here 1 means pass and 0 means fail + The quickstart project already sets this up for you! -!!! info - There are many other types of metrics that are available in `ragas` (with and without `reference`), and you may also create your own metrics if none of those fits your case. To explore this more checkout [more on metrics](../concepts/metrics/index.md). +=== "Anthropic Claude" + Set your Anthropic API key: -### Evaluating on a Dataset + ```sh + export ANTHROPIC_API_KEY="your-anthropic-key" + ``` -In the examples above, we used only a single sample to evaluate our application. However, evaluating on just one sample is not robust enough to trust the results. To ensure the evaluation is reliable, you should add more test samples to your test data. + In your `evals.py`: -Here, we’ll load a dataset from Hugging Face Hub, but you can load data from any source, such as production logs or other datasets. Just ensure that each sample includes all the required attributes for the chosen metric. + ```python + from ragas.llms import instructor_llm_factory -In our case, the required attributes are: -- **`user_input`**: The input provided to the application (here the input text report). -- **`response`**: The output generated by the application (here the generated summary). + llm = instructor_llm_factory("anthropic", model="claude-3-5-sonnet-20241022") + ``` -For example +=== "Google Cloud" + Set up your Google credentials: -```python -[ - # Sample 1 - { - "user_input": "summarise given text\nThe Q2 earnings report revealed a significant 15% increase in revenue, ...", - "response": "The Q2 earnings report showed a 15% revenue increase, ...", - }, - # Additional samples in the dataset - ...., - # Sample N - { - "user_input": "summarise given text\nIn 2023, North American sales experienced a 5% decline, ...", - "response": "Companies are strategizing to adapt to market challenges and ...", - } -] -``` + ```sh + export GOOGLE_API_KEY="your-google-api-key" + ``` -```python -from datasets import load_dataset -from ragas import EvaluationDataset -eval_dataset = load_dataset("explodinggradients/earning_report_summary",split="train") -eval_dataset = EvaluationDataset.from_hf_dataset(eval_dataset) -print("Features in dataset:", eval_dataset.features()) -print("Total samples in dataset:", len(eval_dataset)) -``` + In your `evals.py`: -Output -``` -Features in dataset: ['user_input', 'response'] -Total samples in dataset: 50 -``` - -Evaluate using dataset + ```python + from ragas.llms import instructor_llm_factory -```python -from ragas import evaluate + llm = instructor_llm_factory("google", model="gemini-1.5-pro") + ``` -results = evaluate(eval_dataset, metrics=[metric]) -results -``` +=== "Local Models (Ollama)" + Install and run Ollama locally, then in your `evals.py`: -!!! tip "Async Usage" - For production async applications, use `aevaluate()` to avoid event loop conflicts: ```python - from ragas import aevaluate + from ragas.llms import instructor_llm_factory - # In an async function - results = await aevaluate(eval_dataset, metrics=[metric]) + llm = instructor_llm_factory( + "ollama", + model="mistral", + base_url="http://localhost:11434" # Default Ollama URL + ) ``` - Or disable nest_asyncio in sync code: +=== "Custom / Other Providers" + For any LLM with OpenAI-compatible API: + ```python - results = evaluate(eval_dataset, metrics=[metric], allow_nest_asyncio=False) + from ragas.llms import instructor_llm_factory + + llm = instructor_llm_factory( + "openai", + api_key="your-api-key", + base_url="https://your-api-endpoint" + ) ``` -Output -``` -{'summary_accuracy': 0.84} -``` + For more details, learn about [LLM integrations](../concepts/metrics/index.md). -This score shows that out of all the samples in our test data, only 84% of summaries passes the given evaluation criteria. Now, **It's -important to see why is this the case**. +### Using Pre-Built Metrics -Export the sample level scores to pandas dataframe +`ragas` comes with pre-built metrics for common evaluation tasks. For example, [AspectCritic](../concepts/metrics/available_metrics/aspect_critic.md) evaluates any aspect of your output: ```python -results.to_pandas() -``` +from ragas.metrics.collections import AspectCritic +from ragas.llms import instructor_llm_factory + +# Setup your evaluator LLM +evaluator_llm = instructor_llm_factory("openai", model="gpt-4o") + +# Use a pre-built metric +metric = AspectCritic( + name="summary_accuracy", + definition="Verify if the summary is accurate and captures key information.", + llm=evaluator_llm +) -Output +# Score your application's output +score = await metric.ascore( + user_input="Summarize this text: ...", + response="The summary of the text is..." +) +print(f"Score: {score.value}") # 1 = pass, 0 = fail +print(f"Reason: {score.reason}") ``` - user_input response summary_accuracy -0 summarise given text\nThe Q2 earnings report r... The Q2 earnings report showed a 15% revenue in... 1 -1 summarise given text\nIn 2023, North American ... Companies are strategizing to adapt to market ... 1 -2 summarise given text\nIn 2022, European expans... Many companies experienced a notable 15% growt... 1 -3 summarise given text\nSupply chain challenges ... Supply chain challenges in North America, caus... 1 + +Pre-built metrics like this save you from defining evaluation logic from scratch. Explore [all available metrics](../concepts/metrics/available_metrics/index.md). + +!!! info + There are many other types of metrics that are available in `ragas` (with and without `reference`), and you may also create your own metrics if none of those fits your case. To explore this more checkout [more on metrics](../concepts/metrics/index.md). + +### Evaluating on a Dataset + +In your quickstart project, you'll see in the `load_dataset()` function, which creates test data with multiple samples: + +```python +from ragas import Dataset + +# Create a dataset with multiple test samples +dataset = Dataset( + name="test_dataset", + backend="local/csv", # Can also use JSONL, Google Drive, or in-memory + root_dir=".", +) + +# Add samples to the dataset +data_samples = [ + { + "user_input": "What is ragas?", + "response": "Ragas is an evaluation framework...", + "expected": "Ragas provides objective metrics..." + }, + { + "user_input": "How do metrics work?", + "response": "Metrics score your application...", + "expected": "Metrics evaluate performance..." + }, +] + +for sample in data_samples: + dataset.append(sample) + +# Save to disk +dataset.save() ``` -Viewing the sample-level results in a CSV file, as shown above, is fine for quick checks but not ideal for detailed analysis or comparing results across evaluation runs. +This gives you multiple test cases instead of evaluating one example at a time. Learn more about [datasets and experiments](../concepts/components/eval_dataset.md). + +Your generated project includes sample data in the `evals/datasets/` folder - you can edit those files to add more test cases. ### Want help in improving your AI application using evals? diff --git a/docs/howtos/integrations/_haystack.md b/docs/howtos/integrations/_haystack.md index ba99746bb..be4435f45 100644 --- a/docs/howtos/integrations/_haystack.md +++ b/docs/howtos/integrations/_haystack.md @@ -50,7 +50,7 @@ docs = [Document(content=doc) for doc in dataset] ```python -from haystack.components.embedders import OpenAITextEmbedder, OpenAIDocumentEmbedder +from haystack.components.embedders import OpenAIDocumentEmbedder, OpenAITextEmbedder document_embedder = OpenAIDocumentEmbedder(model="text-embedding-3-small") text_embedder = OpenAITextEmbedder(model="text-embedding-3-small") @@ -133,8 +133,8 @@ Make sure to include all relevant data for each metric to ensure accurate evalua ```python from haystack_integrations.components.evaluators.ragas import RagasEvaluator - from langchain_openai import ChatOpenAI + from ragas.llms import LangchainLLMWrapper from ragas.metrics import AnswerRelevancy, ContextPrecision, Faithfulness @@ -252,7 +252,7 @@ In the example below, we will define two custom Ragas metrics: ```python -from ragas.metrics import RubricsScore, AspectCritic +from ragas.metrics import AspectCritic, RubricsScore SportsRelevanceMetric = AspectCritic( name="sports_relevance_metric", diff --git a/docs/howtos/integrations/_helicone.md b/docs/howtos/integrations/_helicone.md index 318f2d80b..e4e781577 100644 --- a/docs/howtos/integrations/_helicone.md +++ b/docs/howtos/integrations/_helicone.md @@ -25,16 +25,18 @@ First, let's install the required packages and set up our environment. ```python import os + from datasets import Dataset + from ragas import evaluate -from ragas.metrics import faithfulness, answer_relevancy, context_precision from ragas.integrations.helicone import helicone_config # import helicone_config - +from ragas.metrics import answer_relevancy, context_precision, faithfulness # Set up Helicone -helicone_config.api_key = ( +HELICONE_API_KEY = ( "your_helicone_api_key_here" # Replace with your actual Helicone API key ) +helicone_config.api_key = HELICONE_API_KEY os.environ["OPENAI_API_KEY"] = ( "your_openai_api_key_here" # Replace with your actual OpenAI API key ) diff --git a/docs/howtos/integrations/_langchain.md b/docs/howtos/integrations/_langchain.md index 0a31b98cf..475565c0b 100644 --- a/docs/howtos/integrations/_langchain.md +++ b/docs/howtos/integrations/_langchain.md @@ -13,8 +13,9 @@ With this integration you can easily evaluate your QA chains with the metrics of ```python # attach to the existing event loop when using jupyter notebooks -import nest_asyncio import os + +import nest_asyncio import openai from dotenv import load_dotenv @@ -35,9 +36,9 @@ First lets load the dataset. We are going to build a generic QA system over the ```python -from langchain_community.document_loaders import TextLoader -from langchain.indexes import VectorstoreIndexCreator from langchain.chains import RetrievalQA +from langchain.indexes import VectorstoreIndexCreator +from langchain_community.document_loaders import TextLoader from langchain_openai import ChatOpenAI loader = TextLoader("./nyc_wikipedia/nyc_text.txt") @@ -155,10 +156,10 @@ result["result"] ```python from ragas.langchain.evalchain import RagasEvaluatorChain from ragas.metrics import ( - faithfulness, answer_relevancy, context_precision, context_recall, + faithfulness, ) # create evaluation chains diff --git a/docs/howtos/integrations/_langsmith.md b/docs/howtos/integrations/_langsmith.md index d936c1f43..cedbe71e3 100644 --- a/docs/howtos/integrations/_langsmith.md +++ b/docs/howtos/integrations/_langsmith.md @@ -26,9 +26,9 @@ Once langsmith is setup, just run the evaluations as your normally would ```python from datasets import load_dataset -from ragas.metrics import context_precision, answer_relevancy, faithfulness -from ragas import evaluate +from ragas import evaluate +from ragas.metrics import answer_relevancy, context_precision, faithfulness fiqa_eval = load_dataset("explodinggradients/fiqa", "ragas_eval") diff --git a/docs/howtos/integrations/_zeno.md b/docs/howtos/integrations/_zeno.md index ebf2a6ca0..1386313f9 100644 --- a/docs/howtos/integrations/_zeno.md +++ b/docs/howtos/integrations/_zeno.md @@ -13,7 +13,7 @@ pip install zeno-client Next, create an account at [hub.zenoml.com](https://hub.zenoml.com) and generate an API key on your [account page](https://hub.zenoml.com/account). -We can now pick up the evaluation where we left off at the [Getting Started](./../../getstarted/index.md) guide: +We can now pick up the evaluation where we left off at the [Getting Started](../../getstarted/evaluation.md) guide: ```python @@ -21,6 +21,8 @@ import os import pandas as pd from datasets import load_dataset +from zeno_client import ZenoClient, ZenoMetric + from ragas import evaluate from ragas.metrics import ( answer_relevancy, @@ -28,7 +30,6 @@ from ragas.metrics import ( context_recall, faithfulness, ) -from zeno_client import ZenoClient, ZenoMetric ``` diff --git a/mkdocs.yml b/mkdocs.yml index 5fb0fb4cd..11bbbb93d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -252,7 +252,8 @@ extra: provider: google property: !ENV GOOGLE_ANALYTICS_KEY plugins: - - social + - social: + enabled: !ENV [MKDOCS_CI, true] - search - git-revision-date-localized: enabled: !ENV [MKDOCS_CI, false] From 38f52a408d836e1846f37642ea38ab09f5cf4469 Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Sun, 26 Oct 2025 13:39:27 +0530 Subject: [PATCH 3/9] update: instructor_llm_factory to llm_factory --- README.md | 4 +-- docs/getstarted/evals.md | 36 +++++++++---------- .../howtos/applications/align-llm-as-judge.md | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index bcf8329f7..592fbf473 100644 --- a/README.md +++ b/README.md @@ -102,10 +102,10 @@ This is a simple example evaluating a summary for accuracy: ```python import asyncio from ragas.metrics.collections import AspectCritic -from ragas.llms import instructor_llm_factory +from ragas.llms import llm_factory # Setup your LLM -llm = instructor_llm_factory("openai", model="gpt-4o") +llm = llm_factory("gpt-4o") # Create a metric metric = AspectCritic( diff --git a/docs/getstarted/evals.md b/docs/getstarted/evals.md index 739a64233..c4520aa39 100644 --- a/docs/getstarted/evals.md +++ b/docs/getstarted/evals.md @@ -53,10 +53,10 @@ In your generated project, you'll see the `DiscreteMetric` - a flexible metric t ```python from ragas.metrics import DiscreteMetric -from ragas.llms import instructor_llm_factory +from ragas.llms import llm_factory # Create your evaluator LLM -evaluator_llm = instructor_llm_factory("openai", model="gpt-4o") +evaluator_llm = llm_factory("gpt-4o") # Define a custom metric my_metric = DiscreteMetric( @@ -78,7 +78,7 @@ What you see in your generated `evals.py` lets you define evaluation logic that ### Choosing Your Evaluator LLM -Your evaluation metrics need an LLM to score your application. Ragas works with **any LLM provider** through the `instructor_llm_factory`. Your quickstart project uses OpenAI by default, but you can easily swap to any provider by updating the LLM creation in your `evals.py`: +Your evaluation metrics need an LLM to score your application. Ragas works with **any LLM provider** through the `llm_factory`. Your quickstart project uses OpenAI by default, but you can easily swap to any provider by updating the LLM creation in your `evals.py`: === "OpenAI" Set your OpenAI API key: @@ -90,9 +90,9 @@ Your evaluation metrics need an LLM to score your application. Ragas works with In your `evals.py`: ```python - from ragas.llms import instructor_llm_factory + from ragas.llms import llm_factory - llm = instructor_llm_factory("openai", model="gpt-4o") + llm = llm_factory("gpt-4o") ``` The quickstart project already sets this up for you! @@ -107,9 +107,9 @@ Your evaluation metrics need an LLM to score your application. Ragas works with In your `evals.py`: ```python - from ragas.llms import instructor_llm_factory + from ragas.llms import llm_factory - llm = instructor_llm_factory("anthropic", model="claude-3-5-sonnet-20241022") + llm = llm_factory("claude-3-5-sonnet-20241022", provider="anthropic") ``` === "Google Cloud" @@ -122,20 +122,20 @@ Your evaluation metrics need an LLM to score your application. Ragas works with In your `evals.py`: ```python - from ragas.llms import instructor_llm_factory + from ragas.llms import llm_factory - llm = instructor_llm_factory("google", model="gemini-1.5-pro") + llm = llm_factory("gemini-1.5-pro", provider="google") ``` === "Local Models (Ollama)" Install and run Ollama locally, then in your `evals.py`: ```python - from ragas.llms import instructor_llm_factory + from ragas.llms import llm_factory - llm = instructor_llm_factory( - "ollama", - model="mistral", + llm = llm_factory( + "mistral", + provider="ollama", base_url="http://localhost:11434" # Default Ollama URL ) ``` @@ -144,10 +144,10 @@ Your evaluation metrics need an LLM to score your application. Ragas works with For any LLM with OpenAI-compatible API: ```python - from ragas.llms import instructor_llm_factory + from ragas.llms import llm_factory - llm = instructor_llm_factory( - "openai", + llm = llm_factory( + "model-name", api_key="your-api-key", base_url="https://your-api-endpoint" ) @@ -161,10 +161,10 @@ Your evaluation metrics need an LLM to score your application. Ragas works with ```python from ragas.metrics.collections import AspectCritic -from ragas.llms import instructor_llm_factory +from ragas.llms import llm_factory # Setup your evaluator LLM -evaluator_llm = instructor_llm_factory("openai", model="gpt-4o") +evaluator_llm = llm_factory("gpt-4o") # Use a pre-built metric metric = AspectCritic( diff --git a/docs/howtos/applications/align-llm-as-judge.md b/docs/howtos/applications/align-llm-as-judge.md index 6ef8edd46..dc3865a70 100644 --- a/docs/howtos/applications/align-llm-as-judge.md +++ b/docs/howtos/applications/align-llm-as-judge.md @@ -185,7 +185,7 @@ async def judge_experiment( ```python import os from openai import AsyncOpenAI -from ragas.llms import instructor_llm_factory +from ragas.llms import llm_factory from ragas_examples.judge_alignment import load_dataset # Load dataset @@ -194,7 +194,7 @@ print(f"Dataset loaded with {len(dataset)} samples") # Initialize LLM client openai_client = AsyncOpenAI(api_key=os.environ.get("OPENAI_API_KEY")) -llm = instructor_llm_factory("openai", model="gpt-4o-mini", client=openai_client) +llm = llm_factory("gpt-4o-mini", client=openai_client) # Run the experiment results = await judge_experiment.arun( From 7e1df049423350e017468f2526eda2170693e050 Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Sun, 26 Oct 2025 14:03:36 +0530 Subject: [PATCH 4/9] add: quickstart guide --- docs/getstarted/index.md | 1 + docs/getstarted/quickstart.md | 143 ++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 145 insertions(+) create mode 100644 docs/getstarted/quickstart.md diff --git a/docs/getstarted/index.md b/docs/getstarted/index.md index 26ae9923a..15016bb7e 100644 --- a/docs/getstarted/index.md +++ b/docs/getstarted/index.md @@ -11,6 +11,7 @@ If you have any questions about Ragas, feel free to join and ask in the `#questi Let's get started! + - [Evaluate your first AI app](./evals.md) - [Run ragas metrics for evaluating RAG](rag_eval.md) - [Generate test data for evaluating RAG](rag_testset_generation.md) diff --git a/docs/getstarted/quickstart.md b/docs/getstarted/quickstart.md new file mode 100644 index 000000000..0f6fe9fb7 --- /dev/null +++ b/docs/getstarted/quickstart.md @@ -0,0 +1,143 @@ +# Quick Start: Get Evaluations Running in a flash + +Get started with Ragas in seconds. No installation needed! Just set your API key and run one command. + +## 1. Set Your API Key + +Choose your LLM provider: + +```sh +# OpenAI (default) +export OPENAI_API_KEY="your-openai-key" + +# Or use Anthropic Claude +export ANTHROPIC_API_KEY="your-anthropic-key" +``` + +## 2. Create Your Project + +Create a complete project with a single command using `uvx` (no installation required): + +```sh +uvx ragas quickstart rag_eval +cd rag_eval +``` + +That's it! You now have a fully configured evaluation project ready to use. + +## Project Structure + +Your generated project includes: + +```sh +rag_eval/ +├── README.md # Project documentation +├── evals.py # Evaluation configuration +├── rag.py # Your LLM application +└── evals/ + ├── datasets/ # Test data (CSV/JSON files) + ├── experiments/ # Evaluation results + └── logs/ # Execution logs +``` + +## Run Evaluations + +### Run the Evaluation + +Execute the evaluation on your dataset: + +```sh +uvx ragas evals evals.py --dataset test_data --metrics faithfulness,answer_correctness +``` + +Or, if you prefer to use Python directly (after installing ragas): + +```sh +python evals.py +``` + +This will: +- Load test data from `evals/datasets/` +- Evaluate your application using pre-configured metrics +- Save results to `evals/experiments/` + +### View Results + +Results are saved as CSV files in `evals/experiments/`: + +```python +import pandas as pd + +# Load and view results +df = pd.read_csv('evals/experiments/results.csv') +print(df[['user_input', 'response', 'faithfulness', 'answer_correctness']]) + +# Quick statistics +print(f"Average Faithfulness: {df['faithfulness'].mean():.2f}") +print(f"Average Correctness: {df['answer_correctness'].mean():.2f}") +``` + +... + +## Customize Your Evaluation + +### Add More Test Cases + +Edit `evals/datasets/test_data.csv`: + +```csv +user_input,response,reference +What is Ragas?,Ragas is an evaluation framework for LLM applications,Ragas provides objective metrics for evaluating LLM applications +How do metrics work?,Metrics score your LLM outputs,Metrics evaluate the quality and performance of LLM responses +``` + +### Change the LLM Provider + +In `evals.py`, update the LLM configuration: + +```python +from ragas.llms import llm_factory + +# Use Anthropic Claude +llm = llm_factory("claude-3-5-sonnet-20241022", provider="anthropic") + +# Use Google Gemini +llm = llm_factory("gemini-1.5-pro", provider="google") + +# Use local Ollama +llm = llm_factory("mistral", provider="ollama", base_url="http://localhost:11434") +``` + +### Select Different Metrics + +In `evals.py`, modify the metrics list: + +```python +from ragas.metrics import ( + Faithfulness, # Does response match context? + AnswerCorrectness, # Is the answer correct? + ContextPrecision, # Is retrieved context relevant? + ContextRecall, # Is all needed context retrieved? +) + +# Use only specific metrics +metrics = [ + Faithfulness(), + AnswerCorrectness(), +] +``` + +## What's Next? + +- **Learn the concepts**: Read the [Evaluate a Simple LLM Application](evals.md) guide for deeper understanding +- **Custom metrics**: [Write your own metrics](../howtos/customizations/metrics/_write_your_own_metric.md) tailored to your use case +- **Production integration**: [Integrate evaluations into your CI/CD pipeline](../howtos/index.md) +- **RAG evaluation**: Evaluate [RAG systems](rag_eval.md) with specialized metrics +- **Agent evaluation**: Explore [AI agent evaluation](../howtos/applications/text2sql.md) +- **Test data generation**: [Generate synthetic test datasets](rag_testset_generation.md) for your evaluations + +## Getting Help + +- 📚 [Full Documentation](https://docs.ragas.io/) +- 💬 [Join our Discord Community](https://discord.gg/5djav8GGNZ) +- 🐛 [Report Issues](https://github.com/explodinggradients/ragas/issues) diff --git a/mkdocs.yml b/mkdocs.yml index 60eb469f9..ff3cc9408 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,6 +11,7 @@ nav: - "": index.md - 🚀 Get Started: - getstarted/index.md + # - Quick Start: getstarted/quickstart.md - Installation: getstarted/install.md - Evaluate your first LLM App: getstarted/evals.md - Evaluate a simple RAG: getstarted/rag_eval.md From a0b6eba08279222b0d1d5d46de6def1442d6a0ca Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 30 Oct 2025 01:37:36 +0530 Subject: [PATCH 5/9] feat: enhance quickstart with end-to-end workflow, updated docs, CSV export utility, and fix venv issues --- docs/_static/imgs/results/rag_eval_result.png | Bin 0 -> 40793 bytes docs/getstarted/evals.md | 112 +++++------ docs/getstarted/index.md | 4 +- docs/getstarted/quickstart.md | 183 ++++++++++------- examples/ragas_examples/rag_eval/evals.py | 184 ++++++++++++------ .../ragas_examples/rag_eval/export_csv.py | 73 +++++++ .../ragas_examples/rag_eval/pyproject.toml | 26 +++ mkdocs.yml | 2 +- src/ragas/cli.py | 138 +++++++++---- 9 files changed, 501 insertions(+), 221 deletions(-) create mode 100644 docs/_static/imgs/results/rag_eval_result.png create mode 100644 examples/ragas_examples/rag_eval/export_csv.py create mode 100644 examples/ragas_examples/rag_eval/pyproject.toml diff --git a/docs/_static/imgs/results/rag_eval_result.png b/docs/_static/imgs/results/rag_eval_result.png new file mode 100644 index 0000000000000000000000000000000000000000..1892b53395e6e9375a84ff07bb28c51ea5a9cc33 GIT binary patch literal 40793 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 U^*pgkoB;?tUHx3vIVCg!0Pkh}TmS$7 literal 0 HcmV?d00001 diff --git a/docs/getstarted/evals.md b/docs/getstarted/evals.md index c4520aa39..3bb547bf5 100644 --- a/docs/getstarted/evals.md +++ b/docs/getstarted/evals.md @@ -5,17 +5,22 @@ The purpose of this guide is to illustrate a simple workflow for testing and eva !!! tip "Get a Working Example" The fastest way to see these concepts in action is to create a project using the quickstart command: - ```sh - ragas quickstart rag_eval - ``` - - This generates a complete project with sample code. Follow along with this guide to understand what's happening in your generated code. - - ```sh - cd rag_eval - ``` - - Let's get started + === "uvx (Recommended)" + ```sh + uvx ragas quickstart rag_eval + cd rag_eval + uv sync + ``` + + === "Install Ragas First" + ```sh + pip install ragas + ragas quickstart rag_eval + cd rag_eval + uv sync + ``` + + This generates a complete project with sample code. Follow along with this guide to understand what's happening in your generated code. Let's get started! ## Project Structure @@ -23,62 +28,59 @@ Here's what gets created for you: ```sh rag_eval/ -├── README.md # Quick start guide for your project -├── evals.py # Your evaluation code (metrics + datasets) +├── README.md # Project documentation and setup instructions +├── pyproject.toml # Project configuration for uv and pip +├── evals.py # Your evaluation workflow +├── export_csv.py # Optional CSV export utility ├── rag.py # Your RAG/LLM application +├── __init__.py # Makes this a Python package └── evals/ # Evaluation artifacts - ├── datasets/ # Test data files (edit these to add more test cases) - ├── experiments/ # Results from running evaluations + ├── datasets/ # Test data files (optional) + ├── experiments/ # Results from running evaluations (CSV files saved here) └── logs/ # Evaluation execution logs ``` **Key files to focus on:** -- **`evals.py`** - Where you define metrics and load test data (we'll explore this next) -- **`rag.py`** - Your application code (query engine, retrieval, etc.) -- **`evals/datasets/`** - Add your test cases here as CSV or JSON files +- **`evals.py`** - Your evaluation workflow with dataset loading and evaluation logic +- **`export_csv.py`** - Optional standalone script to export results to CSV with timestamp +- **`rag.py`** - Your RAG/LLM application code (query engine, retrieval, etc.) ## Understanding the Code -In your generated project's `evals.py` file, you'll see two key patterns for evaluation: - -1. **Metrics** - Functions that score your application's output -2. **Datasets** - Test data that your application is evaluated against +In your generated project's `evals.py` file, you'll see the main workflow pattern: -`ragas` offers a variety of evaluation methods, referred to as [metrics](../concepts/metrics/available_metrics/index.md). Let's walk through the most common ones you'll encounter. +1. **Load Dataset** - Define your test cases with `SingleTurnSample` +2. **Query RAG System** - Get responses from your application +3. **Evaluate Responses** - Validate responses against ground truth +4. **Display Results** - Show evaluation summary in console +5. **Export to CSV** - Optional: Save results with timestamp -### Custom Evaluation with LLMs - -In your generated project, you'll see the `DiscreteMetric` - a flexible metric that uses an LLM to evaluate based on any criteria you define: +The template provides modular functions you can customize: ```python -from ragas.metrics import DiscreteMetric -from ragas.llms import llm_factory - -# Create your evaluator LLM -evaluator_llm = llm_factory("gpt-4o") - -# Define a custom metric -my_metric = DiscreteMetric( - name="correctness", - prompt="Check if the response is correct. Return 'pass' or 'fail'.\nResponse: {response}\nExpected: {expected}", - allowed_values=["pass", "fail"], -) - -# Use it to score -score = my_metric.score( - llm=evaluator_llm, - response="The capital of France is Paris", - expected="Paris" -) -print(f"Score: {score.value}") # Output: 'pass' +from ragas.dataset_schema import SingleTurnSample +from ragas import EvaluationDataset + +def load_dataset(): + """Load test dataset for evaluation.""" + data_samples = [ + SingleTurnSample( + user_input="What is Ragas?", + response="", # Will be filled by querying RAG + reference="Ragas is an evaluation framework for LLM applications", + retrieved_contexts=[], + ), + # Add more test cases... + ] + return EvaluationDataset(samples=data_samples) ``` -What you see in your generated `evals.py` lets you define evaluation logic that matters for your application. Learn more about [custom metrics](../concepts/metrics/index.md). +You can extend this with [metrics](../concepts/metrics/available_metrics/index.md) and more sophisticated evaluation logic. Learn more about [evaluation in Ragas](../concepts/evaluation/index.md). -### Choosing Your Evaluator LLM +### Choosing Your LLM Provider -Your evaluation metrics need an LLM to score your application. Ragas works with **any LLM provider** through the `llm_factory`. Your quickstart project uses OpenAI by default, but you can easily swap to any provider by updating the LLM creation in your `evals.py`: +Your quickstart project initializes the OpenAI LLM by default in the `_init_clients()` function. You can easily swap to any provider through the `llm_factory`: === "OpenAI" Set your OpenAI API key: @@ -87,7 +89,7 @@ Your evaluation metrics need an LLM to score your application. Ragas works with export OPENAI_API_KEY="your-openai-key" ``` - In your `evals.py`: + In your `evals.py` `_init_clients()` function: ```python from ragas.llms import llm_factory @@ -95,7 +97,7 @@ Your evaluation metrics need an LLM to score your application. Ragas works with llm = llm_factory("gpt-4o") ``` - The quickstart project already sets this up for you! + This is already set up in your quickstart project! === "Anthropic Claude" Set your Anthropic API key: @@ -104,7 +106,7 @@ Your evaluation metrics need an LLM to score your application. Ragas works with export ANTHROPIC_API_KEY="your-anthropic-key" ``` - In your `evals.py`: + In your `evals.py` `_init_clients()` function: ```python from ragas.llms import llm_factory @@ -112,14 +114,14 @@ Your evaluation metrics need an LLM to score your application. Ragas works with llm = llm_factory("claude-3-5-sonnet-20241022", provider="anthropic") ``` -=== "Google Cloud" +=== "Google Gemini" Set up your Google credentials: ```sh export GOOGLE_API_KEY="your-google-api-key" ``` - In your `evals.py`: + In your `evals.py` `_init_clients()` function: ```python from ragas.llms import llm_factory @@ -128,7 +130,7 @@ Your evaluation metrics need an LLM to score your application. Ragas works with ``` === "Local Models (Ollama)" - Install and run Ollama locally, then in your `evals.py`: + Install and run Ollama locally, then in your `evals.py` `_init_clients()` function: ```python from ragas.llms import llm_factory diff --git a/docs/getstarted/index.md b/docs/getstarted/index.md index 15016bb7e..0931d1ea7 100644 --- a/docs/getstarted/index.md +++ b/docs/getstarted/index.md @@ -11,8 +11,8 @@ If you have any questions about Ragas, feel free to join and ask in the `#questi Let's get started! - +- [Quick Start: Get Running in 5 Minutes](./quickstart.md) - [Evaluate your first AI app](./evals.md) - [Run ragas metrics for evaluating RAG](rag_eval.md) - [Generate test data for evaluating RAG](rag_testset_generation.md) -- [Run your first experiment](experiments_quickstart.md) \ No newline at end of file +- [Run your first experiment](experiments_quickstart.md) diff --git a/docs/getstarted/quickstart.md b/docs/getstarted/quickstart.md index 0f6fe9fb7..13d81d8f5 100644 --- a/docs/getstarted/quickstart.md +++ b/docs/getstarted/quickstart.md @@ -1,29 +1,56 @@ -# Quick Start: Get Evaluations Running in a flash +# Quick Start: Get Evaluations Running in a Flash -Get started with Ragas in seconds. No installation needed! Just set your API key and run one command. +Get started with Ragas in minutes. Create a complete evaluation project with just a few commands. -## 1. Set Your API Key +## Step 1: Create Your Project -Choose your LLM provider: +Choose one of the following methods: + +=== "uvx (Recommended)" + No installation required. `uvx` automatically downloads and runs ragas: + + ```sh + uvx ragas quickstart rag_eval + cd rag_eval + ``` + +=== "Install Ragas First" + Install ragas first, then create the project: + + ```sh + pip install ragas + ragas quickstart rag_eval + cd rag_eval + ``` + +## Step 2: Install Dependencies + +Install the project dependencies: ```sh -# OpenAI (default) -export OPENAI_API_KEY="your-openai-key" +uv sync +``` -# Or use Anthropic Claude -export ANTHROPIC_API_KEY="your-anthropic-key" +Or if you prefer `pip`: + +```sh +pip install -e . ``` -## 2. Create Your Project +## Step 3: Set Your API Key -Create a complete project with a single command using `uvx` (no installation required): +Choose your LLM provider and set the environment variable: ```sh -uvx ragas quickstart rag_eval -cd rag_eval -``` +# OpenAI (default) +export OPENAI_API_KEY="your-openai-key" + +# Or use Anthropic Claude +export ANTHROPIC_API_KEY="your-anthropic-key" -That's it! You now have a fully configured evaluation project ready to use. +# Or use Google Gemini +export GOOGLE_API_KEY="your-google-key" +``` ## Project Structure @@ -32,100 +59,124 @@ Your generated project includes: ```sh rag_eval/ ├── README.md # Project documentation -├── evals.py # Evaluation configuration -├── rag.py # Your LLM application +├── pyproject.toml # Project configuration +├── rag.py # Your RAG application +├── evals.py # Evaluation workflow +├── export_csv.py # CSV export utility +├── __init__.py # Makes this a Python package └── evals/ - ├── datasets/ # Test data (CSV/JSON files) + ├── datasets/ # Test data files ├── experiments/ # Evaluation results └── logs/ # Execution logs ``` -## Run Evaluations +## Step 4: Run Your Evaluation -### Run the Evaluation - -Execute the evaluation on your dataset: +Run the evaluation script: ```sh -uvx ragas evals evals.py --dataset test_data --metrics faithfulness,answer_correctness +uv run python evals.py ``` -Or, if you prefer to use Python directly (after installing ragas): +Or if you installed with `pip`: ```sh python evals.py ``` -This will: -- Load test data from `evals/datasets/` -- Evaluate your application using pre-configured metrics -- Save results to `evals/experiments/` +The evaluation will: +- Load test data from the `load_dataset()` function in `evals.py` +- Query your RAG application with test questions +- Evaluate responses +- Display results in the console -### View Results +## Step 5: Export Results to CSV (Optional) -Results are saved as CSV files in `evals/experiments/`: +To export your evaluation results to a CSV file with a timestamp: -```python -import pandas as pd +```sh +uv run python export_csv.py +``` -# Load and view results -df = pd.read_csv('evals/experiments/results.csv') -print(df[['user_input', 'response', 'faithfulness', 'answer_correctness']]) +Or if you installed with `pip`: -# Quick statistics -print(f"Average Faithfulness: {df['faithfulness'].mean():.2f}") -print(f"Average Correctness: {df['answer_correctness'].mean():.2f}") +```sh +python export_csv.py ``` -... +This will: +- Run the complete evaluation workflow +- Save results to `evals/experiments/evaluation_results_YYYYMMDD_HHMMSS.csv` + +![](../_static/imgs/results/rag_eval_result.png) + +--- ## Customize Your Evaluation ### Add More Test Cases -Edit `evals/datasets/test_data.csv`: +Edit the `load_dataset()` function in `evals.py` to add more test questions: -```csv -user_input,response,reference -What is Ragas?,Ragas is an evaluation framework for LLM applications,Ragas provides objective metrics for evaluating LLM applications -How do metrics work?,Metrics score your LLM outputs,Metrics evaluate the quality and performance of LLM responses +```python +from ragas.dataset_schema import SingleTurnSample + +def load_dataset(): + """Load test dataset for evaluation.""" + data_samples = [ + SingleTurnSample( + user_input="What is Ragas?", + response="", # Will be filled by querying RAG + reference="Ragas is an evaluation framework for LLM applications", + retrieved_contexts=[], + ), + SingleTurnSample( + user_input="How do metrics work?", + response="", + reference="Metrics evaluate the quality and performance of LLM responses", + retrieved_contexts=[], + ), + # Add more test cases here + ] + + dataset = EvaluationDataset(samples=data_samples) + return dataset ``` ### Change the LLM Provider -In `evals.py`, update the LLM configuration: +In the `_init_clients()` function in `evals.py`, update the LLM factory call: ```python from ragas.llms import llm_factory -# Use Anthropic Claude -llm = llm_factory("claude-3-5-sonnet-20241022", provider="anthropic") +def _init_clients(): + """Initialize OpenAI client and RAG system.""" + openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) + rag_client = default_rag_client(llm_client=openai_client) -# Use Google Gemini -llm = llm_factory("gemini-1.5-pro", provider="google") + # Use Anthropic Claude instead + llm = llm_factory("claude-3-5-sonnet-20241022", provider="anthropic") -# Use local Ollama -llm = llm_factory("mistral", provider="ollama", base_url="http://localhost:11434") + # Or use Google Gemini + # llm = llm_factory("gemini-1.5-pro", provider="google") + + # Or use local Ollama + # llm = llm_factory("mistral", provider="ollama", base_url="http://localhost:11434") + + return openai_client, rag_client, llm ``` -### Select Different Metrics +### Customize Dataset and RAG System -In `evals.py`, modify the metrics list: +The template includes: +- `load_dataset()` - Define your test cases with `SingleTurnSample` +- `query_rag_system()` - Connect to your RAG system +- `evaluate_dataset()` - Implement your evaluation logic +- `display_results()` - Show results in the console +- `save_results_to_csv()` - Export results to CSV -```python -from ragas.metrics import ( - Faithfulness, # Does response match context? - AnswerCorrectness, # Is the answer correct? - ContextPrecision, # Is retrieved context relevant? - ContextRecall, # Is all needed context retrieved? -) - -# Use only specific metrics -metrics = [ - Faithfulness(), - AnswerCorrectness(), -] -``` +Edit these functions to customize your evaluation workflow. ## What's Next? diff --git a/examples/ragas_examples/rag_eval/evals.py b/examples/ragas_examples/rag_eval/evals.py index c88fcaad3..e030fe9c5 100644 --- a/examples/ragas_examples/rag_eval/evals.py +++ b/examples/ragas_examples/rag_eval/evals.py @@ -1,84 +1,156 @@ +import csv import os +import sys +from datetime import datetime +from pathlib import Path from openai import OpenAI -from ragas import Dataset, experiment +from ragas import EvaluationDataset +from ragas.dataset_schema import SingleTurnSample from ragas.llms import llm_factory -from ragas.metrics import DiscreteMetric -from .rag import default_rag_client +# Add the current directory to the path so we can import rag module +sys.path.insert(0, str(Path(__file__).parent)) +from rag import default_rag_client -openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) -rag_client = default_rag_client(llm_client=openai_client) -llm = llm_factory("gpt-4o", client=openai_client) +def _init_clients(): + """Initialize OpenAI client and RAG system.""" + openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) + rag_client = default_rag_client(llm_client=openai_client) + llm = llm_factory("gpt-4o", client=openai_client) + return openai_client, rag_client, llm -def load_dataset(): - dataset = Dataset( - name="test_dataset", - backend="local/csv", - root_dir=".", - ) +def load_dataset(): + """Load test dataset for evaluation.""" data_samples = [ - { - "question": "What is ragas 0.3", - "grading_notes": "- experimentation as the central pillar - provides abstraction for datasets, experiments and metrics - supports evals for RAG, LLM workflows and Agents", - }, - { - "question": "how are experiment results stored in ragas 0.3?", - "grading_notes": "- configured using different backends like local, gdrive, etc - stored under experiments/ folder in the backend storage", - }, - { - "question": "What metrics are supported in ragas 0.3?", - "grading_notes": "- provides abstraction for discrete, numerical and ranking metrics", - }, + SingleTurnSample( + user_input="What is ragas?", + response="", # Will be filled by querying RAG + reference="Ragas is an evaluation framework for LLM applications", + retrieved_contexts=[], # Empty for this simple example + ), + SingleTurnSample( + user_input="How are experiment results stored?", + response="", + reference="Results are stored under experiments/ folder using different backends", + retrieved_contexts=[], + ), + SingleTurnSample( + user_input="What metrics are supported?", + response="", + reference="Ragas provides discrete, numerical and ranking metrics", + retrieved_contexts=[], + ), ] - for sample in data_samples: - row = {"question": sample["question"], "grading_notes": sample["grading_notes"]} - dataset.append(row) + # Create an EvaluationDataset with the samples + dataset = EvaluationDataset(samples=data_samples) + return dataset + + +def query_rag_system(dataset): + """Query the RAG system for answers to each question.""" + print("Querying RAG system...") + _, rag_client, _ = _init_clients() + for sample in dataset.samples: + response_dict = rag_client.query(sample.user_input) + sample.response = response_dict.get("answer", "No response") + print(f" ✓ {sample.user_input} -> Got response") - # make sure to save it - dataset.save() return dataset -my_metric = DiscreteMetric( - name="correctness", - prompt="Check if the response contains points mentioned from the grading notes and return 'pass' or 'fail'.\nResponse: {response} Grading Notes: {grading_notes}", - allowed_values=["pass", "fail"], -) +def evaluate_dataset(dataset): + """Evaluate the dataset by comparing responses with ground truth.""" + print("\nEvaluating responses...") + + results = [] + for i, sample in enumerate(dataset.samples): + # Simple evaluation: check if response is not empty + is_valid = bool(sample.response and sample.response.strip() != "No response") + + result = { + "index": i + 1, + "user_input": sample.user_input, + "response": sample.response[:50] + "..." if len(sample.response) > 50 else sample.response, + "reference": sample.reference[:50] + "..." if len(sample.reference) > 50 else sample.reference, + "valid_response": is_valid, + } + results.append(result) + + status = "✓ PASS" if is_valid else "✗ FAIL" + print(f" {status} - {sample.user_input}") + + return results + +def save_results_to_csv(results, output_dir="evals/experiments"): + """Save evaluation results to a CSV file.""" + if not results: + print("No results to save") + return None -@experiment() -async def run_experiment(row): - response = rag_client.query(row["question"]) + # Create output directory if it doesn't exist + Path(output_dir).mkdir(parents=True, exist_ok=True) - score = my_metric.score( - llm=llm, - response=response.get("answer", " "), - grading_notes=row["grading_notes"], - ) + # Generate filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + csv_file = Path(output_dir) / f"evaluation_results_{timestamp}.csv" - experiment_view = { - **row, - "response": response.get("answer", ""), - "score": score.value, - "log_file": response.get("logs", " "), - } - return experiment_view + # Write results to CSV + fieldnames = ["index", "user_input", "response", "reference", "valid_response"] + with open(csv_file, "w", newline="", encoding="utf-8") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(results) + print(f"Results saved to: {csv_file}") + return csv_file -async def main(): + +def display_results(results): + """Display evaluation results in console.""" + print("\n" + "=" * 60) + print("EVALUATION SUMMARY") + print("=" * 60) + + if results: + passed = sum(1 for r in results if r["valid_response"]) + total = len(results) + print(f"\nResults: {passed}/{total} responses valid") + print("\nDetails:") + for result in results: + print(f" {result['index']}. {result['user_input']}") + print(f" Response: {result['response']}") + print(f" Reference: {result['reference']}") + + +def main(): + """Main evaluation workflow.""" + print("Starting RAG evaluation...\n") + + # Load test dataset dataset = load_dataset() - print("dataset loaded successfully", dataset) - experiment_results = await run_experiment.arun(dataset) - print("Experiment completed successfully!") - print("Experiment results:", experiment_results) + print(f"Loaded {len(dataset.samples)} test cases") + + # Query RAG system + dataset = query_rag_system(dataset) + + # Evaluate using Ragas + results = evaluate_dataset(dataset) + + # Display results + display_results(results) + + return results if __name__ == "__main__": - import asyncio + results = main() - asyncio.run(main()) + # Optionally save results to CSV + # Uncomment the line below to export results: + # save_results_to_csv(results) diff --git a/examples/ragas_examples/rag_eval/export_csv.py b/examples/ragas_examples/rag_eval/export_csv.py new file mode 100644 index 000000000..5392caa4f --- /dev/null +++ b/examples/ragas_examples/rag_eval/export_csv.py @@ -0,0 +1,73 @@ +import csv +import sys +from datetime import datetime +from pathlib import Path + +from evals import load_dataset, query_rag_system, evaluate_dataset + + +def save_results_to_csv(results, output_dir="evals/experiments"): + """Save evaluation results to a CSV file.""" + if not results: + print("No results to save") + return None + + # Create output directory if it doesn't exist + Path(output_dir).mkdir(parents=True, exist_ok=True) + + # Generate filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + csv_file = Path(output_dir) / f"evaluation_results_{timestamp}.csv" + + # Write results to CSV + fieldnames = ["index", "user_input", "response", "reference", "valid_response"] + with open(csv_file, "w", newline="", encoding="utf-8") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(results) + + print(f"Results saved to: {csv_file}") + return csv_file + + +def main(): + """Run evaluation and export results to CSV.""" + print("Running evaluation and exporting to CSV...\n") + + # Load test dataset + dataset = load_dataset() + print(f"Loaded {len(dataset.samples)} test cases") + + # Query RAG system + dataset = query_rag_system(dataset) + + # Evaluate using Ragas + results = evaluate_dataset(dataset) + + # Display results + print("\n" + "=" * 60) + print("EVALUATION SUMMARY") + print("=" * 60) + + if results: + passed = sum(1 for r in results if r["valid_response"]) + total = len(results) + print(f"\nResults: {passed}/{total} responses valid") + print("\nDetails:") + for result in results: + print(f" {result['index']}. {result['user_input']}") + print(f" Response: {result['response']}") + print(f" Reference: {result['reference']}") + + # Save to CSV + csv_file = save_results_to_csv(results) + + if csv_file: + print(f"\n✓ Results exported successfully to CSV") + else: + print("\n✗ Failed to export results") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/examples/ragas_examples/rag_eval/pyproject.toml b/examples/ragas_examples/rag_eval/pyproject.toml new file mode 100644 index 000000000..f7d1d146d --- /dev/null +++ b/examples/ragas_examples/rag_eval/pyproject.toml @@ -0,0 +1,26 @@ +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "rag-eval" +version = "0.1.0" +description = "RAG evaluation example using Ragas" +requires-python = ">=3.9" +dependencies = [ + "ragas[all]>=0.3.0", + "openai>=1.0.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0", +] + +[tool.setuptools] +py-modules = [] + +[tool.uv] +managed = true +# Note: When developing locally, use: +# uv sync --override ragas@path/to/ragas diff --git a/mkdocs.yml b/mkdocs.yml index ff3cc9408..673f45b0c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,7 +11,7 @@ nav: - "": index.md - 🚀 Get Started: - getstarted/index.md - # - Quick Start: getstarted/quickstart.md + - Quick Start: getstarted/quickstart.md - Installation: getstarted/install.md - Evaluate your first LLM App: getstarted/evals.md - Evaluate a simple RAG: getstarted/rag_eval.md diff --git a/src/ragas/cli.py b/src/ragas/cli.py index 6b901f685..70eb5f08a 100644 --- a/src/ragas/cli.py +++ b/src/ragas/cli.py @@ -648,7 +648,14 @@ def quickstart( console=console, ) as live: live.update(Spinner("dots", text="Copying template files...", style="green")) - shutil.copytree(source_path, output_path) + + # Copy template but exclude .venv and __pycache__ + def ignore_patterns(directory, files): + return { + f for f in files if f in {".venv", "__pycache__", "*.pyc", "uv.lock"} + } + + shutil.copytree(source_path, output_path, ignore=ignore_patterns) time.sleep(0.3) live.update( @@ -668,55 +675,107 @@ def quickstart( {template_info["description"]} -## Setup +## Quick Start + +### 1. Set Your API Key + +Choose your LLM provider: + +```bash +# OpenAI (default) +export OPENAI_API_KEY="your-openai-key" + +# Or use Anthropic Claude +export ANTHROPIC_API_KEY="your-anthropic-key" + +# Or use Google Gemini +export GOOGLE_API_KEY="your-google-key" +``` + +### 2. Install Dependencies + +Using `uv` (recommended): + +```bash +uv sync +``` + +Or using `pip`: + +```bash +pip install -e . +``` + +### 3. Run the Evaluation -1. Set your OpenAI API key (or other LLM provider): - ```bash - export OPENAI_API_KEY="your-api-key" - ``` +Using `uv`: -2. Install dependencies: - ```bash - pip install ragas openai - ``` +```bash +uv run python evals.py +``` -## Running the Example +Or using `pip`: -Run the evaluation: ```bash -python app.py +python evals.py ``` -Or run via the CLI: +### 4. Export Results to CSV + +Using `uv`: + +```bash +uv run python export_csv.py +``` + +Or using `pip`: + ```bash -ragas evals evals/evals.py --dataset test_data --metrics [metric_names] +python export_csv.py ``` ## Project Structure ``` {template}/ -├── app.py # Your application code (RAG system, agent, etc.) -├── evals/ # Evaluation-related code and data -│ ├── evals.py # Evaluation metrics and experiment definitions -│ ├── datasets/ # Test datasets -│ ├── experiments/ # Experiment results -│ └── logs/ # Evaluation logs and traces -└── README.md +├── README.md # This file +├── pyproject.toml # Project configuration +├── rag.py # Your RAG application code +├── evals.py # Evaluation workflow +├── export_csv.py # CSV export utility +├── __init__.py # Makes this a Python package +└── evals/ # Evaluation-related data + ├── datasets/ # Test datasets + ├── experiments/ # Experiment results (CSVs saved here) + └── logs/ # Evaluation logs and traces ``` -This structure separates your application code from evaluation code, making it easy to: -- Develop and test your application independently -- Run evaluations without mixing concerns -- Track evaluation results separately from application logic +## Customization + +### Modify the LLM Provider + +In `evals.py`, update the LLM configuration: + +```python +from ragas.llms import llm_factory + +# Use Anthropic Claude +llm = llm_factory("claude-3-5-sonnet-20241022", provider="anthropic") + +# Use Google Gemini +llm = llm_factory("gemini-1.5-pro", provider="google") + +# Use local Ollama +llm = llm_factory("mistral", provider="ollama", base_url="http://localhost:11434") +``` + +### Customize Test Cases + +Edit the `load_dataset()` function in `evals.py` to add or modify test cases. -## Next Steps +### Change Evaluation Metrics -1. Implement your application logic in `app.py` -2. Review and modify the metrics in `evals/evals.py` -3. Customize the dataset in `evals/datasets/` -4. Run experiments and analyze results -5. Iterate on your prompts and system design +Update the `my_metric` definition in `evals.py` to use different grading criteria. ## Documentation @@ -741,15 +800,12 @@ def quickstart( # Success message with next steps success(f"\n✓ Created {template_info['name']} project at: {output_path}") console.print("\n[bold cyan]Next Steps:[/bold cyan]") - console.print(f" 1. cd {output_path}") - console.print(" 2. export OPENAI_API_KEY='your-api-key'") - console.print(" 3. pip install ragas openai") - console.print(" 4. python app.py") - console.print("\n[bold]Project Structure:[/bold]") - console.print(" app.py - Your application code") - console.print(" evals/ - All evaluation-related code and data") - console.print("\n[bold]Quick Start:[/bold]") - console.print(f" cd {output_path} && python app.py\n") + console.print(f" cd {output_path}") + console.print(" uv sync") + console.print(" export OPENAI_API_KEY='your-api-key'") + console.print(" uv run python evals.py") + console.print("\n📚 For detailed instructions, see:") + console.print(" https://docs.ragas.io/en/latest/getstarted/quickstart/\n") @app.command() From a19abd8dc437ae9a8402128963455b4ad452425f Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 30 Oct 2025 15:03:25 +0530 Subject: [PATCH 6/9] fix: evals example --- docs/_static/imgs/results/rag_eval_result.png | Bin 40793 -> 61913 bytes examples/ragas_examples/rag_eval/evals.py | 180 ++++++------------ .../ragas_examples/rag_eval/export_csv.py | 45 +++-- 3 files changed, 83 insertions(+), 142 deletions(-) diff --git a/docs/_static/imgs/results/rag_eval_result.png b/docs/_static/imgs/results/rag_eval_result.png index 1892b53395e6e9375a84ff07bb28c51ea5a9cc33..5c86fab60a49b52d8beaa217b97609ee2f8d9702 100644 GIT binary patch literal 61913 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 z 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 U^*pgkoB;?tUHx3vIVCg!0Pkh}TmS$7 diff --git a/examples/ragas_examples/rag_eval/evals.py b/examples/ragas_examples/rag_eval/evals.py index e030fe9c5..b2b98e058 100644 --- a/examples/ragas_examples/rag_eval/evals.py +++ b/examples/ragas_examples/rag_eval/evals.py @@ -1,156 +1,88 @@ -import csv import os import sys -from datetime import datetime from pathlib import Path from openai import OpenAI -from ragas import EvaluationDataset -from ragas.dataset_schema import SingleTurnSample +from ragas import Dataset, experiment from ragas.llms import llm_factory +from ragas.metrics import DiscreteMetric -# Add the current directory to the path so we can import rag module +# Add the current directory to the path so we can import rag module when run as a script sys.path.insert(0, str(Path(__file__).parent)) from rag import default_rag_client - -def _init_clients(): - """Initialize OpenAI client and RAG system.""" - openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) - rag_client = default_rag_client(llm_client=openai_client) - llm = llm_factory("gpt-4o", client=openai_client) - return openai_client, rag_client, llm +openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) +rag_client = default_rag_client(llm_client=openai_client) +llm = llm_factory("gpt-4o", client=openai_client) def load_dataset(): - """Load test dataset for evaluation.""" + dataset = Dataset( + name="test_dataset", + backend="local/csv", + root_dir=".", + ) + data_samples = [ - SingleTurnSample( - user_input="What is ragas?", - response="", # Will be filled by querying RAG - reference="Ragas is an evaluation framework for LLM applications", - retrieved_contexts=[], # Empty for this simple example - ), - SingleTurnSample( - user_input="How are experiment results stored?", - response="", - reference="Results are stored under experiments/ folder using different backends", - retrieved_contexts=[], - ), - SingleTurnSample( - user_input="What metrics are supported?", - response="", - reference="Ragas provides discrete, numerical and ranking metrics", - retrieved_contexts=[], - ), + { + "question": "What is ragas 0.3", + "grading_notes": "- experimentation as the central pillar - provides abstraction for datasets, experiments and metrics - supports evals for RAG, LLM workflows and Agents", + }, + { + "question": "how are experiment results stored in ragas 0.3?", + "grading_notes": "- configured using different backends like local, gdrive, etc - stored under experiments/ folder in the backend storage", + }, + { + "question": "What metrics are supported in ragas 0.3?", + "grading_notes": "- provides abstraction for discrete, numerical and ranking metrics", + }, ] - # Create an EvaluationDataset with the samples - dataset = EvaluationDataset(samples=data_samples) - return dataset - - -def query_rag_system(dataset): - """Query the RAG system for answers to each question.""" - print("Querying RAG system...") - _, rag_client, _ = _init_clients() - for sample in dataset.samples: - response_dict = rag_client.query(sample.user_input) - sample.response = response_dict.get("answer", "No response") - print(f" ✓ {sample.user_input} -> Got response") + for sample in data_samples: + row = {"question": sample["question"], "grading_notes": sample["grading_notes"]} + dataset.append(row) + # make sure to save it + dataset.save() return dataset -def evaluate_dataset(dataset): - """Evaluate the dataset by comparing responses with ground truth.""" - print("\nEvaluating responses...") - - results = [] - for i, sample in enumerate(dataset.samples): - # Simple evaluation: check if response is not empty - is_valid = bool(sample.response and sample.response.strip() != "No response") - - result = { - "index": i + 1, - "user_input": sample.user_input, - "response": sample.response[:50] + "..." if len(sample.response) > 50 else sample.response, - "reference": sample.reference[:50] + "..." if len(sample.reference) > 50 else sample.reference, - "valid_response": is_valid, - } - results.append(result) - - status = "✓ PASS" if is_valid else "✗ FAIL" - print(f" {status} - {sample.user_input}") - - return results - +my_metric = DiscreteMetric( + name="correctness", + prompt="Check if the response contains points mentioned from the grading notes and return 'pass' or 'fail'.\nResponse: {response} Grading Notes: {grading_notes}", + allowed_values=["pass", "fail"], +) -def save_results_to_csv(results, output_dir="evals/experiments"): - """Save evaluation results to a CSV file.""" - if not results: - print("No results to save") - return None - # Create output directory if it doesn't exist - Path(output_dir).mkdir(parents=True, exist_ok=True) +@experiment() +async def run_experiment(row): + response = rag_client.query(row["question"]) - # Generate filename with timestamp - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - csv_file = Path(output_dir) / f"evaluation_results_{timestamp}.csv" + score = my_metric.score( + llm=llm, + response=response.get("answer", " "), + grading_notes=row["grading_notes"], + ) - # Write results to CSV - fieldnames = ["index", "user_input", "response", "reference", "valid_response"] - with open(csv_file, "w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - writer.writeheader() - writer.writerows(results) + experiment_view = { + **row, + "response": response.get("answer", ""), + "score": score.value, + "log_file": response.get("logs", " "), + } + return experiment_view - print(f"Results saved to: {csv_file}") - return csv_file - -def display_results(results): - """Display evaluation results in console.""" - print("\n" + "=" * 60) - print("EVALUATION SUMMARY") - print("=" * 60) - - if results: - passed = sum(1 for r in results if r["valid_response"]) - total = len(results) - print(f"\nResults: {passed}/{total} responses valid") - print("\nDetails:") - for result in results: - print(f" {result['index']}. {result['user_input']}") - print(f" Response: {result['response']}") - print(f" Reference: {result['reference']}") - - -def main(): - """Main evaluation workflow.""" - print("Starting RAG evaluation...\n") - - # Load test dataset +async def main(): dataset = load_dataset() - print(f"Loaded {len(dataset.samples)} test cases") - - # Query RAG system - dataset = query_rag_system(dataset) - - # Evaluate using Ragas - results = evaluate_dataset(dataset) - - # Display results - display_results(results) - - return results + print("dataset loaded successfully", dataset) + experiment_results = await run_experiment.arun(dataset) + print("Experiment completed successfully!") + print("Experiment results:", experiment_results) if __name__ == "__main__": - results = main() + import asyncio - # Optionally save results to CSV - # Uncomment the line below to export results: - # save_results_to_csv(results) + asyncio.run(main()) diff --git a/examples/ragas_examples/rag_eval/export_csv.py b/examples/ragas_examples/rag_eval/export_csv.py index 5392caa4f..af7b58db5 100644 --- a/examples/ragas_examples/rag_eval/export_csv.py +++ b/examples/ragas_examples/rag_eval/export_csv.py @@ -1,13 +1,14 @@ +import asyncio import csv import sys from datetime import datetime from pathlib import Path -from evals import load_dataset, query_rag_system, evaluate_dataset +from evals import load_dataset, run_experiment def save_results_to_csv(results, output_dir="evals/experiments"): - """Save evaluation results to a CSV file.""" + """Save experiment results to a CSV file.""" if not results: print("No results to save") return None @@ -19,8 +20,13 @@ def save_results_to_csv(results, output_dir="evals/experiments"): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") csv_file = Path(output_dir) / f"evaluation_results_{timestamp}.csv" + # Extract fieldnames from first result + if results: + fieldnames = list(results[0].keys()) + else: + fieldnames = [] + # Write results to CSV - fieldnames = ["index", "user_input", "response", "reference", "valid_response"] with open(csv_file, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() @@ -30,19 +36,21 @@ def save_results_to_csv(results, output_dir="evals/experiments"): return csv_file -def main(): +async def main(): """Run evaluation and export results to CSV.""" - print("Running evaluation and exporting to CSV...\n") + print("Running experiment and exporting to CSV...\n") # Load test dataset dataset = load_dataset() - print(f"Loaded {len(dataset.samples)} test cases") + print(f"Loaded {len(list(dataset))} test cases") - # Query RAG system - dataset = query_rag_system(dataset) + # Run experiment with DiscreteMetric + print("Running experiments with DiscreteMetric evaluation...\n") + results = await run_experiment.arun(dataset) - # Evaluate using Ragas - results = evaluate_dataset(dataset) + # Convert results to list if needed + if hasattr(results, '__iter__') and not isinstance(results, list): + results = list(results) # Display results print("\n" + "=" * 60) @@ -50,14 +58,15 @@ def main(): print("=" * 60) if results: - passed = sum(1 for r in results if r["valid_response"]) - total = len(results) - print(f"\nResults: {passed}/{total} responses valid") + print(f"\nTotal experiments: {len(results)}") print("\nDetails:") - for result in results: - print(f" {result['index']}. {result['user_input']}") - print(f" Response: {result['response']}") - print(f" Reference: {result['reference']}") + for i, result in enumerate(results, 1): + question = result.get("question", "N/A") + response = result.get("response", "N/A") + score = result.get("score", "N/A") + print(f" {i}. {question}") + print(f" Response: {response[:100]}...") + print(f" Score: {score}") # Save to CSV csv_file = save_results_to_csv(results) @@ -70,4 +79,4 @@ def main(): if __name__ == "__main__": - main() + asyncio.run(main()) From 0308082dc4f105d443a427c5869a6a8b9f7dc5aa Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 30 Oct 2025 15:07:04 +0530 Subject: [PATCH 7/9] remove other cli options till examples are updated; --- src/ragas/cli.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/ragas/cli.py b/src/ragas/cli.py index 70eb5f08a..09a0538b1 100644 --- a/src/ragas/cli.py +++ b/src/ragas/cli.py @@ -483,32 +483,34 @@ def quickstart( from pathlib import Path # Define available templates with descriptions + # Currently only rag_eval is available and fully tested templates = { "rag_eval": { "name": "RAG Evaluation", "description": "Evaluate a RAG (Retrieval Augmented Generation) system with custom metrics", "source_path": "ragas_examples/rag_eval", }, - "agent_evals": { - "name": "Agent Evaluation", - "description": "Evaluate AI agents with structured metrics and workflows", - "source_path": "ragas_examples/agent_evals", - }, - "benchmark_llm": { - "name": "LLM Benchmarking", - "description": "Benchmark and compare different LLM models with datasets", - "source_path": "ragas_examples/benchmark_llm", - }, - "prompt_evals": { - "name": "Prompt Evaluation", - "description": "Evaluate and compare different prompt variations", - "source_path": "ragas_examples/prompt_evals", - }, - "workflow_eval": { - "name": "Workflow Evaluation", - "description": "Evaluate complex LLM workflows and pipelines", - "source_path": "ragas_examples/workflow_eval", - }, + # Coming soon - not yet fully implemented: + # "agent_evals": { + # "name": "Agent Evaluation", + # "description": "Evaluate AI agents with structured metrics and workflows", + # "source_path": "ragas_examples/agent_evals", + # }, + # "benchmark_llm": { + # "name": "LLM Benchmarking", + # "description": "Benchmark and compare different LLM models with datasets", + # "source_path": "ragas_examples/benchmark_llm", + # }, + # "prompt_evals": { + # "name": "Prompt Evaluation", + # "description": "Evaluate and compare different prompt variations", + # "source_path": "ragas_examples/prompt_evals", + # }, + # "workflow_eval": { + # "name": "Workflow Evaluation", + # "description": "Evaluate complex LLM workflows and pipelines", + # "source_path": "ragas_examples/workflow_eval", + # }, } # If no template specified, list available templates @@ -533,7 +535,7 @@ def quickstart( console.print(" ragas quickstart [template_name]") console.print("\n[bold]Example:[/bold]") console.print(" ragas quickstart rag_eval") - console.print(" ragas quickstart agent_evals --output-dir ./my-project\n") + console.print(" ragas quickstart rag_eval --output-dir ./my-project\n") return # Validate template name From 6b350a222944d57de9e0cdb58c3de237de038c92 Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 30 Oct 2025 15:13:58 +0530 Subject: [PATCH 8/9] remove other cli options till examples are updated --- tests/unit/test_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index 2ad5f7ffc..5ca58f83e 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -44,8 +44,8 @@ def test_quickstart_list_templates(): assert result.exit_code == 0 assert "Available Ragas Quickstart Templates" in result.stdout assert "rag_eval" in result.stdout - assert "agent_evals" in result.stdout - assert "benchmark_llm" in result.stdout + # Note: Other templates (agent_evals, benchmark_llm, etc.) are currently hidden + # as they are not yet fully implemented. Only rag_eval is available. def test_quickstart_invalid_template(): From e6a08c5a04b74db820b18a536f921b9f6d1b39ff Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Thu, 30 Oct 2025 20:53:59 +0530 Subject: [PATCH 9/9] refactor: merge csv generation in evals.py and eliminated need for export_csv.py --- docs/getstarted/evals.md | 4 +- docs/getstarted/quickstart.md | 20 +---- examples/ragas_examples/rag_eval/evals.py | 5 ++ .../ragas_examples/rag_eval/export_csv.py | 82 ------------------- 4 files changed, 7 insertions(+), 104 deletions(-) delete mode 100644 examples/ragas_examples/rag_eval/export_csv.py diff --git a/docs/getstarted/evals.md b/docs/getstarted/evals.md index 3bb547bf5..5179d67ee 100644 --- a/docs/getstarted/evals.md +++ b/docs/getstarted/evals.md @@ -31,7 +31,6 @@ rag_eval/ ├── README.md # Project documentation and setup instructions ├── pyproject.toml # Project configuration for uv and pip ├── evals.py # Your evaluation workflow -├── export_csv.py # Optional CSV export utility ├── rag.py # Your RAG/LLM application ├── __init__.py # Makes this a Python package └── evals/ # Evaluation artifacts @@ -43,7 +42,6 @@ rag_eval/ **Key files to focus on:** - **`evals.py`** - Your evaluation workflow with dataset loading and evaluation logic -- **`export_csv.py`** - Optional standalone script to export results to CSV with timestamp - **`rag.py`** - Your RAG/LLM application code (query engine, retrieval, etc.) ## Understanding the Code @@ -54,7 +52,7 @@ In your generated project's `evals.py` file, you'll see the main workflow patter 2. **Query RAG System** - Get responses from your application 3. **Evaluate Responses** - Validate responses against ground truth 4. **Display Results** - Show evaluation summary in console -5. **Export to CSV** - Optional: Save results with timestamp +5. **Save Results** - Automatically saved to CSV in `evals/experiments/` directory The template provides modular functions you can customize: diff --git a/docs/getstarted/quickstart.md b/docs/getstarted/quickstart.md index 13d81d8f5..a81bf12e0 100644 --- a/docs/getstarted/quickstart.md +++ b/docs/getstarted/quickstart.md @@ -62,7 +62,6 @@ rag_eval/ ├── pyproject.toml # Project configuration ├── rag.py # Your RAG application ├── evals.py # Evaluation workflow -├── export_csv.py # CSV export utility ├── __init__.py # Makes this a Python package └── evals/ ├── datasets/ # Test data files @@ -89,24 +88,7 @@ The evaluation will: - Query your RAG application with test questions - Evaluate responses - Display results in the console - -## Step 5: Export Results to CSV (Optional) - -To export your evaluation results to a CSV file with a timestamp: - -```sh -uv run python export_csv.py -``` - -Or if you installed with `pip`: - -```sh -python export_csv.py -``` - -This will: -- Run the complete evaluation workflow -- Save results to `evals/experiments/evaluation_results_YYYYMMDD_HHMMSS.csv` +- Save results to CSV in the `evals/experiments/` directory ![](../_static/imgs/results/rag_eval_result.png) diff --git a/examples/ragas_examples/rag_eval/evals.py b/examples/ragas_examples/rag_eval/evals.py index b2b98e058..de33483a3 100644 --- a/examples/ragas_examples/rag_eval/evals.py +++ b/examples/ragas_examples/rag_eval/evals.py @@ -81,6 +81,11 @@ async def main(): print("Experiment completed successfully!") print("Experiment results:", experiment_results) + # Save experiment results to CSV + experiment_results.save() + csv_path = Path(".") / "experiments" / f"{experiment_results.name}.csv" + print(f"\nExperiment results saved to: {csv_path.resolve()}") + if __name__ == "__main__": import asyncio diff --git a/examples/ragas_examples/rag_eval/export_csv.py b/examples/ragas_examples/rag_eval/export_csv.py deleted file mode 100644 index af7b58db5..000000000 --- a/examples/ragas_examples/rag_eval/export_csv.py +++ /dev/null @@ -1,82 +0,0 @@ -import asyncio -import csv -import sys -from datetime import datetime -from pathlib import Path - -from evals import load_dataset, run_experiment - - -def save_results_to_csv(results, output_dir="evals/experiments"): - """Save experiment results to a CSV file.""" - if not results: - print("No results to save") - return None - - # Create output directory if it doesn't exist - Path(output_dir).mkdir(parents=True, exist_ok=True) - - # Generate filename with timestamp - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - csv_file = Path(output_dir) / f"evaluation_results_{timestamp}.csv" - - # Extract fieldnames from first result - if results: - fieldnames = list(results[0].keys()) - else: - fieldnames = [] - - # Write results to CSV - with open(csv_file, "w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - writer.writeheader() - writer.writerows(results) - - print(f"Results saved to: {csv_file}") - return csv_file - - -async def main(): - """Run evaluation and export results to CSV.""" - print("Running experiment and exporting to CSV...\n") - - # Load test dataset - dataset = load_dataset() - print(f"Loaded {len(list(dataset))} test cases") - - # Run experiment with DiscreteMetric - print("Running experiments with DiscreteMetric evaluation...\n") - results = await run_experiment.arun(dataset) - - # Convert results to list if needed - if hasattr(results, '__iter__') and not isinstance(results, list): - results = list(results) - - # Display results - print("\n" + "=" * 60) - print("EVALUATION SUMMARY") - print("=" * 60) - - if results: - print(f"\nTotal experiments: {len(results)}") - print("\nDetails:") - for i, result in enumerate(results, 1): - question = result.get("question", "N/A") - response = result.get("response", "N/A") - score = result.get("score", "N/A") - print(f" {i}. {question}") - print(f" Response: {response[:100]}...") - print(f" Score: {score}") - - # Save to CSV - csv_file = save_results_to_csv(results) - - if csv_file: - print(f"\n✓ Results exported successfully to CSV") - else: - print("\n✗ Failed to export results") - sys.exit(1) - - -if __name__ == "__main__": - asyncio.run(main())