IA Local con Ollama y Nginx: Una Guía para Validación Automatizada

Propósito del Proyecto
El objetivo de este proyecto es ejecutar un proceso de IA para analizar tarjetas de bingo y determinar si los números proporcionados resultan en un bingo. Esto se logra utilizando LLMs locales con Ollama para la inferencia de IA y Nginx como una capa de seguridad para manejar SSL y autenticación. Aunque este tutorial se centra en el análisis de tarjetas de bingo, el mismo concepto se puede aplicar a muchas otras tareas que requieran validación impulsada por IA.
Este tutorial cubre la configuración de Ollama en un sistema Linux, incluyendo la configuración de Nginx como proxy inverso con SSL.
Este es solo un ejemplo para fines de prueba y no está destinado para entornos de producción.
Para más detalles sobre Ollama, visita el repositorio oficial: Ollama GitHub.
Instalar Ollama
Para instalar Ollama, ejecuta los siguientes comandos:
curl -fsSL https://ollama.com/install.sh | sh
Descargar Modelos
Obtén los modelos necesarios:
ollama pull llama3.2-vision
ollama pull phi4
Instalar Nginx
Instala Nginx en tu servidor:
sudo apt update
sudo apt install nginx -y
Obtener Certificados SSL
Necesitas obtener un certificado SSL para una comunicación segura. Puedes usar Let’s Encrypt u otra autoridad certificadora, pero el proceso no está cubierto en este tutorial.
Configurar Nginx como Proxy Inverso con SSL y Autenticación
Edita la configuración de Nginx:
sudo vim /etc/nginx/nginx.conf
Agrega o modifica el siguiente contenido:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 3600;
client_max_body_size 5G;
client_body_timeout 3600;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
server {
listen 11435 ssl;
server_name yourdomain.com;
ssl_certificate /path/to/your/cert.pem;
ssl_certificate_key /path/to/your/key.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 60m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:11434;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
Para detalles sobre la configuración de autenticación en Nginx, consulta la documentación oficial: Configuración de Autenticación Básica en HTTP.
Reinicia Nginx para aplicar los cambios:
sudo systemctl restart nginx
El Código
Primero, necesitamos importar las bibliotecas necesarias y definir algunas constantes.
import base64
import requests
import json
OLLAMA_SERVER_URL = "https://ollama.zerogap.com:11435"
MODEL_NAME = "llama3.2-vision"
IMAGE_PATH = "bingo.jpg"
Función para Leer y Codificar la Imagen
Esta función se encarga de leer la imagen del cartón de bingo y codificarla en base64 para poder enviarla en la solicitud POST.
def encode_image(image_path):
with open(image_path, 'rb') as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
Función para Enviar una Solicitud POST
Esta función se encarga de enviar una solicitud POST al servidor con la imagen codificada y el modelo de IA.
Seed: asegura la consistencia de las respuestas al fijar la aleatoriedad del modelo.
Temperatura: controla la creatividad de las respuestas; valores bajos generan salidas predecibles, mientras que valores altos permiten mayor variabilidad.
def send_post_request(model, prompt, images=None, stream=False, response_format=None, system_prompt=None):
body = {
"model": model,
"prompt": prompt,
"system": system_prompt,
"images": images if images else [],
"stream": stream,
"format": response_format if response_format else {},
"options": {
"seed": 46,
"temperature": 0.1
}
}
headers = {
"Content-Type": "application/json",
}
try:
response = requests.post(f"{OLLAMA_SERVER_URL}/api/generate", json=body, headers=headers)
response.raise_for_status() # Raise an HTTPError on bad response
return response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
return None
Definición del Formato de Respuesta Esperado
Aquí definimos el formato que esperamos recibir como respuesta de la IA. Esto nos ayudará a estructurar los datos de manera adecuada. El uso de structured output en modelos de lenguaje (LLMs) mejora la precisión, facilita la integración con sistemas externos, reduce ambigüedades y optimiza el análisis de datos. Esto permite mayor confiabilidad, interoperabilidad y escalabilidad en flujos automatizados.
response_format = {
"type": "object",
"properties": {
"found_numbers": {
"type": "array",
"items": {"type": "number"}
}
},
"required": ["found_numbers"]
}
validation_response_format = {
"type": "object",
"properties": {
"is_valid": {"type": "boolean"}
},
"required": ["is_valid"]
}
Preparación de la Imagen y Solicitud Inicial
Leemos y codificamos la imagen del cartón de bingo, luego preparamos el prompt para identificar los números presentes en la imagen.
encoded_image = encode_image(IMAGE_PATH)
vision_identifier_prompt = "Identify and return all numbers present in the image. The output should be a structured JSON object with a key 'found_numbers' containing a list of all detected numbers."
system_prompt="Your task is to analyze the image provided, following specific instructions from the user."
vision_results = send_post_request(MODEL_NAME, vision_identifier_prompt, images=[encoded_image],response_format=response_format, system_prompt=system_prompt)
Análisis de Resultados y Validación
Analizamos los resultados obtenidos de la IA para obtener la lista de números encontrados y posteriormente validamos si se puede cantar bingo con una lista de números seleccionados.
if vision_results:
message_content = vision_results.get('response')
vision_data = json.loads(message_content)
found_numbers = vision_data.get('found_numbers', [])
validator_prompt = (
f"""CARD: {found_numbers}, SELECTED: [32,29,17,75]"""
)
system_prompt = "Your task is to determine if all numbers in the 'SELECTED' list are present in the 'CARD' list. Return only a JSON object with the key 'is_valid' and the boolean value true if all numbers are found, or false otherwise. Do not provide any explanation or code. Example: Input: { \"CARD\": [3, 9, 10, 14, 17, 20, 24, 25, 29, 32, 34, 37, 42, 47, 48, 50, 55, 57, 62, 68, 69, 72, 70, 75], \"SELECTED\": [302, 29, 17, 9] } Output: { \"is_valid\": false }"
MODEL_NAME = "phi4"
validation_results = send_post_request(MODEL_NAME, validator_prompt, response_format=validation_response_format, system_prompt=system_prompt)
if validation_results:
validation_message_content = validation_results.get('response')
validation_data = json.loads(validation_message_content)
validated_numbers = validation_data.get('is_valid', [])
Código completo
import base64
import requests
import json
# Define constants for server URL and model
OLLAMA_SERVER_URL = "https://ollama.zerogap.com:11435"
MODEL_NAME = "llama3.2-vision"
IMAGE_PATH = "bingo.jpg"
# Function to read and encode the image
def encode_image(image_path):
with open(image_path, 'rb') as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
# Function to send a POST request
def send_post_request(model, prompt, images=None, stream=False, response_format=None, system_prompt=None):
body = {
"model": model,
"prompt": prompt,
"system": system_prompt,
"images": images if images else [],
"stream": stream,
"format": response_format if response_format else {},
"options": {
"seed": 46,
"temperature": 0.1
}
}
headers = {
"Content-Type": "application/json",
}
try:
response = requests.post(f"{OLLAMA_SERVER_URL}/api/generate", json=body, headers=headers)
response.raise_for_status() # Raise an HTTPError on bad response
return response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
return None
# Define the expected response format for a list of numbers
response_format = {
"type": "object",
"properties": {
"found_numbers": {
"type": "array",
"items": {"type": "number"}
}
},
"required": ["found_numbers"]
}
# Define the expected response format for validations (boolean)
validation_response_format = {
"type": "object",
"properties": {
"is_valid": {"type": "boolean"}
},
"required": ["is_valid"]
}
# Prepare the image
encoded_image = encode_image(IMAGE_PATH)
# Improved vision identifier prompt
vision_identifier_prompt = "Identify and return all numbers present in the image. The output should be a structured JSON object with a key 'found_numbers' containing a list of all detected numbers."
system_prompt="Your task is to analyze the image provided."
vision_results = send_post_request(MODEL_NAME, vision_identifier_prompt, images=[encoded_image],response_format=response_format, system_prompt=system_prompt)
if vision_results:
# Parse the structured output from the vision results
message_content = vision_results.get('response')
vision_data = json.loads(message_content)
found_numbers = vision_data.get('found_numbers', [])
# Validate the results
validator_prompt = (
f"""CARD: {found_numbers}, SELECTED: [32,29,17,75]"""
)
system_prompt = "Your task is to determine if all numbers in the 'SELECTED' list are present in the 'CARD' list. Return only a JSON object with the key 'is_valid' and the boolean value true if all numbers are found, or false otherwise. Do not provide any explanation or code. Example: Input: { \"CARD\": [3, 9, 10, 14, 17, 20, 24, 25, 29, 32, 34, 37, 42, 47, 48, 50, 55, 57, 62, 68, 69, 72, 70, 75], \"SELECTED\": [302, 29, 17, 9] } Output: { \"is_valid\": false }"
MODEL_NAME = "phi4"
validation_results = send_post_request(MODEL_NAME, validator_prompt, response_format=validation_response_format, system_prompt=system_prompt)
if validation_results:
# Parse the structured output from the validation results
validation_message_content = validation_results.get('response')
validation_data = json.loads(validation_message_content)
validated_numbers = validation_data.get('is_valid', [])
# Print the final analysis
print("--- Final Analysis ---")
if validated_numbers:
print("BINGO!!!")
else:
print("No BINGO :( !!!")
Este proceso te permitirá ejecutar un servidor Ollama seguro en Linux con SSL y autenticación, mientras realizas el reconocimiento de números y validación usando modelos de IA.
Nota: Este ejemplo está destinado únicamente para pruebas y desarrollo. No se recomienda para entornos de producción.
