====== 2. Proxy Meteorológico con n8n, AEMET y Ollama ====== Este ejercicio consiste en crear un flujo de trabajo en n8n que actúe como un puente (proxy) entre un cliente de IA (Open WebUI) y los datos reales de la AEMET, procesados por un modelo local de Ollama. ===== Paso 1: Webhook y Extracción de Datos de AEMET (JSON Crudo) ===== El primer objetivo es crear un punto de entrada que, al ser consultado, devuelva el JSON técnico de la estación meteorológica de Valencia. **Configuración detallada:** * **Nodo Webhook:** * HTTP Method: ''GET'' * Path: ''clima-valencia'' * Response Mode: ''When Last Node Finishes'' * **Nodo HTTP Request (Paso A):** * URL: ''https://opendata.aemet.es/opendata/api/observacion/convencional/datos/estacion/8416Y'' (Estación Valencia Aeropuerto). * Header: Añadir llave ''api_key'' con tu token de AEMET OpenData. * **Nodo HTTP Request (Paso B):** * AEMET no envía los datos directamente, envía un JSON con una URL en el campo ''datos''. * Debes crear un segundo nodo HTTP Request cuya URL sea la expresión: ''{{ $json.datos }}''. * **Resultado esperado:** Al llamar a la URL del Webhook, el navegador debe mostrar el array de datos meteorológicos (temperatura, humedad, etc.). ===== Paso 2: Integración de IA con Ollama (Procesamiento de Lenguaje) ===== Añadiremos una capa de inteligencia para que la respuesta no sea un JSON técnico, sino un informe humano. **Instrucciones:** 1. Instalar y levantar **Ollama** con el modelo ''llama3'' o ''mistral''. 2. En n8n, añadir el nodo **Ollama** (o un AI Agent con el modelo Ollama). 3. Configurar el **Prompt**: > "Actúa como un meteorólogo. Basándote en estos datos: Temperatura {{ $json[0].ta }}°C y Humedad {{ $json[0].hr }}%, genera un breve informe del tiempo en Valencia en español." 4. Ahora, el flujo debe devolver una cadena de texto amigable. ===== Paso 3: Emulación de la API de OpenAI (Modo Proxy) ===== Este es el paso crítico. Para que **Open WebUI** reconozca nuestro n8n como si fuera "ChatGPT", debemos disfrazar la respuesta. Las aplicaciones de IA no esperan un texto suelto, esperan un objeto JSON específico. **Configuración Técnica para el alumno:** * **Cambiar Webhook:** El método debe ser obligatoriamente ''POST'' y el path ''v1/chat/completions''. * **Nodo Respond to Webhook:** Debes configurar el cuerpo de la respuesta en formato **JSON** con la siguiente estructura exacta: { "id": "chatcmpl-n8n-aemet", "object": "chat.completion", "created": {{ Math.floor(Date.now() / 1000) }}, "model": "aemet-valencia-bot", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "{{ $node["Ollama"].json.output }}" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } **¿Por qué hacemos esto?** * **id/object/created:** Son campos de control que el estándar de OpenAI exige. * **choices:** Es una lista porque la API original permite devolver varias respuestas, aunque aquí solo enviamos la nuestra en la posición ''[0]''. * **content:** Es donde inyectamos el texto que generó Ollama en el paso anterior. ===== Paso 4: Consumo desde Open WebUI ===== Finalmente, integramos nuestro n8n como un proveedor de servicios en la interfaz de usuario. **Instrucciones:** * En **Open WebUI**, ir a //Settings > Connections > OpenAI API//. * Añadir la URL de nuestro webhook de n8n (omitiendo el final ''/v1/chat/completions''). * Seleccionar el nuevo modelo en el chat y comprobar que, al preguntar cualquier cosa, la IA responde con el clima real de Valencia obtenido de la AEMET. ===== Anexo Técnico: Emulación del Estándar OpenAI Chat Completions ===== Para que una interfaz como **Open WebUI** o cualquier cliente compatible pueda consumir nuestro flujo de n8n, el Webhook debe comportarse exactamente como un servidor de OpenAI. Esto implica cumplir con una ruta específica y una estructura de respuesta estricta. ==== 1. Configuración del Endpoint (Ruta) ==== Open WebUI busca por defecto el recurso en la ruta estándar de la API de Chat. Por lo tanto, el nodo **Webhook** de n8n debe configurarse con: * **HTTP Method:** ''POST'' * **HTTP Path:** ''v1/chat/completions'' * **Response Mode:** ''When Last Node Finishes'' (Esto es crítico: n8n debe esperar a que Ollama termine para responder al chat). ==== 2. La Estructura del JSON de Respuesta ==== El cliente de chat espera un objeto JSON complejo. Si n8n responde solo con texto plano, el chat dará un error de "Formato Inválido". El nodo final **Respond to Webhook** debe configurarse para devolver un **JSON** con esta estructura mínima: { "id": "chatcmpl-n8n-aemet", "object": "chat.completion", "created": {{ Math.floor(Date.now() / 1000) }}, "model": "aemet-valencia-bot", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "{{ $node["Ollama"].json.output }}" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 } } ==== 3. Desglose de Campos Clave ==== | Campo | Función | | **id** | Identificador único de la respuesta. Usamos un prefijo estático seguido de un ID aleatorio si se desea. | | **created** | Marca de tiempo en formato Unix (segundos). En n8n usamos la expresión ''Math.floor(Date.now() / 1000)''. | | **model** | Nombre que aparecerá en la interfaz del chat como "modelo activo". | | **choices[0].message.content** | Aquí es donde insertamos la respuesta generada por el nodo de **Ollama**. Es el texto que el usuario leerá en su burbuja de chat. | | **finish_reason** | Indica que la IA ha terminado de hablar. Debe ser ''"stop"'' para que el chat cierre la conexión correctamente. | ==== 4. Conexión en el Cliente (Open WebUI) ==== En la configuración de conexiones de Open WebUI: * La **Base URL** debe ser la URL de n8n hasta antes de la versión: Ej. ''https://n8n.tu-centro.es/webhook/''. * Automáticamente, el cliente añadirá ''v1/chat/completions'' al final de esa URL para realizar la petición POST.