Lección 236 · 30 min · Gratis

Grafos de conocimiento con conciencia temporal y recuperación multi-salto (parte 3 de 3)

4.1.6. Recuperador

Diseñamos un recuperador simple que contiene solo un método de ejecución que abarca el paso de planificación y un bucle while para ejecutar cada llamada a la herramienta que el orquestador realiza antes de devolver una respuesta final.

import json


class MultiStepRetriever:
    """Retrieve information in multiple steps using an OpenAI client."""
    def __init__(self, client: AsyncOpenAI):
        self.client = client
        # This helps us simplify our tool calling functionality in run()
        self.function_map = {
            "factual_qa": factual_qa,
            "trend_analysis": trend_analysis
        }

    async def run(self, user_question: str) -> tuple[str, dict]:
        """Run the multi-step retrieval process for a user question."""
        # -------------------------------------------------------
        # Step 1: Generate initial plan
        # -------------------------------------------------------

        initial_plan = await initial_planner(user_question=user_question)

        # -------------------------------------------------------
        # Step 2: Make initial model call
        # -------------------------------------------------------

        retriever_user_prompt = (
            "You are a helpful assistant. "
            "You are provided with a user question: \n\n"
            f"{user_question} \n\n"
            "You have access to a set of tools. You may choose to use these tools to retrieve information to "
            "help you answer the user's question. These tools allow you to query a knowledge graph that contains "
            "information that has been extracted from companies' earnings call transcripts. "
            "You should not use your own memory of these companies to answer questions. "
            "When returning an answer to the user, all of your content must be derived from the content "
            "you have retrieved from the tools used. This is to ensure that is is accurate, as the data in "
            "this knowledge graph has been carefully check to ensure its accuracy. The knowledge graph contains "
            "data spanning from 2016-2020. \n\n"
            "You are provided with a plan of action as follows: \n"
            f"{initial_plan} \n\n"
            "You should generally stick to this plan to help you answer the question, though you may deviate "
            "from it should you deem it suitable. You may make more than one tool call."
        )

        input_messages = [
            {"role":"user", "content":retriever_user_prompt}
        ]

        response = await self.client.responses.create(
            model="gpt-4.1",
            input=input_messages,
            tools=tools,
            parallel_tool_calls=False,
        )

        # -------------------------------------------------------
        # Step 3: While loop until no more tool calls are made
        # -------------------------------------------------------

        tools_used = {}

        while response.output[0].type == "function_call":
            tool_call = response.output[0]
            args = json.loads(tool_call.arguments)
            name = tool_call.name

            if name in self.function_map:
                tool_func = self.function_map[name]
                tool_response_text = await tool_func(**args)

                input_messages.append(tool_call)
                input_messages.append({
                    "type": "function_call_output",
                    "call_id": tool_call.call_id,
                    "output": tool_response_text
                })

            tools_used[name] = [args, tool_response_text]

            response = await self.client.responses.create(
                model="gpt-4.1",
                input=input_messages,
                tools=tools,
                parallel_tool_calls=False
            )

        return response.output_text, tools_used

Ahora podemos ejecutar nuestro MultiStepRetriever.

Observamos que la respuesta devuelta es detallada e incluye un recorrido detallado de cómo evolucionaron las prioridades de investigación de AMD de 2016 a 2020, con referencias a las citas subyacentes que se utilizaron para derivar estas respuestas.

retriever = MultiStepRetriever(client=client)

answer, tools_used = await retriever.run(user_question="How have AMD's research & development priorities changed over time?")

print(answer)

También podemos inspeccionar las herramientas utilizadas por nuestro MultiStepRetriever para responder a esta consulta.

for key, value in tools_used.items():
    if value:
        print(f"{key}: {value[0]}")
    else:
        print(f"{key}: [empty list]")

La sección del Apéndice A.5, "Escalando y llevando a producción nuestro agente de recuperación" describe las pautas para llevar a producción el agente de recuperación que hemos construido.

4.1.7. Selección del modelo adecuado para la recuperación de grafos de conocimiento de varios pasos

Los agentes de recuperación de varios pasos necesitan un razonamiento sólido para saltar entre entidades y relaciones, verificar respuestas y decidir qué hacer a continuación. La latencia sigue siendo importante para los usuarios, pero generalmente menos que la precisión bruta. Por lo tanto, este es uno de los dominios donde los modelos de razonamiento o3 y o4-mini de OpenAI brillan.

Una vez más, para el desarrollo recomendamos una escalera de "empezar en grande, luego especializar":

  1. Empieza con o3 – asegúrate de que tu lógica de recuperación (encadenamiento, reordenamiento, prompts de respaldo) sea sólida. o3 también puede ser la mejor opción para producción si tu sistema de recuperación trabaja con datos particularmente complejos, como datos farmacéuticos o legales. Puedes probar esto observando la gravedad de la degradación del rendimiento con modelos más pequeños. Si la caída en el rendimiento es grande, considera quedarte con o3.
  2. Pasa a o4-mini
    • Mejora de prompts - optimiza tus prompts para llevar el rendimiento del sistema o4-mini lo más cerca posible al del modelo o3 completo.
    • Fine-tuning por refuerzo (RFT) - la oferta de Reinforcement Fine-Tuning de OpenAI te permite ajustar los modelos de la serie o de OpenAI para mejorar su rendimiento en tareas de razonamiento difíciles. Con tan solo ~50 respuestas doradas, puedes aprovechar el poder del aprendizaje por refuerzo para ajustar o4-mini, lo que puede ayudarlo a acercarse o incluso superar el rendimiento del o3 base en la misma tarea.
  3. Vuelve a GPT 4.1 cuando la latencia domine – para los casos en que la latencia sea particularmente importante o hayas ajustado tus prompts lo suficientemente bien como para que la caída del rendimiento sea mínima, considera pasar a la serie GPT 4.1.
Modelo Costo relativo Latencia relativa Inteligencia Rol ideal en el flujo de trabajo
o3 ★★★ ★★ ★★★ (más alta) Prototipado inicial, trabajo con datos complejos, generación de conjuntos de datos dorados
o4-mini ★★ ★ ★★ Motor de producción principal, puede mejorar el rendimiento con RFT
Serie GPT 4.1 ★ (más bajo) ★ (más rápido) ★ Puntuación en segundo plano crítica para la latencia o a gran escala

¿Por qué el Reinforcement Fine-Tuning es potente para tareas de recuperación de largo horizonte y varios pasos?

RFT tiene una serie de beneficios sobre el Supervised Fine-Tuning o la Direct Preference Optimization para este caso de uso.

En primer lugar, el fine-tuning por refuerzo se puede realizar con un número mucho menor de ejemplos, a veces requiriendo tan solo 50 ejemplos de entrenamiento.

Además, RFT elimina la necesidad de proporcionar trayectorias etiquetadas paso a paso. Al proporcionar solo la respuesta final correcta, el sistema aprende implícitamente cómo navegar el grafo de conocimiento de manera efectiva. Esta característica es particularmente valiosa en contextos del mundo real donde los usuarios finales suelen tener limitaciones de tiempo y pueden tener dificultades para curar los extensos conjuntos de ejemplos etiquetados (a menudo cientos o miles) requeridos por los métodos SFT tradicionales.

4.2 Evaluación de tu sistema de recuperación

  1. "Respuestas doradas" anotadas por humanos

    La línea base tradicional para la evaluación de la recuperación: un conjunto curado de pares consulta → respuesta dorada, verificado por expertos en el dominio. Las métricas como precisión@k o recall@k se calculan haciendo coincidir los pasajes recuperados con estos tramos dorados.

    Ventajas: Máxima fiabilidad, umbrales claros de aprobación/rechazo, excelente para pruebas de regresión
    Desventajas: Costoso de crear, lento de actualizar, cobertura limitada (rápidamente se vuelve obsoleto cuando la base de conocimiento evoluciona)

  2. Respuestas generadas sintéticamente

    Usa un LLM para generar respuestas o juicios de referencia, lo que permite una expansión rápida y de bajo costo del conjunto de evaluación. Tres vías comunes:

    • LLM como juez: Alimenta la consulta, los pasajes recuperados y la respuesta candidata a un modelo juez que emite una puntuación graduada o, por ejemplo, "sí / parcial / no".
    • Vía de uso de herramientas: Para diferentes tipos de preguntas, puedes generar manual o sintéticamente las vías de uso de herramientas 'correctas' y puntuar las respuestas en función de esto.

    Ventajas: Rápido, infinitamente escalable, más fácil de seguir el ritmo de una especificación de aplicación dinámica
    Desventajas: La calidad del juicio suele ser inferior a la de las soluciones anotadas por expertos humanos

  3. Retroalimentación humana

    Recopila calificaciones directamente de los usuarios finales o revisores de dominio (pulgar arriba/abajo, puntuaciones de cinco estrellas, comparaciones por pares). Puede ser en el bucle (el modelo se entrena continuamente con retroalimentación en vivo) o fuera de línea (rondas de evaluación periódicas).

    Ventajas: Captura la utilidad del mundo real, saca a la luz casos extremos que las pruebas sintéticas no detectan
    Desventajas: Ruidoso y subjetivo; requiere una agregación cuidadosa (por ejemplo, puntuación ELO), riesgo de que los sesgos del usuario se incorporen al modelo

¿Cuál es el mejor método de evaluación?

No existe un único método mejor. Sin embargo, un flujo de trabajo que hemos encontrado que funciona bien en los proyectos es:

  1. Empieza a construir e itera evaluaciones sintéticas.
  2. Prueba con tu conjunto de evaluaciones humanas "doradas" antes de la implementación.
  3. Facilita que los usuarios finales anoten las respuestas buenas y malas, y utiliza esta retroalimentación para seguir desarrollando tu aplicación con el tiempo.

5. Del prototipo a la producción


La transición de tu sistema de grafo de conocimiento de una prueba de concepto a una canalización robusta y de grado de producción requiere que abordes varios puntos clave:

  • Almacenamiento y recuperación de datos de grafos de alto volumen
  • Gestión y poda de conjuntos de datos
  • Implementación de concurrencia en la canalización de ingesta
  • Minimización del costo de los tokens
  • Escalado de agentes de recuperación
  • Salvaguardas

Esta sección sirve como un recorrido por las consideraciones clave y las mejores prácticas para garantizar que tu grafo de conocimiento con conciencia temporal pueda operar de manera confiable en un entorno del mundo real. Una sección del Apéndice "Del prototipo a la producción" más detallada se puede encontrar al final de este manual.

  1. Almacenamiento y recuperación de datos de grafos de alto volumen

    Sección del Apéndice A.1. "Almacenamiento y recuperación de datos de grafos de alto volumen"

    Gestiona la escalabilidad mediante un diseño de esquema cuidadoso, sharding y particionamiento. Define claramente las entidades, relaciones y asegura la flexibilidad del esquema para futuras evoluciones. Usa campos de alta cardinalidad como las marcas de tiempo para un particionamiento eficiente de los datos.

  2. Validez temporal y versionado

    Sección del Apéndice A.1.2. "Validez temporal y versionado"

    Incluye marcadores temporales (valid_from, valid_to) para cada declaración. Mantén registros históricos de forma no destructiva marcando los hechos obsoletos como inactivos e indexando los campos temporales para consultas eficientes.

  3. Indexación y búsqueda semántica

    Sección del Apéndice A.1.3. "Indexación y búsqueda semántica"

    Utiliza índices B-tree para consultas temporales eficientes. Aprovecha la extensión pgvector de PostgreSQL para la búsqueda semántica con algoritmos de vecinos más cercanos aproximados como ivfflat, ivfpq y hnsw para optimizar la velocidad de consulta y el uso de memoria.

  4. Gestión y poda de conjuntos de datos

    Sección del Apéndice A.2. "Gestión y poda de conjuntos de datos"

    Establece políticas de TTL y archivo para la retención de datos basadas en la fiabilidad y relevancia de la fuente. Implementa tareas de archivo automatizadas y poda inteligente con puntuación de relevancia para optimizar el tamaño del grafo.

  5. Canalización de ingesta concurrente

    Sección del Apéndice A.3. "Implementación de concurrencia en la canalización de ingesta"

    Implementa el procesamiento por lotes con etapas de canalización separadas y escalables para la fragmentación, extracción, invalidación y resolución de entidades. Optimiza el rendimiento y el paralelismo para gestionar los cuellos de botella de la ingesta.

  6. Minimización de los costos de los tokens

    Sección del Apéndice A.4. "Minimización del costo de los tokens"

    Utiliza estrategias de almacenamiento en caché para evitar llamadas redundantes a la API. Adopta niveles de servicio como la opción flex de OpenAI para reducir costos y reemplaza las costosas consultas de modelos con una incrustación eficiente y una búsqueda de vecinos más cercanos.

  7. Escalado de agentes de recuperación

    Sección del Apéndice A.5. "Escalado y puesta en producción de nuestro agente de recuperación"

    Utiliza una arquitectura de controlador y trabajadores de recorrido para manejar consultas de varios saltos. Implementa la extracción paralela de subgrafos, el recorrido dinámico con razonamiento encadenado, el almacenamiento en caché y el autoescalado para un alto rendimiento.

  8. Salvaguardas y verificación

    Sección del Apéndice A.6. "Salvaguardas"

    Implementa verificación de salida multicapa, registro estructurado y monitoreo para garantizar la integridad de los datos y la fiabilidad operativa. Rastrea métricas críticas y realiza auditorías regulares.

  9. Optimización de prompts

    Sección del Apéndice A.7. "Optimización de prompts"

    Optimiza las interacciones de LLM con personas, prompts de pocos ejemplos, métodos de cadena de pensamiento, gestión dinámica de contexto y pruebas A/B automatizadas de variaciones de prompts para una mejora continua del rendimiento.

Consideraciones finales

Este manual te proporciona técnicas fundamentales y flujos de trabajo concretos para construir e implementar eficazmente grafos de conocimiento con conciencia temporal, junto con potentes capacidades de recuperación de varios saltos.

Ya sea que estés comenzando con un prototipo o refinando un sistema de producción, aprovechar los datos de grafos estructurados con los modelos de OpenAI puede desbloquear interacciones más ricas y matizadas con tus datos. A medida que estas tecnologías evolucionan rápidamente, busca actualizaciones en la línea de modelos de OpenAI y sigue experimentando con métodos de indexación y estrategias de recuperación para mejorar continuamente tus soluciones de IA centradas en el conocimiento.

Puedes adaptar fácilmente los marcos presentados en este manual a tu dominio respectivo personalizando las ontologías proporcionadas y refinando los prompts de extracción. Intercambiar Neo4j como base de datos de grafos te lleva por buen camino hacia una aplicación a nivel de MVP, proporcionando persistencia de datos de forma inmediata. También abre la puerta a mejorar las herramientas de tu recuperador con consultas Cypher.

Desarrolla tu solución de forma iterativa utilizando evaluaciones sintéticas y luego prueba tu solución con soluciones "doradas" anotadas por expertos humanos. Una vez en producción, puedes iterar rápidamente a partir de la retroalimentación humana para llevar tu aplicación a nuevas alturas.

Colaboradores

Este manual es una colaboración conjunta entre OpenAI y Tomoro.

Apéndice


Dentro de este apéndice, encontrarás una sección más detallada de Del prototipo a la producción.

A. Del prototipo a la producción

A.1. Almacenamiento y recuperación de datos de grafos de alto volumen

A.1.1. Volumen de datos y complejidad del esquema

A medida que tu conjunto de datos escala a millones o incluso miles de millones de nodos y aristas, gestionar el rendimiento y la mantenibilidad se vuelve crítico. Esto requiere enfoques bien pensados tanto para el diseño del esquema como para la partición de datos:

  1. Diseño de esquema para el crecimiento y el cambio

    Define claramente los tipos de entidades centrales (por ejemplo, Person, Organization, Event) y las relaciones. Diseña el esquema pensando en el versionado y la flexibilidad, lo que permite la evolución futura del esquema con un tiempo de inactividad mínimo.

  2. Fragmentación y partición

    Usa campos de alta cardinalidad (como marcas de tiempo o ID de entidad únicos) para la partición, con el fin de preservar el rendimiento de las consultas a medida que el volumen de datos crece. Esto es particularmente importante para los datos con conciencia temporal. Por ejemplo:

    CREATE TABLE statements (
      statement_id UUID PRIMARY KEY,
      entity_id UUID NOT NULL,
      text TEXT NOT NULL,
      valid_from TIMESTAMP NOT NULL,
      valid_to TIMESTAMP,
      status VARCHAR(16) DEFAULT 'active',
      embedding VECTOR(1536),
      ...
    ) PARTITION BY RANGE (valid_from);
    

A.1.2. Validez temporal y versionado

En nuestro grafo de conocimiento temporal, cada declaración incluye marcadores temporales (por ejemplo, valid_from, valid_to).

  1. Preserva el historial de forma no destructiva

    Evita eliminar o sobrescribir registros. En su lugar, marca los hechos obsoletos como inactivos estableciendo un status (por ejemplo, inactive).

  2. Optimiza el acceso temporal

    Indexa los campos temporales (valid_from, valid_to) para admitir consultas eficientes tanto de estados actuales como históricos.

Ejemplo: Actualizaciones no destructivas

En lugar de eliminar o sobrescribir un registro, actualiza su estado y cierra su ventana de validez:

UPDATE statements
SET status = 'inactive', valid_to = '2025-03-15T00:00:00Z'
WHERE statement_id = '...' AND entity_id = '...';

A.1.3. Indexación y búsqueda semántica

Índices temporales

Para admitir consultas temporales eficientes, crea índices B-tree en valid_from y valid_to. Un índice 'B-tree' es una estructura de datos de árbol que mantiene los datos ordenados para facilitar búsquedas rápidas, consultas de rango y escaneos ordenados en tiempo logarítmico. Es el tipo de índice predeterminado en muchas bases de datos relacionales.

CREATE INDEX ON statements (valid_from);
CREATE INDEX ON statements (valid_to);
Búsqueda semántica con pgvector

Almacenar embeddings vectoriales en PostgreSQL (a través de la extensión pgvector) permite la recuperación basada en similitud mediante la búsqueda semántica. Esto sigue un proceso de dos pasos:

  1. Almacena vectores de alta dimensión que representan el significado semántico del texto. Estos se pueden crear con modelos de embedding como text-embedding-3-small y text-embedding-3-large de OpenAI.
  2. Usa Approximate Nearest-Neighbour (ANN) para una coincidencia de similitud eficiente a escala.

Hay varias opciones de indexación diferentes disponibles en pgvector, cada una con propósitos distintos. Estas opciones de indexación se describen con más detalle, junto con pasos de implementación en profundidad, en el README del repositorio de Github para pgvector.

Tipo de índice
Tiempo de construcción
Velocidad de consulta
Uso de memoria
Precisión
Escala recomendada
Notas
flat
Mínimo
Lento
(escaneo lineal)
Bajo
100%
(exacto)
Muy pequeño
(< 100 K vectores)
Sin indexación aproximada: escanea todos los vectores. Ideal para recuperación exacta en colecciones pequeñas.
ivfflat
Moderado
Rápido si se ajusta
Moderado
Alto
(ajustable)
Pequeño a mediano
(100 K–200 M)
Usa indexación de archivos invertidos. Los parámetros en tiempo de consulta controlan las compensaciones.
ivfpq
Alto
Muy rápido
Bajo
(cuantificado)
Ligeramente menor
que ivfflat
Mediano a grande
(1 M–500 M)
Combina archivos invertidos con cuantificación de producto para un menor uso de memoria.
hnsw
Más alto
Más rápido
(especialmente a escala)
Alto
(en memoria)
Muy alto
Grande a muy grande
(100 M–Miles de millones+)
Construye un grafo navegable jerárquico. Ideal para sistemas de alta escala y sensibles a la latencia.
Parámetros de ajuste para la indexación vectorial

ivfflat

  • lists: Número de particiones (por ejemplo, 100)
  • probes: Número de particiones a escanear en tiempo de consulta (por ejemplo, 10-20), controla la recuperación vs. la latencia

ivfpq

  • subvectors: Número de bloques a cuantificar (por ejemplo, 16)
  • bits: Número de bits por bloque (por ejemplo, 8)
  • probes: Igual que en ivfflat

hnsw

  • M: Conexiones máximas por nodo (por ejemplo, 16)
  • ef_construction: Tamaño de la lista de candidatos dinámicos en tiempo de construcción (por ejemplo, 200)
  • ef_search: Grupo de candidatos en tiempo de consulta (por ejemplo, 64-128)
Mejores prácticas
  • flat para depuración o conjuntos de datos pequeños
  • ivfflat cuando quieres precisión ajustable con buena velocidad
  • ivfpq cuando la eficiencia de la memoria es crítica
  • hnsw cuando optimizas para la latencia más baja en colecciones masivas
Otras opciones de bases de datos vectoriales en el ecosistema
Vector DB Características clave Ventajas Desventajas
Pinecone Totalmente gestionado, sin servidor; soporta HNSW y SPANN Autoescalado, con SLA, fácil de integrar Dependencia del proveedor; el costo escala con el tamaño
Weaviate API GraphQL, módulos integrados para codificación y vectorización Consultas híbridas (metadatos + vector), modular La implementación en producción requiere Kubernetes
Milvus Soporta indexación GPU; IVF, HNSW, ANNOY Alto rendimiento a escala, indexación dinámica Complejidad operativa; sistema separado
Qdrant Ligero, actualizaciones en tiempo real, filtrado de carga útil Configuración sencilla, buen soporte de consultas híbridas Carece de uniones relacionales nativas; consistencia eventual en clústeres
Vectara Gestionado con clasificación y reclasificación semántica Fuertes características de relevancia; fácil integración Propietario; control de índice limitado
Elegir el almacén de vectores adecuado
Escala
Recomendación
Detalles
Escala pequeña a mediana
(menos de 100M vectores)
PostgreSQL + pgvector
con índice ivfflat
A menudo suficiente para cargas de trabajo moderadas. Configuraciones recomendadas: lists = 100–200, probes = 10–20.
Gran escala
(100M – 1B+ vectores)
Milvus o Qdrant
Adecuado para cargas de trabajo de alto rendimiento, especialmente cuando se necesita indexación acelerada por GPU o latencia de sub-milisegundos.
Escenarios híbridos
PostgreSQL para metadatos
+ DB vectorial dedicada
Usa PostgreSQL para el almacenamiento de metadatos de entidades y una DB vectorial (por ejemplo, Milvus, Qdrant) para la búsqueda de similitud. Sincroniza los embeddings usando pipelines CDC (por ejemplo, Debezium).

Para obtener información más detallada, consulta el cookbook de OpenAI sobre bases de datos vectoriales.

Almacenamiento en disco duradero y respaldo

Para algunos casos, especialmente aquellos que requieren alta disponibilidad o recuperación de estado después de reinicios, puede valer la pena persistir el estado en un almacenamiento en disco confiable e implementar una estrategia de respaldo.

Si la durabilidad es una preocupación, considera usar discos persistentes con respaldos regulares o sincronizar el estado con almacenamiento externo. Aunque no es necesario para todas las implementaciones, puede proporcionar una salvaguarda valiosa contra la pérdida de datos o la interrupción operativa en entornos donde la consistencia y la tolerancia a fallos son importantes.

A.2. Gestión y depuración de conjuntos de datos

A.2.1. Políticas de TTL (Time-to-Live) y archivo

Establece políticas claras para determinar qué hechos deben conservarse indefinidamente (por ejemplo, registros legalmente requeridos para reguladores) y cuáles pueden archivarse después de un período definido (por ejemplo, declaraciones obtenidas de redes sociales con más de un año de antigüedad).

Prácticas clave a incluir:

  1. Trabajos de archivo automatizados

    Configura una tarea en segundo plano que consulte periódicamente los registros con, por ejemplo, valid_to < NOW() - INTERVAL 'X days' y los mueva a una tabla de archivo para almacenamiento a largo plazo.

  2. Políticas de retención específicas de la fuente

    Adapta las duraciones de retención por fuente de datos o tipo de entidad. Por ejemplo, las fuentes de alta autoridad como las publicaciones gubernamentales pueden justificar una retención más prolongada que los datos menos confiables, como titulares de noticias raspados o contenido generado por el usuario.

A.2.2. Puntuación de relevancia y depuración inteligente

A medida que tu grafo de conocimiento crece, la utilidad de muchos hechos disminuirá. Para mantener el grafo enfocado y maximizar el rendimiento:

  1. Indexa una puntuación de relevancia

    Introduce una columna (o columnas) numérica relevance_score que incorpore métricas como la actualidad, la confiabilidad de la fuente y la frecuencia de las consultas de producción.

  2. Lógica de depuración automatizada

    Programa un trabajo rutinario para depurar o archivar hechos que caigan por debajo de un umbral de relevancia predefinido.

Reducción avanzada de grafos basada en relevancia

Reducir eficientemente el tamaño de un grafo de conocimiento es importante al escalar. Una encuesta de 2024 clasifica las técnicas en esparsificación, engrosamiento y condensación, todas destinadas a reducir el grafo mientras se preservan las semánticas críticas para la tarea. Estos métodos ofrecen ganancias sustanciales en tiempo de ejecución y memoria en KGs a gran escala.

Patrón de implementación de ejemplo:

  1. Puntúa cada triple

    Calcula una relevance_score compuesta, por ejemplo:

    relevance_score = β1 * recency_score + β2 * source_trust_score + β3 * retrieval_count

    Donde:

    • recency_score: decaimiento exponencial desde valid_from
    • source_trust_score: valor de confianza del dominio de la fuente
    • retrieval_count: frecuencia de consulta de producción
  2. Aplica una estrategia de reducción
    • Esparsifica: Selecciona y retén solo las aristas o nodos más relevantes basándose en criterios como la centralidad, la similitud espectral o la preservación de embeddings.
    • Engrosa: Agrupa nodos de baja importancia o semánticamente similares en supernodos y agrega sus características y conexiones.
    • Condensa: Construye un mini-grafo optimizado para la tarea desde cero.
  3. Valida en modo sombra

    Registra y compara las salidas del grafo depurado vs. el original antes de enrutar el tráfico de producción.

  4. Vuelve a puntuar regularmente

    Vuelve a calcular la relevancia (por ejemplo, todas las noches) para asegurar que los hechos nuevos o frecuentemente accedidos vuelvan a la cima.

A.3. Implementación de concurrencia en el pipeline de ingesta

Pasar del prototipo a la producción a menudo requiere transformar tu pipeline de procesamiento lineal en un pipeline concurrente y escalable. En lugar de procesar documentos secuencialmente (documento → chunking → extracción de declaraciones → extracción de entidades → invalidación de declaraciones → resolución de entidades), implementa un pipeline por etapas donde cada fase pueda escalar de forma independiente.

Diseña tu pipeline con una serie de etapas especializadas, cada una con su propia cola y pool de workers. Esto te permite escalar los cuellos de botella de forma independiente y mantener la fiabilidad del sistema bajo cargas variables.

  1. Chunking por lotes

    Comienza recolectando documentos en lotes de, por ejemplo, 100-500 usando una cola de trabajos como Redis o Amazon SQS. Procesa estos documentos en paralelo, dividiendo cada uno en sus respectivos chunks. La etapa de chunking a menudo debe optimizar la paralelización de E/S, ya que la lectura de documentos suele ser el cuello de botella. Luego puedes almacenar los chunks y sus metadatos respectivos en tu tabla chunk_store, usando operaciones de inserción masiva para minimizar la sobrecarga.

  2. Extracción de declaraciones y entidades

    Extrae chunks en lotes de, por ejemplo, 50-100 y envíalos a tu LLM elegido (por ejemplo, GPT-4.1-mini) usando solicitudes de API paralelas. Implementa la limitación de velocidad con semáforos u otros métodos para mantenerte de forma segura dentro de los límites de la API de OpenAI mientras maximizas tu rendimiento. Hemos cubierto la limitación de velocidad con más detalle en nuestro cookbook sobre Cómo manejar los límites de velocidad. Una vez extraídos, puedes escribirlos en la tabla relevante de tu base de datos.

    Luego puedes agrupar de manera similar las declaraciones que acabamos de extraer en lotes, y ejecutar los procesos de extracción de entidades de manera similar antes de almacenarlos.

  3. Invalidación de declaraciones

    Agrupa los ID de declaraciones extraídas por sus clústeres de entidades asociados (por ejemplo, todas las declaraciones relacionadas con una entidad específica como "Acme Corp."). Envía cada clúster a tu LLM (por ejemplo, GPT-4.1-mini) en paralelo para evaluar qué declaraciones están desactualizadas o han sido reemplazadas. Usa la salida del modelo para actualizar el campo status en tu tabla statements, por ejemplo, estableciendo status = 'inactive'. Paraleliza los trabajos de invalidación para mejorar el rendimiento y considera programar barridos periódicos para mantener la consistencia.

  4. Resolución de entidades

    Toma lotes de menciones de entidades recién extraídas y calcula embeddings usando el endpoint de embedding de tu modelo. Insértalos en tu tabla entity_registry, asignando a cada uno un entity_id provisional o canónico. Realiza búsquedas de vecinos más cercanos aproximados (ANN) usando pgvector para identificar duplicados cercanos o alias. Luego puedes actualizar la tabla entities con ID canónicos resueltos, asegurando que las tareas posteriores hagan referencia a representaciones unificadas.

Ventajas del procesamiento por lotes

  • Rendimiento – El procesamiento por lotes reduce la sobrecarga de las llamadas individuales a la API y las transacciones de la base de datos.

  • Paralelismo – Cada etapa puede escalar horizontalmente: puedes ejecutar múltiples procesos de worker para chunking, extracción, invalidación, etc., cada uno leyendo de una cola.

  • Contrapresión y fiabilidad – Si una etapa se vuelve lenta (por ejemplo, la invalidación de declaraciones durante un aumento repentino de datos), las etapas anteriores pueden almacenar más elementos en la cola hasta que se libere capacidad.

A.4. Minimización del costo de tokens

A.4.1. Caché de prompts

Evita llamadas redundantes a la API memorizando las respuestas a sub-prompts frágiles.

Estrategia de implementación:

  • Almacena en caché consultas frecuentes: Por ejemplo, prompts repetidos como "Extrae entidades de esta declaración" en declaraciones idénticas.
  • Usa claves hash: Genera una clave de caché única usando el hash MD5 del texto de la declaración: md5(statement_text)
  • Opciones de almacenamiento: Redis para persistencia escalable o caché LRU en memoria para simplicidad y velocidad.
  • Omite las llamadas a la API: Si una declaración se encuentra en la caché, omite la llamada a la API.

A.4.2. Nivel de servicio: Flex

Utiliza el parámetro service_tier=flex en el SDK de OpenAI Responses para habilitar completaciones parciales y reducir costos.

Configuración de la API:

{
  "model": "o4-mini",
  "prompt": "<your prompt>",
  "service_tier": "flex"
}

Beneficios de costos:

  • Solo cobra por los tokens generados, no por los tokens del prompt.
  • Puede reducir los costos hasta en un 40% para extracciones cortas (por ejemplo, listas de entidades de una sola frase).

Puedes obtener más información sobre el poder del procesamiento Flex y cómo utilizarlo en la documentación de la API para el procesamiento Flex.

A.4.3. Minimiza la "charlatanería"

Reemplaza las costosas llamadas de generación de texto con alternativas más eficientes siempre que sea posible.

Enfoque alternativo:

  • Usa el endpoint de embeddings (más barato por token) combinado con la búsqueda de vecinos más cercanos de pgvector.
  • En lugar de preguntarle al modelo "¿Qué declaración existente es más similar?", calcula los embeddings una vez y consulta directamente en Postgres.
  • Este enfoque es particularmente efectivo para tareas de similitud semántica.

Beneficios:

  • Menor costo por operación.
  • Tiempos de respuesta de consulta más rápidos.
  • Reducción de la dependencia de la API para búsquedas de similitud.

A.5. Escalado y puesta en producción de nuestro agente de recuperación

Una vez que tu grafo está poblado, necesitas un mecanismo para responder consultas de múltiples saltos a escala. Esto requiere:

  1. Arquitectura del agente
    • Agente controlador (Frontend): Recibe una pregunta del usuario (por ejemplo, "¿Qué eventos llevaron a la IPO de Acme Corp.?"), luego la descompone en subpreguntas o pasos de recorrido.
    • Agentes trabajadores de recorrido: Cada trabajador puede realizar un recorrido de grafo local (por ejemplo, "Encuentra todos los hechos donde Acme Corp. tiene EventType = Adquisición entre 2020 y 2025"), posiblemente en paralelo en diferentes particiones del grafo.
  2. Extracción paralela de subgrafos
    • Particiona el grafo por hash de ID de entidad (por ejemplo, módulo 16). Para una consulta dada, identifica qué particiones es probable que contengan aristas relevantes, luego despacha tareas de recorrido en paralelo a cada trabajador.
    • Los trabajadores devuelven subgrafos parciales (nodos + aristas), y el Agente Controlador los fusiona.
  3. Razonamiento encadenado de LLM

    Para preguntas de múltiples saltos, el Controlador puede solicitar a un modelo (por ejemplo, GPT-4.1) con el subgrafo parcial y preguntar "¿Qué siguiente arista debo recorrer?". Esto permite un recorrido dinámico y consciente del contexto en lugar de una búsqueda ciega en amplitud.

  4. Almacenamiento en caché y memorización

    Para consultas o patrones de subgrafos frecuentes, almacena los resultados en caché (por ejemplo, en Redis o una vista materializada de Postgres) con un TTL igual a la fecha valid_to del hecho, de modo que las solicitudes posteriores accedan a la caché en lugar de volver a recorrer.

  5. Balanceo de carga y autoescalado

    Despliega los Agentes Trabajadores de Recorrido en un clúster de Kubernetes con Horizontal Pod Autoscalers. Usa métricas de CPU y memoria (y la longitud promedio de la cola) para escalar durante el pico de uso.

A.6. Salvaguardas

A.6.1 Verificación de salida multicapa

Ejecuta un pipeline de validación ligero para asegurar que las salidas sean las deseadas. Algunos ejemplos de lo que se puede incluir en esto:

  • Verifica que las fechas se ajusten a ISO-8601.
  • Verifica que los tipos de entidad coincidan con tu vocabulario controlado (por ejemplo, si el modelo produce una etiqueta inesperada, márcala para revisión manual).
  • Despliega una llamada a función de "verificación de cordura" a un modelo más pequeño y económico para verificar la consistencia de las salidas (por ejemplo, "¿Esta declaración se analiza correctamente como un Hecho? Sí/No.").

A.6.2. Registro de auditoría y monitoreo

  • Implementa un registro estructurado con niveles de verbosidad configurables (por ejemplo, debug, info, warn, error).
  • Almacena los pasos de preprocesamiento de entrada, las salidas intermedias y los resultados finales con trazabilidad completa, como la que ofrece el tracing de OpenAI.
  • Rastrea el rendimiento de tokens, la latencia y las tasas de error.
  • Monitorea las métricas de calidad de datos cuando sea posible, como la cobertura de documentos o declaraciones, las tasas de resolución temporal y más.
  • Mide métricas relacionadas con el negocio, como el número de usuarios, el volumen promedio de mensajes y la satisfacción del usuario.

A.7. Optimización de prompts

  1. Personas

    Introducir una persona al modelo es una forma efectiva de mejorar el rendimiento. Una vez que hayas definido la especialización del componente para el que estás desarrollando el prompt, puedes crear una persona en el prompt del sistema que ayude a moldear el comportamiento del modelo. Usamos esto en nuestro modelo planificador para crear un prompt de sistema como este:

    initial_planner_system_prompt = (
        "You work for the leading financial firm, ABC Incorporated, one of the largest financial firms in the world. "
        "Due to your long and esteemed tenure at the firm, various equity research teams will often come to you "
        "for guidance on research tasks they are performing. Your expertise is particularly strong in the area of "
        "ABC Incorporated's proprietary knowledge base of earnings call transcripts. This contains details that have been "
        "extracted from the earnings call transcripts of various companies with labelling for when these statements are, or "
        "were, valid. You are an expert at providing instructions to teams on how to use this knowledge graph to answer "
        "their research queries. \n"
    )

    Los prompts de persona pueden ser mucho más desarrollados y específicos que este, pero esto debería darte una idea de cómo se ve en la práctica.

  2. Few-Shot Prompting y Chain-of-Thought

    Para tareas relacionadas con la extracción, como la extracción de declaraciones, un prompt few-shot conciso (2 a 5 ejemplos) generalmente ofrecerá una mayor precisión que un prompt zero-shot, con un aumento marginal en el costo.

    Por ejemplo, para tareas de reconciliación temporal, los métodos chain-of-thought, donde guías al modelo a través de la lógica de comparación, son más apropiados. Esto puede verse así:

    Example 1: [Old fact], [New fact] → Invalidate
    Example 2: [Old fact], [New fact] → Coexist
    Now: [Old fact], [New fact] →
  3. Dynamic Prompting y Gestión de Contexto

    También puedes apoyarte en otros LLM o métodos más estructurados para podar y preparar material que se pasará dinámicamente a los prompts. Vimos un ejemplo de esto al construir las herramientas para nuestro retriever anterior, donde la herramienta timeline_generation clasifica el material recuperado antes de pasarlo de nuevo al orquestador central.

    Los pasos para limpiar el contexto o comprimirlo a mitad de ejecución también pueden ser muy efectivos para consultas de mayor duración.

  4. Biblioteca de Plantillas y Pruebas A/B

    Mantén un conjunto de plantillas de prompt en un directorio con control de versiones (por ejemplo, prompts/statement_extraction.json, prompts/entity_extraction.json) para poder auditar cambios pasados y revertir si es necesario. Puedes utilizar los prompts reutilizables de OpenAI para esto. En el panel de control de OpenAI, puedes desarrollar prompts reutilizables para usar en las solicitudes de API. Esto te permite construir y evaluar tus prompts, implementando versiones actualizadas y mejoradas sin cambiar el código.

    Automatiza las pruebas A/B muestreando periódicamente hechos extraídos del pipeline, volviéndolos a ejecutar a través de prompts alternativos y comparando las puntuaciones de rendimiento (puedes hacer un seguimiento de esto en un arnés de evaluación separado).

    Haz un seguimiento de los indicadores clave de rendimiento (KPI) como la latencia de extracción, las tasas de error y la precisión de invalidación.

    Si alguna métrica se desvía más allá de un umbral (por ejemplo, la precisión de invalidación cae por debajo del 90%), activa una alerta y revierte a una versión anterior del prompt.

Lección del curso «OpenAI Cookbook» de OpenAI, publicado con licencia MIT. Traducción y adaptación al español de IA con Clase. IA con Clase no está afiliado a OpenAI. Ver el original · Licencia
Esta lección es gratuita. El resto del curso se abre con la Membresía de IA con Clase, que incluye todos los cursos del catálogo. Ver precios