IA local com Ollama e Nginx: um guia para validação automatizada
Traduzido do original em espanhol. Ler em espanhol

Objetivo do projeto
O objetivo deste projeto é executar um processo de IA para analisar cartelas de bingo e determinar se os números informados resultam em bingo. Para isso, usamos LLMs locais com o Ollama para a inferência de IA e o Nginx como camada de segurança para gerenciar SSL e autenticação. Embora este tutorial se concentre na análise de cartelas de bingo, o mesmo conceito pode ser aplicado a muitas outras tarefas que exijam validação com IA.
Este tutorial cobre a configuração do Ollama em um sistema Linux, incluindo a configuração do Nginx como proxy reverso com SSL.
Este é apenas um exemplo para fins de teste e não se destina a ambientes de produção.
Para mais detalhes sobre o Ollama, visite o repositório oficial: Ollama GitHub.
Instalar o Ollama
Para instalar o Ollama, execute os seguintes comandos:
curl -fsSL https://ollama.com/install.sh | sh
Baixar os modelos
Obtenha os modelos necessários:
ollama pull llama3.2-vision
ollama pull phi4
Instalar o Nginx
Instale o Nginx no seu servidor:
sudo apt update
sudo apt install nginx -y
Obter certificados SSL
Você precisa obter um certificado SSL para uma comunicação segura. Pode usar o Let’s Encrypt ou outra autoridade certificadora, mas o processo não é abordado neste tutorial.
Configurar o Nginx como proxy reverso com SSL e autenticação
Edite a configuração do Nginx:
sudo vim /etc/nginx/nginx.conf
Adicione ou modifique o seguinte conteúdo:
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 detalhes sobre a configuração de autenticação no Nginx, consulte a documentação oficial: Configuração de autenticação básica HTTP.
Reinicie o Nginx para aplicar as mudanças:
sudo systemctl restart nginx
O código
Primeiro, precisamos importar as bibliotecas necessárias e definir algumas constantes.
import base64
import requests
import json
OLLAMA_SERVER_URL = "https://ollama.zerogap.com:11435"
MODEL_NAME = "llama3.2-vision"
IMAGE_PATH = "bingo.jpg"
Função para ler e codificar a imagem
Esta função lê a imagem da cartela de bingo e a codifica em base64 para enviá-la na requisição POST.
def encode_image(image_path):
with open(image_path, 'rb') as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
Função para enviar uma requisição POST
Esta função envia uma requisição POST ao servidor com a imagem codificada e o modelo de IA.
Seed: garante a consistência das respostas ao fixar a aleatoriedade do modelo.
Temperatura: controla a criatividade das respostas; valores baixos geram saídas previsíveis, enquanto valores altos permitem maior variabilidade.
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
Definição do formato de resposta esperado
Aqui definimos o formato que esperamos receber como resposta da IA. Isso nos ajuda a estruturar os dados de forma adequada. O uso de structured output em modelos de linguagem (LLMs) melhora a precisão, facilita a integração com sistemas externos, reduz ambiguidades e otimiza a análise de dados. Isso traz mais confiabilidade, interoperabilidade e escalabilidade em fluxos 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"]
}
Preparação da imagem e requisição inicial
Lemos e codificamos a imagem da cartela de bingo e depois preparamos o prompt para identificar os números presentes na imagem.
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álise dos resultados e validação
Analisamos os resultados obtidos da IA para ter a lista de números encontrados e depois validamos se é possível cantar bingo com uma lista de números sorteados.
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 processo permite executar um servidor Ollama seguro em Linux, com SSL e autenticação, enquanto você faz o reconhecimento de números e a validação com modelos de IA.
Nota: Este exemplo se destina apenas a testes e desenvolvimento. Não é recomendado para ambientes de produção.
