¿Cómo manejar mejor las solicitudes HTTP de larga duración y grandes cargas JSON de ScholarAPI.net en un flujo RAG?

Hola comunidad n8n,

Actualmente estoy construyendo un pipeline automatizado de ingesta de investigación usando n8n para potenciar un sistema académico RAG (Retrieval-Augmented Generation).

Objetivo del flujo de trabajo:

El flujo de trabajo de automatización se activa cuando se registra un nuevo tema de investigación. Llama a un motor de infraestructura de datos académicos llamado ScholarAPI.net para extraer datos académicos de texto completo y metadatos de citaciones altamente detallados. Una vez que se recupera la carga JSON, n8n pasa los datos a un nodo de incrustación (embedding) y los envía a un Almacén Vectorial.

El desafío:

Al realizar la llamada del nodo HTTP Request a ScholarAPI.net, la respuesta JSON que contiene el texto completo de múltiples trabajos científicos puede ser bastante grande (a veces varios megabytes de texto limpio y estructurado por lote).

Tengo dos preguntas arquitectónicas específicas para mantener el flujo de trabajo optimizado:

  1. Manejo de tiempos de espera / límites de ejecución: Para lotes grandes de datos de texto completo, el procesamiento de la API ascendente podría tomar algunos momentos. ¿Cuál es la mejor práctica en n8n para configurar perfiles de reintento resilientes o extender los tiempos de espera en el nodo HTTP Request para que el flujo de trabajo no falle prematuramente?

  2. División de memoria/datos: Procesar cargas JSON anidadas enormes directamente en un único hilo de ejecución causa un uso intensivo de memoria. ¿Debería usar el nodo “Item Lists” para dividir las matrices de texto entrantes inmediatamente después de recibirlas de ScholarAPI.net, o es un nodo de Código personalizado (JavaScript/Python) más eficiente para fragmentar cadenas de texto antes de enviarlas a incrustaciones vectoriales?

¡Agradecería cualquier consejo o plantilla de flujo de trabajo de cualquiera que haya construido pipelines de ingesta/raspado de texto a gran escala dentro de n8n!

Gracias de antemano.

@Asgef_Sha para los timeouts, el nodo HTTP Request tiene un campo Timeout bajo Options, auméntalo para las llamadas lentas, y activa Retry On Fail en Settings con Max Tries y Wait Between Tries. gotcha, el retry solo se activa si On Error es Stop Workflow, cámbialo a una opción Continue e n8n ignora los conteos de reintentos.

para los payloads, no hagas chunking manual con Item Lists o un nodo Code, n8n tiene un nodo Recursive Character Text Splitter para exactamente esto (Chunk Size + Chunk Overlap) alimentando el Default Data Loader en tu vector store. Divide primero el array papers para que cada uno fluya por su cuenta en lugar de mantener el blob completo de varios mb en una sola ejecución.

¡Bienvenido @Asgef_Sha! El consejo de achamm sobre el divisor de texto es exactamente correcto. Un patrón adicional para lotes multi-artículos: después de obtener la respuesta HTTP, usa un nodo SplitInBatches para procesar artículos en grupos de 5-10 a la vez, luego pasa cada lote a través del divisor de texto e inserción en el almacén de vectores. Esto previene que una carga útil grande única permanezca en memoria durante la incrustación, lo que puede causar que la ejecución se estanque incluso con el tiempo de espera extendido. También vale la pena establecer el tiempo de espera de la solicitud HTTP en al menos 120 segundos para las llamadas de texto completo de ScholarAPI, ya que la agregación del lado del servidor puede ser lenta en consultas grandes.

Para este tipo de ingesta de RAG, no mantendría todo en una única ejecución síncrona larga. Guarda primero la respuesta bruta de ScholarAPI, divide el payload en fragmentos manejables, luego procesa/embebe en pasos más pequeños con reintentos y puntos de control. JSON grande más llamadas HTTP largas es donde la confiabilidad aburrida le gana a un flujo ingenioso todo en uno. ¿Eres capaz de guardar la respuesta bruta en algún lugar antes de que comience el paso de embedding?

Buenas respuestas ya arriba. El divisor de texto más Split Out más SplitInBatches cubren la división en fragmentos, y OMGItsDerek tiene razón en que querrás persistir la respuesta sin procesar antes de incrustar. Yo ampliaría ese último punto, porque para este tipo de ingesta la arquitectura importa más que cualquier configuración de nodo individual.

Algunas cosas que me han salvado en grandes tuberías de ingesta de texto:

  1. Divídelo en dos flujos de trabajo, no uno. El Flujo de trabajo A simplemente llama a ScholarAPI y escribe cada artículo en una tabla o almacén de objetos con estado “obtenido”. El Flujo de trabajo B recoge filas que están “obtenidas”, las divide en fragmentos, las incrustra y las cambia a “incrustadas”. La costosa extracción de API y la lenta incrustación fallan de manera independiente. Si la incrustación falla en el artículo 40 de 50, nunca vuelves a extraer el lote, B simplemente reanuda las filas no terminadas. Esa columna de estado es tu punto de control.

  2. Hazlo idempotente. Identifica cada artículo con un id estable (DOI o id de ScholarAPI) y verifica el almacén antes de incrustar. Los reintentos y las reejecuciones son inevitables en trabajos largos, y sin una clave de deduplicación pagas por incrustar el mismo artículo dos veces y terminas con vectores duplicados que contaminan la recuperación. Es el triunfo de confiabilidad más barato que existe.

  3. Corrige el tamaño en la fuente si puedes. Si ScholarAPI admite paginación o una extracción por artículo, extrae páginas más pequeñas en lugar de un lote de varios mb. La forma más confiable de no quedarte sin memoria en un JSON enorme es nunca mantenerlo como un único elemento en primer lugar. Usar Split Out temprano ayuda, las llamadas ascendentes más pequeñas ayudan más.

  4. Una pequeña aclaración sobre el punto de reintento anterior: Retry On Fail reintentará el nodo independientemente. La configuración On Error decide qué sucede después de que se agotan los reintentos, Stop Workflow detiene, Continue pasa el elemento fallido aguas abajo. Para un trabajo de ingesta mantengo los reintentos activos, On Error configurado para continuar (usando salida de error), y dirijo los fallos a una tabla de letras muertas para que un artículo incorrecto no hunda la ejecución completa.

  5. Si estás alojado localmente y las cargas útiles son genuinamente grandes, dos palancas de entorno ayudan: aumenta el heap de Node con NODE_OPTIONS=–max-old-space-size, y reduce la retención de datos de ejecución para que las ejecuciones de varios mb no engordes tu base de datos.

En conclusión, la división aburrida productor-consumidor con una columna de estado y una clave de deduplicación te llevará mucho más lejos que ajustar tiempos de espera en una gran ejecución. Estoy feliz de ampliar cualquiera de estos puntos.

Hola :waving_hand:
Creo que entiendo lo que quieres decir — el problema aquí no es solo timeout o batching, sino el hecho de que n8n está manejando un payload JSON muy grande dentro de un único contexto de ejecución.

En casos como ScholarAPI devolviendo texto estructurado masivo, el cuello de botella real suele ser la memoria de ejecución + encadenamiento de nodos, no solo límites HTTP.

He visto comportamiento similar donde dividir o hacer «async» el flujo por sí solo no lo resuelve completamente porque los datos siguen almacenados en memoria durante el ciclo de vida de la ejecución.

¿Ya has intentado aislar completamente el paso de ingesta del paso de transformación (no solo dividiendo dentro del mismo workflow)?

Dos configuraciones concretas de n8n para solucionar esto: primero, en el nodo HTTP Request > Options > Timeout, establécelo en 300000 (5 min) para que la llamada no se corte a mitad de la respuesta en cargas útiles grandes. Segundo, si ScholarAPI devuelve un array de papers, canaliza la salida a través de un nodo Split In Batches configurado con tamaño de lote 1 antes del paso de embedding - esto mantiene solo un paper en el contexto de ejecución a la vez en lugar de todos ellos. Para aislar completamente el alcance de memoria como sugirió @Bella123, llama a un sub-workflow separado a través de Execute Workflow para el paso de embedding + almacenamiento. De esa manera, el texto de cada paper se recolecta como basura después de que el sub-workflow regresa en lugar de acumularse en la ejecución padre.

Yo dividiría esto en ingesta, normalización e incrustación en lugar de intentar que un único flujo de trabajo grande maneje todo de una vez.

Para este tipo de ruta ScholarAPI → RAG, la parte arriesgada generalmente no es solo la solicitud HTTP larga. Es que una carga útil enorme puede fallar en varios límites diferentes:

  1. límite de obtención
    La llamada HTTP puede agotar el tiempo de espera o devolver demasiados datos para que una ejecución los mantenga cómodamente.

  2. límite de normalización
    Necesitas decidir qué significa un “documento” antes de dividirlo: artículo, resumen, sección, bloque de citación o metadatos del autor.

  3. límite de división
    El paso de incrustación debe recibir unidades predecibles, no cargas útiles sin procesar de la API.

  4. límite de reintentos
    Si un registro falla, no quieres volver a obtener e incrustar todo el conjunto de resultados.

El patrón que usaría:

  • solicitar una página/lote de registros;
  • escribir inmediatamente metadatos de respuesta sin procesar en algún lugar duradero;
  • dividir cada artículo en un elemento normalizado;
  • almacenar un id de elemento + estado de procesamiento;
  • ejecutar chunking/incrustación como un segundo flujo de trabajo sobre elementos pendientes;
  • marcar cada elemento como obtenido, normalizado, dividido, incrustado, fallido o omitido.

Eso te da restartabilidad. También facilita la depuración de la calidad de RAG porque puedes inspeccionar qué etapa produjo chunks deficientes.

Una pregunta no sensible: ¿ScholarAPI te permite paginar o filtrar resultados por fecha/consulta, o estás recibiendo una única respuesta grande que debe dividirse después de la solicitud?