Avaliações para Geração Aumentada por Recuperação: TruLens + Milvus
Este artigo foi originalmente publicado em The New Stack e é republicado aqui com permissão.
A crescente popularidade dos grandes modelos de linguagem (LLMs) impulsionou o crescimento das tecnologias de busca vetorial, incluindo bancos de dados vetoriais desenvolvidos especificamente, como Milvus e Zilliz Cloud, bibliotecas de busca vetorial como FAISS e plugins de busca vetorial integrados a bancos de dados tradicionais.
Cada vez mais, a busca vetorial se tornou o caso de uso empresarial essencial para IA generativa na forma de aplicações de perguntas e respostas com geração aumentada por recuperação, ou RAGs. Esse estilo de construção permite que os LLMs tenham acesso fácil a uma base de conhecimento verificada que podem usar como contexto para responder a perguntas. Milvus é um banco de dados vetorial open source altamente escalável desenvolvido especificamente para esta aplicação.
Construindo um RAG
Ao criar uma aplicação de LLM eficaz no estilo RAG, há muitas opções de configuração a escolher que podem afetar significativamente a qualidade da recuperação. Algumas dessas opções incluem:
Construindo o Vector DB
- Seleção de dados
- Modelo de embedding
- Tipo de índice
Encontrar dados de alta qualidade que correspondam precisamente aos requisitos da sua aplicação é fundamental. O processo de recuperação pode fornecer resultados irrelevantes se você não tiver os dados corretos.
Após selecionar seus dados, considere o modelo de embedding que você usa, pois ele influencia significativamente a qualidade da recuperação. Mesmo que sua base de conhecimento contenha as informações corretas, o recuperador pode produzir resultados incorretos se o modelo de embedding precisar de uma compreensão semântica do seu domínio.
A relevância do contexto é uma métrica útil para avaliar a qualidade da recuperação, e essas seleções a afetam bastante.
Por fim, o tipo de índice pode ter um impacto significativo na eficiência da busca semântica. Isso é especialmente verdadeiro para grandes conjuntos de dados; essa escolha permite equilibrar taxa de recall, velocidade e requisitos de recursos. Milvus oferece suporte a vários tipos de índice, como índices flat, índices baseados em quantização de produto e índices baseados em grafos. Você pode ler mais sobre diferentes tipos de índice.
Recuperação
- Quantidade de contexto recuperado (top k)
- Tamanho do chunk
Quando chegamos à recuperação, top k é um parâmetro frequentemente discutido que controla o número de chunks de contexto recuperados. Um top k mais alto nos dá uma chance maior de recuperar as informações necessárias e aumenta a probabilidade de nosso LLM incorporar informações irrelevantes em sua resposta. Para perguntas simples, um top k mais baixo costuma ser o mais performático.
O tamanho do chunk controla o tamanho de cada contexto recuperado. Um chunk maior pode ser útil para perguntas mais complexas, enquanto chunks menores são suficientes para perguntas simples que podem ser respondidas com apenas uma quantidade mínima de informações.
Para muitas dessas escolhas, não existe uma solução única para todos os casos. O desempenho pode variar muito dependendo do tamanho e do tipo de dados, dos LLMs usados, da sua aplicação e mais. Precisamos de uma ferramenta de avaliação para analisar a qualidade dessas recuperações para nosso caso de uso específico. É aqui que entra o TruLens.
TruLens para rastreamento e avaliação de LLM
TruLens é uma biblioteca open source para avaliar e rastrear o desempenho de apps de LLM, como RAGs. Com o TruLens, também ganhamos a capacidade de usar os próprios LLMs para avaliar a saída, a qualidade da recuperação e muito mais.
Quando construímos aplicações de LLM, a questão mais importante na mente de muitas pessoas é a alucinação. RAGs ajudam muito a garantir informações precisas ao fornecer contexto recuperado ao LLM, mas não conseguem garantir isso. Avaliações são essenciais aqui para verificar a ausência de alucinação em nosso aplicativo. A TruLens oferece três testes para essa necessidade: relevância do contexto, fundamentação e relevância da resposta. Vamos revisar cada um deles para entender como podem nos beneficiar.
Relevância do contexto
A primeira etapa de qualquer aplicação RAG é a recuperação; para verificar a qualidade da nossa recuperação, queremos garantir que cada trecho de contexto seja relevante para a consulta de entrada. Isso é crítico porque o LLM usará esse contexto para formar uma resposta, então qualquer informação irrelevante no contexto poderia ser incorporada a uma alucinação.
Fundamentação
Depois que o contexto é recuperado, ele é então transformado em uma resposta por um LLM. LLMs frequentemente se desviam dos fatos fornecidos, exagerando ou expandindo para uma resposta que parece correta. Para verificar a fundamentação da nossa aplicação, devemos separar a resposta em declarações distintas e buscar independentemente evidências que apoiem cada uma dentro do contexto recuperado.
Relevância da resposta
Por fim, nossa resposta ainda precisa responder de forma útil à pergunta original. Podemos verificar isso avaliando a relevância da resposta final em relação à entrada do usuário.
RAGs livres de alucinações
Ao alcançar avaliações satisfatórias para esta tríade, podemos fazer uma declaração diferenciada sobre a correção da nossa aplicação; ela é verificada como livre de alucinações até o limite da sua base de conhecimento. Em outras palavras, se o banco de dados vetorial contém apenas informações precisas, então as respostas fornecidas pelo RAG também são precisas.
Tornando isso concreto
Como mencionamos antes, muitas das escolhas de configuração para o nosso RAG podem ter um impacto substancial na alucinação. Para ilustrar isso, construiremos uma aplicação RAG de perguntas e respostas com base em artigos da Wikipedia sobre um pequeno conjunto de cidades. O LlamaIndex atuará como o framework para esta aplicação.
Acompanhe este exemplo no Google Colab.
Carregar dados da Wikipedia
Para construir nosso armazenamento vetorial, primeiro precisamos carregar dados. Aqui, usaremos um carregador de dados do LlamaIndex para carregar dados diretamente da Wikipedia.
from llama_index import WikipediaReader
cities = [
"Los Angeles", "Houston", "Honolulu", "Tucson", "Mexico City",
"Cincinatti", "Chicago"
]
wiki_docs = []
for city in cities:
try:
doc = WikipediaReader().load_data(pages=[city])
wiki_docs.extend(doc)
except Exception as e:
print(f"Error loading page for city {city}: {e}")
Configurar avaliadores
Em seguida, queremos configurar nossos avaliadores. Especificamente, usaremos a tríade que mencionamos anteriormente: relevância do contexto, fundamentação e relevância da resposta para testar alucinação.
A TruLens fornece um conjunto de avaliadores ou funções de feedback com prompts úteis para esta avaliação que usam um provedor de modelo específico, como OpenAI, Anthropic ou HuggingFace.
# Initialize OpenAI-based feedback function collection class:
openai_gpt4 = feedback.OpenAI()
Depois de definirmos nosso provedor de modelo, escolhemos relevância pergunta-declaração para usar em nossa primeira avaliação. Para cada avaliação neste exemplo, também usaremos razões de cadeia de pensamento para entender melhor as avaliações. Isso é indicado pelo sufixo da função de feedback 1_with_cot_reason.
Quando fazemos isso, também precisamos selecionar qual texto passar para nossa função de feedback. A TruLens serializa a aplicação, que é então indexada por uma estrutura semelhante a JSON. Usaremos este índice para seleção de texto. A TruLens fornece uma série de funções auxiliares para facilitar isso:
on_input()encontra automaticamente a entrada principal passada para a nossa aplicação LlamaIndex para usá-la como o primeiro texto passado para a nossa função de feedback.TruLlama.select_source_nodes()identifica os nós de origem usados em uma recuperação do LlamaIndex.
Por fim, precisamos agregar a relevância de cada parte do contexto em uma única pontuação. Para este exemplo, usaremos o máximo para a agregação, a fim de medir a relevância do trecho mais relevante. Outras métricas, como média ou mínimo, também poderiam ser usadas.
# Question/statement relevance between question and each context chunk.
f_context_relevance = Feedback(openai.qs_relevance_with_cot_reason, name = "Context Relevance").on_input().on(
TruLlama.select_source_nodes().node.text
).aggregate(np.max)
A fundamentação é configurada de forma semelhante, com uma agregação ligeiramente diferente. Neste caso, pegaremos a pontuação máxima de fundamentação de cada afirmação e, em seguida, a pontuação média de fundamentação em todas as afirmações.
grounded = Groundedness(groundedness_provider=openai_gpt4)
f_groundedness = Feedback(grounded.groundedness_measure_with_cot_reason, name = "Groundedness").on(
TruLlama.select_source_nodes().node.text # context
).on_output().aggregate(grounded.grounded_statements_aggregator)
A relevância da resposta é a função de feedback mais simples de configurar, pois depende apenas de entrada/saída. Podemos usar uma nova função auxiliar do TruLens para isso — .on_input_output().
# Question/answer relevance between overall question and answer.
f_qa_relevance = Feedback(openai.relevance_with_cot_reason,
name = "Answer Relevance").on_input_output()
Definindo o espaço de configuração
Agora que carregamos nossos dados e configuramos nossos avaliadores, é hora de construir nosso RAG. Nesse processo, construiremos uma série de RAGs com diferentes configurações, avaliaremos cada um e selecionaremos a melhor escolha ideal.
Como mencionamos anteriormente, limitaremos nosso espaço de configuração a algumas escolhas impactantes para RAGs. Testaremos tipo de índice, modelo de embedding, top k e tamanho do chunk neste exemplo; no entanto, você é incentivado a testar outras configurações, como diferentes métricas de distância e parâmetros de busca.
Iterando pelas nossas seleções
Depois de definir o espaço de configuração, usaremos itertools para testar todas as combinações dessas escolhas e avaliar cada uma. Além disso, o Milvus nos oferece um bom benefício com o parâmetro overwrite. Isso nos permite iterar facilmente por diferentes configurações sem procedimentos lentos de desmontagem e instanciação que podem ser necessários com outros bancos de dados vetoriais.
Em cada iteração, passaremos a seleção do parâmetro de índice para MilvusVectorStore e para a nossa aplicação usando o contexto de armazenamento. Passaremos nosso modelo de embedding para o contexto de serviço e, em seguida, criaremos nosso índice.
vector_store = MilvusVectorStore(index_params={
"index_type": index_param,
"metric_type": "L2"
},
search_params={"nprobe": 20},
overwrite=True)
llm = OpenAI(model="gpt-3.5-turbo")
storage_context = StorageContext.from_defaults(vector_store = vector_store)
service_context = ServiceContext.from_defaults(embed_model = embed_model, llm = llm, chunk_size = chunk_size)
index = VectorStoreIndex.from_documents(wiki_docs,
service_context=service_context,
storage_context=storage_context)
Em seguida, podemos construir um mecanismo de consulta usando este índice — definindo top_k aqui:
query_engine = index.as_query_engine(similarity_top_k = top_k)
Após a construção, usaremos o TruLens para envolver a aplicação. Aqui, daremos a ela um nome facilmente identificável, registraremos as configurações como metadados da aplicação e definiremos as funções de feedback para avaliação.
tru_query_engine = TruLlama(query_engine,
app_id=f"App-{index_param}-{embed_model_name}-{top_k}",
feedbacks=[f_groundedness, f_qa_relevance, f_context_relevance],
metadata={
'index_param':index_param,
'embed_model':embed_model_name,
'top_k':top_k
})
Este tru_query_engine funcionará exatamente como o mecanismo de consulta original.
Por fim, usaremos um pequeno conjunto de prompts de teste para avaliação, chamando a aplicação para fornecer uma resposta a cada prompt. Como estamos chamando a API da OpenAI em rápida sucessão, Tenacity é útil aqui para nos ajudar a evitar problemas de limite de taxa por meio de backoff exponencial.
@retry(stop=stop_after_attempt(10), wait=wait_exponential(multiplier=1, min=4, max=10))
def call_tru_query_engine(prompt):
return tru_query_engine.query(prompt)
for prompt in test_prompts:
call_tru_query_engine(prompt)
Os resultados
Qual configuração teve o melhor desempenho?
| Tipo de Índice | Modelo de Embedding | Similarity Top k | Tamanho do Chunk |
|---|---|---|---|
| IVF Flat | text-embedding-ada-002 | 3 | 200 |
Qual configuração teve o pior desempenho?
| Tipo de Índice | Modelo de Embedding | Similarity Top k | Tamanho do Chunk |
|---|---|---|---|
| IVF Flat | Multilingual MiniLM L12 v2 | 1 | 500 |
Quais modos de falha foram identificados?
Um modo de falha que observamos foi a recuperação de informações sobre a cidade errada. Você pode ver um exemplo disso com o raciocínio de cadeia de pensamento abaixo, onde o contexto sobre Tucson foi recuperado em vez de Houston.
Da mesma forma, também vimos problemas em que recuperamos contexto sobre a cidade correta, mas o contexto era irrelevante para a pergunta de entrada.
Dado esse contexto irrelevante, o modelo de conclusão passou a alucinar. É importante observar aqui que alucinação não é necessariamente factualmente incorreta; é apenas quando o modelo responde sem evidências de apoio.
Além disso, até encontramos exemplos de respostas irrelevantes.
Entendendo o desempenho
Por tipo de índice
O tipo de índice não teve um impacto significativo no desempenho em termos de velocidade, uso de tokens ou avaliações. Isso provavelmente é resultado do pequeno tamanho dos dados ingeridos para este exemplo, e o tipo de índice pode ser uma escolha mais importante para corpora maiores.
Por modelo de embedding
Text-embedding-ada-002 superou o modelo de embedding MiniLM em fundamentação (0,72 em comparação com 0,60 em média) e relevância da resposta (0,82 em comparação com 0,62 em média). Os dois modelos de embedding tiveram desempenho igualmente bom em relevância do contexto.
Essas pontuações de avaliação melhoradas podem ser atribuídas aos embeddings da OpenAI mais adequados às informações da Wikipedia.
Similarity Top K
Aumentar o top k resultou em uma qualidade máxima de recuperação ligeiramente melhor (medida pela relevância do contexto). Ao recuperar um número maior de chunks, o recuperador tem mais tentativas para recuperar contexto de alta qualidade.
Um top k mais alto também melhorou a fundamentação (0,71 em comparação com 0,62 em média) e a relevância da resposta (0,76 em comparação com 0,68 em média). Ao recuperar mais chunks de contexto, fornecemos mais evidências para que o modelo de conclusão faça e sustente afirmações.
Como esperado, essas melhorias vêm ao custo de um uso de tokens muito maior (uma média de 590 tokens adicionais por chamada).
Tamanho do chunk
Aumentar o tamanho do chunk diminuiu a fundamentação do nosso recuperador ao forçar a inclusão de texto circundante irrelevante para a pergunta de entrada.
Do lado positivo, um tamanho de chunk maior forneceu mais evidências para verificar. Então, quando o LLM faz afirmações, elas têm maior probabilidade de serem sustentadas pelo contexto recuperado.
Por fim, aumentar o tamanho do chunk aumentou o uso médio de tokens em 400 tokens por registro.
Crie um RAG melhor com TruLens e Milvus
Nesta publicação, aprendemos como criar um RAG com várias configurações e parâmetros, incluindo tipo de índice, modelo de embeddings, top k e tamanho de chunk. A grande quantidade de configurações suportadas e o suporte a sobrescrita no Milvus permitiram essa experimentação dinâmica. Fundamentalmente, também usamos o TruLens para rastrear e avaliar cada experimento, identificar e explicar novos modos de falha e encontrar rapidamente a combinação mais eficiente.
Para testar por conta própria. Você pode conferir o código aberto TruLens e instalar o código aberto Milvus ou Zilliz Cloud.
Continue lendo

Build for the Boom: Why AI Agent Startups Should Build Scalable Infrastructure Early
Explore strategies for developing AI agents that can handle rapid growth. Don't let inadequate systems undermine your success during critical breakthrough moments.

What is the K-Nearest Neighbors (KNN) Algorithm in Machine Learning?
KNN is a supervised machine learning technique and algorithm for classification and regression. This post is the ultimate guide to KNN.

Why Deepseek is Waking up AI Giants Like OpenAI And Why You Should Care
Discover how DeepSeek R1's open-source AI model with superior reasoning capabilities and lower costs is disrupting the AI landscape and challenging tech giants like OpenAI.



