IA locale con Ollama e Nginx: una guida alla validazione automatizzata
Tradotto dall'originale in spagnolo. Leggi in spagnolo

Scopo del progetto
L’obiettivo di questo progetto è eseguire un processo di IA che analizza le cartelle della tombola (bingo) e stabilisce se i numeri forniti fanno bingo. Lo facciamo usando LLM locali con Ollama per l’inferenza e Nginx come livello di sicurezza per gestire SSL e autenticazione. Anche se questo tutorial si concentra sull’analisi delle cartelle, lo stesso concetto si può applicare a molti altri compiti che richiedono una validazione basata sull’IA.
Questo tutorial copre la configurazione di Ollama su un sistema Linux, inclusa la configurazione di Nginx come reverse proxy con SSL.
È solo un esempio a scopo di test e non è pensato per ambienti di produzione.
Per maggiori dettagli su Ollama, visita il repository ufficiale: Ollama GitHub.
Installare Ollama
Per installare Ollama, esegui i seguenti comandi:
curl -fsSL https://ollama.com/install.sh | sh
Scaricare i modelli
Ottieni i modelli necessari:
ollama pull llama3.2-vision
ollama pull phi4
Installare Nginx
Installa Nginx sul tuo server:
sudo apt update
sudo apt install nginx -y
Ottenere i certificati SSL
Ti serve un certificato SSL per una comunicazione sicura. Puoi usare Let’s Encrypt o un’altra autorità di certificazione, ma la procedura non è trattata in questo tutorial.
Configurare Nginx come reverse proxy con SSL e autenticazione
Modifica la configurazione di Nginx:
sudo vim /etc/nginx/nginx.conf
Aggiungi o modifica il seguente contenuto:
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;
}
}
}
Per i dettagli sulla configurazione dell’autenticazione in Nginx, consulta la documentazione ufficiale: Configurare l’autenticazione HTTP Basic.
Riavvia Nginx per applicare le modifiche:
sudo systemctl restart nginx
Il codice
Per prima cosa dobbiamo importare le librerie necessarie e definire alcune costanti.
import base64
import requests
import json
OLLAMA_SERVER_URL = "https://ollama.zerogap.com:11435"
MODEL_NAME = "llama3.2-vision"
IMAGE_PATH = "bingo.jpg"
Funzione per leggere e codificare l’immagine
Questa funzione legge l’immagine della cartella e la codifica in base64 per poterla inviare nella richiesta POST.
def encode_image(image_path):
with open(image_path, 'rb') as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
Funzione per inviare una richiesta POST
Questa funzione invia una richiesta POST al server con l’immagine codificata e il modello di IA.
Seed: garantisce la coerenza delle risposte fissando la casualità del modello.
Temperatura: controlla la creatività delle risposte; valori bassi producono output prevedibili, mentre valori alti consentono maggiore variabilità.
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
Definizione del formato di risposta atteso
Qui definiamo il formato che ci aspettiamo come risposta dall’IA. Questo ci aiuta a strutturare correttamente i dati. L’uso dello structured output nei modelli linguistici (LLM) migliora la precisione, facilita l’integrazione con sistemi esterni, riduce le ambiguità e ottimizza l’analisi dei dati. Ne derivano maggiore affidabilità, interoperabilità e scalabilità nei flussi automatizzati.
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"]
}
Preparazione dell’immagine e richiesta iniziale
Leggiamo e codifichiamo l’immagine della cartella, poi prepariamo il prompt per identificare i numeri presenti nell’immagine.
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)
Analisi dei risultati e validazione
Analizziamo i risultati ottenuti dall’IA per ricavare l’elenco dei numeri trovati e poi verifichiamo se si può fare bingo con un elenco di numeri estratti.
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', [])
Codice 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 :( !!!")
Questo processo ti permette di eseguire un server Ollama sicuro su Linux, con SSL e autenticazione, mentre esegui il riconoscimento dei numeri e la validazione con modelli di IA.
Nota: questo esempio è pensato solo per test e sviluppo. Non è consigliato per ambienti di produzione.
