Startseite / Artikel

Agierende KI – Lassen Sie Ollama Ihren Python-Code schreiben und ausführen, lokal

Ein lokaler Agent-basierter AI-Code-Generator: Ollama schreibt Python-Code, führt ihn aus und liest die Ausgabe in einem Agenten-Loop ein. Voraussetzungen, Projektstruktur und der vollständige Build-Prozess.

Verlag: REACTAPP.TOP11 Min. Lesezeit2446 Wörter

Quellen: Agentic AI — Let Ollama Write and Run Your Python, A Local Code Generator

Dieser Leitfaden zeigt Schritt für Schritt den Weg von Rohstoffen bis zu einem funktionsfähigen System für: Agentic AI – Lassen Sie Ollama Ihren Python-Code schreiben und ausführen, ein lokaler Codegenerator. Der Schwerpunkt liegt auf durchführbaren Schritten, expliziten Überprüfungen sowie Code, den Sie ohne Rätseln über die Absicht direkt in ein Repository einfügen können. In der Übersichtsphase sollten Sie die Eingaben, den Verantwortlichen für den Schritt sowie die Abbruchkriterien definieren, bevor Sie Code ändern. Die 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. Betrachten Sie diese Phase als Vertrag zwischen den Eingaben und den validierten Ausgaben. Benennen Sie die Erzeugnisse, definieren Sie Erfolgskontrollen und lehnen Sie stille, unvollständige Abschlüsse ab.

Was wir entwickeln

Während der Phase „Was wir entwickeln“ 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.

Voraussetzungen

Während der Voraussetzungsphase 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, Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken intermittierende Fehler des Anbieters wie Bugs in der Anwendung.

ollama serve
ollama pull llama3.1:8b
pip install ollama

Projektstruktur

Während der Projektplanungsphase 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. Dokumentieren Sie sowohl den erfolgreichen Ablauf als auch den Wiederherstellungsprozess gemeinsam. Wiederholte Versuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken intermittierende Fehler des Anbieters wie Programmfehler. Während der Projektplanungsphase 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. 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 Vorgänge ab.

╔═══════════╦════════════════════════════════════════════════════╗
║   File    ║                         Role                       ║
╠═══════════╬════════════════════════════════════════════════════╣
║ main.py   ║ CLI, work-dir prompt / --dir, starts the loop      ║
║ agent.py  ║ Chat loop, tool execution, fake tool-call handling ║
║ tools.py  ║ Read / write / run, plus a soft ~/repo path limit  ║
║ config.py ║ Model name, iteration cap, system prompt           ║
╚═══════════╩════════════════════════════════════════════════════╝

Zuerst Konfiguration (config.py)

Die Config first config py-Phase funktioniert am besten, wenn sie als messbare Größe betrachtet wird. Erfassen Sie ein optimales Ergebnis, 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 gemeinsam genutzte Umgebungen übergeht. Fixieren Sie den Interpreter sowie die Abhängigkeitsdatei, bevor Sie Schleifen erklären. Unterschiede zwischen dem Laptop und den CI-Umgebungen sind die häufigste Ursache für stillschweigende Ausfälle bei API-Demos.

MODEL_NAME = "llama3.1:8b"
MAX_ITERATIONS = 15

SYSTEM_PROMPT = (
    "You are an autonomous local AI assistant with file tools. "
    "When creating or editing code, you MUST call write_local_file with the COMPLETE source in content. "
    "Never write empty files. Never create a placeholder file first. "
    "Always use relative filenames (for example 'calculator.py'). "
    "Do not invent absolute paths like /repo/.... "
    "Never describe or simulate tool calls in text, XML, JSON, or markdown - only use real tool calls. "
    "Keep calling tools until the task is actually done; then reply with a short plain-text summary."
)

Die Tools (tools.py)

Die Tools von py stage funktionieren am besten, wenn sie als messbare Oberfläche betrachtet werden. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie die Rollback-Anmerkung, 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 Graphen überprüfen können. Fixieren Sie den Interpreter sowie die Abhängigkeitslockdatei, bevor Sie Schleifen implementieren. Unterschiede zwischen Laptop und CI sind die häufigsten stillen Störungen bei API-Demos.

read_local_file

Die Phase „readlocalfile“ funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zum Rollback, bevor Sie den Umfang erweitern. Dokumentieren Sie gleichzeitig den erfolgreichen Ablauf sowie den Wiederherstellungsprozess. Wiederholversuche, menschliche Kontrollen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Fixieren Sie den Interpreter sowie die Abhängigkeits-Lockdatei, bevor Sie mit dem Schleifenverfahren arbeiten. Abweichungen zwischen Laptop und CI sind die häufigste Ursache für stille Ausfälle bei API-Demos. Die Phase „readlocalfile“ funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zum Rollback, bevor Sie den Umfang erweitern. Betrachten 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.

def read_local_file(filepath: str) -> str:
    """Reads and returns the contents of a local file.

    Args:
        filepath: The path to the file to read. Must be under ~/repo.
    """
    try:
        with open(_resolve_tool_path(filepath), "r") as f:
            return f.read()
    except Exception as e:
        return f"Error reading file: {str(e)}"

write_local_file

Zur Phase „writelocalfile“ sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Codeändern 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. 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 der Ablauf von Demonstrationsumgebungen in gemeinsam genutzte Umgebungen wechselt. 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.

def write_local_file(filepath: str, content: str) -> str:
    """Writes or overwrites text content to a local file.

    Args:
        filepath: Target filename or path. Must be under ~/repo.
        content: The text content to write inside the file. Must not be empty.
    """
    try:
        if content is None or not str(content).strip():
            return (
                "Error writing file: content is empty. "
                "Call write_local_file again with the full file source in content."
            )
        path = _resolve_tool_path(filepath)
        path.parent.mkdir(parents=True, exist_ok=True)
        with open(path, "w") as f:
            f.write(content)
        return f"Successfully wrote file to {path} ({len(content)} bytes)"
    except Exception as e:
        return f"Error writing file: {str(e)}"

run_python_script

Für die Phase „runpythonscript“ sollten Eingaben, der Verantwortliche für den Schritt sowie die Beendigungskriterien 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änden schließen zu müssen. Die Konfiguration sollte außerhalb des Anwendungscode gespeichert werden. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort zusammengefasst sein, den die Operator überprüfen können, ohne den gesamten Ablaufverlauf durchlesen zu müssen. Trennen Sie die Erstellung des Clients von dem Nachrichtenzyklus, damit Provider ausgetauscht werden können, ohne die Zustandsmaschine der Konversation umschreiben zu müssen.

def run_python_script(filepath: str) -> str:
    """Executes a local Python file and returns STDOUT and STDERR.

    Args:
        filepath: The path to the Python file to run. Must be under ~/repo.
    """
    try:
        path = _resolve_tool_path(filepath)
        result = subprocess.run(
            ["python3", str(path)],
            capture_output=True,
            text=True,
            timeout=10,
            cwd=str(_work_dir),
        )
        return f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
    except Exception as e:
        return f"Execution error: {str(e)}"

Registrierung

Zur Registrierungsphase sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien bereits vor dem Codeändern definiert werden. Die Mitarbeiter sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf verborgene Zustände schließen zu müssen. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf sowie den Notfallweg. Wiederholungsversuche, menschliche Überprüfungen und die Handhabung von Fehlnachrichten gehören zum Produkt selbst und nicht zu späteren Optimierungen. Trennen Sie den Aufbau des Clients von dem Nachrichtenzyklus, damit Anbieter ausgetauscht werden können, ohne die Zustandsmaschine der Konversation umschreiben zu müssen. Zur Registrierungsphase sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien bereits vor dem Codeändern definiert werden. Die Mitarbeiter sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf verborgene Zustände schließen zu müssen. Betrachten Sie diese Phase als Vertrag zwischen den Eingaben und den validierten Ausgaben. Benennen Sie die Ergebnisdokumente, definieren Sie Erfolgskontrollen und lehnen Sie stille, unvollständige Abschlüsse ab.

REGISTERED_TOOLS = {
    "read_local_file": read_local_file,
    "write_local_file": write_local_file,
    "run_python_script": run_python_script,
}

Soft-Path-Beschränkungen unter ~/repo

Wenn Sie mit den Soft-Path-Beschränkungen während des Entwicklungsprozesses arbeiten, 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. Notieren 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 Prozess von einer Demo-Umgebung in gemeinsam genutzte Umgebungen übertragen wird. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken gelegentliche Fehler des Anbieters wie Fehler in der Anwendung.

ALLOWED_ROOT = (Path.home() / "repo").resolve()
_work_dir = ALLOWED_ROOT

def _under_repo(path: Path) -> Path:
    resolved = path.expanduser().resolve()
    try:
        resolved.relative_to(ALLOWED_ROOT)
    except ValueError as exc:
        raise PermissionError(
            f"Path '{resolved}' is outside allowed root '{ALLOWED_ROOT}'."
        ) from exc
    return resolved

Der Agent-Loop (agent.py)

Beim Arbeiten an der Agentenphase des The agent loop schreiben Sie zunächst den Vertrag auf: 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 überprüfen können, ohne den gesamten Code durchzulesen. 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.

# ollama api call
response = ollama.chat(
    model=MODEL_NAME,
    messages=messages,
    tools=tools_list,  # the Python functions from REGISTERED_TOOLS
)
message = response["message"]
messages.append(message)
tool_calls = message.get("tool_calls")

Wie echte tool_calls aussehen

Beim Bearbeiten der Phase „What a real toolcalls“ 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. Dokumentieren Sie sowohl den erfolgreichen Ablauf als auch den Wiederherstellungsprozess gemeinsam. Wiederholte Versuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Protokollieren Sie bei jedem Aufruf die Anfrage-ID, die Modell-ID sowie die Latenzzeit. Ohne diese Aufzeichnungen wirken intermittierende Fehler des Anbieters wie Programmfehler. Beim Bearbeiten der Phase „What a real toolcalls“ 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. Betrachten Sie diese Phase als Vertrag zwischen Eingaben und validierten Ausgaben. Benennen Sie die relevanten Artefakte, definieren Sie Erfolgsprüfungen und lehnen Sie stille, teilweise abgeschlossene Abläufe ab.

{
  "message": {
    "role": "assistant",
    "content": "",
    "tool_calls": [
      {
        "function": {
          "name": "write_local_file",
          "arguments": {
            "filepath": "hello.py",
            "content": "print('hi')\n"
          }
        }
      }
    ]
  }
}
for call in tool_calls:
    func_name = call["function"]["name"]
    func_args = call["function"]["arguments"]

if func_name in REGISTERED_TOOLS:
        observation = REGISTERED_TOOLS[func_name](**func_args)
    else:
        observation = f"Error: Tool '{func_name}' is not registered."
    messages.append({
        "role": "tool",
        "name": func_name,
        "content": str(observation),
    })

Wie eine gefälschte tool_call aussieht

Die Phase „Wie sieht eine gefälschte toolcall aus?“ funktioniert am besten, wenn sie als messbare Größe betrachtet wird. Erfassen Sie ein optimales Transkript, einen Fehlerfall sowie die Notizen zum Rollback, 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 Einsatzbereich von einer Demo in gemeinsame Umgebungen wechselt. Fixieren Sie den Interpreter sowie die Abhängigkeitsdatei, bevor Sie dem Benutzer den Schleifenmechanismus erklären. Abweichungen zwischen Laptop und CI sind die häufigste Ursache für stillschweigende Ausfälle bei API-Demos.

I'll create the file now.
<tool_call>
{"name": "write_local_file", "arguments": {"filepath": "hello.py", "content": "print('hi')"}}
</tool_call>
CONTINUE_NUDGE = (
    "That response was not a valid tool call. "
    "Call write_local_file now with the full file contents. "
    "Do not write empty content. Do not describe the tool call in text."
)

if not tool_calls:
    content = message.get("content") or ""
    if _looks_like_fake_tool_call(content):
        messages.append({"role": "user", "content": CONTINUE_NUDGE})
        continue  # next iteration; yes, this burns a turn
    print(f"\n[Agent Completed Task]:\n{content}")
    return

Kontextbereinigung

Die Phase der Kontextkürzung funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung, 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 Durchsicht des gesamten Systems prüfen können. Fixieren Sie den Interpreter sowie die Abhängigkeitsdateien, bevor Sie Schleifen implementieren. Unterschiede zwischen Laptop und CI sind die häufigsten stillen Störungen bei API-Demos.

def prune_context(messages: list, max_history: int = 6) -> list:
    if len(messages) <= max_history + 2:
        return messages
    return messages[:2] + messages[-max_history:]p

Die CLI (main.py)

Die Phase der CLI mit main.py funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern.

python main.py --dir demos/hello 'Create hello.py that prints Hello and run it to verify.'

Ausführung

cd ~/repo/python-agent
python main.py 'Single-file tkinter calculator with digit buttons 0-9, + - * / = C. Show expression and result. No matplotlib. Write calculator.py only. After writing, fix any syntax or import errors. Prefer a complete working file; do not rely on running mainloop in the test harness.'
Files will be written under: /home/<user_name>/repo
Enter a directory under ~/repo for the program (relative path like 'demos/factorial', or absolute under ~/repo): calculator
Work directory: /home/<user_name>/repo/calculator
Starting agent with goal: Single-file tkinter calculator with digit buttons 0-9, + - * / = C. Show expression and result. No matplotlib. Write calculator.py only. After writing, fix any syntax or import errors. Prefer a complete working file; do not rely on running mainloop in the test harness.

[Iter 1] Executing Tool: write_local_file({'content': "import tkinter as tk\n...", 'filepath': 'calculator.py'})
  -> Successfully wrote file to ... (N bytes)
[Agent Completed Task]:
This is a complete working single-file tkinter calculator with digit buttons 0-9, + - * / = C. The expression and result are shown in the entry field at the top. The 'C' button clears the entry field.
cd ~/repo/calculator
python calculator.py

Betriebskontrollliste