Yerel Ollama ve LLM: Modelleri Kendi Donanımınızda Çalıştırma
2023'te Büyük Dil Modelini yerel olarak çalıştırmak yalnızca kimin sorumluluğundaydı
derin bir teknik uzmanlığa sahipti: llama.cpp'yi derlemek, ağırlıkları dönüştürmek, yapılandırmak
GGML parametreleri, karmaşık bağımlılıkları yönetir. Sonra geldi Ollama,
ve her şey değişti. Tek bir komutla — ollama run llama3 — herkes
dakikalar içinde dizüstü bilgisayarlarında rekabetçi bir LLM'nin çalışmasını sağlayabilirler.
Trend patlayıcı. Ollama 2024'te aylık 1 milyonun üzerinde indirmeye ulaştı, yıllık yüzde 300 büyüme kaydetti. Piyasa açıkça seçiyor mahremiyet (veriler cihazdan ayrılmaz), sıfır maliyet API'nin, özelleştirme (özel modeller, sabit sistem istemleri) e kullanılabilirlik çevrimdışı. Bu avantajlar göçü tetikliyor Bulut API'sinden yerel dağıtıma kadar pek çok iş akışı.
Bu kılavuzda kurulumdan üretime: Ollama'nın nasıl yapılandırılacağı, doğru modeli seçin, özel Model dosyaları oluşturun, REST API'leri kullanıma açın, LangChain'i entegre edin çevrimdışı RAG işlem hatları için ve belirli kullanım durumları için GGUF modellerinin ince ayarının yapılması, dizüstü bilgisayarlarda, sunucularda ve Raspberry Pi'de.
Ne Öğreneceksiniz
- Windows, macOS ve Linux'ta Ollama kurulumu
- Model seçme kılavuzu: Llama, Qwen, Phi, Gemma, Mistral, DeepSeek
- Model dosyası: özel parametrelerle özel asistanlar oluşturun
- Ollama REST API: Python, JavaScript ve cURL ile entegrasyon
- Resmi kütüphane ve OpenAI uyumluluğu aracılığıyla Python ile Ollama
- LangChain ve FAISS ile çevrimdışı RAG boru hattı
- Raspberry Pi'ye ve systemd ile başsız sunucuya dağıtım
- OpenWebUI: Tamamen çevrimdışı ChatGPT benzeri arayüz
- Ayrıntılı kıyaslamalar ve nicemleme düzeyi seçimi
- Üretim için çoklu model yönetimi ve optimizasyonu
Ollama Dahili Olarak Nasıl Çalışır?
Ollama'yı kullanmadan önce, onun aslında ne işe yaradığını anlamakta fayda var. Ollama ve ambalaj kağıdı etrafında lama.cpp, bunu mümkün kılan C++ çıkarım motoru emtia donanımında nicemlenmiş modelleri çalıştırın. Ollama şunu ekliyor:
- Model kaydı: GGUF modelleri için Docker Hub benzeri çekme/itme sistemi
- REST API sunucusu: 11434 numaralı bağlantı noktasında yerel bir HTTP sunucusunu ortaya çıkarır
- Model önbelleğe alma: İstekler arasında modelleri RAM'de yüklü tutar
- GPU algılama: NVIDIA CUDA, AMD ROCm ve Apple Metal'i otomatik olarak algılar
- Bağlam yönetimi: bağlam penceresini ve KV önbelleğini yönetir
# Architettura Ollama - diagramma semplificato
#
# Client (Python/cURL/Browser)
# |
# v
# [Ollama REST API - port 11434]
# |
# v
# [Model Manager] --- ~/.ollama/models/ (storage GGUF)
# |
# v
# [llama.cpp backend]
# |
# _____|______
# | |
# [CPU] [GPU/Metal]
# ARM/x86 CUDA/ROCm/Metal
#
# Formato modelli: GGUF (GPT-Generated Unified Format)
# Quantization levels: Q4_K_M, Q5_K_M, Q6_K, Q8_0, F16
#
# Dove sono i modelli sul disco:
# macOS/Linux: ~/.ollama/models/
# Windows: C:\Users\USERNAME\.ollama\models\
#
# Struttura directory:
# ~/.ollama/models/
# ├── blobs/ (file GGUF binari, identificati da SHA256)
# └── manifests/ (metadata: quale blob = quale modello:tag)
import subprocess, json
def ollama_status():
"""Controlla status Ollama e modelli caricati."""
result = subprocess.run(
["ollama", "list"], capture_output=True, text=True
)
print("Modelli installati:")
print(result.stdout)
# Controlla processo
ps = subprocess.run(
["pgrep", "-x", "ollama"], capture_output=True, text=True
)
running = ps.returncode == 0
print(f"Ollama in esecuzione: {running}")
ollama_status()
Kurulum ve İlk Adımlar
Ollama tek komutla kurulur ve herhangi bir konfigürasyon gerektirmez. Destek macOS (Apple Silicon ve Intel), Windows (NVIDIA veya AMD GPU'larla) ve Linux (deb/rpm/generic).
# ================================================================
# INSTALLAZIONE OLLAMA
# ================================================================
# macOS / Linux (un comando):
# curl -fsSL https://ollama.com/install.sh | sh
# Windows:
# Download installer da https://ollama.com/download
# (include supporto CUDA automatico se GPU NVIDIA presente)
# Verifica installazione:
# ollama --version
# ollama serve (avvia il server manualmente se non e attivo)
# ================================================================
# COMANDI BASE
# ================================================================
# Esegui un modello (download automatico se non presente)
# ollama run llama3.2
# Lista modelli disponibili localmente
# ollama list
# Pull senza eseguire (per pre-scaricare)
# ollama pull llama3.2:3b
# Informazioni dettagliate su un modello
# ollama show llama3.2
# Rimuovi un modello (libera spazio disco)
# ollama rm llama3.2:old-version
# Copia un modello con nome diverso
# ollama cp llama3.2 my-custom-model
# ================================================================
# VARIABILI D'AMBIENTE UTILI
# ================================================================
# Ascolta su tutte le interfacce (per accesso dalla rete)
# export OLLAMA_HOST=0.0.0.0:11434
# Directory custom per i modelli
# export OLLAMA_MODELS=/mnt/ssd/ollama-models
# Numero massimo richieste parallele (default: 1)
# export OLLAMA_NUM_PARALLEL=4
# Massimo modelli in memoria (default: 1)
# export OLLAMA_MAX_LOADED_MODELS=2
# Tempo prima di scaricare un modello dalla RAM (default: 5m)
# export OLLAMA_KEEP_ALIVE=30m
# ================================================================
# MODELLI POPOLARI e REQUISITI HARDWARE (2025)
# ================================================================
MODELS_GUIDE = {
# Modelli PICCOLI (per Raspberry Pi / laptop 8 GB)
"qwen2.5:1.5b": {"size": "0.9 GB", "ram": "2 GB", "quality": 7, "rpi5_tps": 4.5},
"llama3.2:1b": {"size": "1.3 GB", "ram": "2 GB", "quality": 7, "rpi5_tps": 5.1},
"phi3.5:mini": {"size": "2.2 GB", "ram": "4 GB", "quality": 8, "rpi5_tps": 2.8},
"qwen2.5:3b": {"size": "1.9 GB", "ram": "4 GB", "quality": 8, "rpi5_tps": 2.1},
"gemma2:2b": {"size": "1.6 GB", "ram": "3 GB", "quality": 8, "rpi5_tps": 3.2},
# Modelli MEDI (laptop 16+ GB / desktop)
"llama3.2:3b": {"size": "2.0 GB", "ram": "4 GB", "quality": 8, "rpi5_tps": 1.8},
"mistral:7b": {"size": "4.1 GB", "ram": "8 GB", "quality": 9, "rpi5_tps": 0.8},
"llama3.1:8b": {"size": "4.7 GB", "ram": "8 GB", "quality": 9, "rpi5_tps": 0.6},
"qwen2.5:7b": {"size": "4.4 GB", "ram": "8 GB", "quality": 9, "rpi5_tps": 0.7},
"deepseek-r1:8b": {"size": "4.9 GB", "ram": "8 GB", "quality": 9, "rpi5_tps": 0.5},
# Modelli GRANDI (workstation 24+ GB / server)
"llama3.1:70b": {"size": "40 GB", "ram": "64 GB", "quality": 10, "rpi5_tps": None},
"qwen2.5:72b": {"size": "41 GB", "ram": "64 GB", "quality": 10, "rpi5_tps": None},
"deepseek-r1:32b": {"size": "19 GB", "ram": "32 GB", "quality": 10, "rpi5_tps": None},
}
print("Modelli consigliati per hardware:")
print(" Raspberry Pi 5 (8GB): qwen2.5:1.5b, llama3.2:1b, gemma2:2b")
print(" Laptop 16GB: llama3.1:8b, qwen2.5:7b, mistral:7b")
print(" Mac M2/M3 (24GB): llama3.1:8b, gemma2:9b, qwen2.5:14b")
print(" Workstation 48GB+: llama3.1:70b, deepseek-r1:32b")
Niceleme Düzeyleri: Hangi GGUF'u Seçmelisiniz?
Bittiğinde ollama pull llama3.1:8bOllama otomatik olarak indirir
donanımınız için en uygun nicemleme. Ancak açıkça seçmek mümkündür
önemli bir kalite/boyut/hız değiş tokuşu ile nicemleme seviyesi.
GGUF Niceleme Seviyeleri Kılavuzu
| Etiketler / Format | Bit/ağırlık | Boyut (7B) | Şaşkınlık kaybı | Şunun için önerilir: |
|---|---|---|---|---|
| S2_K | 2,63 bit | 2,7GB | +%15-20 | Yalnızca RAM mutlak kısıtlama ise |
| S4_K_S | 4,37 bit | 4,5 GB | +%2-3 | İyi hız/kalite dengesi |
| S4_K_M | 4,58 bit | 4,8 GB | +%1-2 | Önerilen varsayılan (etkili nokta) |
| S5_K_M | 5,68 bit | 5,7 GB | +%0,5-1 | <6 GB RAM ile en yüksek kalite |
| S6_K | 6,57 bit | 6,6GB | +%0,1-0,3 | F16 ile neredeyse aynı, daha fazla RAM gerektirir |
| S8_0 | 8,5 bit | 8,5GB | ~%0 | En yüksek kalite, 9+ GB RAM gerektirir |
| F16 | 16 bit | 14 GB | %0 (temel) | Eğitim/ince ayar, çıkarım amaçlı değil |
# Scegliere esplicitamente la quantizzazione su Ollama
# I tag dipendono dal modello - usa 'ollama show' per vedere le opzioni
# Default (Ollama sceglie automaticamente, solitamente Q4_K_M):
# ollama pull llama3.1:8b
# Specifica quantizzazione manualmente (sintassi dipende dal modello):
# ollama pull llama3.1:8b-instruct-q4_K_M
# ollama pull llama3.1:8b-instruct-q5_K_M
# ollama pull llama3.1:8b-instruct-q8_0
# Per modelli HuggingFace non su Ollama registry:
# Scarica GGUF manualmente e importa con Modelfile:
IMPORT_GGUF_MODELFILE = """
FROM ./path/to/model-q4_k_m.gguf
PARAMETER temperature 0.7
PARAMETER num_ctx 4096
SYSTEM "Sei un assistente utile."
"""
# echo IMPORT_GGUF_MODELFILE > Modelfile
# ollama create mio-modello -f Modelfile
# ollama run mio-modello
# Confronto performance Q4 vs Q5 vs Q8 (Llama 3.1 8B, MacBook M3 Pro):
QUANT_BENCHMARK = {
"Q4_K_M": {"size_gb": 4.8, "tps": 38.2, "quality_vs_f16": "98.5%"},
"Q5_K_M": {"size_gb": 5.7, "tps": 33.1, "quality_vs_f16": "99.2%"},
"Q6_K": {"size_gb": 6.6, "tps": 29.4, "quality_vs_f16": "99.7%"},
"Q8_0": {"size_gb": 8.5, "tps": 24.8, "quality_vs_f16": "99.9%"},
}
for quant, data in QUANT_BENCHMARK.items():
print(f"{quant}: {data['size_gb']}GB, {data['tps']}t/s, qualità={data['quality_vs_f16']}")
Model dosyası: Özel Asistanlar Oluşturun
Un Model dosyası ve Ollama'nın özel şablonlar oluşturmaya yönelik mekanizması. Tanımlamanıza olanak tanır: temel model, sistem istemi, üretim parametreleri (sıcaklık, top_p, bağlam penceresi) ve hatta bir modeli ek dosyalarla genişletin. Bu eşdeğer bir Dockerfile'a, ancak dil modelleri için.
# ================================================================
# ESEMPI PRATICI DI MODELFILE
# ================================================================
# --- Modelfile 1: Assistente tecnico italiano ---
MODEL_FILE_TECH = """
FROM qwen2.5:7b
# Parametri di generazione
PARAMETER temperature 0.3 # Bassa = risposte più deterministiche
PARAMETER top_p 0.9 # Nucleus sampling
PARAMETER top_k 40 # Top-k sampling
PARAMETER num_ctx 8192 # Context window (4096-32768)
PARAMETER repeat_penalty 1.1 # Evita ripetizioni
# System prompt (definisce il comportamento del modello)
SYSTEM \"\"\"
Sei un assistente tecnico esperto in Python, deep learning e machine learning.
Rispondi SEMPRE in italiano, in modo conciso e tecnico.
Quando mostri codice, usa sempre blocchi markdown con il linguaggio specificato.
Se non sei sicuro di qualcosa, dillo esplicitamente.
Non inventare informazioni o API che non esistono.
\"\"\"
# Messaggio di benvenuto
MESSAGE user "Ciao!"
MESSAGE assistant "Ciao! Sono il tuo assistente tecnico. Come posso aiutarti oggi con Python, deep learning o machine learning?"
"""
# Crea il modello:
# echo MODEL_FILE_TECH > Modelfile-tech-it
# ollama create assistente-tech-it -f Modelfile-tech-it
# ollama run assistente-tech-it
# --- Modelfile 2: Code review assistant ---
MODEL_FILE_CODE = """
FROM llama3.1:8b
PARAMETER temperature 0.1 # Molto deterministico per codice
PARAMETER num_ctx 16384 # Context grande per file lunghi
PARAMETER repeat_penalty 1.05
SYSTEM \"\"\"
You are an expert code reviewer. When reviewing code:
1. Identify bugs, security issues, and performance problems
2. Suggest specific improvements with code examples
3. Follow PEP8/language standards
4. Be concise: list issues with severity (CRITICAL/HIGH/MEDIUM/LOW)
Be direct and actionable. Never hallucinate API methods.
\"\"\"
"""
# --- Modelfile 3: RAG con documenti aziendali ---
MODEL_FILE_RAG = """
FROM qwen2.5:7b
PARAMETER temperature 0.1
PARAMETER num_ctx 32768 # Contesto lungo per documenti
PARAMETER repeat_penalty 1.0
SYSTEM \"\"\"
Sei un assistente che risponde SOLO basandosi sui documenti forniti nel contesto.
Se non trovi la risposta nel contesto, dì esattamente: "Non ho informazioni su questo nei documenti forniti."
Non aggiungere mai informazioni esterne. Cita sempre il documento sorgente nella risposta.
Rispondi in italiano.
\"\"\"
"""
print("Modelfile pronti. Per creare:")
print(" ollama create assistente-tech-it -f Modelfile-tech-it")
print(" ollama create code-reviewer -f Modelfile-code")
print(" ollama create rag-assistant -f Modelfile-rag")
Ollama REST API: Python ile entegrasyon
Ollama iki API'yi kullanıma sunuyor: kendi yerel API'si ve OpenAI uyumlu bir API. OpenAI uyumluluğu, OpenAI API'lerini Ollama ile değiştirmenize olanak tanır uygulama kodunu değiştirmeden temel URL'yi değiştirme.
# pip install ollama openai requests
import ollama
import json, time
from typing import Iterator
# ================================================================
# 1. LIBRERIA OLLAMA UFFICIALE (Python)
# ================================================================
# Chat semplice (non-streaming)
def chat_simple(model: str, message: str) -> str:
response = ollama.chat(
model=model,
messages=[{"role": "user", "content": message}]
)
return response['message']['content']
# Chat con streaming (token per token)
def chat_streaming(model: str, messages: list) -> Iterator[str]:
stream = ollama.chat(
model=model,
messages=messages,
stream=True
)
for chunk in stream:
if chunk['message']['content']:
yield chunk['message']['content']
# Embeddings per RAG (usa nomic-embed-text o mxbai-embed-large)
def get_embedding(model: str, text: str) -> list:
response = ollama.embeddings(model=model, prompt=text)
return response['embedding']
# Chat con immagini (modelli multimodali: llava, bakllava, moondream)
def chat_with_image(model: str, prompt: str, image_path: str) -> str:
import base64
with open(image_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode()
response = ollama.chat(
model="llava:7b", # oppure moondream
messages=[{
"role": "user",
"content": prompt,
"images": [image_data]
}]
)
return response['message']['content']
# Chatbot con storia della conversazione
def interactive_chat(model: str = "llama3.2:3b"):
history = []
print(f"Chat con {model} (digita 'exit' per uscire)")
while True:
user_input = input("Tu: ").strip()
if user_input.lower() == "exit":
break
history.append({"role": "user", "content": user_input})
print("Assistant: ", end="", flush=True)
full_response = ""
for chunk in chat_streaming(model, history):
print(chunk, end="", flush=True)
full_response += chunk
print()
history.append({"role": "assistant", "content": full_response})
# ================================================================
# 2. API COMPATIBILE OPENAI (drop-in replacement)
# ================================================================
from openai import OpenAI
# Cambia solo la base_url: zero modifiche al codice!
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # Qualsiasi stringa
)
def chat_openai_compatible(model: str, prompt: str) -> str:
"""Identico all'API OpenAI, ma usa Ollama in locale."""
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=500
)
return response.choices[0].message.content
# ================================================================
# 3. RAW REST API (senza librerie Python)
# ================================================================
import requests
def ollama_raw_api(model: str, prompt: str, stream: bool = False) -> str:
"""Chiama l'API Ollama direttamente con requests."""
resp = requests.post(
"http://localhost:11434/api/generate",
json={
"model": model,
"prompt": prompt,
"stream": stream,
"options": {
"temperature": 0.7,
"num_predict": 200,
"num_ctx": 4096
}
},
timeout=120
)
if not stream:
return resp.json()["response"]
else:
# Streaming: ogni riga e un JSON
result = ""
for line in resp.iter_lines():
if line:
data = json.loads(line)
result += data.get("response", "")
if data.get("done"):
break
return result
# ================================================================
# 4. BENCHMARK VELOCITA MODELLI
# ================================================================
def benchmark_model(model: str, n_runs: int = 3):
"""Misura velocità di generazione in token/s."""
prompt = "Explain quantum computing in one paragraph."
results = []
for _ in range(n_runs):
t0 = time.time()
response = ollama.generate(
model=model,
prompt=prompt,
options={"num_predict": 100}
)
elapsed = time.time() - t0
eval_count = response.get('eval_count', 100)
tps = eval_count / elapsed
results.append(tps)
avg_tps = sum(results) / len(results)
print(f"{model}: {avg_tps:.1f} token/s (media {n_runs} run)")
return avg_tps
# Risultati tipici su MacBook M3 Pro 18GB:
# qwen2.5:1.5b ~85 t/s
# llama3.2:3b ~62 t/s
# qwen2.5:7b ~42 t/s
# llama3.1:8b ~38 t/s
# qwen2.5:14b ~22 t/s
# llama3.1:70b ~8 t/s
LangChain ile Ollama: RAG Pipeline Çevrimdışı
Ollama, LangChain ile yerel olarak bütünleşerek RAG boru hatları oluşturmanıza olanak tanır
(Geri Alma-Artırılmış Nesil) tamamen çevrimdışı. Bu ve özellikle
hassas verileri buluta gönderemeyen kurumsal uygulamalar için geçerlidir.
Model nomic-embed-text ve yerel yerleştirmeler için idealdir.
# pip install langchain langchain-ollama langchain-community
# pip install faiss-cpu chromadb pypdf
from langchain_ollama import OllamaLLM, OllamaEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_community.document_loaders import (
DirectoryLoader, TextLoader, PyPDFLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
import os
# ================================================================
# PIPELINE RAG OFFLINE CON OLLAMA - VERSIONE PRODUZIONE
# ================================================================
class OllamaRAGSystem:
"""
Sistema RAG completo e offline con Ollama.
Supporta PDF, TXT e directory intere.
Usa FAISS per vector store locale.
"""
def __init__(
self,
llm_model: str = "llama3.1:8b",
embed_model: str = "nomic-embed-text", # ollama pull nomic-embed-text
kb_dir: str = "./knowledge_base"
):
self.llm_model = llm_model
self.embed_model = embed_model
self.kb_dir = kb_dir
self.embeddings = OllamaEmbeddings(model=embed_model)
self.llm = OllamaLLM(
model=llm_model,
temperature=0.1,
num_ctx=8192,
num_predict=512
)
self.vectorstore = None
def load_documents(self, docs_dir: str) -> list:
"""Carica documenti da directory (PDF, TXT, MD)."""
docs = []
# Carica TXT e MD
txt_loader = DirectoryLoader(
docs_dir, glob="**/*.txt", loader_cls=TextLoader
)
docs.extend(txt_loader.load())
# Carica PDF
for pdf_file in os.listdir(docs_dir):
if pdf_file.endswith(".pdf"):
loader = PyPDFLoader(os.path.join(docs_dir, pdf_file))
docs.extend(loader.load())
print(f"Caricati {len(docs)} documenti da {docs_dir}")
return docs
def build_knowledge_base(self, docs_dir: str) -> None:
"""Crea e salva la knowledge base da una directory."""
documents = self.load_documents(docs_dir)
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", " ", ""]
)
texts = splitter.split_documents(documents)
print(f"Creati {len(texts)} chunk")
self.vectorstore = FAISS.from_documents(texts, self.embeddings)
self.vectorstore.save_local(self.kb_dir)
print(f"Knowledge base salvata in {self.kb_dir}")
def load_knowledge_base(self) -> None:
"""Carica knowledge base esistente da disco."""
self.vectorstore = FAISS.load_local(
self.kb_dir, self.embeddings,
allow_dangerous_deserialization=True
)
print(f"Knowledge base caricata: {self.vectorstore.index.ntotal} vettori")
def create_qa_chain(self) -> RetrievalQA:
"""Crea chain per Q&A su documenti."""
prompt_template = """Usa il seguente contesto per rispondere alla domanda.
Se non trovi la risposta nel contesto, dì esplicitamente che non lo sai.
Non inventare informazioni non presenti nel contesto.
Contesto:
{context}
Domanda: {question}
Risposta in italiano:"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
retriever = self.vectorstore.as_retriever(
search_type="mmr", # Maximum Marginal Relevance (più diversificato)
search_kwargs={"k": 5, "fetch_k": 20}
)
return RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True
)
def ask(self, question: str, qa_chain: RetrievalQA) -> dict:
"""Poni una domanda al sistema RAG."""
result = qa_chain.invoke({"query": question})
sources = list(set([
doc.metadata.get("source", "Unknown")
for doc in result["source_documents"]
]))
return {
"answer": result["result"],
"sources": sources,
"n_docs": len(result["source_documents"])
}
# Utilizzo:
# rag = OllamaRAGSystem(llm_model="llama3.1:8b")
# rag.build_knowledge_base("./documenti_aziendali")
# chain = rag.create_qa_chain()
# result = rag.ask("Qual e la policy ferie aziendale?", chain)
# print(result["answer"])
# print("Fonti:", result["sources"])
print("Sistema RAG pronto!")
OpenWebUI: Ollama için ChatGPT arayüzü
OpenWebUI (eski adıyla Ollama WebUI) ve en çok kullanılan arayüz Ollama, ChatGPT ile aynı ancak tamamen çevrimdışı bir kullanıcı deneyimine sahip. Destek sohbet, belge yükleme, görüşme yönetimi, anında paylaşım, entegre RAG ve görüntüler için çoklu mod.
# ================================================================
# SETUP OPENWEBUI CON DOCKER
# ================================================================
# Caso 1: Ollama sullo stesso host
# docker run -d -p 3000:8080 \
# -v open-webui:/app/backend/data \
# -e OLLAMA_BASE_URL=http://host.docker.internal:11434 \
# --name open-webui \
# ghcr.io/open-webui/open-webui:main
# Caso 2: OpenWebUI con Ollama integrato (tutto in uno)
# docker run -d -p 3000:8080 \
# -v ollama:/root/.ollama \
# -v open-webui:/app/backend/data \
# --gpus all \
# --name open-webui \
# ghcr.io/open-webui/open-webui:ollama
# Accesso: http://localhost:3000
# ================================================================
# DOCKER COMPOSE (raccomandato per produzione)
# ================================================================
DOCKER_COMPOSE = """
version: '3.8'
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_NUM_PARALLEL=4
- OLLAMA_MAX_LOADED_MODELS=2
# Per GPU NVIDIA:
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: all
# capabilities: [gpu]
restart: unless-stopped
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
ports:
- "3000:8080"
volumes:
- webui_data:/app/backend/data
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- WEBUI_AUTH=True
- WEBUI_SECRET_KEY=cambia-questa-chiave-segreta
depends_on:
- ollama
restart: unless-stopped
volumes:
ollama_data:
webui_data:
"""
# ================================================================
# OLLAMA COME SERVIZIO SYSTEMD (Linux production)
# ================================================================
SYSTEMD_SERVICE = """
# /etc/systemd/system/ollama.service
[Unit]
Description=Ollama LLM Service
After=network-online.target
[Service]
ExecStart=/usr/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3
Environment="OLLAMA_HOST=0.0.0.0"
Environment="OLLAMA_MODELS=/opt/ollama/models"
Environment="OLLAMA_NUM_PARALLEL=2"
Environment="OLLAMA_MAX_LOADED_MODELS=2"
Environment="OLLAMA_KEEP_ALIVE=10m"
[Install]
WantedBy=default.target
"""
# sudo systemctl enable ollama
# sudo systemctl start ollama
# sudo journalctl -u ollama -f # Log in real-time
print("Setup Ollama come servizio completato!")
Raspberry Pi'de Dağıtım: Optimize Edilmiş Kurulum
8 GB RAM'e sahip Raspberry Pi 5, yerel Yüksek Lisans'lar için en erişilebilir uç cihazdır. Doğru konfigürasyonla 1,5B parametreli modeller 4-5 token/s'ye ulaşır; bu yeterlidir birçok gerçek zamanlı olmayan kullanım durumu için: düşük hacimli sohbet robotları, toplu metin analizi, olay tetikleyicileri ile otomasyon.
# ================================================================
# OLLAMA SU RASPBERRY PI 5 (setup ottimizzato)
# ================================================================
# Installazione (identica a Linux x86):
# curl -fsSL https://ollama.com/install.sh | sh
# Configurazione ottimale per RPi5 in /etc/environment:
# OLLAMA_NUM_PARALLEL=1 # Un request alla volta (RAM limitata)
# OLLAMA_MAX_LOADED_MODELS=1 # Un modello in memoria
# OLLAMA_KEEP_ALIVE=5m # Scarica modello dopo 5 min inattivita
# OLLAMA_NUM_THREAD=4 # Tutti i core Cortex-A76
# Modelli raccomandati per RPi5 (8GB):
# ollama pull qwen2.5:1.5b (veloce: ~4.5 t/s, 1.8 GB RAM)
# ollama pull llama3.2:1b (bilanciato: ~5.1 t/s, 1.4 GB RAM)
# ollama pull gemma2:2b (qualità: ~3.2 t/s, 2.5 GB RAM)
import ollama
import time, statistics, psutil
def benchmark_ollama_rpi(model: str = "qwen2.5:1.5b",
n_tests: int = 5):
"""Test velocità e consistenza su RPi."""
prompt = "Spiega in 3 frasi cos'è il machine learning."
results = []
latencies_to_first = []
print(f"Benchmark {model} su {n_tests} test...")
for i in range(n_tests):
t0 = time.time()
first_token = None
full_response = ""
for chunk in ollama.chat(
model=model,
messages=[{"role": "user", "content": prompt}],
stream=True,
options={"temperature": 0, "top_k": 1, "num_predict": 50}
):
content = chunk['message']['content']
if content and first_token is None:
first_token = time.time() - t0
latencies_to_first.append(first_token * 1000)
full_response += content
elapsed = time.time() - t0
n_tokens = len(full_response.split()) # Approssimazione
tps = n_tokens / elapsed
results.append(tps)
print(f" Test {i+1}: {tps:.1f} t/s, TTFT: {first_token*1000:.0f}ms")
mean_tps = statistics.mean(results)
mean_ttft = statistics.mean(latencies_to_first)
mem = psutil.virtual_memory()
print(f"\nRisultati {model} su RPi5:")
print(f" Velocita media: {mean_tps:.1f} t/s")
print(f" TTFT medio: {mean_ttft:.0f} ms")
print(f" RAM usata: {mem.used/(1024**3):.1f} GB / {mem.total/(1024**3):.1f} GB")
return mean_tps
# ================================================================
# AUTOMAZIONE: Aggiornamento modelli e monitoring
# ================================================================
import subprocess, datetime
def update_ollama_models(models: list = ["qwen2.5:1.5b", "nomic-embed-text"]):
"""Aggiorna i modelli Ollama (da eseguire con cron)."""
log = []
for model in models:
print(f"Aggiornamento {model}...")
result = subprocess.run(
["ollama", "pull", model],
capture_output=True, text=True, timeout=600
)
status = "OK" if result.returncode == 0 else "FAIL"
log.append({
"model": model,
"status": status,
"time": datetime.datetime.now().isoformat()
})
print(f" {model}: {status}")
return log
# Cron job consigliato (ogni domenica alle 3:00):
# 0 3 * * 0 /usr/bin/python3 /home/pi/update_models.py >> /var/log/ollama-update.log 2>&1
Gerçek Örnek Olay İncelemesi: Çevrimdışı Ticari Chatbot
Gerçek bir kullanım örneği: gizli belgeleri (sözleşmeler, İK politikaları, teknik kılavuzlar), verileri buluta ifşa etmeden dahili bir sohbet robotu istiyor. Ollama'yla birlikte + RAG, bir günden daha kısa sürede tamamen hava boşluklu bir sistem oluşturur.
# ================================================================
# CHATBOT AZIENDALE OFFLINE - Stack completo
# ================================================================
# Stack:
# - Ollama con llama3.1:8b (o qwen2.5:7b per italiano migliore)
# - nomic-embed-text per embeddings
# - FAISS per vector store
# - FastAPI per REST API
# - OpenWebUI per interfaccia utente
# fastapi_chatbot.py
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
from typing import Optional
import ollama, json
from pathlib import Path
app = FastAPI(title="Corporate AI Assistant", version="2.0")
# Stato globale (in produzione usa Redis)
conversation_store = {}
class ChatRequest(BaseModel):
session_id: str
message: str
model: str = "qwen2.5:7b"
use_rag: bool = True
class ChatResponse(BaseModel):
session_id: str
response: str
sources: list = []
model: str
tokens_per_sec: float
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
# Recupera storia conversazione
if request.session_id not in conversation_store:
conversation_store[request.session_id] = []
history = conversation_store[request.session_id]
# Aggiungi contesto RAG se richiesto
context = ""
sources = []
if request.use_rag and rag_system and rag_system.vectorstore:
docs = rag_system.vectorstore.similarity_search(
request.message, k=3
)
context = "\n\n".join([d.page_content for d in docs])
sources = list(set([d.metadata.get("source", "") for d in docs]))
# Inietta contesto nel messaggio
augmented_message = f"""Contesto dai documenti aziendali:
{context}
Domanda: {request.message}"""
else:
augmented_message = request.message
history.append({"role": "user", "content": augmented_message})
# Genera risposta
t0 = time.time()
response = ollama.chat(
model=request.model,
messages=history,
options={"num_ctx": 8192, "temperature": 0.3}
)
elapsed = time.time() - t0
assistant_msg = response['message']['content']
history.append({"role": "assistant", "content": assistant_msg})
# Tronca la storia se troppo lunga (sliding window)
if len(history) > 20:
history = history[-20:]
conversation_store[request.session_id] = history
eval_count = response.get('eval_count', 50)
tps = eval_count / elapsed if elapsed > 0 else 0
return ChatResponse(
session_id=request.session_id,
response=assistant_msg,
sources=sources,
model=request.model,
tokens_per_sec=round(tps, 1)
)
@app.delete("/chat/{session_id}")
async def clear_session(session_id: str):
"""Azzera la storia di una sessione."""
if session_id in conversation_store:
del conversation_store[session_id]
return {"status": "cleared"}
@app.get("/models")
async def list_models():
"""Lista modelli disponibili su questo server Ollama."""
models = ollama.list()
return {
"models": [
{"name": m['name'], "size_gb": m['size'] / 1e9}
for m in models['models']
]
}
# Avvio: uvicorn fastapi_chatbot:app --host 0.0.0.0 --port 8080
Yaygın Kullanım Durumlarına İlişkin Modellerin Karşılaştırılması
| Kullanım Örneği | Önerilen model | RAM Min. | Neden |
|---|---|---|---|
| İtalyan sohbet robotu | qwen2.5:7b | 8GB | Mükemmel çok dilli, uzun bağlam |
| Kod oluşturma | qwen2.5-kodlayıcı:7b | 8GB | İnce ayarlı kod, 90'dan fazla dil |
| RAG / Soru-Cevap belgeleri | lama3.1:8b | 8GB | Mükemmel talimat takibi, 128K bağlam |
| Gelişmiş muhakeme | derin arama-r1:8b | 8GB | Düşünce zinciri, matematik, mantık |
| Raspberry Pi (hızlı) | lama3.2:1b | 2GB | 5+ t/s, basit görevler |
| Raspberry Pi (kalite) | qwen2.5:3b | 4 cigabayt | Optimum kalite/hız dengesi |
| Mac M serisi (hızlı) | qwen2.5:14b | 16GB | M2/M3'te 22+ t/s, GPT-4'e yakın kalite |
| Görüntü analizi | llava:7b veya aydream | 8GB | Görüş açısından optimize edilmiş multimodal modeller |
Üretim İçin En İyi Uygulamalar
Ollama'nın üretimde kullanılması, kullanıma özgü bazı hususlar gerektirir kişisel. İşte en önemli kalıplar.
# ================================================================
# PATTERN PRODUZIONE: Load Balancing con più istanze Ollama
# ================================================================
# Se si ha più di un server con Ollama, si può fare load balancing.
# nginx.conf (upstream round-robin):
NGINX_CONFIG = """
upstream ollama_cluster {
least_conn; # Instrada alla connessione con meno richieste
server server1:11434;
server server2:11434;
server server3:11434;
}
server {
listen 80;
location /api/ {
proxy_pass http://ollama_cluster;
proxy_read_timeout 300s; # Timeout elevato per generazione lunga
proxy_connect_timeout 10s;
proxy_set_header Host $host;
}
}
"""
# ================================================================
# HEALTH CHECK E MONITORING
# ================================================================
import requests, time
def monitor_ollama(host: str = "localhost", port: int = 11434):
"""Controlla disponibilità e carico Ollama."""
try:
# API health endpoint
resp = requests.get(f"http://{host}:{port}/api/tags", timeout=5)
if resp.status_code == 200:
models = resp.json().get("models", [])
print(f"Ollama OK: {len(models)} modelli disponibili")
return True
except requests.exceptions.RequestException as e:
print(f"Ollama NON RAGGIUNGIBILE: {e}")
return False
# ================================================================
# GESTIONE ERRORI E RETRY
# ================================================================
import functools, random
def with_ollama_retry(max_attempts: int = 3, backoff: float = 1.0):
"""Decorator per retry automatico su errori Ollama."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
wait = backoff * (2 ** attempt) + random.uniform(0, 0.5)
print(f"Tentativo {attempt+1} fallito: {e}. Retry in {wait:.1f}s")
time.sleep(wait)
return wrapper
return decorator
@with_ollama_retry(max_attempts=3, backoff=1.0)
def robust_chat(model: str, message: str) -> str:
"""Chat con retry automatico su errori di rete/timeout."""
response = ollama.chat(
model=model,
messages=[{"role": "user", "content": message}],
options={"num_predict": 500}
)
return response['message']['content']
Üretime İlişkin Sınırlamalar ve Hususlar
-
Ollama varsayılan olarak çok kiracılı değildir: paylaşılan bir sunucuda,
istekler serileştirilir. Vergi
OLLAMA_NUM_PARALLEL=4için eşzamanlı istekleri yönetin (daha fazla RAM gerektirir: 7B modelinde istek başına ~8 GB). -
Büyük modellerde RPi'de zaman aşımı: llama3.1:8b 10-15 saniye sürer
RPi'de ilk yanıtı oluşturmak için. Amerika
num_ctx=512azaltmak için Zamana duyarlı durumlarda ön doldurma süresi. TTFT <2s için 1-3B modellerini kullanın. - Otomatik otomatik ölçeklendirme yok: Bulut API'lerinin aksine Ollama ölçeklenmiyor. Yüksek trafik için sunuculardaki birden fazla Ollama örneğiyle yük dengelemeyi kullanın farklı veya GPU dağıtımları için vLLM'yi düşünün.
-
Sürekli güç tüketimi: Ollama'yı bir modelle aktif tutun
yüklendiğinde RPi5'te ~15W, Jetson Orin NX'te ~45W tüketir. Amerika
OLLAMA_KEEP_ALIVE=0modeli her istekten hemen sonra indirmek için. - Emniyet: Ollama varsayılan olarak isteklerin kimliğini doğrulamaz. Üretimde, kimlik doğrulama ve hız sınırlama ile her zaman önüne bir ters proxy (nginx) koyun. 11434 numaralı bağlantı noktasını asla doğrudan internete maruz bırakmayın.
Sonuçlar
Ollama, yerel yapay zekaya giriş engelini sıfıra indirdi. Tek bir komutla Dizüstü bilgisayarınızda tam ve sıfır gizlilikle çalışan rekabetçi bir Yüksek Lisans eğitimine sahip olabilirsiniz API maliyetleri. i'ye doğru eğilim Yerel Yüksek Lisans ve durdurulamaz: Gartner şunu öngörüyor: 2027 yılına kadar SLM'lerin (Küçük Dil Modelleri) bulut LLM'lerini frekans olarak 3 kat aşacağı öngörülüyor kullanım maliyetlerinde %70 oranında azalma sağlanır.
Üretim için Ollama harika bir başlangıç noktasıdır ancak bazı hususların dikkate alınması gerekir: eşzamanlılık yönetimi, izleme, model güncelleme, güvenlik ve entegrasyon Mevcut sistemlerle. En güçlü model Ollama'yı RAG boru hattıyla birleştirmektir. modelin buluta veri göndermeden özel bilgi tabanlarına erişmesine izin verin.
Serinin bir sonraki makalesi çemberi i ile kapatıyor Karşılaştırmalar e Optimizasyon: herkesin performansının sistematik olarak nasıl ölçüleceği seride görülen araçlar (kuantizasyon, damıtma, budama, uç konuşlandırma) ve kullanım durumunuz için en uygun kombinasyonu seçin.
Sonraki Adımlar
- Sonraki makale: Karşılaştırma ve Optimizasyon: 48 GB'tan 8 GB RTX'e
- İlgili: Uç Cihazlarda Derin Öğrenme
- İlgili: Niceleme: GPTQ, AWQ, GGUF
- Yapay Zeka Mühendisliği Serisi: Yerel LLM'lerle RAG Boru Hattı
- MLOps Serisi: Üretimde Model Sunumu







