FastAPI + Ollama: un backend AI in Python in 30 minuti
Come costruire un'API REST che interroga modelli AI locali con FastAPI e Ollama. Streaming, endpoint per chat, integrazione con Celery per task pesanti.
Pubblicato: 3 giugno 2025
FastAPI è il framework Python più veloce per costruire API REST. Ollama espone i tuoi modelli AI locali su http://localhost:11434. I due si connettono naturalmente — e in meno di mezz’ora hai un backend AI funzionante, senza cloud, senza costi per token, senza dati che escono dalla tua rete.
Setup minimo: endpoint /chat in 15 righe
Prima installa le dipendenze:
pip install fastapi uvicorn ollama
Poi crea main.py:
from fastapi import FastAPI
from ollama import Client
app = FastAPI()
ollama = Client()
@app.post("/chat")
async def chat(prompt: str):
response = ollama.chat(
model="qwen2.5:7b",
messages=[{"role": "user", "content": prompt}]
)
return {"response": response['message']['content']}
Avvia con uvicorn main:app --reload e hai un endpoint POST su http://localhost:8000/chat. Testa subito:
curl -X POST "http://localhost:8000/chat?prompt=Spiegami+SOLID+in+3+righe"
Ollama deve girare già in background — se non ce l’hai: docker run -d -p 11434:11434 ollama/ollama.
Streaming token-by-token (Server-Sent Events)
La risposta sincrona blocca finché il modello non ha finito. Per un’esperienza tipo ChatGPT — carattere per carattere mentre arriva — usa lo streaming:
from fastapi.responses import StreamingResponse
@app.post("/chat/stream")
async def chat_stream(prompt: str):
def generate():
stream = ollama.chat(
model="qwen2.5:7b",
messages=[{"role": "user", "content": prompt}],
stream=True
)
for chunk in stream:
token = chunk['message']['content']
yield f"data: {token}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
Il frontend consuma lo stream con EventSource o fetch + ReadableStream. Il vantaggio pratico: l’utente vede la risposta iniziare in ~200ms invece di aspettare 8-10 secondi per una risposta lunga.
Celery per task pesanti: non bloccare il thread HTTP
Se devi elaborare 100 PDF con l’AI, analizzare un dataset, o fare operazioni che durano decine di secondi, non farlo nel thread HTTP. L’utente aspetta, il server si blocca, i timeout arrivano.
Soluzione: Celery con Redis come broker. L’endpoint risponde subito con un task ID, il worker elabora in background.
tasks.py:
from celery import Celery
celery_app = Celery("tasks", broker="redis://localhost:6379/0",
backend="redis://localhost:6379/0")
@celery_app.task
def analyze_document(file_path: str, user_email: str):
from ollama import Client
client = Client()
with open(file_path) as f:
content = f.read()
result = client.chat(
model="qwen2.5:7b",
messages=[{"role": "user", "content": f"Analizza: {content}"}]
)
# send_email(user_email, result['message']['content'])
return result['message']['content']
In main.py aggiungi:
from tasks import analyze_document
@app.post("/analyze")
async def analyze(file_path: str, email: str):
task = analyze_document.delay(file_path, email)
return {"task_id": task.id, "status": "in elaborazione"}
@app.get("/status/{task_id}")
async def status(task_id: str):
from celery.result import AsyncResult
result = AsyncResult(task_id)
return {"status": result.status, "result": result.result}
Avvia il worker: celery -A tasks worker --loglevel=info
Deploy con Docker Compose
docker-compose.yml con tutti e tre i servizi:
services:
api:
build: .
ports:
- "8000:8000"
depends_on: [redis, ollama]
worker:
build: .
command: celery -A tasks worker --loglevel=info
depends_on: [redis, ollama]
redis:
image: redis:7-alpine
ollama:
image: ollama/ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
volumes:
ollama_data:
Dockerfile minimo:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Cosa fare
- Parti dal setup minimo: installa FastAPI + Ollama e fai girare l’endpoint
/chatin locale — 10 minuti, zero infrastruttura. - Aggiungi lo streaming appena vuoi usarlo in un frontend: la differenza di UX è netta e l’implementazione è una manciata di righe.
- Usa Celery solo quando hai task che durano più di 2-3 secondi — per tutto il resto, l’endpoint sincrono o lo streaming sono più che sufficienti.