Ho fatto funzionare ollama sia con docer che con uv. Aggiornato il readme

This commit is contained in:
Simone Garau
2025-09-16 14:46:15 +02:00
parent 96ef5ae745
commit 0f6a7dabb6
8 changed files with 252 additions and 12 deletions

View File

@@ -13,3 +13,10 @@ GOOGLE_API_KEY=
ANTHROPIC_API_KEY= ANTHROPIC_API_KEY=
DEEPSEEK_API_KEY= DEEPSEEK_API_KEY=
OPENAI_API_KEY= OPENAI_API_KEY=
# Dipende dal sistema operativo
# windows: C:\Users\<user>\.ollama
# mac: /Users/<user>/.ollama
# linux: /home/<user>/.ollama
# wsl: /usr/share/ollama/.ollama
OLLAMA_MODELS_PATH=

View File

@@ -2,7 +2,7 @@
# Infatti scegliamo l'immagine ufficiale di uv che ha già tutto configurato # Infatti scegliamo l'immagine ufficiale di uv che ha già tutto configurato
# Nel caso in cui si volesse usare un'altra immagine di base che ha magari CUDA # Nel caso in cui si volesse usare un'altra immagine di base che ha magari CUDA
# bisognerebbe installare uv manualmente come descritto nel README # bisognerebbe installare uv manualmente come descritto nel README
#FROM pytorch/pytorch:2.6.0-cuda12.6-cudnn9-devel # Lo lascio qui nel caso
FROM ghcr.io/astral-sh/uv:python3.12-alpine FROM ghcr.io/astral-sh/uv:python3.12-alpine
# Dopo aver definito la workdir mi trovo già in essa # Dopo aver definito la workdir mi trovo già in essa
@@ -22,4 +22,4 @@ COPY LICENSE .
COPY src ./src COPY src ./src
# Comando di default all'avvio dell'applicazione # Comando di default all'avvio dell'applicazione
CMD ["python", "src/app.py"] CMD ["python", "src/example.py"]

View File

@@ -10,7 +10,9 @@ L'obiettivo di questo progetto è creare un sistema basato su **LLM Agents** e d
# Installazione # Installazione
Per l'installazione si può utilizzare un approccio tramite **uv** (manuale) oppure utilizzare un ambiente **Docker** già pronto (automatico). Per l'installazione si può utilizzare un approccio tramite **uv** (manuale) oppure utilizzare un ambiente **Docker** già pronto (automatico).
Prima di avviare l'applicazione è però necessario configurare correttamente le API keys, altrimenti il progetto, anche se installato correttamente, non riuscirà a partire. Prima di avviare l'applicazione è però necessario configurare correttamente le API keys e installare Ollama per l'utilizzo dei modelli locali, altrimenti il progetto, anche se installato correttamente, non riuscirà a partire.
## API Keys
Le API Keys puoi ottenerle tramite i seguenti servizi: Le API Keys puoi ottenerle tramite i seguenti servizi:
- **Google AI**: [Google AI Studio](https://makersuite.google.com/app/apikey) (gratuito con limiti) - **Google AI**: [Google AI Studio](https://makersuite.google.com/app/apikey) (gratuito con limiti)
- **Anthropic**: [Anthropic Console](https://console.anthropic.com/) - **Anthropic**: [Anthropic Console](https://console.anthropic.com/)
@@ -19,6 +21,27 @@ Le API Keys puoi ottenerle tramite i seguenti servizi:
Nota che alcune API sono gratuite con limiti di utilizzo, altre sono a pagamento. Google offre attualmente l'accesso gratuito con limiti ragionevoli. Nota che alcune API sono gratuite con limiti di utilizzo, altre sono a pagamento. Google offre attualmente l'accesso gratuito con limiti ragionevoli.
## Ollama (Modelli Locali)
Per utilizzare modelli AI localmente, è necessario installare Ollama:
**1. Installazione Ollama**:
- **Linux**:
```sh
curl -fsSL https://ollama.com/install.sh | sh
```
- **macOS/Windows**: Scarica l'installer da [https://ollama.com/download/windows](https://ollama.com/download/windows)
**2. GPU Support (Raccomandato)**:
Per utilizzare la GPU con Ollama, assicurati di avere NVIDIA CUDA Toolkit installato:
- **Download**: [NVIDIA CUDA Downloads](https://developer.nvidia.com/cuda-downloads?target_os=Windows&target_arch=x86_64&target_version=11&target_type=exe_local)
- **Documentazione WSL**: [CUDA WSL User Guide](https://docs.nvidia.com/cuda/wsl-user-guide/index.html)
**3. Installazione Modelli**:
Esempio per installare un modello locale:
```sh
ollama pull gemma3:4b
```
### Variabili d'Ambiente ### Variabili d'Ambiente
**1. Copia il file di esempio**: **1. Copia il file di esempio**:
@@ -26,12 +49,14 @@ Nota che alcune API sono gratuite con limiti di utilizzo, altre sono a pagamento
cp .env.example .env cp .env.example .env
``` ```
**2. Modifica il file .env** creato con le tue API keys, inserendole nella variabile opportuna dopo l'uguale e ***senza*** spazi: **2. Modifica il file .env** creato con le tue API keys e il path dei modelli Ollama, inserendoli nelle variabili opportune dopo l'uguale e ***senza*** spazi:
```dotenv ```dotenv
GOOGLE_API_KEY= GOOGLE_API_KEY=
ANTHROPIC_API_KEY= ANTHROPIC_API_KEY=
DEEPSEEK_API_KEY= DEEPSEEK_API_KEY=
OPENAI_API_KEY= OPENAI_API_KEY=
# Path dove Ollama salva i modelli (es. /home/username/.ollama su Linux)
OLLAMA_MODELS_PATH=
``` ```
### Opzione 1 UV ### Opzione 1 UV

View File

@@ -8,8 +8,30 @@ services:
- .:/app - .:/app
env_file: env_file:
- .env - .env
# Aggiunte chiave:
environment: environment:
# Questa variabile dice alla tua app dove trovare il servizio Ollama
- OLLAMA_HOST=http://ollama:11434
# Le tue API keys esistenti
- GOOGLE_API_KEY=${GOOGLE_API_KEY} - GOOGLE_API_KEY=${GOOGLE_API_KEY}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY}
# Assicura che ollama parta prima della tua app
depends_on:
- ollama
# Nuovo servizio per Ollama
ollama:
image: ollama/ollama
container_name: ollama
# Aggiungi il runtime NVIDIA per GPU support
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
ports:
- "11434:11434"
volumes:
# Mappa la cartella dei modelli del tuo PC a quella interna del container
# ${OLLAMA_MODELS_PATH} sarà letto dal file .env
- ${OLLAMA_MODELS_PATH}:/root/.ollama

View File

@@ -27,5 +27,7 @@ dependencies = [
# altamente consigliata dato che ha anche tools integrati per fare scraping, calcoli e molto altro # altamente consigliata dato che ha anche tools integrati per fare scraping, calcoli e molto altro
# oltre a questa è necessario installare anche le librerie specifiche per i modelli che si vogliono usare # oltre a questa è necessario installare anche le librerie specifiche per i modelli che si vogliono usare
"agno", "agno",
"google-genai" "google-genai",
# ☑️ per usare modelli in locale
"ollama",
] ]

View File

@@ -2,19 +2,27 @@ from agno.agent import Agent
from agno.models.google import Gemini from agno.models.google import Gemini
from agno.tools.reasoning import ReasoningTools from agno.tools.reasoning import ReasoningTools
from dotenv import load_dotenv from dotenv import load_dotenv
import ollama
from ollama_demo import generate_text
try: def run_gemini_poem():
load_dotenv() load_dotenv()
reasoning_agent = Agent( reasoning_agent = Agent(
model=Gemini(), model=Gemini(),
tools=[ tools=[ReasoningTools()],
ReasoningTools(),
],
instructions="Use tables to display data.", instructions="Use tables to display data.",
markdown=True, markdown=True,
) )
result = reasoning_agent.run("Scrivi una poesia su un gatto. Sii breve.") result = reasoning_agent.run("Scrivi una poesia su un gatto. Sii breve.")
print(result.content) print(result.content)
except Exception as e:
pass def run_ollama_codegemma_poem():
prompt = "Scrivi una poesia su un gatto. Sii breve."
response = generate_text(model="gpt-oss:latest", prompt=prompt)
print(response)
if __name__ == "__main__":
print("Risposta Gemini:")
run_gemini_poem()
print("\nRisposta Ollama GPT-OSS:")
run_ollama_codegemma_poem()

161
src/ollama_demo.py Normal file
View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Demo di Ollama (Python) mostra:
1. Elenco dei modelli disponibili
2. Generazione di testo semplice
3. Chat con streaming
4. Calcolo di embeddings
5. Esempio (opzionale) di function calling / tools
Uso:
python ollama_demo.py
Requisiti:
pip install ollama
Avviare il server Ollama (es. 'ollama serve' o l'app desktop) e avere i modelli già pullati.
"""
import ollama
# Configurazione modelli
MODEL = 'gpt-oss:latest' # modello principale per testo/chat
EMBEDDING_MODEL = 'mxbai-embed-large:latest' # modello dedicato embeddings (richiede supporto embeddings)
# 1. Elenco dei modelli -------------------------------------------------------
def list_models():
"""Stampa i modelli caricati nel server Ollama."""
print("\n[1] Modelli disponibili:")
try:
response = ollama.list()
models = getattr(response, 'models', []) or (response.get('models', []) if isinstance(response, dict) else [])
if not models:
print(" (Nessun modello trovato)")
return
for m in models:
name = getattr(m, 'model', None) or (m.get('model') if isinstance(m, dict) else 'sconosciuto')
details = getattr(m, 'details', None)
fmt = getattr(details, 'format', None) if details else 'unknown'
print(f"{name} {fmt}")
except Exception as e:
print(f" ❌ Errore durante il listing: {e}")
# 2. Generazione di testo ------------------------------------------------------
def generate_text(model: str, prompt: str, max_tokens: int = 200) -> str:
"""Genera testo dal modello indicato."""
print(f"\n[2] Generazione testo con '{model}'")
response = ollama.chat(
model=model,
messages=[{"role": "user", "content": prompt}]
)
text = response['message']['content']
print("Risposta:\n" + text + "\n")
return text
# 3. Chat con streaming --------------------------------------------------------
def chat_streaming(model: str, messages: list) -> str:
"""Esegue una chat mostrando progressivamente la risposta."""
print(f"\n[3] Chat (streaming) con '{model}'")
stream = ollama.chat(model=model, messages=messages, stream=True)
full = ""
for chunk in stream:
if 'message' in chunk and 'content' in chunk['message']:
part = chunk['message']['content']
full += part
print(part, end="", flush=True)
print("\n")
return full
# 4. Embeddings ----------------------------------------------------------------
def get_embedding(model: str, text: str):
"""Calcola embedding del testo col modello specificato (se supportato)."""
print(f"\n[4] Embedding con '{model}'")
try:
r = ollama.embeddings(model=model, prompt=text)
except ollama.ResponseError as e:
print(f" ⚠️ Il modello '{model}' non supporta embeddings o errore API: {e}")
return None
emb = r['embedding']
print(f"Dimensione embedding: {len(emb)} (prime 5: {emb[:5]})")
return emb
# 5. Function calling / Tools (opzionale) --------------------------------------
def try_tools(model: str):
"""Esempio di function calling; se non supportato mostra messaggio informativo."""
print(f"\n[5] Function calling / tools con '{model}'")
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Ottiene condizioni meteo sintetiche di una località (demo)",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "Città"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
}
]
try:
response = ollama.chat(
model=model,
messages=[{"role": "user", "content": "Che tempo fa a Milano?"}],
tools=tools
)
msg = response['message']
if 'tool_calls' in msg and msg['tool_calls']:
tool_call = msg['tool_calls'][0]
print("Richiesta funzione:", tool_call['function']['name'])
print("Argomenti:", tool_call['function']['arguments'])
else:
print("Risposta modello senza tool call:", msg.get('content', ''))
except ollama.ResponseError as e:
if 'does not support tools' in str(e).lower():
print("Il modello non supporta i tools.")
else:
print("Errore API:", e)
# Main -------------------------------------------------------------------------
if __name__ == '__main__':
# 1. Elenco modelli
list_models()
# 2. Prompt semplice
generate_text(
model=MODEL,
prompt="Scrivi una poesia breve su un tramonto al mare. Usa circa 40 parole."
)
# 3. Chat con streaming
chat_streaming(
model=MODEL,
messages=[
{"role": "system", "content": "Sei un assistente conciso e utile."},
{"role": "user", "content": "Suggerisci 3 consigli per un cappuccino cremoso a casa."}
]
)
# 4. Embedding (usa modello dedicato se diverso)
if EMBEDDING_MODEL:
get_embedding(
model=EMBEDDING_MODEL,
text="L'intelligenza artificiale accelera l'innovazione in molti settori."
)
else:
print("\n[4] Salto embedding: nessun modello embedding configurato.")
# 5. Function calling (opzionale)
try_tools(MODEL)

15
uv.lock generated
View File

@@ -477,6 +477,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" },
] ]
[[package]]
name = "ollama"
version = "0.5.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/72/62/a36be4555e4218d6c8b35e72e0dfe0823845400097275cd81c9aec4ddf39/ollama-0.5.4.tar.gz", hash = "sha256:75857505a5d42e5e58114a1b78cc8c24596d8866863359d8a2329946a9b6d6f3", size = 45233, upload-time = "2025-09-16T00:25:25.785Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/af/d0a23c8fdec4c8ddb771191d9b36a57fbce6741835a78f1b18ab6d15ae7d/ollama-0.5.4-py3-none-any.whl", hash = "sha256:6374c9bb4f2a371b3583c09786112ba85b006516745689c172a7e28af4d4d1a2", size = 13548, upload-time = "2025-09-16T00:25:24.186Z" },
]
[[package]] [[package]]
name = "orjson" name = "orjson"
version = "3.11.3" version = "3.11.3"
@@ -937,6 +950,7 @@ dependencies = [
{ name = "dotenv" }, { name = "dotenv" },
{ name = "google-genai" }, { name = "google-genai" },
{ name = "gradio" }, { name = "gradio" },
{ name = "ollama" },
] ]
[package.metadata] [package.metadata]
@@ -945,6 +959,7 @@ requires-dist = [
{ name = "dotenv" }, { name = "dotenv" },
{ name = "google-genai" }, { name = "google-genai" },
{ name = "gradio" }, { name = "gradio" },
{ name = "ollama" },
] ]
[[package]] [[package]]