Weiter zum Inhalt

Gemma 4 Tutorial: Baue einen lokalen KI‑Coding‑Agenten mit Gradio und Ollama

Lerne, wie du einen vollständig lokalen, multimodalen KI‑Coding‑Assistenten mit Gemma 4 über Ollama baust – inklusive agentischem Tool‑Einsatz in einer Gradio‑UI.
Aktualisiert 18. Sept. 2026  · 12 Min. lesen

Entdecke KI

ChatGPTClaudePerplexity

Lokale LLMs stehen vor einem Wendepunkt. Dank des langen Kontextfensters, der nativen Multimodalität und Zugänglichkeit von Gemma 4 über Ollama ist es inzwischen praktikabel, einen leistungsfähigen agentischen Coding-Assistenten komplett auf dem eigenen Rechner laufen zu lassen.

In diesem Tutorial zeige ich dir, wie du mit Gemma 4 über Ollama und einer Gradio-Oberfläche einen Coding-Assistenten baust. Die App bietet ein geteiltes Layout mit einem Live-Code-Editor links und einem agentischen Chat rechts. Du kannst Bilder oder Code-Dateien als Kontext hochladen, Tool-Einsatz aktivieren, damit das Modell Code ausführt und validiert, und für knifflige Aufgaben einen Thinking-Mode zuschalten.

Am Ende hast du eine lokale App, die:

  • Code in 15+ Sprachen schreibt, erklärt und debuggt
  • Python-Code in einem gekapselten Subprozess ausführt und die Ergebnisse zurückgibt
  • Bilder und Textdateien als multimodalen Kontext akzeptiert
  • Antworten in Echtzeit aus einem lokal laufenden Gemma‑4‑Modell streamt
  • Als agentische Schleife läuft, Tools aufruft und auf Basis der Ergebnisse nachfasst

Den vollständigen Code zu diesem Tutorial findest du hier.

Was ist Gemma 4?

Gemma 4 ist die Open-Weights-Modellfamilie von Google DeepMind, entwickelt für lokale Bereitstellung und Forschung. Sie baut auf der Gemma-Reihe auf, mit besserem Instruction-Following, längeren Kontextfenstern sowie nativer Multimodal-Eingabe und basiert auf derselben Forschungsinfrastruktur wie Gemini 3.

Model performance vs Size

Abbildung: Modellleistung vs. Größe (Quelle: Gemma‑4‑Blog)

Modelle wie Gemma‑4‑26B (MOE) und Gemma‑4‑31B erreichen Elo-Werte, die mit deutlich größeren Modellen vergleichbar sind – ein Hinweis auf starke Performance pro Parameter. Das 31B‑Modell rangiert aktuell als drittbestes offenes Modell weltweit auf dem Arena‑AI‑Text‑Leaderboard, das 26B‑Modell belegt Platz 6. Damit eignet sich Gemma 4 besonders gut für lokale und ressourcenbeschränkte Deployments, ohne an Leistungsfähigkeit einzubüßen.

Die Gemma‑4‑Modellfamilie

Gemma 4 erscheint in vier vielseitigen Größen: Effective 2B (E2B), Effective 4B (E4B), 26B Mixture of Experts (MoE) und 31B Dense. Die Familie teilt sich in zwei Tiers je nach Zielumgebung auf:

Model

Architecture

Total Params

Active/Effective Params

Context Length

Modalities

Gemma-4-31B

Dense Transformer

31B

31B

256K tokens

Text, Vision, Video

Gemma-4-26B-A4B

MoE (128 Experts)

26B

3.8B active

256K tokens

Text, Vision, Video

Gemma-4-E4B

Dense Transformer

7.9B (with embeddings)

4.5B effective

128K tokens

Text, Audio, Vision, Video

Gemma-4-E2B

Dense Transformer

5.1B (with embeddings)

2.3B effective

128K tokens

Text, Audio, Vision, Video

Schauen wir uns die einzelnen Varianten genauer an:

Visual Guide to Gemma 4

Abbildung: Visuelle Übersicht zu Gemma 4 (Quelle)

  • 31B Dense ist das Flaggschiffmodell, optimiert für Rechenzentrums‑Deployments und komplexe Reasoning‑Workloads. Es unterstützt ein Kontextfenster mit 256K Token und einem Sliding Window von 1024 Token für effiziente Langkontext-Verarbeitung.
  • 26B-A4B (MoE) ist Gemmas erstes MoE‑Modell, das Token über 128 Experts routet, wobei pro Forward‑Pass nur 3,8B Parameter aktiv sind. So erreicht es nahezu 31B‑Qualität bei einem Bruchteil der Rechenkosten pro Token – ideal für High‑Throughput‑Serving.
  • E4B und E2B sind die On‑Device‑ und Mobile‑Tier. Anders als die größeren Varianten unterstützen sie native Audio‑Eingaben für Spracherkennung neben Vision und Video – damit sind sie die multimodal stärksten Modelle der Familie für Edge‑Deployments.

Alle vier Modelle stehen unter der Apache‑2.0‑Lizenz und lassen sich lokal über Ollama, vLLM, llama.cpp oder Unsloth bereitstellen.

Für Coding‑Aufgaben glänzt Gemma 4 bei:

  • Komplettem, strukturiertem Code mit Erklärungen
  • Reasoning über bestehende Codebasen, wenn sie als Kontext gegeben sind
  • Tool‑Einsatz und agentische Workflows in Kombination mit einer Orchestrierungsschicht
  • Umgang mit Bildern neben Code (z. B. UI‑Screenshot lesen und passendes HTML generieren)

In diesem Tutorial verwenden wir die Variante gemma4:e4b (9,6 GB) über Ollama – eine quantisierte Version, die sich gut für lokale Inferenz auf Consumer‑Hardware eignet. 

Gemma 4 über Ollama ausführen

Ollama übernimmt Download, Quantisierung, Serving und stellt eine OpenAI‑kompatible HTTP‑API bereit. In diesem Tutorial dient Ollama als Inferenz‑Backend, und unsere App kommuniziert darüber via localhost:11434.

So holst und startest du das Modell:

curl -fsSL https://ollama.com/install.sh | sh
ollama pull gemma4:e4b 
ollama serve

Die obigen Befehle installieren Ollama lokal über das offizielle Installationsskript. Anschließend laden wir die Modellvariante gemma 4:e4b lokal für On‑Device‑Inferenz. Zum Schluss startet ollama serve den Ollama‑Server, sodass unsere App Anfragen an das Modell senden kann.

Sobald der Server läuft, verbindet sich die Gradio‑App automatisch.

Gemma‑4‑Demo: Baue einen Coding‑Assistenten mit Ollama

In diesem Abschnitt bauen wir den Code‑Assistenten Schritt für Schritt. Auf hoher Ebene erledigt die App Folgendes:

  • Nimmt natürliche Sprache im Chatpanel entgegen – optional mit Bild- oder Dateianhängen

  • Injiziert den aktuellen Editor‑Code als Kontext für das Modell

  • Sendet eine Streaming‑Anfrage an Gemma 4 über Ollamas /api/chat‑Endpoint

  • Ruft optional Tools (Codeausführung, Matheauswertung) in einer agentischen Schleife auf

  • Übernimmt erkannte Codeblöcke aus der Antwort direkt in den Live‑Editor

Lass es uns Schritt für Schritt aufbauen.

Final demo

Schritt 1: Abhängigkeiten installieren

Die App benötigt einige wenige Bibliotheken für UI, Bildverarbeitung und HTTP‑Kommunikation. Da das Modell lokal über Ollama läuft, gibt es keine Cloud‑SDK‑Abhängigkeiten.

pip install gradio requests pillow

In diesem Projekt verwenden wir:

  • gradio für den geteilten Editor und die Chat‑UI

  • requests für die Kommunikation mit der Ollama‑HTTP‑API

  • pillow für die Bildverarbeitung in der UI

Damit bleibt die Umgebung schlank und läuft auf jeder Maschine mit Ollama – inklusive macOS mit Apple Silicon, Linux und Windows via WSL.

Schritt 2: Konfiguration und Konstanten

Bevor wir Logik schreiben, definieren wir Imports, den Modellnamen, die Ollama‑Basis‑URL, unterstützte Sprachen und einen Standard‑Systemprompt. Diese Konstanten steuern das Verhalten der gesamten App.

import base64
import json
import math
import os
import re
import subprocess
import tempfile
from pathlib import Path
import gradio as gr
import requests

OLLAMA_BASE = "http://localhost:11434"
MODEL       = "gemma4:e4b"
LANGUAGES = [
    "python", "javascript", "typescript", "bash", "sql",
    "rust", "go", "java", "c", "cpp", "html", "css",
    "json", "yaml", "markdown", "plaintext",
]
TEXT_EXTS = {
    ".py", ".js", ".ts", ".jsx", ".tsx", ".html", ".css",
    ".json", ".yaml", ".yml", ".toml", ".md", ".txt",
    ".csv", ".sql", ".sh", ".bash", ".rs", ".go",
    ".java", ".c", ".cpp", ".h", ".hpp", ".rb", ".php",
}
IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp"}
DEFAULT_SYSTEM = """\
You are an expert coding assistant. When you write code:
- Always wrap it in a markdown code block with the language tag
- Write complete, working code — not fragments
- Briefly explain what the code does
You have a code-runner tool. Use it to validate logic when helpful.\
"""

Die Sets TEXT_EXTS und IMAGE_EXTS steuern zwei unterschiedliche Anhangsverhalten. Text‑/Code‑Dateien werden gelesen und als Kontext in den Prompt injiziert, während Bilddateien base64‑kodiert und im Images‑Feld für Gemma 4s Vision‑Fähigkeiten übergeben werden.

Der DEFAULT_SYSTEM‑Prompt beeinflusst den Stil der Codegenerierung und fordert explizit vollständige Codeblöcke mit Sprach‑Tags an, da die App diese parst, um Code in den Editor zu übernehmen.

Schritt 3: Agentische Tools definieren

Der Assistent kann in einem agentischen Modus arbeiten, in dem er während der Inferenz Tools aufruft. Wir definieren zwei Tools im Standard‑Funktionsaufruf‑Schema, das Ollama unterstützt:

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "run_code",
            "description": (
                "Execute Python code in a sandboxed subprocess and return "
                "stdout + stderr. Use this to validate, test, or demonstrate code."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "Python code to run (max ~50 lines, 5 s timeout).",
                    }
                },
                "required": ["code"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Evaluate a mathematical expression precisely.",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Python-compatible math expression, e.g. 'math.sqrt(2) * 100'",
                    }
                },
                "required": ["expression"],
            },
        },
    },
]

Der obige Codeschnipsel definiert das Tool run_code, das Python in einer temporären Datei via Subprozess mit 5‑Sekunden‑Timeout ausführt und sowohl stdout als auch stderr erfasst. So kann das Modell Logik validieren, Beispiele ausführen oder Live‑Output erzeugen – nicht nur statischen Text.

Das Tool calculate wertet Mathe‑Ausdrücke in einem eingeschränkten Namensraum aus – nur mit dem Modul math und sicheren Built‑ins. So erhält das Modell präzise numerische Resultate, ohne einen kompletten Python‑Prozess zu starten.

Schritt 4: Toolausführung

Die Ausführungsschicht mappt Tool‑Namen auf ihre Implementierungen. Sie wird in der agentischen Schleife aufgerufen, sobald das Modell einen Block tool_calls zurückgibt.

def _run_python(code: str) -> str:
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        tmp = f.name
    try:
        r = subprocess.run(
            ["python3", tmp],
            capture_output=True, text=True, timeout=5,
        )
        out = (r.stdout + r.stderr).strip()
        return out[:3000] if out else "(no output)"
    except subprocess.TimeoutExpired:
        return "⏱ Timed out (>5 s)"
    except Exception as e:
        return f"Error: {e}"
    finally:
        os.unlink(tmp)

def _calculate(expr: str) -> str:
    ns = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
    ns.update({"abs": abs, "round": round})
    try:
        return str(eval(expr, {"__builtins__": {}}, ns))  # noqa: S307
    except Exception as e:
        return f"Error: {e}"

def execute_tool(name: str, args: dict) -> str:
    if name == "run_code":
        return _run_python(args.get("code", ""))
    if name == "calculate":
        return _calculate(args.get("expression", ""))
    return f"Unknown tool: {name}"

Einige erwähnenswerte Sicherheitsaspekte der Tool‑Ausführung:

  • _run_python() schreibt in eine temporäre Datei und räumt sie auf – so bleiben keine Dateien zurück, selbst bei Fehlern

  • Output ist auf 3.000 Zeichen begrenzt, damit der Kontext nicht von riesigen Print‑Dumps überflutet wird

  • Die Funktion _calculate() nutzt eval() mit leerem __builtins__, wodurch der Zugriff auf Python‑Built‑ins außerhalb der explizit erlaubten math‑Funktionen verhindert wird

  • Das Timeout ist strikt auf 5 Sekunden gesetzt – lang genug für die meisten Validierungen, kurz genug, um ausufernde Prozesse zu verhindern

Schritt 5: Helfer‑Utilities

Bevor wir die Kern‑Chatlogik bauen, benötigen wir einige Utility‑Funktionen für Pfade, Bildkodierung und Code‑Extraktion. Diese Helfer werden durchgängig in der Streaming‑Pipeline genutzt.

def encode_image(path: str) -> str | None:
    if not path:
        return None
    try:
        with open(path, "rb") as f:
            return base64.b64encode(f.read()).decode()
    except Exception:
        return None

def file_as_context(path: str) -> str | None:
    if not path:
        return None
    p = Path(path)
    if p.suffix.lower() not in TEXT_EXTS:
        return None
    try:
        content = p.read_text(encoding="utf-8", errors="replace")[:8000]
        lang = p.suffix.lstrip(".")
        return f"\n\n**Attached file — {p.name}:**\n```{lang}\n{content}\n```"
    except Exception:
        return None

def resolve_gradio_path(val) -> str | None:
    if val is None:
        return None
    if isinstance(val, Path):
        s = str(val)
        return s if s.strip() else None
    if isinstance(val, str):
        s = val.strip()
        return s if s else None
    if isinstance(val, dict):
        p = val.get("path")
        if isinstance(p, str) and p.strip():
            return p.strip()
        nested = val.get("file")
        if isinstance(nested, dict):
            np = nested.get("path")
            if isinstance(np, str) and np.strip():
                return np.strip()
        return None
    name = getattr(val, "name", None)
    if isinstance(name, str) and name.strip():
        return name.strip()
    return None

def is_image_path(path: str | None) -> bool:
    return bool(path) and Path(path).suffix.lower() in IMAGE_EXTS

def extract_last_code_block(text: str) -> tuple[str | None, str]:
    blocks = re.findall(r"```(\w*)\n(.*?)```", text, re.DOTALL)
    if blocks:
        lang, code = blocks[-1]
        return code.strip(), lang.strip() or "python"
    return None, "python"

def ollama_ok() -> bool:
    try:
        requests.get(f"{OLLAMA_BASE}/", timeout=2)
        return True
    except Exception:
        return False

def _gradio_content_to_text(content) -> str:
    if content is None:
        return ""
    if isinstance(content, str):
        return content
    if isinstance(content, list):
        parts: list[str] = []
        for block in content:
            if isinstance(block, dict):
                if block.get("type") == "text":
                    parts.append(str(block.get("text", "")))
                elif "text" in block:
                    parts.append(str(block["text"]))
            elif isinstance(block, str):
                parts.append(block)
        return "".join(parts)
    return str(content)

def _append_chat_turn(
    history: list | None, user_text: str, assistant_text: str
) -> list:
    base = list(history) if history else []
    return base + [
        {"role": "user", "content": user_text},
        {"role": "assistant", "content": assistant_text},
    ]

def run_code_btn(code: str) -> str:
    if not code.strip():
        return "Nothing to run."
    result = _run_python(code)
    return result

Diese Utilities ermöglichen einen reibungslosen Umgang mit multimodalen Eingaben und strukturierten Ausgaben:

  • encode_image()‑Funktion: Sie wandelt Bilddateien in einen base64‑String um, der im images‑Feld von Ollama übergeben wird. So kann Gemma Bilder zusammen mit Text für multimodales Reasoning verarbeiten.

  • file_as_context()‑Funktion: Sie liest Text‑/Code‑Dateien und injiziert sie direkt als formatierten Markdown‑Block in den Prompt. Das erspart ein separates Retrieval, da alles inline als Kontext übergeben wird.

  • resolve_gradio_path()‑Funktion: Sie normalisiert unterschiedliche Dateiformate, die Gradio zurückgibt, zu einem nutzbaren Pfad. Das verhindert Edge‑Case‑Bugs beim Dateihandling.

  • is_image_path()‑Funktion: Schneller Check, ob eine Datei ein Bild ist – wichtig für korrektes Routing von Bildeingaben.

  • extract_last_code_block()‑Funktion: Extrahiert den letzten Codeblock aus der Modellantwort. 

  • ollama_ok()‑Funktion: Prüft, ob der lokale Ollama‑Server läuft, bevor Anfragen gesendet werden.

  • _gradio_content_to_text() und _append_chat_turn(): Flatten Gradio‑Nachrichten ins Plain‑Text‑Format und pflegen die Chat‑Historie.

  • run_code_btn()‑Funktion: Führt den generierten Code aus, wenn du im UI den Run code‑Button klickst.

Mit den Helpers an Bord implementieren wir nun die Kernfunktion chat().

Schritt 6: Kern‑Chat‑Streaming‑Generator

Diese Funktion steuert die komplette Interaktion – von der Input‑Vorbereitung über Streaming‑Antworten bis zur Tool‑Ausführung.

Auf hoher Ebene erledigt chat() Folgendes:

  • Baut die Gesprächshistorie auf
  • Anreichern der aktuellen Nutzereingabe mit Kontext (Code, Dateien, Bilder)
  • Senden einer Streaming‑Anfrage an Ollama
  • Optionales Ausführen von Tools (agentischer Modus)
  • Yield von Teilantworten für Live‑Updates in der UI
def chat(
    message, history, image_path, file_path,
    editor_code, language, system_prompt,
    agentic, thinking, temperature,
):

Die Funktion folgt dieser Abfolge:

Schritt 1: Nachrichtenhistorie aufbauen

Wir konvertieren zuerst vorherige Chat‑Turns in Ollamas Nachrichtenformat. Der System‑Prompt wird vorangestellt, um das Modell zu steuern und die volle Gesprächshistorie vor der Antwort zu geben.

for h in history or []:
        if not isinstance(h, dict):
            continue
        role = h.get("role")
        if role not in ("user", "assistant"):
            continue
        messages.append(
            {"role": role, "content": _gradio_content_to_text(h.get("content"))}
        )
    content = message

Schritt 2: Nutzer‑Turn komponieren

Als Nächstes bauen wir die aktuelle Nutzernachricht, indem wir zusätzlichen Kontext injizieren. Arbeitet der Nutzer mit Code, hängen wir den Editor‑Inhalt an:

if editor_code.strip() and editor_code.strip() != STARTER_CODE.strip():
    content += (
        f"\n\n**Current code in editor ({language}):**\n"
        f"```{language}\n{editor_code}\n```"
    )

Hochgeladene Dateien werden als Inline‑Kontext ergänzt, Bilder werden base64‑kodiert und über das Images‑Feld übergeben. So kann das Modell Text, Code und Bilder gemeinsam verarbeiten.

Schritt 3: Ollama‑Request senden

Das Payload enthält Modellname, Nachrichtenverlauf, Streaming‑Flag und Optionen:

payload = {
    "model": MODEL,
    "messages": messages,
    "stream": True,
    "options": options,
}

if agentic:
    payload["tools"] = TOOLS

Ist der Thinking‑Mode aktiviert, wird options["think"] = True gesetzt – das aktiviert erweitertes Chain‑of‑Thought‑Reasoning in unterstützten Modellen.

Schritt 4: Tool‑Aufrufe handhaben (agentischer Modus)

Im agentischen Modus kann das Modell statt Text Tool‑Aufrufe zurückgeben. Jedes Tool wird dann ausgeführt, die Ergebnisse werden dem Gespräch hinzugefügt und eine Folgeanfrage setzt die Generierung fort.

if agentic and msg.get("tool_calls"):
    for tc in msg["tool_calls"]:
        fn_name = tc["function"]["name"]
        fn_args = tc["function"]["arguments"]
        result = execute_tool(fn_name, fn_args)
        if fn_name == "run_code" and fn_args.get("code"):
            new_code = fn_args["code"]
        messages.append(msg)
        messages.append({"role": "tool", "content": result})
    resp2 = requests.post(f"{OLLAMA_BASE}/api/chat", ...)

So entsteht eine agentische Ein‑Turn‑Schleife: Das Modell ruft ein Tool auf, sieht das Ergebnis und generiert dann die finale Antwort weiter. Tool‑Logs werden für den Nutzer im Chat vorangestellt.

Schritt 5: Code extrahieren und Editor aktualisieren

Nach Abschluss des Streamings extrahieren wir den letzten Codeblock aus der Antwort:

extracted, _ = extract_last_code_block(full_response)

if extracted:
    new_code = extracted

Das Modell schreibt also Code, der Editor aktualisiert sich automatisch – ganz ohne Copy & Paste.

Schritt 7: Gradio‑UI‑Layout

Mit der Kernlogik an Ort und Stelle gestalten wir das UI mit Gradio. Ziel ist ein Layout, das Coding‑Workflows und KI‑Interaktion nebeneinander unterstützt.

Wir nutzen gr.Blocks für ein Zwei‑Spalten‑Layout:

  • Links steht der Code‑Editor mit Ausführung im Fokus
  • Rechts laufen Chat, Anhänge und Modellsteuerung
with gr.Blocks(title="Gemma 4 · Code Assistant") as demo:
    with gr.Row(equal_height=False):
        # LEFT: Code Editor
        with gr.Column(scale=11):
            with gr.Row():
                lang_sel = gr.Dropdown(choices=LANGUAGES, value="python", label="Language")
                run_btn  = gr.Button("Run Code", elem_classes=["run-btn"])
                clear_ed = gr.Button("Clear")
            code_editor = gr.Code(
                value=STARTER_CODE,
                language="python",
                label="Editor",
                lines=24,
                interactive=True,
            )
            run_output = gr.Textbox(
                label="Output",
                lines=6,
                interactive=False,
                elem_id="run-output",
            )
        # RIGHT: Chat
        with gr.Column(scale=9):
            chatbot = gr.Chatbot(value=[], elem_id="chatbot", height=430)
            with gr.Row():
                image_upload = gr.Image(label="Image (vision)", type="filepath")
                file_upload  = gr.File(label="Code / text file")
            with gr.Row():
                msg_input = gr.Textbox(placeholder="Ask the agent...", scale=6)
                send_btn  = gr.Button("Send", variant="primary")
            with gr.Row():
                agentic_cb  = gr.Checkbox(label="Enable Agentic", value=True)
                thinking_cb = gr.Checkbox(label="Enable Thinking", value=False)
                clear_chat  = gr.Button("Clear chat")
            with gr.Accordion("Settings", open=False):
                sys_prompt  = gr.Textbox(value=DEFAULT_SYSTEM, label="System prompt")
                temperature = gr.Slider(minimum=0.0, maximum=2.0, value=0.7)

So greift das UI mit den beschriebenen Komponenten ineinander:

  • Code‑Workspace: Das linke Panel bietet Sprachwahl, Editor sowie Ausführen/Zurücksetzen. Die Ausgaben erscheinen in einer hervorgehobenen Textbox.
  • Chat und Interaktion: Das rechte Panel enthält den Chatbot, das Eingabefeld für Fragen sowie Uploads – Bilder für multimodales Reasoning, Dateien zur Kontextinjektion. Zusätzlich schalten Toggles Tool‑Einsatz und optionales Reasoning frei.
  • Layout und Usability: Zwei Spalten mit ca. 55/45‑Aufteilung – der Editor hat Priorität, der Chat bleibt schnell zugänglich. Das sorgt für einen flüssigen Workflow zwischen Coding, KI‑Hilfe und Ausführung.

Schritt 8: Event‑Verkabelung

Senden und Submit nutzen dieselben Inputs/Outputs – Buttonklick und Enter im Textfeld lösen identisches Verhalten aus:

_inputs = [
    msg_input, chatbot, image_upload, file_upload,
    code_editor, lang_sel,
    sys_prompt, agentic_cb, thinking_cb, temperature,
]
_outputs = [chatbot, code_editor, msg_input]
send_btn.click(fn=respond, inputs=_inputs, outputs=_outputs)
msg_input.submit(fn=respond, inputs=_inputs, outputs=_outputs)

Der Wrapper respond() wandelt Gradio‑Pfadobjekte in String‑Pfade um, bevor er sie an den Streaming‑Generator übergibt. Danach wird bei jedem Chunk (history, code, "") ausgegeben, sodass das leere Feld die Eingabe direkt beim ersten Yield leert.

Schritt 9: Theme und CSS

Zur besseren Usability und Konsistenz ergänzen wir ein Custom‑Theme und CSS auf Basis des Gradio‑Standard‑Styles. So erhält die App einen polierten, GitHub‑ähnlichen Look – mit Light‑ und Dark‑Mode. Optional, aber empfehlenswert.

Wir definieren zunächst ein Basistheme mit gr.themes.Base:

THEME = gr.themes.Base(
    primary_hue   = "blue",
    secondary_hue = "slate",
    neutral_hue   = "slate",
    font      = [gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
    font_mono = [gr.themes.GoogleFont("JetBrains Mono"), "monospace"],
)

Das sorgt für eine konsistente Farbpalette und eine klare Typografie (Inter) mit entwicklerfreundlicher Monospace‑Schrift (JetBrains Mono). Danach überschreiben wir zentrale CSS‑Variablen für das Erscheinungsbild:

gradio-app {
    --body-background-fill:   #f0f3f7;
    --block-background-fill:  #ffffff;
    --body-text-color:        #1f2328;
    --button-primary-background-fill: #1a7f37;
}

body.dark gradio-app {
    --body-background-fill:   #0d1117;
    --block-background-fill:  #1c2128;
    --body-text-color:        #e6edf3;
    --button-primary-background-fill: #238636;
}

Der erste Block definiert das helle Theme, der zweite überschreibt Werte für den Dark Mode. Auch wenn optional, steigern Theme und CSS die User Experience deutlich.

Mit der strukturierten und gestylten UI kommen wir zum letzten Schritt: dem Start der Anwendung.

Schritt 10: Start

Zum Schluss startest du die App mit:

if __name__ == "__main__":
    print(f"Model  : {MODEL}")
    print(f"Ollama : {OLLAMA_BASE}")
    if not ollama_ok():
        print("Ollama not detected — run ollama serve before chatting")
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        theme=THEME,
        css=CSS,
    )

Mit server_name="0.0.0.0" ist die App im lokalen Netzwerk erreichbar, und share=False hält alles lokal. Der Check ollama_ok() sendet einen schnellen GET an localhost:11434 und warnt beim Start, falls Ollama nicht läuft – statt erst beim ersten Senden einer Nachricht einen Fehler zu zeigen.

Öffne dann http://localhost:7860, sobald die App läuft.

So sieht der komplette Ablauf für Nutzer aus:

Demo with code generation

  • Im Chatpanel eine Nachricht tippen, optional Bild oder Code‑Datei hochladen und Agentic/Thinking umschalten

  • Beim Absenden baut respond() die vollständige Ollama‑Anfrage – inklusive Editor‑Code und Anhängen als Kontext

  • Die Antwort streamt Token für Token, Teilantworten werden live im Gradio‑Chatbot dargestellt

  • Ist der agentische Modus an und das Modell ruft ein Tool auf, läuft es lokal, Ergebnisse werden angehängt, und eine Folgeanfrage streamt die finale Antwort

  • Nach dem Streaming wird der letzte fenced Codeblock extrahiert und automatisch in den Editor übernommen

Fazit

In diesem Tutorial haben wir mit Gemma 4, Ollama und Gradio einen vollständig lokalen KI‑Coding‑Assistenten gebaut. Die App unterstützt multimodale Eingaben, echten Tool‑Einsatz, Streaming‑Antworten und einen Live‑Code‑Editor – alles auf deinem Rechner, ohne externe API.

Der Whole‑Context‑Ansatz, den Editor‑Code in jeden Prompt zu injizieren, ist für Single‑File‑Workflows simpler als RAG und eignet sich besonders gut fürs Erklären, Refactoren oder Erweitern des Codes, an dem du gerade arbeitest.

Darauf kannst du aufbauen:

  • Einen Dateibaum hinzufügen, um Multi‑File‑Projekte zu unterstützen und ausgewählte Dateien als Kontext zu injizieren
  • Weitere Ollama‑Modelle per Dropdown unterstützen, damit Nutzer zwischen Gemma 4, Llama 3 und anderen wechseln können
  • Chat‑Historie auf Disk persistieren, damit Sessions Neustarts überleben

Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Ich bin Google Developers Expertin für ML (Gen AI), dreifache Kaggle-Expertin und Women-Techmakers-Botschafterin mit über drei Jahren Erfahrung in der Tech-Branche. 2020 habe ich ein Health-Tech-Startup mitgegründet und absolviere derzeit einen Master in Informatik an der Georgia Tech mit Schwerpunkt Machine Learning.

Gemma 4 FAQs

Braucht das eine GPU?

Nicht zwingend. Gemma 4 e4b ist ein quantisiertes Modell, das auf der CPU läuft – eine GPU beschleunigt die Inferenz aber deutlich.

Worin unterscheidet sich der agentische Modus vom normalen Chat?

Im normalen Modus streamt das Modell nur Text. Im agentischen Modus kann es run_code oder calculate aufrufen, die Ergebnisse prüfen und sie vor dem Abschluss der Antwort einarbeiten.

Wie aktualisiert sich der Editor automatisch, wenn der Agent Code schreibt?

Die Helper‑Funktion extract_last_code_block() scannt die Antwort des Assistenten nach dem letzten fenced Codeblock und übergibt ihn nach Abschluss des Streamings an den Editor. Darum weist der System‑Prompt das Modell an, Code stets in fenced Blöcken mit Sprach‑Tag zu kapseln.

Was passiert, wenn Ollama nicht läuft?

Der Check ollama_ok() fängt das beim Start und bei jeder Chat‑Eingabe ab. Ist Ollama nicht erreichbar, gibt der Chat eine formatierte Fehlermeldung aus, statt abzustürzen.

Themen
Künstliche Intelligenz

Lerne mit DataCamp

Kurs

Praxisnahe KI mit Google Gemini und NotebookLM

2 Std.
9.8K
Lerne Gemini und NotebookLM, um Aufgaben zu automatisieren, produktiver zu sein und im KI-Ökosystem von Google smarter zu arbeiten.
Details anzeigenRight Arrow
Kurs Starten
Mehr anzeigenRight Arrow
Ähnlich

Blog

Arten von KI-Agenten: Ihre Rollen, Strukturen und Anwendungen verstehen

Lerne die wichtigsten Arten von KI-Agenten kennen, wie sie mit ihrer Umgebung interagieren und wie sie in verschiedenen Branchen eingesetzt werden. Verstehe einfache reflexive, modellbasierte, zielbasierte, nutzenbasierte, lernende Agenten und mehr.

Blog

Die 36 wichtigsten Fragen und Antworten zum Thema generative KI für 2026

Dieser Blog hat eine ganze Reihe von Fragen und Antworten zu generativer KI, von den Grundlagen bis hin zu fortgeschrittenen Themen.
Hesam Sheikh Hassani's photo

Hesam Sheikh Hassani

15 Min.

Tutorial

30 coole Python-Tricks für besseren Code mit Beispielen

Wir haben 30 coole Python-Tricks zusammengestellt, mit denen du deinen Code verbesserst und deine Python-Kompetenzen ausbaust.
Kurtis Pykes 's photo

Kurtis Pykes

15 Min.

Tutorial

Python-Lambda-Funktionen: Ein Leitfaden für Anfänger

Lerne mehr über Python-Lambda-Funktionen, wozu sie gut sind und wann man sie benutzt. Enthält praktische Beispiele und bewährte Methoden für eine effektive Umsetzung.
Mark Pedigo's photo

Mark Pedigo

10 Min.

Tutorial

Python-Anweisungen IF, ELIF und ELSE

In diesem Tutorial lernst du ausschließlich Python if else-Anweisungen kennen.
Sejal Jaiswal's photo

Sejal Jaiswal

9 Min.

Tutorial

Fibonacci-Folge in Python: Lerne und entdecke Programmiertechniken

Finde raus, wie die Fibonacci-Folge funktioniert. Schau dir die mathematischen Eigenschaften und die Anwendungen in der echten Welt an.
Laiba Siddiqui's photo

Laiba Siddiqui

6 Min.

Mehr AnzeigenMehr Anzeigen