Published · 10 min read

Construyendo un Agente de IA Versátil

Una Guía para Principiantes sobre el Desarrollo de Modelos de Lenguaje Grande Personalizables para Resolver Casos de Uso Simples

Autor: Rachel Martinez | Rol: Desarrolladora | Etiqueta: Agentes de IA


Introducción

Bienvenido a nuestro blog técnico, donde hacemos que los conceptos complejos sean accesibles para todos, sin importar su trayectoria. Hoy, profundizaremos en un proyecto emocionante: desarrollar un Agente de Modelo de Lenguaje Grande (LLM) que pueda adaptarse a diversas tareas.

Crearemos una infraestructura robusta de Recuperación, Aumento y Generación (RAG) para ayudarnos a seleccionar el LLM más adecuado para cada tarea. Empresas como H2O y HuggingFace ya han lanzado sistemas similares, y hemos recibido interés de startups ansiosas por integrar nuestra tecnología de IA en sus aplicaciones.

Nuestro caso de uso particular requiere la capacidad de solicitar al LLM utilizando una única entrada (one-shot) o encadenando múltiples solicitudes. Esto requiere crear módulos de LLM personalizables y fáciles de usar para diversos escenarios.

Mantente atento mientras embarcamos en este viaje, desglosando cada paso de manera simple y atractiva.

Caso de Uso Actual

En nuestro caso de uso actual, mejoramos el contenido del curriculo de un usuario a través de un proceso de dos pasos:

  1. Entrada: El usuario introduce las secciones de "Resumen Profesional" y "Experiencia" de su curriculo en el sistema y consulta al LLM para generar una versión más pulida del texto.
  2. Revisión: El usuario revisa el contenido generado por el LLM y proporciona solicitudes adicionales o pautas para refinar y personalizar la salida, asegurando que se alinee con sus habilidades y experiencias únicas.

Siguiendo este enfoque, los usuarios pueden aprovechar el poder del LLM para crear un curriculo atractivo que se destaque ante posibles empleadores.

Objetivo

Nuestro objetivo es construir un módulo de LLM completamente personalizable que apoye nuestro caso de uso y automatice al menos parcialmente el proceso de creación de curriculos.

Implementación

Paso 1: Elegir el LLM

Al seleccionar el LLM adecuado para tus necesidades, hay dos categorías principales a considerar: LLMs de código abierto y LLMs propietarios. Discutiremos opciones populares de ambos grupos, centrándonos en los beneficios de elegir LLMs de código abierto.

LLMs de Código Abierto

Ejemplos: Mistral AI, Phi 3, LLaMa 3.

Ventajas:

  • Soporte de la Comunidad: Grandes comunidades activas de desarrolladores y usuarios brindan orientación, comparten recursos y colaboran en mejoras.
  • Personalizabilidad: El acceso al código fuente permite modificaciones y adaptaciones para ajustarse a casos de uso específicos.
  • Transparencia: Comprender el funcionamiento interno del modelo es crucial para aplicaciones con estrictas pautas éticas o de privacidad.

LLMs Propietarios

Ejemplos: GPT-4.

Ventajas: Impresionante rendimiento y características únicas.

Limitaciones:

  • Falta de Personalizabilidad: Limitado a usar el modelo tal como está.
  • Costo: Las tarifas basadas en el uso pueden acumularse con el tiempo.
  • Naturaleza Opaca: Difícil de entender los mecanismos subyacentes, lo que puede ser un problema para aquellos que priorizan la transparencia y la explicabilidad.

En conclusión, aunque tanto los LLMs de código abierto como los propietarios tienen sus ventajas y desventajas, los LLMs de código abierto como Mistral AI, Phi 3 y LLaMa 3 ofrecen accesibilidad, personalizabilidad y transparencia que pueden ser muy beneficiosas para una amplia gama de usuarios.

Paso 2: Revisar Costos y Características

Los LLMs de código abierto suelen ser rentables, ya que pueden ser alojados en tus propios servidores o con proveedores como Azure o AWS con un modelo de pago por uso. Aquí hay un resumen de los costos y características de alojar varios LLMs:

LLMDetallesLongitud de ContextoCosto
Mixtral 8x7BMoE LLM de Mistral AEntrenado: 8192 tokens$0.7/1M tokens (chea
Mixtral 8x22BSucesor de MixtralEntrenado: 32768 tokens$2/1M tokens (availa
LLaMa 3 70BModelo no-MoE de MetEntrenado: 2048 tokens$0.02/1K tokens (Azu
Phi 3 128KMultimodal-SLM de MeEntrenado y Total: 3$0.002/1K tokens (Az
GPT-4 TurboLLM propietario de O16K por defecto, más altoNo disponible en AWS

Paso 3: Pruebas Usando APIs Públicas Cumpliendo con OpenRouter

Seleccionar y probar el mejor LLM requiere una forma simplificada de acceder y evaluar diferentes modelos. OpenRouter, un estándar de API de código abierto, permite un cambio fluido entre varios modelos y servicios de IA. Hemos elegido Fireworks AI como nuestro proveedor porque:

  • Diversidad de Modelos: Ofrece una amplia variedad de modelos de IA, incluidos LLMs de código abierto populares como Mistral AI, Phi 3 y LLaMa 3.
  • Facilidad de Integración: Se adhiere al estándar de OpenRouter, simplificando la integración en la infraestructura existente.
  • Escalabilidad: Diseñado para manejar grandes volúmenes de solicitudes y escalar sin problemas.

Aquí hay una función de muestra para invocar respuestas de modelo y los resultados de latencia utilizando el mismo hardware:

import json

def get_sanitized_json_response(user_input, guardrail):
    final = chain.invoke({"text": f"Sanitize {user_input}. Guardrail: {guardrail}"})
    final = chain.invoke({"text": final.content})
    return json.loads(final.content.replace("<json>", "").replace("</json>", "").replace("\n",""))
ModeloLatenciaObservaciones
Mixtral 8x7B2.1sModelo más ligero, perf
Mixtral 8x22B5.3sModelo sucesor, sim
LLaMa 3 70B2.6sRespuesta decente, pe
Phi 3 128K1.5 minModelo multimodal con

Se utilizó una guardrail y entrada muy simple para probar lo anterior:

guardrail = """Formatear detalles como viñetas y usar el método XYZ para formatear el detalle relevante.
            Por ejemplo:
            - Hice X aumentando Y por ciento, resultando en Z.
"""

Ob tuvimos la siguiente respuesta JSON sanitizada después de ser invocada:

get_sanitized_json_response("""Serví como Junior como Ingeniero de Software en TechCorp en Sídney en enero de 2011 y trabajé allí hasta diciembre de 2018. Durante mi mandato,

                            Lideré el equipo que desarrolló herramientas avanzadas de análisis financiero impulsadas por IA para procesar cientos de millones de registros en un
                            entorno distribuido y creé la versión inicial del canal de datos propietario de la empresa. En mi rol, fui responsable de
                            diseñar soluciones e implementar múltiples APIs. En mi último año gestionando un equipo enfocado en soluciones de blockchain.""", guardrail)

{'experience': [{'title': 'Ingeniero de Software',
                'company': 'TechCorp',
                'location': 'Sídney',
                'start_date': '2011-01',
                'end_date': '2018-12',
                'highlights': ['- Diseñé sistemas de bases de datos escalables para un proyecto principal que mejoró la velocidad de procesamiento de datos del cliente en un 40%.',
                              '- Lideré un equipo enfocado en soluciones impulsadas por IA.',
                              '- Desarrollé herramientas de análisis financiero impulsadas por IA que procesan más de 100M de registros en entornos distribuidos.',
                              '- Creé el canal de datos propietario de la empresa.',
                              '- Gestioné el equipo en soluciones de blockchain en mi último año.']}]}

En conclusión, dado que la calidad de la respuesta fue bastante comparable entre todos estos modelos, se redujo a la latencia y el costo. El experimento se realizó 3 veces, logrando resultados muy similares. Así que elegimos Mixtral de Mistral AI debido a su favorable equilibrio entre latencia y costo.

Paso 4: Construyendo una Capa de Orquestación

Gestionar y coordinar múltiples modelos de lenguaje a gran escala o un solo modelo a través de varias aplicaciones es crucial para un rendimiento y adaptabilidad óptimos. En lugar de construir desde cero usando PyTorch estándar, emplear un marco de orquestación es más práctico y eficiente. Aquí hay tres opciones:

Langchain

Ventajas: Amplia gama de integraciones, soporta Python y JavaScript, amplio soporte de la comunidad.

Desventajas: Introduce una dependencia adicional, potencial curva de aprendizaje.

LlamaIndex

Ventajas: Respaldado por Meta, ofrece interoperabilidad con Langchain.

Desventajas: Sintaxis menos amigable para desarrolladores.

Haystack

Ventajas: Sintaxis fácil de usar, respaldado por importantes corporaciones tecnológicas.

Desventajas: Integraciones limitadas ya que aún está en proceso de maduración.

Usar un marco de orquestación simplifica el desarrollo, mejora la escalabilidad y proporciona acceso a una comunidad de apoyo, lo que lo convierte en una opción más favorable para nuestro proyecto.

Diseño y Flujo de Datos

La siguiente imagen muestra el diagrama de diseño:

El flujo de datos dentro de nuestro sistema impulsado por IA es un aspecto esencial para garantizar una experiencia de usuario fluida y eficiente. Mientras que el diagrama de diseño proporciona una representación visual del proceso, una explicación detallada puede ayudar a aclarar los pasos involucrados:

  1. Entrada del Usuario: El viaje comienza cuando el usuario ingresa sus datos, como las secciones "sobre" y "experiencia" de su Curriculo, en el sistema.
  2. Generación de Prompts: La entrada del usuario se procesa y transforma en uno o más prompts, que se usarán para consultar el Modelo de Lenguaje Grande (LLM) para obtener una versión más pulida e impresionante del texto.
  3. Integración de LLM a través de Langchain: Los prompts generados se pasan a Langchain, nuestro marco de orquestación elegido, que facilita la interacción con el cliente LLM. Langchain aprovecha un esquema de Pydantic en forma de clase para analizar y validar los datos utilizando kor, asegurando que cumpla con el formato requerido.
  4. Generación de JSONResponse: Una vez que los datos son validados, se convierten en un JSONResponse final, un formato de datos estructurado y fácil de trabajar que simplifica el procesamiento y manipulación posteriores.
  5. Extracción de Contenido e Indexación: La parte de contenido del JSONResponse se extrae utilizando kor e indexa calculando embeddings, que son representaciones matemáticas del texto. Estos embeddings se almacenan en una base de datos vectorial, como Qdrant, para una recuperación y comparación eficientes durante consultas subsecuentes.
  6. Caché para el Módulo de Contexto: Todo el JSONResponse se almacena en caché para que el módulo de contexto funcione correctamente. Esto asegura que el sistema pueda mantener el contexto de la entrada del usuario y la salida del LLM durante las consultas de seguimiento.
  7. Consultas de Seguimiento: Cuando el usuario proporciona entrada adicional o solicita más refinamiento del texto generado, el JSONResponse en caché se modifica de acuerdo con el nuevo contexto y se encadena al prompt actualizado. Este prompt modificado luego pasa por todo el proceso nuevamente, desde la integración con el LLM hasta la extracción de contenido e indexación, para proporcionar al usuario una respuesta personalizada y precisa.

Al entender el flujo de datos dentro de nuestro sistema, podemos apreciar la importancia de cada paso y el papel del marco de orquestación en la optimización del proceso, lo que resulta, en última instancia, en una solución impulsada por IA eficiente y fácil de usar.

Construcción y encadenamiento de prompts usando LCEL

Este es un ejemplo de una plantilla de Prompt:

from langchain.core.output_parsers import PydanticOutputParser
prompt = PromptTemplate(
    template="Por favor, extraiga la siguiente información del texto dado y formatee como un objeto JSON de acuerdo con el esquema:\n\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": output_parser.get_format_instructions()},
)

chain = prompt | chat_model | output_parser

Una plantilla típica consta de dos argumentos principales (hay más, pero eso está fuera del alcance de este blog) que son input_variables y template. El primer argumento acepta una lista de cadenas que se inyectarían en la plantilla del prompt en tiempo de ejecución. Esta variable actúa típicamente como las de Jinja2Template o f-strings. El chat_model aquí sería el LLM que estamos utilizando y que se está encadenando al prompt. El operador | (pipe), similar al operador | de BASH, representa una cadena. Esta es una forma corta conveniente dada por la especificación LCEL (LangChain Expression Language). Esto nos permitiría encadenar la respuesta de un prompt a una entrada de otro. También admite la llamada a funciones, en otras palabras, se pueden encadenar expresiones lambda o algunas funciones para sanear típicamente la salida. Esto reduce la complejidad del código al escribir cadenas en lugar de funciones grandes.

Paso 5: Uso de VectorDBs

Las bases de datos vectoriales (VectorDBs) almacenan, gestionan y buscan de manera eficiente vectores de alta dimensión, como embebidos de texto generados por LLMs. Aquí hay una comparación de opciones populares:

Qdrant

Ventajas: Alto rendimiento, fácil integración, comunidad activa.

Desventajas: Ninguna significativa.

Milvus

Ventajas: Múltiples métricas de distancia, herramienta de visualización de datos.

Desventajas: Curva de aprendizaje más pronunciada.

Chroma

Ventajas: API de Python fácil de usar, diseño modular.

Desventajas: Relativamente nuevo, soporte comunitario menos extenso.

PGVector

Ventajas: Aprovecha las capacidades de PostgreSQL, consultas basadas en SQL.

Desventajas: Menos rendimiento para la búsqueda de vectores de alta dimensión.

FAISS

Ventajas: Búsqueda de alto rendimiento, soporte de GPU.

Desventajas: Requiere integración con otras bases de datos.

La escalabilidad, facilidad de integración y desarrollo activo de Qdrant lo convierten en una opción adecuada para nuestra solución impulsada por IA.

Aprovechando VectorDBs como una solución robusta de IR

Crear una solución de Recuperación de Información (IR) usando VectorDBs implica:

  1. Preprocesamiento de texto: Limpiar y preprocesar datos de texto en bruto.
  2. Generación de embebidos de texto: Convertir texto preprocesado en vectores de alta dimensión usando un LLM.
  3. Almacenamiento de embebidos en VectorDB: Indexar embebidos para búsqueda eficiente.
  4. Procesamiento de consultas: Preprocesar y convertir consultas de usuarios en embebidos.
  5. Búsqueda de similitud: Realizar búsqueda de similitud en VectorDB para encontrar embebidos relevantes.
  6. Clasificación y visualización de resultados: Clasificar embebidos recuperados y convertirlos de nuevo a texto para su visualización.

Siguiendo este proceso y utilizando VectorDBs, podemos construir un sistema de IR eficiente y efectivo.

¿Listo para contratar con precisión?

Empieza gratis, invita a tu equipo y ve al primer candidato en 15 min.

es