Ollama ile RAG Sistemi Kurarak Kendi Dokümanlarını Sorgula
Şirket içi dokümantasyonu aramak, eski sistem kılavuzlarını karıştırmak ya da onlarca PDF arasında kaybolup gitmek… Bunlar her sysadmin’in çok iyi bildiği acılar. Peki ya bu dokümanlarının tamamını yerel bir yapay zeka modeline yükleyip, doğal dille soru sorabilseydin? İşte RAG (Retrieval-Augmented Generation) tam da bunu yapıyor. Ve en güzel yanı, Ollama sayesinde bunu tamamen kendi sunucunda, internete tek bir paket bile göndermeden kurabiliyorsun.
RAG Nedir ve Neden Önemli?
RAG, büyük dil modellerinin (LLM) kendi eğitim verilerinin dışındaki bilgilere erişmesini sağlayan bir mimari. Klasik bir LLM’e “Şirketimizin VPN kurulum prosedürü nedir?” diye sorduğunda, model bunu bilemez çünkü bu bilgi onun eğitim setinde yok. RAG bu sorunu şu şekilde çözüyor:
- Önce dokümanların parçalara bölünüp vektör veritabanına kaydedilmesi
- Kullanıcı sorusunun vektöre dönüştürülerek benzer içeriklerin bulunması
- Bulunan içeriklerin LLM’e bağlam olarak verilmesi
- Modelin bu bağlama dayanarak cevap üretmesi
Bu sayede model hiçbir zaman “görmediği” verilere sanki hep biliyormuş gibi erişebiliyor. Sysadmin açısından baktığında bu şu anlama geliyor: Runbook’larını, network diyagramlarını, incident raporlarını ve her türlü iç dokümantasyonu sorgulanabilir hale getirebilirsin.
Sistem Gereksinimleri ve Mimari
Bu kurulumda kullanacağımız bileşenler şunlar:
- Ollama: Yerel LLM sunucusu
- Python 3.10+: Orchestration katmanı
- ChromaDB: Vektör veritabanı (yerel, hafif, kurulumu basit)
- LangChain: RAG pipeline’ı yönetimi
- nomic-embed-text: Embedding modeli (Ollama üzerinden)
- llama3.2 veya mistral: Ana dil modeli
Donanım tarafında minimum 8GB RAM ve tercihen bir GPU öneriyorum. CPU-only da çalışır ama yanıt süreleri uzar. Benim test ortamım Ubuntu 22.04 LTS üzerinde 32GB RAM ve RTX 3080’lik bir iş istasyonu.
Ollama Kurulumu ve Model İndirme
Ollama zaten kurulu değilse önce onu hazırlayalım:
# Ollama kurulum scripti
curl -fsSL https://ollama.com/install.sh | sh
# Servisi başlat ve enable et
sudo systemctl enable ollama
sudo systemctl start ollama
# Servis durumunu kontrol et
sudo systemctl status ollama
Şimdi ihtiyacımız olan modelleri indirelim. Embedding için nomic-embed-text, metin üretimi için llama3.2 kullanacağız:
# Embedding modeli - küçük ve hızlı
ollama pull nomic-embed-text
# Ana LLM - Türkçe için mistral de iyi bir seçenek
ollama pull llama3.2
# İndirilen modelleri listele
ollama list
Eğer Türkçe dokümanlarla çalışacaksan mistral veya llama3.1:8b modellerini de değerlendirmen iyi olur. Llama 3.2’nin Türkçe performansı oldukça iyi ama büyük teknik Türkçe metinlerde bazen kırılabiliyor.
Python Ortamı ve Bağımlılıklar
Projeyi izole bir sanal ortamda kurmak her zaman daha temiz:
# Proje dizinini oluştur
mkdir ~/rag-system && cd ~/rag-system
# Virtual environment
python3 -m venv venv
source venv/bin/activate
# Gerekli paketleri kur
pip install langchain langchain-community langchain-ollama
pip install chromadb
pip install pypdf docx2txt markdown
pip install sentence-transformers
pip install gradio # Web arayüzü için
# Kurulumu doğrula
pip list | grep -E "langchain|chroma|ollama"
Doküman Hazırlama ve Yükleme
Şimdi asıl işe başlayalım. Önce bir doküman yükleyici script yazacağız. Bu script PDF, Word ve Markdown dosyalarını destekleyecek:
# ingest.py - Dokümanları vektör veritabanına yükle
import os
import sys
from pathlib import Path
from langchain_community.document_loaders import (
PyPDFLoader,
Docx2txtLoader,
UnstructuredMarkdownLoader,
TextLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_ollama import OllamaEmbeddings
# Konfigürasyon
DOCS_DIR = "./docs"
CHROMA_DIR = "./chroma_db"
EMBED_MODEL = "nomic-embed-text"
CHUNK_SIZE = 1000
CHUNK_OVERLAP = 200
def load_document(file_path: str):
"""Dosya uzantısına göre uygun loader'ı seç"""
ext = Path(file_path).suffix.lower()
if ext == ".pdf":
loader = PyPDFLoader(file_path)
elif ext in [".docx", ".doc"]:
loader = Docx2txtLoader(file_path)
elif ext == ".md":
loader = UnstructuredMarkdownLoader(file_path)
elif ext == ".txt":
loader = TextLoader(file_path, encoding="utf-8")
else:
print(f"Desteklenmeyen dosya tipi: {ext}, atlanıyor...")
return []
return loader.load()
def ingest_documents():
docs_path = Path(DOCS_DIR)
if not docs_path.exists():
print(f"Hata: {DOCS_DIR} dizini bulunamadı!")
sys.exit(1)
all_documents = []
file_count = 0
# Tüm desteklenen dosyaları bul ve yükle
supported_extensions = [".pdf", ".docx", ".doc", ".md", ".txt"]
for ext in supported_extensions:
for file_path in docs_path.rglob(f"*{ext}"):
print(f"Yükleniyor: {file_path}")
docs = load_document(str(file_path))
all_documents.extend(docs)
file_count += 1
if not all_documents:
print("Hiç doküman bulunamadı!")
sys.exit(1)
print(f"nToplam {file_count} dosya, {len(all_documents)} sayfa/bölüm yüklendi.")
# Dokümanları parçala
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["nn", "n", ".", " ", ""]
)
chunks = splitter.split_documents(all_documents)
print(f"Toplam {len(chunks)} chunk oluşturuldu.")
# Embedding oluştur ve ChromaDB'ye kaydet
print("nEmbedding'ler oluşturuluyor (bu biraz zaman alabilir)...")
embeddings = OllamaEmbeddings(model=EMBED_MODEL)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=CHROMA_DIR
)
print(f"nBaşarılı! {len(chunks)} chunk ChromaDB'ye kaydedildi.")
print(f"Veritabanı konumu: {CHROMA_DIR}")
if __name__ == "__main__":
ingest_documents()
Şimdi docs dizinini oluştur ve dokümanlarını oraya koy:
mkdir docs
# Dokümanlarını buraya kopyala
cp /path/to/your/docs/*.pdf docs/
cp /path/to/your/runbooks/*.md docs/
# Yükleme scriptini çalıştır
python ingest.py
RAG Query Engine
Dokümanlar yüklendikten sonra sorgu motoru:
# query.py - RAG sorgu motoru
from langchain_community.vectorstores import Chroma
from langchain_ollama import OllamaEmbeddings, OllamaLLM
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
CHROMA_DIR = "./chroma_db"
EMBED_MODEL = "nomic-embed-text"
LLM_MODEL = "llama3.2"
# Özelleştirilmiş prompt - Türkçe ve teknik odaklı
PROMPT_TEMPLATE = """Sen bir sistem yönetimi asistanısın.
Sana verilen bağlam bilgilerini kullanarak soruyu yanıtla.
Eğer bağlamda yeterli bilgi yoksa, "Bu konuda elimdeki dokümanlarda
yeterli bilgi bulamadım" de ve ne bilip bilmediğini açıkla.
Cevaplarında her zaman Türkçe kullan.
Bağlam:
{context}
Soru: {question}
Cevap:"""
def create_rag_chain():
embeddings = OllamaEmbeddings(model=EMBED_MODEL)
vectorstore = Chroma(
persist_directory=CHROMA_DIR,
embedding_function=embeddings
)
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 5} # En ilgili 5 chunk'ı getir
)
llm = OllamaLLM(
model=LLM_MODEL,
temperature=0.1, # Düşük temperature = daha tutarlı cevaplar
num_predict=2048
)
prompt = PromptTemplate(
template=PROMPT_TEMPLATE,
input_variables=["context", "question"]
)
chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": prompt},
return_source_documents=True
)
return chain
def query_documents(question: str, chain=None):
if chain is None:
chain = create_rag_chain()
result = chain.invoke({"query": question})
print(f"nSoru: {question}")
print(f"nCevap:n{result['result']}")
print("nKullanılan Kaynaklar:")
seen_sources = set()
for doc in result['source_documents']:
source = doc.metadata.get('source', 'Bilinmiyor')
page = doc.metadata.get('page', '')
source_key = f"{source}:{page}"
if source_key not in seen_sources:
seen_sources.add(source_key)
if page:
print(f" - {source} (Sayfa: {page + 1})")
else:
print(f" - {source}")
return result
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
question = " ".join(sys.argv[1:])
query_documents(question)
else:
print("Kullanım: python query.py 'sorunuz buraya'")
Web Arayüzü ile Kullanım
Command line güzel ama ekip arkadaşların için bir web arayüzü çok daha kullanışlı olacaktır. Gradio ile basit ama işlevsel bir arayüz kuralım:
# app.py - Gradio web arayüzü
import gradio as gr
from query import create_rag_chain, query_documents
# Chain'i bir kez oluştur, her sorguda tekrar yükleme
print("RAG sistemi başlatılıyor...")
rag_chain = create_rag_chain()
print("Sistem hazır!")
def chat_with_docs(message, history):
if not message.strip():
return "", history
result = query_documents(message, rag_chain)
answer = result['result']
# Kaynak bilgilerini cevaba ekle
sources = []
seen = set()
for doc in result['source_documents']:
source = doc.metadata.get('source', 'Bilinmiyor')
if source not in seen:
seen.add(source)
sources.append(source)
if sources:
answer += "nn**Kaynaklar:**n"
for src in sources:
answer += f"- `{src}`n"
history.append((message, answer))
return "", history
with gr.Blocks(title="Şirket Doküman Asistanı", theme=gr.themes.Soft()) as demo:
gr.Markdown("## Şirket Doküman Asistanı")
gr.Markdown("Dokümanlarınız hakkında Türkçe sorular sorun.")
chatbot = gr.Chatbot(height=500)
msg = gr.Textbox(
placeholder="Sorunuzu buraya yazın...",
label="Soru",
lines=2
)
with gr.Row():
submit = gr.Button("Gönder", variant="primary")
clear = gr.Button("Temizle")
submit.click(chat_with_docs, [msg, chatbot], [msg, chatbot])
msg.submit(chat_with_docs, [msg, chatbot], [msg, chatbot])
clear.click(lambda: ([], ""), outputs=[chatbot, msg])
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)
# Web arayüzünü başlat
python app.py
# Arka planda çalıştırmak için
nohup python app.py > rag_app.log 2>&1 &
# Systemd servisi olarak kurmak istersen
sudo nano /etc/systemd/system/rag-assistant.service
Systemd Servisi Olarak Kurulum
Sistemi restart’larda da çalışır hale getirmek için:
[Unit]
Description=RAG Document Assistant
After=network.target ollama.service
Requires=ollama.service
[Service]
Type=simple
User=sysadmin
WorkingDirectory=/home/sysadmin/rag-system
Environment="PATH=/home/sysadmin/rag-system/venv/bin"
ExecStart=/home/sysadmin/rag-system/venv/bin/python app.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable rag-assistant
sudo systemctl start rag-assistant
sudo systemctl status rag-assistant
Pratik Senaryolar ve İpuçları
Doküman güncellemesi durumunda ne yapacaksın? Her yeni doküman eklendiğinde veritabanını tamamen yeniden oluşturmak yerine incremental ekleme yapabilirsin:
# Yeni doküman ekle, mevcut DB'yi koru
# ingest.py'yi tekrar çalıştırmak var olan chunk'ların üzerine yazar
# Bu yüzden değişiklik takibi yapman önerilir
python ingest.py --incremental
Chunk boyutu ayarlaması performansı doğrudan etkiliyor. Deneyimlerime göre:
- Teknik dökümanlar ve runbook’lar: 800-1200 karakter iyi çalışıyor
- Uzun raporlar: 1500-2000 karakter daha iyi sonuç veriyor
- Kısa notlar ve wiki sayfaları: 400-600 karakter yeterli
k değeri yani kaç chunk’ın getirileceği de kritik. Çok az chunk getirirse model bilgiden yoksun kalır, çok fazla getirirse context window dolup model performansı düşer. 4-6 arası genellikle iyi bir başlangıç noktası.
Gizlilik ve güvenlik açısından önemli bir not: Bu sistemde hiçbir veri dışarı çıkmıyor. Ollama tamamen yerel çalışıyor, ChromaDB yerel bir dizinde, Gradio ise local network’e bind ediyor. Hassas şirket dokümanlarını bu şekilde güvenle kullanabilirsin. Yine de sistemi iç ağda tutmak ve gereksiz portları dışarıya açmamak iyi bir pratik.
Dil performansı konusunda gerçekçi olalım: Türkçe dokümanlarla çalışırken embedding kalitesi düşebiliyor. nomic-embed-text İngilizce ağırlıklı eğitilmiş. Tamamen Türkçe bir doküman havuzunda multilingual-e5-large gibi çok dilli embedding modelleri daha iyi sonuç verebilir ama bunları Ollama üzerinden değil, HuggingFace üzerinden kullanman gerekiyor.
Sorun Giderme
Kurulum sırasında karşılaşabileceğin yaygın sorunlar:
- Ollama bağlantı hatası:
ollama serveile servisi manuel başlatmayı dene vehttp://localhost:11434adresinin erişilebilir olduğunu doğrula - ChromaDB izin hatası:
chroma_dbdizinine yazma izni olduğundan emin ol, virtual environment dışında root olarak çalıştırma - Bellek yetersizliği: Büyük modellerde GPU VRAM dolabilir,
llama3.2:1bgibi daha küçük bir model dene - Yavaş yanıt süresi: CPU-only kullanıyorsan sabırlı ol.
ollama psile aktif modeli ve bellek kullanımını izleyebilirsin
Sonuç
Bu kurulumla artık yüzlerce sayfalık şirket dokümantasyonunu saniyeler içinde sorgulayabiliyorsun. Incident sırasında “acaba runbook’ta ne yazıyordu?” diye saatlerce arama yapmak yerine, direkt soruyu soruyorsun ve cevabı alıyorsun. Ekip içinde yeni bir meslektaş onboarding sürecinde “bu sistem nasıl çalışıyor?” sorularına da bu araç güzel cevap veriyor.
Sistemin en güçlü yanı tamamen offline ve yerelde çalışması. Hiçbir şirket verisi, API çağrısı veya cloud servisi söz konusu değil. Kontrol tamamen sende. Bir sonraki adım olarak doküman havuzunu otomatik senkronize etmek için bir cron job eklemek, ya da Slack bot entegrasyonu ile ekibin doğrudan Slack’ten sorgu yapabilmesini sağlamak düşünülebilir. Ama bu konuları başka bir yazıya bırakalım.
