Evaluaciones para generación aumentada por recuperación: TruLens + Milvus
Este artículo se publicó originalmente en The New Stack y se republica aquí con permiso.
La creciente popularidad de los grandes modelos de lenguaje (LLMs) ha impulsado el auge de las tecnologías de búsqueda vectorial, incluidas bases de datos vectoriales diseñadas específicamente como Milvus y Zilliz Cloud, bibliotecas de búsqueda vectorial como FAISS y complementos de búsqueda vectorial integrados con bases de datos tradicionales.
Cada vez más, la búsqueda vectorial se ha convertido en el caso de uso empresarial esencial para la IA generativa en forma de aplicaciones de generación aumentada por recuperación, o RAGs, de preguntas y respuestas. Este estilo de construcción permite que los LLMs tengan fácil acceso a una base de conocimiento verificada que pueden usar como contexto para responder preguntas. Milvus es una base de datos vectorial de código abierto altamente escalable diseñada específicamente para esta aplicación.
Construcción de un RAG
Al crear una aplicación LLM efectiva de estilo RAG, hay muchas opciones de configuración entre las que elegir que pueden afectar significativamente la calidad de la recuperación. Algunas de estas opciones incluyen:
Construcción de la base de datos vectorial
- Selección de datos
- Modelo de embeddings
- Tipo de índice
Encontrar datos de alta calidad que coincidan con precisión con los requisitos de tu aplicación es fundamental. El proceso de recuperación podría proporcionar resultados irrelevantes si no tienes los datos correctos.
Después de seleccionar tus datos, considera el modelo de embeddings que usas, ya que influye significativamente en la calidad de la recuperación. Incluso si tu base de conocimiento contiene la información correcta, el recuperador puede producir resultados incorrectos si el modelo de embeddings necesita una comprensión semántica de tu dominio.
La relevancia del contexto es una métrica útil para evaluar la calidad de la recuperación, y estas selecciones la afectan en gran medida.
Por último, el tipo de índice puede tener un impacto significativo en la eficiencia de la búsqueda semántica. Esto es especialmente cierto para grandes conjuntos de datos; esta elección te permite equilibrar la tasa de recuperación, la velocidad y los requisitos de recursos. Milvus admite varios tipos de índices, como índices planos, índices basados en cuantización de producto e índices basados en grafos. Puedes leer más sobre diferentes tipos de índices.
Recuperación
- Cantidad de contexto recuperado (top k)
- Tamaño del fragmento
Cuando llegamos a la recuperación, top k es un parámetro que se comenta a menudo y que controla el número de fragmentos de contexto recuperados. Un top k más alto nos da una mayor probabilidad de recuperar la información necesaria y aumenta la probabilidad de que nuestro LLM incorpore información irrelevante en su respuesta. Para preguntas simples, un top k más bajo suele ser el más eficaz.
El tamaño del fragmento controla el tamaño de cada contexto recuperado. Un tamaño de fragmento mayor puede ser útil para preguntas más complejas, mientras que los fragmentos más pequeños son suficientes para preguntas simples que pueden responderse con solo una cantidad mínima de información.
Para muchas de estas opciones, no existe una solución universal. El rendimiento puede variar enormemente según el tamaño y el tipo de datos, los LLMs utilizados, tu aplicación y más. Necesitamos una herramienta de evaluación para valorar la calidad de estas recuperaciones para nuestro caso de uso específico. Aquí es donde entra TruLens.
TruLens para el seguimiento y la evaluación de LLM
TruLens es una biblioteca de código abierto para evaluar y hacer seguimiento del rendimiento de aplicaciones LLM, como los RAGs. Con TruLens, también obtenemos la capacidad de usar los propios LLMs para evaluar la salida, la calidad de la recuperación y más.
Cuando construimos aplicaciones de LLM, el problema más importante en la mente de muchas personas es la alucinación. Los RAG contribuyen en gran medida a garantizar información precisa al proporcionar contexto recuperado al LLM, pero no pueden garantizarlo. Las evaluaciones son esenciales aquí para verificar la ausencia de alucinación en nuestra aplicación. TruLens ofrece tres pruebas para esta necesidad: relevancia del contexto, fundamentación y relevancia de la respuesta. Revisemos cada una de estas para entender cómo pueden beneficiarnos.
Relevancia del contexto
El primer paso de cualquier aplicación RAG es la recuperación; para verificar la calidad de nuestra recuperación, queremos asegurarnos de que cada fragmento de contexto sea relevante para la consulta de entrada. Esto es fundamental porque el LLM usará este contexto para formar una respuesta, por lo que cualquier información irrelevante en el contexto podría entretejerse en una alucinación.
Fundamentación
Después de recuperar el contexto, un LLM lo convierte en una respuesta. Los LLM a menudo se desvían de los hechos proporcionados, exagerando o expandiendo hacia una respuesta que suena correcta. Para verificar la fundamentación de nuestra aplicación, debemos separar la respuesta en declaraciones separadas y buscar de forma independiente evidencia que respalde cada una dentro del contexto recuperado.
Relevancia de la respuesta
Por último, nuestra respuesta aún necesita responder de manera útil a la pregunta original. Podemos verificar esto evaluando la relevancia de la respuesta final con respecto a la entrada del usuario.
RAG sin alucinaciones
Al alcanzar evaluaciones satisfactorias para esta tríada, podemos hacer una afirmación matizada sobre la corrección de nuestra aplicación; se verifica que está libre de alucinaciones hasta el límite de su base de conocimientos. En otras palabras, si la base de datos vectorial contiene solo información precisa, entonces las respuestas proporcionadas por el RAG también son precisas.
Haciéndolo concreto
Como mencionamos antes, muchas de las elecciones de configuración para nuestro RAG pueden tener un impacto sustancial en la alucinación. Para ilustrar esto, construiremos una aplicación RAG de preguntas y respuestas sobre artículos de Wikipedia de un pequeño conjunto de ciudades. LlamaIndex actuará como el marco para esta aplicación.
Sigue este ejemplo en Google Colab.
Cargar datos desde Wikipedia
Para construir nuestro almacén vectorial, primero necesitamos cargar datos. Aquí, usaremos un cargador de datos de LlamaIndex para cargar datos directamente desde 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 evaluadores
A continuación, queremos configurar nuestros evaluadores. Específicamente, usaremos la tríada que mencionamos antes: relevancia del contexto, fundamentación y relevancia de la respuesta para detectar alucinaciones.
TruLens proporciona un conjunto de evaluadores o funciones de retroalimentación con prompts útiles para esta evaluación que utilizan un proveedor de modelo específico, como OpenAI, Anthropic o HuggingFace.
# Initialize OpenAI-based feedback function collection class:
openai_gpt4 = feedback.OpenAI()
Después de haber establecido nuestro proveedor de modelo, elegimos la relevancia pregunta-declaración para usarla en nuestra primera evaluación. Para cada evaluación en este ejemplo, también usaremos razones de cadena de pensamiento para comprender mejor las evaluaciones. Esto se denota con el sufijo de la función de retroalimentación 1_with_cot_reason.
Cuando hacemos esto, también necesitamos seleccionar qué texto pasar a nuestra función de retroalimentación. TruLens serializa la aplicación, que luego se indexa mediante una estructura similar a JSON. Usaremos este índice para la selección de texto. TruLens proporciona varias funciones auxiliares para facilitar esto:
on_input()encuentra automáticamente la entrada principal pasada a nuestra aplicación LlamaIndex para usarla como el primer texto pasado a nuestra función de feedback.TruLlama.select_source_nodes()identifica los nodos de origen utilizados en una recuperación de LlamaIndex.
Por último, necesitamos agregar la relevancia de cada pieza de contexto en una única puntuación. Para este ejemplo, usaremos el máximo para la agregación con el fin de medir la relevancia del fragmento más relevante. También podrían usarse otras métricas como el promedio o el mínimo.
# 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)
La fundamentación se configura de manera similar, con una agregación ligeramente diferente. En este caso, tomaremos la puntuación máxima de fundamentación de cada afirmación y luego la puntuación promedio de fundamentación entre todas las afirmaciones.
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)
La relevancia de la respuesta es la función de feedback más sencilla de configurar, ya que solo depende de la entrada/salida. Podemos usar una nueva función auxiliar de TruLens para esto — .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()
Definir el espacio de configuración
Ahora que hemos cargado nuestros datos y configurado nuestros evaluadores, es hora de construir nuestro RAG. En este proceso, construiremos una serie de RAGs con diferentes configuraciones, evaluaremos cada uno y seleccionaremos la mejor opción óptima.
Como mencionamos anteriormente, limitaremos nuestro espacio de configuración a unas pocas opciones impactantes para RAGs. En este ejemplo probaremos el tipo de índice, el modelo de embeddings, top k y el tamaño de fragmento; sin embargo, te animamos a probar otras configuraciones, como diferentes métricas de distancia y parámetros de búsqueda.
Iterar por nuestras selecciones
Después de definir el espacio de configuración, usaremos itertools para probar cada combinación de estas opciones y evaluar cada una. Además, Milvus nos da una buena ventaja con el parámetro overwrite. Esto nos permite iterar fácilmente por diferentes configuraciones sin los lentos procedimientos de desmontaje e instanciación que pueden requerirse con otras bases de datos vectoriales.
En cada iteración, pasaremos la selección de parámetros del índice a MilvusVectorStore y a nuestra aplicación usando el contexto de almacenamiento. Pasaremos nuestro modelo de embeddings al contexto de servicio y luego crearemos nuestro í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)
Luego, podemos construir un motor de consultas usando este índice — definiendo top_k aquí:
query_engine = index.as_query_engine(similarity_top_k = top_k)
Después de la construcción, usaremos TruLens para envolver la aplicación. Aquí, le daremos un nombre fácilmente identificable, registraremos las configuraciones como metadatos de la aplicación y definiremos las funciones de feedback para la evaluación.
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á igual que el motor de consultas original.
Por último, usaremos un pequeño conjunto de prompts de prueba para la evaluación, llamando a la aplicación para que proporcione una respuesta a cada prompt. Como estamos llamando a la API de OpenAI en rápida sucesión, Tenacity resulta útil aquí para ayudarnos a evitar problemas de límite de tasa mediante retroceso 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)
Los resultados
¿Qué configuración tuvo el mejor rendimiento?
| Tipo de índice | Modelo de embedding | Top k de similitud | Tamaño del chunk |
|---|---|---|---|
| IVF Flat | text-embedding-ada-002 | 3 | 200 |
¿Qué configuración tuvo el peor rendimiento?
| Tipo de índice | Modelo de embedding | Top k de similitud | Tamaño del chunk |
|---|---|---|---|
| IVF Flat | Multilingual MiniLM L12 v2 | 1 | 500 |
¿Qué modos de fallo se identificaron?
Un modo de fallo que observamos fue la recuperación de información sobre la ciudad equivocada. Puedes ver un ejemplo de esto con el razonamiento de cadena de pensamiento a continuación, donde se recuperó contexto sobre Tucson en lugar de Houston.
De forma similar, también vimos problemas en los que recuperábamos contexto sobre la ciudad correcta, pero el contexto era irrelevante para la pregunta de entrada.
Dado este contexto irrelevante, el modelo de completado procedió a alucinar. Es importante señalar aquí que una alucinación no es necesariamente factualmente incorrecta; simplemente ocurre cuando el modelo responde sin evidencia de respaldo.
Además, incluso encontramos ejemplos de respuestas irrelevantes.
Comprender el rendimiento
Por tipo de índice
El tipo de índice no tuvo un impacto significativo en el rendimiento en términos de velocidad, uso de tokens o evaluaciones. Probablemente esto sea resultado del pequeño tamaño de los datos ingeridos para este ejemplo, y el tipo de índice puede ser una selección más importante para corpus más grandes.
Por modelo de embedding
Text-embedding-ada-002 superó al modelo de embedding MiniLM en groundedness (0,72 frente a 0,60 de media) y relevancia de la respuesta (0,82 frente a 0,62 de media). Los dos modelos de embedding tuvieron un rendimiento igualmente bueno en relevancia del contexto.
Estas puntuaciones de evaluación mejoradas pueden atribuirse a que los embeddings de OpenAI están mejor adaptados a la información de Wikipedia.
Top K de similitud
Aumentar el top k dio como resultado una calidad máxima de recuperación ligeramente mejorada (medida por la relevancia del contexto). Al recuperar un mayor número de chunks, el recuperador tiene más intentos para recuperar contexto de alta calidad.
Un top k más alto también mejoró la groundedness (0,71 frente a 0,62 de media) y la relevancia de la respuesta (0,76 frente a 0,68 de media). Al recuperar más chunks de contexto, proporcionamos más evidencia para que el modelo de completado haga y respalde afirmaciones.
Como era de esperar, estas mejoras tienen el coste de un uso de tokens mucho mayor (una media de 590 tokens adicionales por llamada).
Tamaño del chunk
Aumentar el tamaño del chunk redujo la groundedness de nuestro recuperador al forzar la inclusión de texto circundante irrelevante para la pregunta de entrada.
En el lado positivo, un tamaño de chunk mayor proporcionó más evidencia con la que contrastar. Así que cuando el LLM hace afirmaciones, es más probable que estén respaldadas por el contexto recuperado.
Por último, aumentar el tamaño del chunk incrementó el uso medio de tokens en 400 tokens por registro.
Construye un RAG mejor con TruLens y Milvus
En esta publicación, aprendimos cómo crear un RAG con varias configuraciones y parámetros, incluidos el tipo de índice, el modelo de embedding, top k y el tamaño de fragmento. La gran cantidad de configuraciones compatibles y el soporte de sobrescritura en Milvus hicieron posible esta experimentación dinámica. De manera crucial, también usamos TruLens para rastrear y evaluar cada experimento, identificar y explicar nuevos modos de fallo, y encontrar rápidamente la combinación con mejor rendimiento.
Para probarlo tú mismo. Puedes consultar el código abierto TruLens e instalar el código abierto Milvus o Zilliz Cloud.
Sigue leyendo
Stop Building AI Data Infra for the Wrong Stage
Learn how AI data infrastructure should evolve from prototype to enterprise scale, and when Vector Lakebase becomes the right architecture for AI apps.

Zilliz Cloud Now Available in Azure North Europe: Bringing AI-Powered Vector Search Closer to European Customers
The addition of the Azure North Europe (Ireland) region further expands our global footprint to better serve our European customers.

Why Not All VectorDBs Are Agent-Ready
Explore why choosing the right vector database is critical for scaling AI agents, and why traditional solutions fall short in production.



