Menu Docs
Página inicial do Docs
/ /

Avalie seu aplicativo RAG

Neste tutorial, você aprenderá como avaliar um aplicaçãoRAG. A avaliação ajuda você a escolher o modelo correto, garantir que o desempenho do seu modelo seja convertido do protótipo para a produção e capturar regressões de desempenho.

Especificamente, você executa as seguintes ações:

  • Configure o ambiente.

  • Baixe um conjunto de dados de avaliação.

  • Crie chunks e incorporações de documento .

  • Faça a ingestão das incorporações no Atlas.

  • Compare modelos de incorporação para recuperação.

  • Compare modelos de conclusão para geração.

  • Meça o desempenho geral do RAG.

  • Acompanhe o desempenho ao longo do tempo com MongoDB Charts.

Observação

Este tutorial se concentra em avaliar aplicativos LLM, não modelos LLM. A avaliação de modelos de LLM envolve a medição do desempenho de um determinado modelo em diferentes tarefas. A avaliação do aplicaçãoLLM trata da avaliação de diferentes componentes de um aplicação LLM , como prompts e recuperadores, bem como do sistema como um todo.

Trabalhe com uma versão executável deste tutorial como um bloco de anotações Python.

Este tutorial usa a estrutura de avaliação de código aberto RAGAS para medir o desempenho do RAG com as seguintes métricas:

  • Métricas de recuperação: a precisão do contexto e a recuperação do contexto medem a eficiência com que o recuperador encontra informações relevantes.

  • Métricas de geração: a fidelidade e a relevância da resposta medem a eficiência com que seu LLM gera respostas precisas e relevantes.

  • Métricas gerais: a similaridade da resposta e a correção da resposta comparam as respostas geradas com a verdade fundamental.

Para saber mais sobre essas métricas, consulte Métricas RGAS na documentação do RGAS.

Este tutorial usa o conjunto de dados rasas-wikiqa do Abraçando o Face, que contém aproximadamente 230 perguntas de conhecimento geral com respostas de verdade.

Para concluir este tutorial, você deve ter o seguinte:

  • Um cluster do MongoDB Atlas executando o MongoDB versão 6.0.11 ou posterior. Certifique-se de que seu endereço IP esteja na lista de acesso do seu projeto.

  • Uma chave de API OpenAI para usar os modelos de incorporação e conclusão de chat do OpenAI.

  • Um terminal configurado com o seguinte:

    • Python 3.10 ou posterior.

    • Um ambiente para executar blocos de anotações Python interativos, como VS Code ou Jupyter Notebook.

1

Execute o seguinte comando para instalar as bibliotecas necessárias:

pip install -qU datasets ragas langchain langchain-mongodb langchain-openai \
pymongo pandas tqdm matplotlib seaborn nest_asyncio
2

Execute o seguinte código em seu bloco de anotações para configurar a string de conexão do MongoDB e a chave de API OpenAI:

import getpass
import os
from openai import OpenAI
MONGODB_URI = getpass.getpass("Enter your MongoDB connection string:")
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API Key:")
openai_client = OpenAI()

Baixe o conjunto de dados rasas-wikiqa do Abraçando o Face e converta-o em um dataframe de Pandas:

from datasets import load_dataset
import pandas as pd
data = load_dataset("explodinggradients/ragas-wikiqa", split="train")
df = pd.DataFrame(data)

O conjunto de dados contém as seguintes colunas:

  • question: Perguntas do usuário

  • correct_answer: Respostas da verdade fundamental

  • context: Lista de textos de referência para responder às perguntas

Divida os textos de referência em blocos menores antes de incorporar:

from langchain.text_splitter import RecursiveCharacterTextSplitter
# Split text by tokens using the tiktoken tokenizer
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base", keep_separator=False, chunk_size=200, chunk_overlap=30
)
def split_texts(texts):
chunked_texts = []
for text in texts:
chunks = text_splitter.create_documents([text])
chunked_texts.extend([chunk.page_content for chunk in chunks])
return chunked_texts
# Split the context field into chunks
df["chunks"] = df["context"].apply(lambda x: split_texts(x))
# Aggregate list of all chunks
all_chunks = df["chunks"].tolist()
docs = [item for chunk in all_chunks for item in chunk]

Dica

Experimente diferentes estratégias de agrupamento ao avaliar a recuperação. Este tutorial se concentra em avaliar modelos de incorporação.

Incorpore os documentos em partes e ingira-os no Atlas. Crie collections separadas para cada modelo de incorporação que você deseja comparar:

1

Crie uma função para gerar incorporações usando a API OpenAI:

from typing import List
def get_embeddings(docs: List[str], model: str) -> List[List[float]]:
"""Generate embeddings using the OpenAI API."""
docs = [doc.replace("\n", " ") for doc in docs]
response = openai_client.embeddings.create(input=docs, model=model)
return [r.embedding for r in response.data]
2

Incorpore e ingira os documentos fragmentados nas collections do Atlas :

from pymongo import MongoClient
from tqdm.auto import tqdm
client = MongoClient(MONGODB_URI)
DB_NAME = "ragas_evals"
db = client[DB_NAME]
batch_size = 128
EVAL_EMBEDDING_MODELS = ["text-embedding-ada-002", "text-embedding-3-small"]
for model in EVAL_EMBEDDING_MODELS:
embedded_docs = []
print(f"Getting embeddings for the {model} model")
for i in tqdm(range(0, len(docs), batch_size)):
end = min(len(docs), i + batch_size)
batch = docs[i:end]
batch_embeddings = get_embeddings(batch, model)
batch_embedded_docs = [
{"text": batch[i], "embedding": batch_embeddings[i]}
for i in range(len(batch))
]
embedded_docs.extend(batch_embedded_docs)
collection = db[model]
collection.delete_many({})
collection.insert_many(embedded_docs)
print(f"Finished inserting embeddings for the {model} model")
3

Crie um índice do MongoDB Vector Search para cada coleção. Use a seguinte definição de índice com o nome de índice vector_index:

{
"fields": [
{
"numDimensions": 1536,
"path": "embedding",
"similarity": "cosine",
"type": "vector"
}
]
}

Para saber como criar o índice, consulte Criar um índice de Vector Search do MongoDB .

Dica

text-embedding-ada-002 e text-embedding-3-small têm dimensões 1536, portanto, a mesma definição de índice funciona para ambas as coleções.

Para garantir a recuperação do contexto correto para o LLM, compare diferentes modelos de incorporação. Este tutorial compara text-embedding-ada-002 e.text-embedding-3-small

1

Crie uma função para obter um recuperador de armazenamento de vetor usando LangChain e MongoDB Atlas:

from langchain_openai import OpenAIEmbeddings
from langchain_mongodb import MongoDBAtlasVectorSearch
from langchain_core.vectorstores import VectorStoreRetriever
def get_retriever(model: str, k: int) -> VectorStoreRetriever:
"""
Get a vector store retriever for a given embedding model.
Args:
model (str): Embedding model to use
k (int): Number of results to retrieve
Returns:
VectorStoreRetriever: A vector store retriever object
"""
embeddings = OpenAIEmbeddings(model=model)
vector_store = MongoDBAtlasVectorSearch.from_connection_string(
connection_string=MONGODB_URI,
namespace=f"{DB_NAME}.{model}",
embedding=embeddings,
index_name="vector_index",
text_key="text",
)
retriever = vector_store.as_retriever(
search_type="similarity", search_kwargs={"k": k}
)
return retriever
2

Extraia as perguntas e respostas da verdade do campo do seu conjunto de dados:

QUESTIONS = df["question"].to_list()
GROUND_TRUTH = df["correct_answer"].tolist()
3

Use as métricas context_precision e context_recall da biblioteca RGAS para avaliar cada modelo de incorporação:

from datasets import Dataset
from ragas import evaluate, RunConfig
from ragas.metrics import context_precision, context_recall
import nest_asyncio
# Allow nested use of asyncio (used by RAGAS)
nest_asyncio.apply()
for model in EVAL_EMBEDDING_MODELS:
data = {"question": [], "ground_truth": [], "contexts": []}
data["question"] = QUESTIONS
data["ground_truth"] = GROUND_TRUTH
retriever = get_retriever(model, 2)
# Get relevant documents for the evaluation dataset
for i in tqdm(range(0, len(QUESTIONS))):
data["contexts"].append(
[doc.page_content for doc in retriever.invoke(QUESTIONS[i])]
)
# RAGAS expects a Dataset object
dataset = Dataset.from_dict(data)
# RAGAS runtime settings to avoid hitting OpenAI rate limits
run_config = RunConfig(max_workers=4, max_wait=180)
result = evaluate(
dataset=dataset,
metrics=[context_precision, context_recall],
run_config=run_config,
raise_exceptions=False,
)
print(f"Result for the {model} model: {result}")

Os resultados da avaliação dos modelos de incorporação no conjunto de dados de amostra são os seguintes:

Modelo
Precisão de contexto
Lembrete de contexto

text-embedding-ada-002

0.9310

0.8561

text-embedding-3-small

0.9116

0.8826

Com base nesses resultados, text-embedding-ada-002 classifica os resultados mais relevantes em primeiro lugar, mas text-embedding-3-small recupera contextos que estão mais alinhados com as respostas da verdade fundamental. Para este tutorial, utilize text-embedding-3-small como modelo de incorporação.

Agora que você selecionou o melhor modelo de incorporação, compare os modelos de conclusão para o componente de geração do seu aplicação RAG.

1

Crie uma função que construa uma cadeia RAG usando LangChain:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.base import RunnableSequence
from langchain_core.output_parsers import StrOutputParser
def get_rag_chain(retriever: VectorStoreRetriever, model: str) -> RunnableSequence:
"""
Create a basic RAG chain.
Args:
retriever (VectorStoreRetriever): Vector store retriever object
model (str): Chat completion model to use
Returns:
RunnableSequence: A RAG chain
"""
# Generate context using the retriever, and pass the user question through
retrieve = {
"context": retriever
| (lambda docs: "\n\n".join([d.page_content for d in docs])),
"question": RunnablePassthrough(),
}
template = """Answer the question based only on the following context: \
{context}
Question: {question}
"""
# Define the chat prompt
prompt = ChatPromptTemplate.from_template(template)
# Define the model for chat completion
llm = ChatOpenAI(temperature=0, model=model)
# Parse output as a string
parse_output = StrOutputParser()
# RAG chain
rag_chain = retrieve | prompt | llm | parse_output
return rag_chain
2

Use as métricas faithfulness e answer_relevancy para avaliar diferentes modelos de conclusão:

from ragas.metrics import faithfulness, answer_relevancy
for model in ["gpt-3.5-turbo-1106", "gpt-3.5-turbo"]:
data = {"question": [], "ground_truth": [], "contexts": [], "answer": []}
data["question"] = QUESTIONS
data["ground_truth"] = GROUND_TRUTH
# Use the best embedding model from the retriever evaluation
retriever = get_retriever("text-embedding-3-small", 2)
rag_chain = get_rag_chain(retriever, model)
for i in tqdm(range(0, len(QUESTIONS))):
question = QUESTIONS[i]
data["answer"].append(rag_chain.invoke(question))
data["contexts"].append(
[doc.page_content for doc in retriever.invoke(question)]
)
# RAGAS expects a Dataset object
dataset = Dataset.from_dict(data)
# RAGAS runtime settings to avoid hitting OpenAI rate limits
run_config = RunConfig(max_workers=4, max_wait=180)
result = evaluate(
dataset=dataset,
metrics=[faithfulness, answer_relevancy],
run_config=run_config,
raise_exceptions=False,
)
print(f"Result for the {model} model: {result}")

Os resultados da avaliação para os modelos de conclusão no conjunto de dados de amostra são os seguintes:

Modelo
Fidelidade
Relevância da resposta

gpt-3.5-turbo

0.9714

0.9087

gpt-3.5-turbo-1106

0.9671

0.9105

Com base nesses resultados, o gpt-3.5-turbo mais recente produz resultados mais consistentes em termos de fato, enquanto a versão mais antiga produz respostas mais pertinentes ao prompt fornecido. Para este tutorial, use gpt-3.5-turbo como modelo de conclusão.

Dica

Se você não quiser escolher entre métricas, considere criar métricas consolidadas usando uma soma ponderada ou personalizar os prompts usados para avaliação.

Avalie o desempenho geral do seu aplicação RAG usando os modelos de melhor desempenho:

from ragas.metrics import answer_similarity, answer_correctness
data = {"question": [], "ground_truth": [], "answer": []}
data["question"] = QUESTIONS
data["ground_truth"] = GROUND_TRUTH
# Use the best embedding model from the retriever evaluation
retriever = get_retriever("text-embedding-3-small", 2)
# Use the best completion model from the generator evaluation
rag_chain = get_rag_chain(retriever, "gpt-3.5-turbo")
for question in tqdm(QUESTIONS):
data["answer"].append(rag_chain.invoke(question))
dataset = Dataset.from_dict(data)
run_config = RunConfig(max_workers=4, max_wait=180)
result = evaluate(
dataset=dataset,
metrics=[answer_similarity, answer_correctness],
run_config=run_config,
raise_exceptions=False,
)
print(f"Overall metrics: {result}")

Esta avaliação mostra que a cadeia RAG produz uma similaridade de resposta de e uma 0.8873 0.5922 correção de resposta de no conjunto de dados de amostra.

Para investigar mais os resultados, converta-os em um dataframe do Pandas e filtre para respostas de baixa pontuação:

result_df = result.to_pandas()
result_df[result_df["answer_correctness"] < 0.7]

Para uma análise visual, crie um mapa térmico de perguntas versus métricas:

import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 8))
sns.heatmap(
result_df[1:10].set_index("question")[["answer_similarity", "answer_correctness"]],
annot=True,
cmap="flare",
)
plt.show()

O código anterior gera o seguinte mapa de calor:

Mapa de calor visualizando o desempenho de um aplicação RAG

Mapa de calor visualizando o desempenho do aplicação RAG

Ao investigar resultados de baixa pontuação, você pode encontrar:

  • Algumas respostas confiáveis no conjunto de dados de avaliação estão incorretas. Embora a resposta gerada pelo LLM esteja correta, ela não corresponde à verdade, resultando em uma pontuação baixa.

  • Algumas respostas da verdade do camposão frases completas, enquanto a resposta gerada pelo LLM é uma única palavra ou número.

Essas descobertas enfatizam a importância de verificar avaliações pontuais de LLM e de selecionar conjuntos de dados de avaliações precisos.

A avaliação não deve ser um evento único . Cada vez que você alterar um componente em seu sistema, avalie as alterações para avaliar como elas impacto o desempenho. Quando seu aplicação estiver em produção, monitore o desempenho em tempo real e detecte alterações.

Use Charts para monitorar o desempenho do seu aplicação LLM. Anote os resultados da avaliação e quaisquer métricas de feedback que você queira acompanhar em uma collection do Atlas :

from datetime import datetime
result["timestamp"] = datetime.now()
collection = db["metrics"]
collection.insert_one(result)

Este código adiciona um campo timestamp ao resultado da avaliação e o escreve em uma coleção metrics no banco de dados ragas_evals. O documento no Atlas é apresentado assim:

{
"answer_similarity": 0.8873,
"answer_correctness": 0.5922,
"timestamp": "2024-04-07T23:27:30.655+00:00"
}

Crie um dashboard no MongoDB Charts para visualizar suas métricas ao longo do tempo. Para saber como criar gráficos e painéis, consulte Criar Charts.

Neste tutorial, você aprenderam como avaliar um aplicação RAG utilizando a estrutura RAGAS e MongoDB Atlas. Você comparou modelos de incorporação para recuperação, modelos de conclusão para geração e mediu o desempenho geral do seu aplicação. Você também aprendera a acompanhar o desempenho ao longo do tempo usando o MongoDB Charts.

Para saber mais sobre como criar aplicativos RAG com MongoDB, consulte os seguintes recursos:

Voltar

Queries de linguagem natural