Inicio / Artículos / Notas prácticas: Aprendí RAG creando un sistema de preguntas y respuestas

Notas prácticas: Aprendí RAG creando un sistema de preguntas y respuestas

Guía paso a paso operativa de las notas prácticas: Aprendí RAG al crear un sistema de preguntas y respuestas: contratos, verificaciones y espacios para código listo para uso destinados a los equipos que implementan este patrón.

2018 palabras

Úselo como una versión reestructurada dirigida a operadores de las ideas presentadas en “Aprendí RAG al crear un sistema de preguntas y respuestas”: etapas claras, espacios ordenados para el código y notas de recuperación que perduran tras la transferencia de tareas. La etapa de Resumen funciona mejor si se considera como una superficie medible. Capture una transcripción clave, un caso de fallo y la nota de reversión antes de ampliar el alcance. Registre los tiempos y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de la demostración a entornos compartidos.

Una breve nota sobre RAG

Una nota rápida sobre el escenario: defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un único lugar al que los operadores puedan acceder para realizar auditorías sin necesidad de leer todo el sistema.

Cómo está estructurado el sistema

Para determinar la etapa en la que se encuentra el sistema, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las intentonas repetidas, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Cite los pasajes que realmente sustentan la respuesta; sin citas, los operadores no podrán distinguir entre alucinaciones y fallos en el indexado.

Construcción de la tubería de ingestión

Para la etapa de construcción del pipeline de ingestión, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un pipeline complicado. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre una alucinación y una laguna en el indexado.

Descarga

En la fase de descarga, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Trate esta fase como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre alucinaciones y lagunas en el indexado.

{
  "doc_id": "bsp-mpr-2023-q3",
  "title": "Monetary Policy Report Q3 2023",
  "period_label": "Q3 2023",
  "publication_date": "2023-08-17",
  "url": "https://www.bsp.gov.ph/..."
}

Análisis

En la fase de análisis, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Cite los pasajes que realmente sirvieron de base para la respuesta; sin citas, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.

Fragmentación, incrustación e inserción

Para la etapa de incrustación por chunking y actualización, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben encontrarse en un lugar que los operadores puedan auditar sin necesidad de leer todo el grafo. Cite los pasajes que realmente sirvieron como base para la respuesta. Sin citaciones, los operadores no pueden distinguir entre una alucinación y una laguna en el indexado.

def chunk_text_by_tokens(text, enc, chunk_size=512, overlap=64):
    tokens = enc.encode(text)
    stride = chunk_size - overlap
    chunks = []
    start = 0
    while start < len(tokens):
        window = tokens[start : start + chunk_size]
        chunks.append(enc.decode(window))
        if start + chunk_size >= len(tokens):
            break
        start += stride
    return chunks

La capa de recuperación y el problema de la recencia

Para la capa y etapa de recuperación, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Cite los pasajes que realmente sustentan la respuesta. Sin citas, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.

RECENCY_WEIGHT = 0.85

def combined_score(hit):
    relevance = hit.score / max_score
    recency = (pub_date - min_date).days / date_span_days  # 0.0 to 1.0
    return (1 - RECENCY_WEIGHT) * relevance + RECENCY_WEIGHT * recency
# scope to Q1 2023 only
retrieve_chunks(query, period_label_key="q1_2023")

# scope to a date range
retrieve_chunks(query, publication_date_from="2023-01-01", publication_date_to="2023-12-31")

La capa de generación

En la etapa de la capa de generación, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre alucinaciones y fallos en el indexado. En la etapa de la capa de generación, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos.

Problemas con los que realmente te topaste

Al trabajar en la etapa de problemas con los que realmente te enfrentaste, anota primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Mantén la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Mide la tasa de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.

Plantillas PDF que contaminan los fragmentos

Al trabajar en la etapa de eliminación de fragmentos no deseados del PDF, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documente junto con ello el camino óptimo y el camino de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Mida la tasa de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.

def _clean_parsed_text(text):
    text = text.replace("\x0c", "\n\n")
    text = re.sub(r"Classification:\s*GENERAL\s*\n", "", text)
    text = re.sub(r"Monetary Policy Report\s*[--][^\n]+\|\s*\d+\s*\n", "", text)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return text.strip()

Borrado accidental de datos recopilados

Al trabajar en la fase de borrado de la colección Accidental, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Prefiera unidades pequeñas y verificables a scripts extensos. Cuando un paso falla, el fallo debe referirse a una sola responsabilidad y no a un proceso complicado. Mida el rendimiento en un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente. Al trabajar en la fase de borrado de la colección Accidental, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando se pasa de entornos de demostración a entornos compartidos.

def _should_recreate_qdrant_collection():
    if _is_production_environment():
        return False
    explicit = os.environ.get("QDRANT_RECREATE_COLLECTION", "").lower()
    if explicit in ("1", "true", "yes"):
        return True
    app_env = (os.environ.get("ENVIRONMENT") or "").lower()
    return app_env in ("development", "dev", "local")

Trozos duplicados después de la reingestión

La etapa de “Trozos duplicados después de la reingestión” funciona mejor cuando se trata como una métrica cuantificable. Capture una transcripción de referencia, un caso de fallo y la nota de reversión antes de ampliar el alcance. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de credenciales y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Separe la política de particionamiento de la política de recuperación. Cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.

client.delete(
    collection_name=collection,
    points_selector=models.Filter(must=[
        models.FieldCondition(
            key="source_file",
            match=models.MatchValue(value=source_file)
        )
    ]),
)

Latencia al iniciar por primera vez en la versión gratuita

La latencia de inicio en frío en la etapa de pruebas funciona mejor cuando se trata como un parámetro medible. Capture una transcripción exitosa, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el camino de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Separe la política de fragmentación de la política de recuperación; cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.

Elegir el tamaño del fragmento

La etapa de selección del tamaño del bloque funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el fallo debe apuntar a una sola responsabilidad y no a un proceso complicado. Separe la política de particionamiento de la política de recuperación; cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad. La etapa de selección del tamaño del bloque funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Registre los tiempos y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando el proceso pasa de la fase de demostración a entornos compartidos.

Qué haría diferente

En la fase de “¿Qué harías?”, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre una alucinación y una laguna en el indexado.

Pruébalo

En la fase de prueba, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Cite los pasajes que realmente sustentan la respuesta. Sin citas, los operadores no pueden distinguir entre alucinaciones y lagunas en el indexado.

Lista de verificación operativa

Al trabajar en la fase de lista de verificación operativa, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista mantiene honestas las futuras modificaciones del código.

Trate esta fase como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina comprobaciones de éxito y rechace las completaciones parciales silenciosas.

Mida el rendimiento de recuperación en un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.

Fije las versiones de dependencias y registre el resumen de la imagen que se utilizó en la demostración. La reproducibilidad es mejor que el conocimiento basado en prácticas internas.

Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas al pasar de la demostración a entornos compartidos.

Mida el rendimiento de recuperación en un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.

Antes de promocionar la solución, congele las versiones, capture una transcripción de referencia para el proceso crítico y confirme los pasos para revertir cambios. Los entornos compartidos requieren límites de uso, verificaciones de asignación y un responsable claro para la rotación de credenciales. Prefiera una fiabilidad sencilla a demostraciones ingeniosas pero puntuales.

Nota por lotes para a30a0175d827: mantener las claves del proveedor fuera del repositorio, establecer un límite para los tokens por sesión y almacenar las transcripciones junto a los archivos de evaluación para que los cambios posteriores en el modelo sigan siendo comparables.