Tobias Ludwig
Start
ErstgesprächKontakt
$whoami
Tobias Ludwig
DevOps · Application Manager · Software Engineer
$ls /pages
projekte/kontakt/kalender/impressum/
$git remote
github.com/nexas105
online·next.js 16·hono·postgresql
© 2026 tjl·stay curious_
/blog/KI / AI/lokale-ki-teil-3

Lokale KI – Teil 3: Lokale Agenten & Pipelines

Lokale KI Teil 3: Agenten mit Tool-Use, RAG mit ChromaDB, Pipelines verketten und mehrere Modelle orchestrieren — vollständig on-prem, DSGVO-konform, mit Python-Code-Beispielen.

16. Juni 20268 min Lesezeit1.656 Wörter
Lokale KIRAGOllamaPythonDatenschutzLLMKI-Agenten
ReiheLokale KI — was alles gehtTeil 3 / 3
  1. 01Lokale KI – Teil 1: LLMs lokal mit Ollama
  2. 02Lokale KI – Teil 2: Bilder & Audio lokal generieren
  3. 03Lokale KI – Teil 3: Lokale Agenten & Pipelines

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
  • ollama lä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

  1. Indexieren: Dokumente in Chunks aufteilen, Embeddings erzeugen, in Vektor-DB speichern.
  2. Abfragen: Frage des Nutzers embedden, ähnliche Chunks per Cosine-Similarity finden.
  3. 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

KriteriumLokal (Ollama + OSS-Modelle)Cloud (OpenAI / Anthropic)
DatenschutzVollständig on-prem, DSGVO-konformDaten verlassen das Haus
KostenEinmalige Hardware, StromPay-per-Token, skaliert
LatenzAbhängig von Hardware (CPU/GPU)Niedrig bei guter Verbindung
ModellqualitätGPT-4-Niveau mit 70B+ (GPU nötig)State-of-the-art
Offline-BetriebVollständig möglichNicht möglich
WartungDu bist verantwortlichManaged Service
AnpassbarkeitFine-Tuning, LoRA, volle KontrolleEingeschränkt
ComplianceVolle Kontrolle über DatenverarbeitungAbhängig von AGB/Zertifizierungen
SkalierungHorizontal aufwändigTrivial

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
Vorheriger TeilLokale KI – Teil 2: Bilder & Audio lokal generieren

Neue Artikel via RSS abonnieren

Inhalt
  • Voraussetzungen
  • Lokale Agenten: Tool-Use mit eigenen Modellen
  • Function-Calling mit Ollama
  • Einfacher ReAct-Agent
  • RAG lokal: Eigene Dokumente durchsuchbar machen
  • Wie RAG funktioniert
  • Lokale Embeddings mit nomic-embed-text
  • Vollständiges RAG-Beispiel mit ChromaDB
  • Pipelines verketten: STT → LLM → TTS
  • Dokumenten-Q&A-Pipeline
  • Mehrere Modelle orchestrieren
  • Lokal vs. Cloud: Trade-offs im Vergleich
  • Häufige Fehler
  • 1. Falsches Modell für Embeddings verwendet
  • 2. Chunk-Größe nicht angepasst
  • 3. Kein Timeout für Tool-Calls gesetzt
  • 4. Vektor-Index nicht persistiert
  • 5. Kontext-Window überschritten
  • 6. RAM-Engpass bei parallelem Betrieb
  • 7. Falsches Modell-Format für Tool-Use
  • Nächste Schritte
Tags
Lokale KIRAGOllamaPythonDatenschutzLLMKI-Agenten
RSS-Feed

Neue Artikel im Reader.