Startseite / Artikel / Praktische Hinweise: Ich habe einen lokalen KI-Agenten mit Ollama erstellt – und der schwierige Teil

Praktische Hinweise: Ich habe einen lokalen KI-Agenten mit Ollama erstellt – und der schwierige Teil

Schritt-für-Schritt-Anleitung zu den Praktischen Hinweisen: Ich habe einen lokalen KI-Agenten mit Ollama erstellt – und der schwierige Teil: Verträge, Überprüfungen sowie Code-Blöcke für Teams, die dieses Muster einsetzen.

2143 Wörter

Dieser Leitfaden zeigt Schritt für Schritt den Weg von Rohstoffen bis zu einem funktionsfähigen System für: Ich habe einen lokalen AI-Agent mit Ollama gebaut – und der schwierige Teil war nicht das Modell. Der Fokus liegt auf ausführbaren Schritten, expliziten Überprüfungen sowie Code, den man ohne Rätseln über die Absicht direkt in ein Repository einfügen kann. In der Übersichtsphase sollten Eingaben, Verantwortliche für die Schritte sowie Abbruchkriterien definiert werden, bevor Code geändert wird. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Bevorzugen Sie kleine, testbare Einheiten statt umfangreicher Skripte. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Pipeline-System.

Warum Sie Ollama gewählt haben

Beim Bearbeiten der Phase „Warum haben Sie Ollama gewählt?“ sollten Sie zunächst einen Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikatoren sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Betrachten Sie diese Phase als Vertrag zwischen Eingaben und validierten Ausgaben. Benennen Sie die Artefakte, definieren Sie Erfolgsprüfungen und lehnen Sie stille, teilweise abgeschlossene Abläufe ab. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken gelegentliche Fehler des Anbieters wie Programmierfehler.

ollama pull qwen3
pip install ollama
from ollama import chat
response = chat(
    model="qwen3",
    messages=[
        {"role": "user", "content": "Explain what an overdue invoice is."}
    ],
)print(response.message.content)

Ein Chatbot antwortet; ein Agent unternimmt Schritte

Wenn Sie mit einer Phase arbeiten, in der der Chatbot antwortet, notieren Sie zunächst den Vertrag: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Erfassen Sie außerdem die Laufzeiten sowie die Kosten für Tokens oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Weg von einer Demo in gemeinsam genutzte Umgebungen wechselt. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken gelegentliche Fehler des Anbieters wie Bugs in der Anwendung.

Fangen Sie mit eingeschränkten Werkzeugen an

Während der Phase „Starte mit eingeschränkten Werkzeugen“ soll man zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikator sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Bewahre die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreiber ohne das Durchlesen des gesamten Systems überprüfen können. Protokolliere bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken intermittierende Fehler des Anbieters wie Bugs in der Anwendung. Während der Phase „Starte mit eingeschränkten Werkzeugen“ soll man zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikator sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Ziehe kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufschema.

CUSTOMERS = {
    "acme plumbing": {
        "customer_id": "cus_1042",
        "name": "Acme Plumbing",
        "email": "billing@example.com",
    }
}
INVOICES = [
    {
        "invoice_id": "INV-2048",
        "customer_id": "cus_1042",
        "amount": 1850.00,
        "days_overdue": 18,
    }
]
def find_customer(name: str) -> dict:
    customer = CUSTOMERS.get(name.strip().lower())
    return customer or {"error": "customer_not_found"}
def get_overdue_invoices(customer_id: str) -> dict:
    matches = [
        invoice
        for invoice in INVOICES
        if invoice["customer_id"] == customer_id
        and invoice["days_overdue"] > 0
    ]
    return {"invoices": matches, "count": len(matches)}

Geben Sie dem Modell Werkzeuge – nicht nur imaginären Zugriff

Die Phase „Geben Sie dem Modell Werkzeuge“ funktioniert am besten, wenn sie als messbare Größe betrachtet wird. Erfassen Sie ein gelungenes Beispiel, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Behandeln Sie diese Phase als Vertrag zwischen Eingaben und validierten Ausgaben. Benennen Sie die Ergebnisdokumente, definieren Sie Erfolgskontrollen und lehnen Sie stille, unvollständige Abschlüsse ab. Fixieren Sie den Interpreter sowie die Abhängigkeitsdatei, bevor Sie dem Modell Schleifen beibringen. Abweichungen zwischen Laptop und CI sind die häufigste Ursache für stille Ausfälle bei API-Demos.

import json
from ollama import chat
def find_customer(name: str) -> dict:
    """Find a customer by business name and return its verified record."""
    customer = CUSTOMERS.get(name.strip().lower())
    return customer or {"error": "customer_not_found"}
def get_overdue_invoices(customer_id: str) -> dict:
    """Return overdue invoices for a verified customer ID."""
    matches = [
        invoice
        for invoice in INVOICES
        if invoice["customer_id"] == customer_id
        and invoice["days_overdue"] > 0
    ]
    return {"invoices": matches, "count": len(matches)}
TOOLS = {
    "find_customer": find_customer,
    "get_overdue_invoices": get_overdue_invoices,
}

Bauen Sie die Agenten-Schleife auf

Die Phase des Aufbaus der Agentenschleife funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie ein optimales Beispiel, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Erfassen Sie außerdem die Laufzeiten sowie die Kosten für Tokens oder Abfragen zusammen mit den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Prozess von einer Demo in gemeinsame Umgebungen übergeht. Fixieren Sie den Interpreter sowie die Abhängigkeitsdatei, bevor Sie der Schleife beibringen. Unterschiede zwischen dem Laptop und den CI-Systemen sind die häufigste Ursache für stillschweigende Ausfälle bei API-Demos.

SYSTEM_PROMPT = """
You are an invoice assistant.
Rules:
- Never invent a customer, invoice, email address, balance, or date.
- Use find_customer before requesting invoices.
- Only use customer IDs returned by tools.
- If a tool returns an error or no records, explain that clearly.
- You may draft communication, but you cannot send it.
"""
def run_agent(user_request: str) -> str:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_request},
    ]    for _ in range(6):
        response = chat(
            model="qwen3",
            messages=messages,
            tools=list(TOOLS.values()),
        )        messages.append(response.message)        if not response.message.tool_calls:
            return response.message.content        for call in response.message.tool_calls:
            name = call.function.name
            arguments = call.function.arguments            if name not in TOOLS:
                result = {"error": "tool_not_allowed"}
            else:
                try:
                    result = TOOLS[name](**arguments)
                except (TypeError, ValueError) as error:
                    result = {
                        "error": "invalid_tool_arguments",
                        "detail": str(error),
                    }            messages.append(
                {
                    "role": "tool",
                    "tool_name": name,
                    "content": json.dumps(result),
                }
            )    return "I stopped because the task exceeded the maximum number of steps."

Die eigentliche Lösung lag nicht in einem besseren Prompt

Die eigentliche Lösung besteht darin, die Testumgebung am besten als messbaren Bereich zu betrachten. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zum Rollback, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne das Durchlesen des gesamten Systems überprüfen können. Fixieren Sie den Interpreter sowie die Abhängigkeitsdateien, bevor Sie Schleifen implementieren. Unterschiede zwischen dem Laptop und den CI-Systemen sind die häufigsten stillen Störungen bei API-Demos. Die eigentliche Lösung besteht darin, die Testumgebung am besten als messbaren Bereich zu betrachten. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zum Rollback, bevor Sie den Umfang erweitern. Ziehen Sie kleine, testbare Einheiten vor großen, komplexen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf einen verworrenen Ablauf.

Fügen Sie strukturierte Ausgaben an der Grenze hinzu

Für die Phase „Strukturierte Ausgabe hinzufügen“ sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Betrachten Sie diese Phase als Vertrag zwischen den Eingaben und den validierten Ausgaben. Benennen Sie die Artefakte, definieren Sie Erfolgskontrollen und lehnen Sie stille, teilweise abgeschlossene Vorgänge ab. Trennen Sie den Client-Aufbau vom Nachrichtenzyklus, damit Provider ausgetauscht werden können, ohne die Zustandsmaschine des Dialogs neu schreiben zu müssen.

from pydantic import BaseModel, Field
class ReminderReview(BaseModel):
    customer_name: str
    invoice_ids: list[str]
    total_due: float = Field(ge=0)
    draft_subject: str
    draft_body: str
    requires_approval: bool = True
review_response = chat(
    model="qwen3",
    messages=messages,
    format=ReminderReview.model_json_schema(),
)
review = ReminderReview.model_validate_json(
    review_response.message.content
)

Zustand und Speicher sind unterschiedliche Dinge

Für den Staat und das Gedächtnis als Schritt sollten Eingaben, der Verantwortliche für diesen Schritt sowie die Abbruchkriterien vor dem Ändern des Codes definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckten Zustände schließen zu müssen. Zeiten sowie Kosten für Token oder Abfragen sollten neben den funktionalen Ergebnissen aufgezeichnet werden. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn sich der Ablauf von einer Demo-Umgebung in eine gemeinsam genutzte Umgebung verschiebt. Die Erstellung des Clients sollte von dem Nachrichtenzyklus getrennt werden, damit Anbieter ausgetauscht werden können, ohne die Zustandsmaschine des Dialogs neu schreiben zu müssen.

task_state = {
    "customer_id": "cus_1042",
    "verified_invoice_ids": ["INV-2048"],
    "approved_actions": [],
}

Lokal bedeutet nicht automatisch sicher

Weil For the Local nicht automatisch eine Umgebung erstellt, müssen vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Operator:innen sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Operator:innen überprüfen können, ohne den gesamten Ablauf durchzulesen. Trennen Sie die Erstellung des Clients von dem Nachrichtenzyklus, damit Provider ausgetauscht werden können, ohne die Zustandsmaschine der Konversation umschreiben zu müssen. Weil For the Local nicht automatisch eine Umgebung erstellt, müssen vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Operator:innen sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Ziehen Sie kleine, testbare Einheiten vor großen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf mehrere.

geformter Pipeline.

Wie man den Agenten testet

Beim Bearbeiten des Schritts „Wie man den Agenten testet“ sollte man zunächst einen Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Betrachten Sie diesen Schritt als Vertrag zwischen Eingaben und validierten Ausgaben. Benennen Sie die Artefakte, definieren Sie Erfolgskontrollen und lehnen Sie stille, teilweise abgeschlossene Abläufe ab. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken intermittierende Fehler des Anbieters wie Programmfehler.

Wie die funktionierende Version aussah

Während der Phase „Was ist die Arbeitsversion?“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikator sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Notieren Sie neben den funktionalen Ergebnissen außerdem die Laufzeiten sowie die Kosten für Tokens oder Abfragen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Weg von einer Demo-Umgebung in gemeinsam genutzte Umgebungen wechselt. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken gelegentliche Fehler des Anbieters wie Bugs in der Anwendung.

User request
  → find_customer(name="Acme Plumbing")
  → verified customer_id: cus_1042
  → get_overdue_invoices(customer_id="cus_1042")
  → verified invoice: INV-2048, $1,850, 18 days overdue
  → generate draft
  → wait for human approval

Letzter Unterrichtspunkt

Beim Bearbeiten der letzten Lektionstufe sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreiber ohne das Durchlesen des gesamten Systems überprüfen können. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken intermittierende Fehler des Anbieters wie Bugs in der Anwendung. Beim Bearbeiten der letzten Lektionstufe sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgssignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Ziehen Sie kleine, testbare Einheiten vor großen, komplexen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortungsbereich verweisen und nicht auf ein verworrenes Ablaufschema.

Betriebscheckliste

Die Phase der Betriebskontrollliste funktioniert am besten, wenn sie als messbarer Rahmen betrachtet wird. Erfassen Sie eine „goldene“ Transkription, einen Fehlerfall sowie die Notizen zum Rollback, bevor Sie den Umfang erweitern.

Dokumentieren Sie gemeinsam den erfolgreichen Ablauf sowie den Wiederherstellungsprozess. Versuche, menschliche Kontrollpunkte und die Handhabung von Fehlern gehören zum Produkt selbst, nicht zu späteren Optimierungen.

Sichern Sie den Interpreter sowie die Abhängigkeitsdateien, bevor Sie mit Schleifen arbeiten. Unterschiede zwischen Laptop und CI sind die häufigsten stillen Störungen bei API-Demos.

Wählen Sie strukturierte Ausgaben mit Schema-Validierung statt freier Prosa, wenn der nächste Schritt die Erstellung von Code oder den Aufruf einer Tool- Funktion ist.

Machen Sie nach aufwändigen Schritten einen Checkpoint. Das Wiederaufnehmen des Vorgangs sollte keine erneute Abrechnung für denselben LLM-Aufruf veranlassen, wenn ein Operator einen späteren Schritt erneut ausführt.

Sichern Sie die Versionen der Abhängigkeiten und speichern Sie den Image-Digest, mit dem die Demo ausgeführt wurde. Reproduzierbarkeit ist besser als „stammesbezogenes Wissen“.

Vor der Einführung des Stacks sollten Versionen eingefroren werden, ein „goldener“ Transkript für den kritischen Pfad erstellt und die Rollback-Schritte bestätigt werden. Gemeinsam genutzte Umgebungen benötigen Rate Limits, Überprüfungen der Nutzerzuordnung sowie einen klaren Verantwortlichen für die Rotation von Geheimnissen. Man sollte langweilige Zuverlässigkeit vor cleveren, einmaligen Demonstrationen bevorzugen.

Batch-Hinweis für a5f763eecd03: Halten Sie die Provider-Schlüssel außerhalb des Repositories, legen Sie eine Obergrenze für Session-Tokens fest und speichern Sie die Transkripte neben den Evaluierungs-Dateien, damit spätere Modellwechsel vergleichbar bleiben.