Разработка RAG на основе метрик
В недавней презентации на Zilliz Unstructured Data Meetup Джитин Джеймс и Шахул Эс, мейнтейнеры Ragas, поделились идеями о применении разработки на основе метрик для оценки систем Retrieval Augmented Generation (RAG). Разработчики могут настраивать свои системы на основе результатов оценки для повышения производительности.
В своем выступлении Джитин и Шахул обсудили как теоретические основы, так и практическое применение оценки RAG-систем. Они объяснили, как понимание теории, лежащей в основе кода оценки, может дать более глубокое представление о его функциональности. После этой части они продемонстрировали процесс оценки на реальной RAG-системе на базе Milvus, ведущей open-source векторной базы данных, известной своей эффективностью в поиске по сходству и AI-приложениях.
Для более подробного понимания посмотрите запись выступления на митапе.
Как оценивать производительность RAG-систем
Шахул затронул вопрос о том, как Ragas вычисляет правдивость ответа, сгенерированного RAG-системой. Это важная метрика оценки, поскольку ответ — это то, что видит конечный пользователь. Давайте немного замедлимся и подробно разберем это. Ragas использует следующую формулу для вычисления правдивости ответа:
Рис. 1 — Формула вычисления метрики answer_truthfulness в Ragas
Расчет корректности ответа в системах Retrieval-Augmented Generation (RAG)
Процесс включает две основные метрики: фактическое сходство и семантическое сходство. Эти метрики помогают определить качество и релевантность сгенерированного ответа по сравнению с эталонной истиной.
Ключевые метрики
Фактическое сходство:
- Эта метрика измеряет фактическую корректность сгенерированного ответа путем сравнения наличия фактической информации в эталонной истине и сгенерированном ответе.
Семантическое сходство:
- Эта метрика измеряет, насколько семантически сгенерированный ответ похож на эталонную истину, гарантируя, что смысл, переданный сгенерированным ответом, соответствует эталонной истине.
Шаги для расчета корректности ответа
Определите истинно положительные (TP), ложноположительные (FP) и ложноотрицательные (FN) элементы:
Истинно положительные: Утверждения, корректно присутствующие как в эталонной истине, так и в сгенерированном ответе.
Ложноположительные: Утверждения, присутствующие в сгенерированном ответе, но отсутствующие в эталонной истине.
Ложноотрицательные: Утверждения, присутствующие в эталонной истине, но отсутствующие в сгенерированном ответе.
Рассчитайте F1-меру:
- F1-мера — это среднее между точностью и полнотой, обеспечивающее баланс между ними. Формула F1-меры показана на Рисунке 1.
Практический пример
Рассмотрим утверждение эталонной истины и сгенерированный ответ:
Эталонная истина: "Алан Тьюринг разработал концепцию машины Тьюринга."
Сгенерированный ответ: "Алан Тьюринг известен разработкой концепции машины Тьюринга и искусственного интеллекта."
Шаг 1: Определите TP, FP и FN
Истинно положительные (TP):
- "Алан Тьюринг разработал концепцию машины Тьюринга."
Ложноположительные (FP):
- "Алан Тьюринг известен разработкой искусственного интеллекта."
Ложноотрицательные (FN):
- Нет (поскольку сгенерированный ответ включает все элементы эталонной истины).
Шаг 2: Рассчитайте F1-меру
TP = 1
FP = 1
FN = 0
F1 = 1 / (1 + 0.5 * (1 + 0))
= 1 / 1.5
≈ 0.67
Интерпретация<
Фактическое сходство: Сгенерированный ответ фактически корректен, поскольку включает ключевую информацию из эталонной истины.
Семантическое сходство: Сгенерированный ответ сохраняет семантическое соответствие эталонной истине, хотя и включает дополнительную информацию.
F1-мера объединяет эти аспекты, чтобы предоставить показатель корректности ответа, балансируя точность и полноту. Этот метод помогает оценить, насколько хорошо сгенерированный ответ соответствует эталонной истине с точки зрения как фактического содержания, так и общего смысла.
Но правдивость ответа — не единственная метрика, которую вы можете использовать для оценки ваших RAG-систем. Другие метрики — это faithfulnes, релевантность ответа, полнота контекста и точность контекста. Давайте практически рассмотрим, как можно оценить и улучшить RAG-систему на базе Mivus.
Оценка и улучшение RAG-системы на базе Milvus
Теперь, когда вы поняли теоретическую основу оценки RAG-систем, давайте перейдем к практическому примеру RAG, построенному с использованием векторной базы данных Milvus. Мы рассмотрим настройку, реализацию и оценку RAG-системы. Затем, основываясь на результатах, мы посмотрим, как можно улучшить систему.
Настройка среды
Сначала нам нужно настроить среду разработки. Для этого сначала установите все необходимые библиотеки:
!pip install pymilvus[model] ragas langchain langchain_openai python-dotenv nest_asyncio pypdf langchain_community
Давайте разберем, для чего будет использоваться каждая библиотека в коде:
Pymilvus[model]поможет вам подключаться к Milvus, создавать коллекции, вставлять данные и выполнять поиск по сходству.Ragasпредоставит метрики оценки, сгенерирует синтетические тестовые наборы и оценит RAG-конвейеры.Langchainбудет загружать документы, разбивать их на фрагменты и подготавливать текстовые данные для эмбеддинга и запросов.Langchain_openaiбудет взаимодействовать с моделями OpenAI, генерировать эмбеддинги и отвечать на вопросы с помощью вызовов API.Python-dotenvбудет загружать переменные окружения из файла .env для управления конфиденциальной информацией, такой как API-ключи.Nest_asyncioпозволит выполнять вложенные асинхронные операции в синхронных средах. Особенно если вы используете блокноты Jupiter.Pypdfбудет загружать PDF-документы, извлекать содержимое и подготавливать текст к обработке.Langchain_communityпредоставит дополнительные загрузчики документов и утилиты для интеграции ресурсов, внесенных сообществом. После установки библиотек импортируйте их в свой код и настройте API-ключи:
import os
import pandas as pd
import nest_asyncio
import openai
from pymilvus import MilvusClient, model
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_recall,
context_precision,
answer_correctness,
)
from ragas.testset.generator import TestsetGenerator
from ragas.testset.evolutions import simple, reasoning, multi_context
from langchain_community.document_loaders import DirectoryLoader
from langchain.document_loaders import PyPDFLoader
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from dotenv import load_dotenv
from datasets import Dataset
# Load environment variables
# Apply nest_asyncio to allow nested event loops
nest_asyncio.apply()
# Set up OpenAI API key
os.environ["OPENAI_API_KEY"] = "Your API Key"
client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
Импорт библиотек и модулей в код позволит вам вызывать и использовать их функции. Ключ OpenAI поможет вам пройти аутентификацию в OpenAI при выполнении API-вызовов. Если у вас его нет, перейдите на страницу OpenAI API и сгенерируйте его.
Загрузка и подготовка документов
Далее мы загрузим наши документы и подготовим их к обработке:
# Load PDF documents from a directory
pdf_loader = DirectoryLoader("/content/data", loader_cls=PyPDFLoader)
documents = pdf_loader.load()
# Ensure each document has a filename in its metadata
for document in documents:
document.metadata['filename'] = document.metadata['source'
Код загружает PDF-документы из директории с помощью класса PyPDFLoader. Затем он создает экземпляр DirectoryLoader, загружает документы и проходит по каждому документу, чтобы добавить атрибут filename в его метаданные. Это гарантирует, что каждый документ будет правильно идентифицирован и управляться вместе с соответствующим именем файла. Этот шаг крайне важен, поскольку он формирует базу знаний для нашей RAG-системы. В этом примере мы используем PDF-документы, но при необходимости вы можете адаптировать это для других типов документов.
Генерация тестового набора
Теперь, когда вы загрузили данные, вам нужен разнообразный набор тестовых вопросов, чтобы эффективно оценить нашу RAG-систему. Мы будем использовать фреймворк Ragas для генерации синтетического тестового набора.
# Initialize OpenAI models for test set generation
generator_llm = ChatOpenAI(model="gpt-3.5-turbo-16k")
critic_llm = ChatOpenAI(model="gpt-4")
embeddings = OpenAIEmbeddings()
# Create a TestsetGenerator
generator = TestsetGenerator.from_langchain(
generator_llm,
critic_llm,
embeddings
)
# Generate synthetic test set with 10 samples
testset = generator.generate_with_langchain_docs(documents, test_size=10, distributions={simple: 0.5, reasoning: 0.25, multi_context: 0.25})
# Convert test set to Pandas DataFrame
testset_df = testset.to_pandas()
print(testset_df)
Этот код инициализирует две языковые модели (gpt-3.5-turbo-16k для генерации тестовых наборов и gpt-4 для оценки) и модель эмбеддингов от OpenAI. Затем он создает экземпляр TestsetGenerator с использованием этих моделей. Генератор создает синтетический тестовый набор из 10 примеров на основе загруженных PDF-документов с заданными распределениями для разных типов вопросов. Этот тестовый набор поможет вам оценить различные аспекты производительности вашей RAG-системы, включая ее способность обрабатывать простые запросы, задачи на рассуждение и вопросы, требующие нескольких контекстов. Вот пример сгенерированного тестового набора.
Рис. 2 — Тестовый набор, сгенерированный с помощью Ragas
Ground truth — это фактический ответ на заданный вопрос. Позже мы будем использовать его, чтобы измерить, насколько хорошо работает наша RAG-система, сравнивая, насколько близок ее вывод к этим эталонным ответам. Как отмечает Shahul в выступлении, генерация синтетического тестового набора с помощью Ragas не означает, что его нужно использовать вслепую. Вам необходимо отфильтровать и использовать только те примеры, которые вам нужны.
Поскольку у вас есть тестовый набор, давайте создадим простую RAG-систему на базе Milvus, а затем оценим ее с помощью приведенного выше тестового набора.
Настройка Milvus и вставка эмбеддингов документов
Теперь давайте настроим нашу векторную базу данных Milvus и вставим эмбеддинги наших документов. Убедитесь, что Milvus установлен и запущен. Если нет, следуйте этому подробному руководству по установке Milvus.
# Connect to Milvus instance
milvus_client = MilvusClient(uri="http://localhost:19530")
# Define collection name
collection_name = "pdf_collection"
# Drop the collection if it already exists
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
# Create a new collection
milvus_client.create_collection(
collection_name=collection_name,
dimension=768, # Dimension of vectors
overwrite=True
)
# Initialize the embedding function
embedding_fn = model.DefaultEmbeddingFunction()
# Extract document texts and generate embeddings
expanded_docs = [doc.page_content for doc in documents]
expanded_vectors = embedding_fn.encode_documents(expanded_docs)
# Prepare data for insertion
expanded_data = [
{"id": i, "vector": expanded_vectors[i], "text": expanded_docs[i], "subject": "pdf_documents"}
for i in range(len(expanded_vectors))
]
# Insert expanded data into the collection
milvus_client.insert(data=expanded_data, collection_name=collection_name)
Приведенный выше код подключается к экземпляру Milvus и настраивает коллекцию с именем pdf_collection для хранения векторов документов. Если коллекция уже существует, он удаляет и заново создает ее с размерностью 768 для векторов. Он инициализирует функцию эмбеддингов, извлекает текст из загруженных документов и генерирует их эмбеддинги. Затем код подготавливает данные с эмбеддингами и связанным текстом и вставляет эти данные в коллекцию Milvus. Именно на этом этапе Milvus действительно проявляет свои сильные стороны, поскольку позволяет выполнять быстрый и эффективный поиск по сходству среди сохраненных эмбеддингов данных, что критически важно для части извлечения в вашей RAG-системе.
Выполнение поиска по сходству
Теперь, когда ваши документы проиндексированы в Milvus, вы можете выполнять поиск по сходству, чтобы получать релевантные контексты для наших вопросов:
# Define the search parameters
search_params = {"metric_type": "COSINE", "params": {"nprobe": 20}} # nprobe for better recall
# Perform the search
query_vectors = embedding_fn.encode_queries(testset_df['question'].tolist())
results = milvus_client.search(
collection_name=collection_name,
data=query_vectors,
anns_field="vector", # specify the vector field name
search_params=search_params,
limit=3, # number of top results to retrieve
output_fields=["id", "text"]
)
Приведенный выше код настраивает параметры поиска для коллекции Milvus с использованием косинусного сходства и значения nprobe, равного 20, для лучшего recall. Он кодирует вопросы тестового набора в векторы запросов и выполняет поиск в коллекции pdf_collection, извлекая три наиболее похожих вектора для каждого запроса. Результаты поиска включают IDs и text совпадающих документов.
Генерация ответов с использованием большой языковой модели
После извлечения релевантных контекстов вы можете генерировать ответы с помощью языковой модели. В данном случае мы будем использовать GPT 3.5 turbo.
# Function to generate answers using OpenAI
def generate_answer(question, contexts):
context_text = " ".join(contexts)
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Context: {context_text}nnQuestion: {question}nAnswer:"}
]
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
max_tokens=100,
temperature=0.7,
)
return response.choices[0].message.content.strip()
# Extract contexts and generate answers using OpenAI
contexts = []
answers = []
for i, result in enumerate(results):
context = [match['entity']['text'] for match in result]
contexts.append(context)
question = testset_df['question'].iloc[i]
answer = generate_answer(question, context)
answers.append(answer)
Код определяет функцию generate_answer, которая использует модель OpenAI gpt-3.5-turbo для генерации ответов на основе заданного вопроса и контекста. Он подготавливает сообщения для вызова API, объединяет контекст и отправляет запрос к модели. Затем он извлекает и возвращает сгенерированный ответ. Последующий цикл проходит по результатам поиска и использует функцию generate_answer для генерации ответов на каждый вопрос в тестовом наборе, сохраняя контексты и ответы в списках. Вопросы, используемые в цикле для генерации ответов, являются теми же, что были сгенерированы в тестовом наборе.
Поскольку теперь у вас есть ответы, сгенерированные RAG-системой, и эталонные ответы, давайте оценим, насколько хорошо работает RAG-система.
Оценка RAG-системы
Наконец, мы оценим производительность нашей RAG-системы с помощью метрик, обсуждавшихся ранее:
# Ensure all lists are the same length
min_length = min(len(testset_df['question']), len(testset_df['ground_truth']), len(answers), len(contexts))
questions = testset_df['question'][:min_length]
ground_truths = testset_df['ground_truth'][:min_length]
answers = answers[:min_length]
contexts = contexts[:min_length]
# Create the dataset with correct column names
data = {
"question": questions,
"answer": answers,
"contexts": contexts,
"ground_truth": ground_truths
}
from datasets import Dataset
# Convert dict to dataset
dataset = Dataset.from_pandas(pd.DataFrame(data))
# Evaluate using RAGAS
metrics = evaluate(
dataset=dataset,
metrics=[
answer_correctness,
context_precision,
context_recall,
faithfulness,
answer_relevancy,
],
raise_exceptions=False #handle async issues
)
# Convert result to DataFrame for better readability
result_df = metrics.to_pandas()
print(result_df)
Код обеспечивает согласованность процесса оценки, обрезая все релевантные списки до одинаковой минимальной длины. Это предотвращает несовпадение длин, которое могло бы вызвать ошибки во время оценки. Затем он создает словарь с этими обрезанными списками и преобразует его в Pandas DataFrame, который затем преобразуется в Hugging Face Dataset. Этот структурированный набор данных позволяет проводить систематическую оценку с использованием фреймворка RAGAS, который сравнивает сгенерированные ответы с эталонными ответами. Метрики оценки (корректность ответа, точность контекста, полнота контекста, достоверность и релевантность ответа) дают оценку производительности RAG-конвейера.
Посмотрите на результаты оценки ниже:
Рис. 3 — результаты оценки RAG-системы с помощью Ragas
Точность контекста нашей RAG-системы и полнота превосходны, а релевантность ответов заслуживает похвалы. Но корректность ответов довольно низкая.
Сторона извлечения работает хорошо благодаря превосходной полноте и точности контекста. Поэтому мы можем сделать вывод, что на корректность ответов может влиять генеративная часть. Чтобы улучшить это, вы можете использовать более мощную модель генерации ответов, например GPT-4.
Заключение
Оценка и улучшение систем Retrieval-Augmented Generation (RAG) — это тонкая, но важная задача в области поиска информации на основе ИИ. Используя подход, основанный на метриках, как продемонстрировали Jithin James и Shahul Es, вы можете систематически совершенствовать свои RAG-системы, чтобы они предоставляли точную, релевантную и надежную информацию.
Для получения более подробной информации по этой теме посмотрите запись выступления на YouTube.
Дополнительные ресурсы
Читать далее

Introducing Zilliz CLI and Agent Skills for Zilliz Cloud
Manage your vector database from your terminal or AI coding agent. Zilliz CLI and Agent Skills work with Claude Code, Cursor, Codex, and Copilot.

VidTok: Rethinking Video Processing with Compact Tokenization
VidTok tokenizes videos to reduce redundancy while preserving spatial and temporal details for efficient processing.

DeepRAG: Thinking to Retrieval Step by Step for Large Language Models
Discover DeepRAG, an advanced retrieval-augmented generation (RAG) model that improves LLM accuracy by retrieving only essential data through step-by-step reasoning.


