In den ersten beiden Teilen dieser Reihe hast du gelernt, wie du lokale KI-Modelle mit Ollama und llama.cpp zum Laufen bringst und erste Integrationen in eigene Anwendungen einbaust. Jetzt wird es ernst: Wir verketten lokale Modelle zu echten Pipelines, bauen ein eigenes RAG-System auf, und du siehst, warum "alles bleibt on-prem" nicht nur ein Marketing-Versprechen ist.
Voraussetzungen
Du solltest die ersten beiden Teile dieser Reihe kennen:
- Teil 1 — Lokale KI einrichten: Ollama installiert, erste Modelle (Llama 3, Mistral, Phi-3) laufen lokal.
- Teil 2 — Lokale KI in Anwendungen einbinden: REST-API, OpenAI-kompatibler Endpoint, erste Python-Anbindung.
Für diesen Teil brauchst du außerdem:
- Python 3.10+ mit
pip ollamaläuft lokal (Port 11434)- ca. 16 GB RAM (für Embeddings + LLM gleichzeitig)
- Optional: eine GPU (CUDA oder Metal) für spürbar mehr Tempo
Lokale Agenten: Tool-Use mit eigenen Modellen
Ein Agent ist ein LLM, das nicht nur Text generiert, sondern Werkzeuge aufrufen kann — eine Datei lesen, eine API abfragen, eine Berechnung durchführen. Das Konzept ist dasselbe wie bei GPT-4 mit Function-Calling, aber alles passiert lokal.
Function-Calling mit Ollama
Neuere Modelle wie Llama 3.1, Mistral-Nemo oder Qwen2.5 unterstützen strukturiertes Tool-Use über das OpenAI-Format. Das sieht so aus:
import ollama
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Gibt die aktuelle Temperatur für eine Stadt zurück",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Name der Stadt, z. B. Berlin"
}
},
"required": ["city"]
}
}
}
]
response = ollama.chat(
model="llama3.1:8b",
messages=[{"role": "user", "content": "Wie warm ist es gerade in München?"}],
tools=tools
)
# Modell antwortet mit einem Tool-Call statt direktem Text
tool_call = response["message"]["tool_calls"][0]
print(tool_call["function"]["name"]) # get_current_weather
print(tool_call["function"]["arguments"]) # {"city": "München"}
Das Modell erkennt selbst, wann es ein Werkzeug braucht, und gibt strukturiertes JSON zurück — kein Cloud-Aufruf, kein API-Key.
Einfacher ReAct-Agent
Das klassische ReAct-Muster (Reason + Act) lässt sich in wenigen Zeilen abbilden. Das Modell denkt nach, entscheidet sich für eine Aktion, bekommt das Ergebnis zurück und entscheidet weiter:
import ollama
import subprocess
def run_shell(cmd: str) -> str:
"""Führt einen Shell-Befehl aus und gibt stdout zurück."""
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
return result.stdout.strip() or result.stderr.strip()
SYSTEM = """Du bist ein Assistent mit Zugriff auf ein Terminal.
Wenn du einen Shell-Befehl ausführen willst, schreibe exakt:
ACTION: <befehl>
Wenn du eine finale Antwort geben willst, schreibe:
ANSWER: <antwort>"""
messages = [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": "Wie viele Python-Dateien liegen im aktuellen Verzeichnis?"}
]
for _ in range(5): # max. 5 Schritte
resp = ollama.chat(model="llama3.1:8b", messages=messages)
content = resp["message"]["content"]
messages.append({"role": "assistant", "content": content})
if content.startswith("ACTION:"):
cmd = content.removeprefix("ACTION:").strip()
output = run_shell(cmd)
messages.append({"role": "user", "content": f"RESULT: {output}"})
elif content.startswith("ANSWER:"):
print(content.removeprefix("ANSWER:").strip())
break
Achtung: Shell-Zugriff für KI-Agenten immer in einer Sandbox (Docker, chroot) betreiben. Niemals ungeprüft auf einem Produktivsystem.
RAG lokal: Eigene Dokumente durchsuchbar machen
Retrieval-Augmented Generation (RAG) löst das größte Problem lokaler Modelle: Sie kennen nur ihr Trainingsdata. Mit RAG gibst du dem Modell zur Laufzeit relevante Dokumente mit — ohne Fine-Tuning, ohne Cloud.
Wie RAG funktioniert
- Indexieren: Dokumente in Chunks aufteilen, Embeddings erzeugen, in Vektor-DB speichern.
- Abfragen: Frage des Nutzers embedden, ähnliche Chunks per Cosine-Similarity finden.
- Generieren: Gefundene Chunks als Kontext an das LLM übergeben.
Lokale Embeddings mit nomic-embed-text
Ollama liefert auch Embedding-Modelle. nomic-embed-text ist schnell, läuft auf CPU und erzeugt 768-dimensionale Vektoren:
ollama pull nomic-embed-text
Vollständiges RAG-Beispiel mit ChromaDB
pip install chromadb ollama
import ollama
import chromadb
# --- 1. Vektordatenbank einrichten ---
client = chromadb.Client()
collection = client.get_or_create_collection(
name="docs",
metadata={"hnsw:space": "cosine"}
)
# --- 2. Dokumente indexieren ---
documents = [
"Die Datenschutz-Grundverordnung (DSGVO) gilt seit Mai 2018 in der EU.",
"Artikel 5 DSGVO definiert die Grundsätze für die Verarbeitung personenbezogener Daten.",
"Verstöße gegen die DSGVO können mit Bußgeldern bis zu 20 Millionen Euro geahndet werden.",
"Das Recht auf Vergessenwerden ist in Artikel 17 DSGVO geregelt.",
"Für die Verarbeitung sensibler Daten gelten besondere Anforderungen nach Artikel 9 DSGVO.",
]
def embed(text: str) -> list[float]:
return ollama.embeddings(model="nomic-embed-text", prompt=text)["embedding"]
# Dokumente in ChromaDB speichern
collection.add(
ids=[str(i) for i in range(len(documents))],
embeddings=[embed(doc) for doc in documents],
documents=documents
)
# --- 3. RAG-Abfrage ---
def rag_query(question: str, top_k: int = 3) -> str:
# Frage embedden und ähnliche Chunks suchen
q_embedding = embed(question)
results = collection.query(
query_embeddings=[q_embedding],
n_results=top_k
)
context_chunks = results["documents"][0]
context = "\n\n".join(context_chunks)
# LLM mit Kontext aufrufen
prompt = f"""Beantworte die folgende Frage ausschließlich auf Basis des gegebenen Kontexts.
Wenn der Kontext keine Antwort enthält, sage das klar.
Kontext:
{context}
Frage: {question}
Antwort:"""
response = ollama.generate(model="llama3.1:8b", prompt=prompt)
return response["response"]
# Test
print(rag_query("Wie hoch können DSGVO-Bußgelder sein?"))
# → "Laut den vorliegenden Informationen können Verstöße gegen die DSGVO mit
# Bußgeldern bis zu 20 Millionen Euro geahndet werden."
Für Produktionseinsätze empfiehlt sich Qdrant statt ChromaDB: Es läuft als eigener Docker-Container, unterstützt persistente Speicherung und skaliert deutlich besser.
Pipelines verketten: STT → LLM → TTS
Einzelne Modelle sind gut, verkettete Pipelines sind mächtig. Ein klassisches Beispiel ist ein vollständig lokaler Sprach-Assistent:
Mikrofon → Whisper (STT) → Llama 3 (LLM) → Kokoro/Piper (TTS) → Lautsprecher
Dokumenten-Q&A-Pipeline
Eine häufige Produktionsanforderung: PDFs, Handbücher oder interne Wikis lokal durchsuchbar machen. Mit LangChain oder LlamaIndex lässt sich das in wenigen Zeilen aufbauen:
pip install llama-index llama-index-llms-ollama llama-index-embeddings-ollama
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
# Modelle konfigurieren — alles lokal
Settings.llm = Ollama(model="llama3.1:8b", request_timeout=120.0)
Settings.embed_model = OllamaEmbedding(model_name="nomic-embed-text")
# Dokumente aus Verzeichnis laden (PDF, TXT, DOCX, …)
documents = SimpleDirectoryReader("./meine-dokumente").load_data()
# Index aufbauen (Embeddings werden lokal erzeugt, in-memory gespeichert)
index = VectorStoreIndex.from_documents(documents)
# Query-Engine erstellen und abfragen
query_engine = index.as_query_engine()
response = query_engine.query("Was sind die wichtigsten Punkte im Kapitel Datenschutz?")
print(response)
Alles — Embeddings, Retrieval, LLM-Aufruf — passiert auf deinem Rechner. Keine Zeile deiner Dokumente verlässt das Haus.
Mehrere Modelle orchestrieren
Nicht jede Aufgabe braucht dasselbe Modell. Ein sinnvolles Setup:
- Routing-Modell (klein, schnell): Phi-3 Mini entscheidet, ob eine Anfrage einfach oder komplex ist.
- Hauptmodell (mittel): Llama 3.1 8B für die meisten Aufgaben.
- Spezialist (groß, selten): Mixtral 8x7B oder Llama 3.3 70B für komplexe Analysen.
import ollama
def route_and_answer(question: str) -> str:
# Schritt 1: Kleines Modell entscheidet über Komplexität
routing_prompt = f"""Ist diese Frage komplex (benötigt tiefes Reasoning) oder einfach?
Antworte nur mit: KOMPLEX oder EINFACH
Frage: {question}"""
routing = ollama.generate(model="phi3:mini", prompt=routing_prompt)
complexity = routing["response"].strip()
# Schritt 2: Passendes Modell auswählen
if "KOMPLEX" in complexity:
model = "llama3.1:70b" # benötigt viel RAM / GPU
else:
model = "llama3.1:8b"
response = ollama.generate(model=model, prompt=question)
return response["response"]
Dieses Muster spart Ressourcen: Die meisten alltäglichen Anfragen landen beim schnellen 8B-Modell, rechenintensive Aufgaben bekommen das große Modell.
Lokal vs. Cloud: Trade-offs im Vergleich
| Kriterium | Lokal (Ollama + OSS-Modelle) | Cloud (OpenAI / Anthropic) |
|---|---|---|
| Datenschutz | Vollständig on-prem, DSGVO-konform | Daten verlassen das Haus |
| Kosten | Einmalige Hardware, Strom | Pay-per-Token, skaliert |
| Latenz | Abhängig von Hardware (CPU/GPU) | Niedrig bei guter Verbindung |
| Modellqualität | GPT-4-Niveau mit 70B+ (GPU nötig) | State-of-the-art |
| Offline-Betrieb | Vollständig möglich | Nicht möglich |
| Wartung | Du bist verantwortlich | Managed Service |
| Anpassbarkeit | Fine-Tuning, LoRA, volle Kontrolle | Eingeschränkt |
| Compliance | Volle Kontrolle über Datenverarbeitung | Abhängig von AGB/Zertifizierungen |
| Skalierung | Horizontal aufwändig | Trivial |
Faustregel: Sensitive Daten, feste Infrastruktur, Compliance-Anforderungen → lokal. Prototypen, variable Last, knappe Hardware → Cloud. Viele Teams fahren einen Hybrid: lokale Modelle für interne Daten, Cloud für öffentliche Inhalte.
Häufige Fehler
1. Falsches Modell für Embeddings verwendet
Embeddings und Generierung sind verschiedene Aufgaben. Niemals ein Chat-Modell (llama3.1:8b) für Embeddings nutzen — es gibt keinen Fehler, aber die Vektoren sind qualitativ minderwertig. Immer ein dediziertes Embedding-Modell wie nomic-embed-text oder mxbai-embed-large verwenden.
2. Chunk-Größe nicht angepasst
Zu große Chunks (>1000 Token) verlieren Präzision beim Retrieval. Zu kleine Chunks (<100 Token) verlieren Kontext. Als Ausgangspunkt: 500 Token mit 50-Token-Overlap. Dann experimentieren.
3. Kein Timeout für Tool-Calls gesetzt
Wenn ein Agent Shell-Befehle ausführen darf und kein Timeout gesetzt ist, kann er auf ewig hängen. Immer timeout in subprocess.run setzen und Schleifenbrecher implementieren.
4. Vektor-Index nicht persistiert
ChromaDB im In-Memory-Modus verliert alle Daten beim Neustart. Für Produktionseinsätze chromadb.PersistentClient(path="./chroma_db") verwenden oder auf Qdrant mit Docker-Volume wechseln.
5. Kontext-Window überschritten
Lokale Modelle haben oft nur 4K–8K Kontext (manche neuere 128K). Wenn du zu viele Chunks als Kontext übergibst, bricht das Modell ab oder halluziniert verstärkt. Retrieval-Ergebnisse auf das nötige Minimum begrenzen (top_k=3 bis top_k=5 reicht fast immer).
6. RAM-Engpass bei parallelem Betrieb
Embedding-Modell + LLM + Vektor-DB gleichzeitig können 24+ GB RAM benötigen. Wenn Ollama das Modell wegen RAM-Mangel entlädt (model not loaded), Embeddings zuerst erzeugen, Modell entladen (ollama stop nomic-embed-text), dann LLM laden.
7. Falsches Modell-Format für Tool-Use
Nicht alle GGUF-Quants unterstützen Function-Calling zuverlässig. q4_K_M und q5_K_M sind gute Kompromisse. Sehr aggressiv quantisierte Modelle (q2_K) neigen bei strukturiertem Output zu Formatierungsfehlern.
Nächste Schritte
Mit diesem dritten Teil schließen wir die Reihe "Lokale KI – was alles geht" ab. Hier der schnelle Rückblick:
- Teil 1 hat gezeigt, wie du mit Ollama und llama.cpp in unter einer Stunde leistungsfähige Sprachmodelle lokal zum Laufen bringst — ohne Cloud-Abhängigkeit.
- Teil 2 hat den Schritt in die eigene Anwendung gemacht: REST-API, OpenAI-kompatible Endpoints, Streaming und erste Python-Integration.
- Teil 3 (dieser Artikel) hat das Ganze zu echten Produktivsystemen erweitert: Agenten mit Tool-Use, RAG für eigene Dokumente, verkettete Pipelines und Orchestrierung mehrerer Modelle.
Das Fazit der Reihe: Lokale KI ist heute produktionsreif — für Unternehmen mit Datenschutzanforderungen, für Entwickler, die volle Kontrolle wollen, und für alle, die nicht unbegrenzt API-Kosten zahlen möchten. Der Preis ist reale Hardware und Wartungsaufwand. Ob sich das lohnt, hängt von deinem Use-Case ab.
Du willst das in deinem Unternehmen umsetzen? Ich helfe dir dabei — von der Modellauswahl über RAG-Setup bis zum produktionsreifen Deployment. Schreib mir einfach über das Kontaktformular.
Verwandte Artikel
- KI / AI· 4 gemeinsame TagsLokale KI – Teil 1: LLMs lokal mit Ollama
- KI / AI· 2 gemeinsame TagsDSGVO-konforme KI-Integration: EU-Hosting, On-Premise-LLMs & Datenschutz-Best-Practices
- KI / AI· 2 gemeinsame TagsLLM-Integration in Bestandssysteme — RAG, Caching & Kostenkontrolle
- KI / AI· 1 gemeinsame TagsLokale KI – Teil 2: Bilder & Audio lokal generieren