Dialogo con Gemini.
Ciao, supponiamo di utilizzare il sistem con nVidia 12 GB, Ubuntu 26.04 ed Ollama. Supponiamo di voler utilizzare python con una AI, come cominceresti?
Con una scheda da 12 GB di VRAM (come una RTX 3060 o 4070), il sweet spot ideale per far risiedere l’intero modello in memoria e avere un’inferenza velocissima è la fascia dei modelli da 7B – 8B parametri quantizzati a 4-bit (es. llama3.1:8b, qwen2.5:7b o mistral:7b), che occupano circa 5-6 GB di VRAM, lasciando ampio spazio per contesti lunghi (KV cache).
1. Verifica del modello e del demone Ollama
Assicurati che il servizio sia attivo e scarica un modello adatto alla tua scheda:
Cohorte
Bash
# Verifica lo stato di Ollama
systemctl status ollama
# Scarica un modello ottimale per 12 GB VRAM
ollama pull llama3.1:8b
2. Setup dell’ambiente virtuale Python
Su Ubuntu è buona norma evitare di installare librerie globalmente:
Bash
mkdir ai-project && cd ai-project
python3 -m venv .venv
source .venv/bin/activate
# Installa l'SDK ufficiale di Ollama (in alternativa puoi usare la compatibilità OpenAI)
pip install ollama
3. Script base: Chat con Streaming
Per le applicazioni interattive, lo streaming evita tempi morti restituendo i token man mano che vengono generati:
Python
import ollama
def chat_stream():
messages = [
{"role": "system", "content": "Sei un assistente tecnico esperto e conciso."},
{"role": "user", "content": "Spiegami brevemente come gestire la memoria GPU con PyTorch."}
]
response = ollama.chat(
model="llama3.1:8b",
messages=messages,
stream=True,
)
for chunk in response:
print(chunk["message"]["content"], end="", flush=True)
print()
if __name__ == "__main__":
chat_stream()
4. Pattern avanzato: Structured Output (JSON / Pydantic)
Se vuoi usare l’LLM all’interno di una pipeline backend (es. per estrarre entità o classificare testi), sfrutta lo schema vincolato:
Python
from pydantic import BaseModel
import ollama
class HardwareProfile(BaseModel):
gpu_name: str
vram_gb: int
optimal_model_size: str
response = ollama.chat(
model="llama3.1:8b",
messages=[{"role": "user", "content": "Configurazione: RTX 3060 con 12 GB VRAM."}],
format=HardwareProfile.model_json_schema(),
)
data = HardwareProfile.model_validate_json(response["message"]["content"])
print(data)
5. Monitoraggio delle risorse
Durante l’esecuzione, apri un secondo terminale con:
Bash
watch -n 1 nvidia-smi
In questo modo puoi verificare che tutti i layer (gpu_layers) siano caricati al 100% sulla memoria della scheda senza fare offload parziale sulla RAM di sistema.
tutti e tre , grazie
Ecco una guida completa che affronta i tre aspetti, integrando gestione della memoria, esecuzione di tool e architettura RAG vettoriale.
1. Ottimizzazione Parametri e Memoria GPU
Con 12 GB di VRAM, il bilanciamento critico riguarda la lunghezza del contesto (num_ctx), poiché la cache KV cresce linearmente con la finestra di contesto e alloca VRAM dedicata.
In Ollama puoi passare parametri runtime direttamente tramite l’argomento options:
Python
import ollama
response = ollama.chat(
model="llama3.1:8b",
messages=[{"role": "user", "content": "Analizza questo scenario..."}],
options={
"num_ctx": 8192, # Default è 2048; 8192 consuma ~1.5 GB in più di VRAM per la KV cache
"temperature": 0.2, # Bassa per compiti analitici/codice
"top_p": 0.9,
"num_predict": 1024, # Max token in output
},
keep_alive="10m" # Mantiene il modello in VRAM per 10 minuti (evita il ricaricamento da disco)
)
Verifica dell’offload GPU: Nel terminale, durante la richiesta, esegui:
Bash
ollama ps
La colonna PROCESSOR deve mostrare 100% GPU. Se vedi una percentuale mista (es. 70%/30% CPU/GPU), riduci num_ctx o chiudi altre applicazioni grafiche per evitare l’offload parziale su RAM di sistema.
2. Tool Use e Function Calling in Python
Ollama supporta il function calling nativo (particolarmente solido su llama3.1:8b e qwen2.5:7b). L’approccio moderno nell’SDK Python consiste nel passare funzioni Python native: il client ne estrae schema e docstring automaticamente.
Python
import json
import subprocess
import ollama
# 1. Definizione dello strumento con docstring dettagliate per l'LLM
def check_disk_usage(path: str = "/") -> str:
"""Restituisce lo spazio su disco disponibile per un percorso specifico del filesystem."""
result = subprocess.run(["df", "-h", path], capture_output=True, text=True)
return result.stdout.strip()
# Mappa per il dispatching
available_tools = {
"check_disk_usage": check_disk_usage
}
# 2. Prima chiamata: invio del prompt e della lista di tool disponibili
messages = [
{"role": "user", "content": "Controlla lo spazio disponibile sulla root / del server."}
]
response = ollama.chat(
model="llama3.1:8b",
messages=messages,
tools=[check_disk_usage],
)
messages.append(response["message"])
# 3. Se il modello richiede l'esecuzione di un tool
if response["message"].get("tool_calls"):
for tool_call in response["message"]["tool_calls"]:
func_name = tool_call["function"]["name"]
args = tool_call["function"]["arguments"]
if func_name in available_tools:
# Esecuzione locale della funzione Python
tool_output = available_tools[func_name](**args)
# Restituzione del risultato del tool al modello
messages.append({
"role": "tool",
"content": str(tool_output)
})
# 4. Seconda chiamata: il modello sintetizza il risultato per l'utente
final_response = ollama.chat(
model="llama3.1:8b",
messages=messages
)
print("Risposta finale:\n", final_response["message"]["content"])
3. Pipeline RAG con Ollama Embeddings e PostgreSQL pgvector
Un’architettura RAG locale performante usa un modello di embedding dedicato leggero (come nomic-embed-text o bge-m3), che richiede meno di 500 MB di VRAM e produce vettori densi ad alta accuratezza.
Step A: Preparazione PostgreSQL
Nel database Postgres, abilita l’estensione e crea la tabella (assumendo i vettori a 768 dimensioni generati da nomic-embed-text):
SQL
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE document_nodes (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
metadata JSONB,
embedding vector(768)
);
-- Indice HNSW per query approssimate veloci con distanza coseno
CREATE INDEX ON document_nodes USING hnsw (embedding vector_cosine_ops);
Step B: Script Python (Ingestion e Retrieval)
Dipendenze richieste:
Bash
pip install psycopg[binary] ollama
ollama pull nomic-embed-text
Python
import ollama
import psycopg
DB_URI = "postgresql://utente:password@localhost:5432/nomedb"
EMBED_MODEL = "nomic-embed-text"
CHAT_MODEL = "llama3.1:8b"
def get_embedding(text: str) -> list[float]:
"""Genera l'embedding a 768 dimensioni tramite Ollama."""
res = ollama.embeddings(model=EMBED_MODEL, prompt=text)
return res["embedding"]
def insert_document(content: str, metadata: dict = None):
"""Archivia documento e relativo embedding in pgvector."""
vec = get_embedding(content)
with psycopg.connect(DB_URI) as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO document_nodes (content, metadata, embedding) VALUES (%s, %s, %s::vector)",
(content, psycopg.types.json.Jsonb(metadata or {}), vec)
)
conn.commit()
def search_similar(query: str, top_k: int = 3) -> list[str]:
"""Recupera i chunk più simili tramite distanza coseno (<=>)."""
query_vec = get_embedding(query)
with psycopg.connect(DB_URI) as conn:
with conn.cursor() as cur:
cur.execute(
"""
SELECT content, 1 - (embedding <=> %s::vector) AS cosine_similarity
FROM document_nodes
ORDER BY embedding <=> %s::vector
LIMIT %s;
""",
(query_vec, query_vec, top_k)
)
rows = cur.fetchall()
return [row[0] for row in rows]
def rag_query(user_query: str):
"""Pipeline completa: recupero contestuale e generazione della risposta."""
# 1. Retrieval
retrieved_chunks = search_similar(user_query, top_k=2)
context_text = "\n---\n".join(retrieved_chunks)
# 2. Augmented Prompt
system_prompt = (
"Sei un assistente tecnico. Rispondi alla domanda dell'utente basandoti "
"esclusivamente sul contesto fornito qui sotto. Se l'informazione non è presente, "
"dichiara chiaramente che non disponi di dati sufficienti.\n\n"
f"CONTESTO RECUPERATO:\n{context_text}"
)
# 3. Generation
response = ollama.chat(
model=CHAT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_query}
],
options={"temperature": 0.0, "num_ctx": 4096}
)
return response["message"]["content"]
if __name__ == "__main__":
# Esempio pratico
insert_document("Il backup locale del cluster PostgreSQL viene eseguito ogni notte alle 03:00 con pg_dump.", {"source": "ops_manual"})
insert_document("La temperatura di soglia per l'allarme GPU è impostata a 82 gradi Celsius.", {"source": "monitoring_spec"})
risultato = rag_query("A che ora viene pianificato il backup del database?")
print("Risposta RAG:\n", risultato)
Gemini è un’AI e può fare errori, a