Weiter zum Inhalt

NVIDIA Nemotron 3 Super Tutorial: Erstelle einen Repo-Analyzer mit Gradio

Lerne, wie du NVIDIAs Nemotron 3 Super über OpenRouter nutzt, um ein GitHub-Repository ohne RAG zu analysieren – mit Prompt-Packing und einer Gradio-App.
Aktualisiert 22. Sept. 2026  · 15 Min. lesen

Entdecke KI

ChatGPTClaudePerplexity

Modelle mit großem Kontext eröffnen eine neue Art, Codebasen zu analysieren. Anstatt ein Repository in Chunks zu zerlegen, einzubetten und relevante Teile zu holen, kannst du ein komplettes kleines bis mittelgroßes Repo in einen einzigen Prompt packen und das Modell Ende-zu-Ende darüber nachdenken lassen.

\n

In diesem Tutorial zeige ich dir, wie du mit NVIDIA Nemotron 3 Super und einer Gradio-Oberfläche einen Analyzer für GitHub-Repositories baust. Die App nimmt eine öffentliche Repo-URL, klont sie, filtert unwichtige Dateien heraus, schätzt den Token-Verbrauch und packt den Rest in einen strukturierten Prompt. Dieser Prompt wird über OpenRouters gehosteten Endpunkt an Nemotron 3 Super gesendet, das dann strukturierte JSON-Ausgaben für Aufgaben wie Architektur-Analyse, Onboarding-Hinweise, Testlücken, Verbesserungspotenziale und mehr zurückgibt.

\n

Es gibt keinen Embedding-Schritt, keine Vektordatenbank und keine Retrieval-Schleife. Stattdessen setzen wir komplett auf Prompt-Packing – das funktioniert gut, solange das Repository in das praktische Kontextfenster des Modells passt. Am Ende dieses Tutorials hast du eine Gradio-App, die Folgendes kann:

\n
    \n
  • Ein öffentliches GitHub-Repository klonen und vorverarbeiten
  • \n
  • Abschätzen, ob es in ein Token-Budget passt
  • \n
  • Eine oder mehrere Repo-Analyseaufgaben ausführen
  • \n
  • Strukturierte Ergebnisse in einer aufgabenabhängigen UI darstellen
  • \n
\n

Das folgende Video zeigt das Projekt in Aktion (mit verkürzter Ladezeit zu Demonstrationszwecken):

Den vollständigen Code zu dieser Demo findest du hier.

\n

Was ist NVIDIA Nemotron 3 Super?

\n

Nemotron 3 Super ist NVIDIAs offenes Reasoning-Modell für agentische und Long-Context-Workflows. Es nutzt eine hybride Mamba-Transformer-Latent-MoE-Architektur mit 120B Gesamtparametern und 12B aktiven Parametern, beinhaltet Multi-Token Prediction (MTP) und ist für reasoning-lastige Aufgaben wie Coding, Tool-Nutzung und Long-Context-Analysen ausgelegt. 

\n

NVIDIA positioniert es gezielt für komplexe Multi-Agent-Anwendungen und Whole-Codebase-Reasoning.

\n

\"Nemotron

\n

Abbildung 1: Nemotron 3 Super Architektur (NVIDIA Blog)

\n

Einige Kernideen hinter Nemotron 3 Super sind:

\n
    \n
  • Ein hybrides Mamba-Transformer-Backbone (Abbildung 1) für effiziente Langsequenzen und präzises Retrieval,
  • \n
  • Latentes MoE, um Tokens bei gleichen Kosten durch mehr Spezialisten zu routen,
  • \n
  • MTP zur Verbesserung von Generationsgeschwindigkeit und Reasoning-Qualität,
  • \n
  • Ein natives 1M-Token-Kontextfenster in NVIDIAs unterstütztem Deployment-Stack.
  • \n
\n

Für dieses Tutorial nutzen wir OpenRouters gehosteten kostenlosen Endpunkt, denn sowohl die Voll- als auch die quantisierten Varianten des Modells benötigen für praktisches Inferenzieren in der Regel mindestens eine H100-GPU. Der gehostete Endpunkt unterstützt derzeit ein Kontextfenster von 262K Tokens (statt der vollen 1M), was in dieser Demo für mittelgroße Repos dennoch ausreicht.

\n

Nemotron 3 Familie

\n

NVIDIAs Nemotron 3 Familie wurde als offene Modellfamilie für agentische KI-Systeme vorgestellt – mit Größen für unterschiedliche Deployment- und Reasoning-Anforderungen: Nemotron Nano, Super und Ultra.

\n

Praktisch kannst du dir die Familie so merken:

\n
    \n
  • Nemotron 3 Nano: Die leichtere Modellreihe für effiziente, gezielte Aufgaben und günstigeres Inferenzieren – fokussiert auf agentische Workloads und gezielte Schritte in größeren Workflows.
  • \n
  • Nemotron 3 Super: Die mittlere „Serious Reasoning“-Stufe, bekannt für dichteres technisches Reasoning, komplexe Coding-Aufgaben, Long-Context-Analysen und höheres Agenten-Volumen.
  • \n
  • Nemotron 3 Ultra: Die größere Familie für noch aufwändigere Reasoning-/Deployment-Szenarien.
  • \n
\n

Für unseren Zweck ist Nemotron 3 Super die richtige Wahl, weil wir ein Modell wollen, das über das gesamte Repository auf einmal schlussfolgern kann – nicht nur einzelne Coding-Teilaufgaben löst.

\n

Nemotron 3 Super Tutorial: Erstelle einen Repo-Analyzer mit Gradio

\n

In diesem Abschnitt bauen wir einen Repo-Analyzer mit Nemotron 3 Super und einer Gradio-Oberfläche. Auf hoher Ebene macht die finale App Folgendes:

\n
    \n
  • Nimmt eine öffentliche GitHub-Repository-URL und optional einen Branch-Namen entgegen.
  • \n
  • Klonen des Repos lokal, Filtern von unwesentlichen Dateien und Abschätzen, ob der verbleibende Code ins verfügbare Kontext-Budget passt.
  • \n
  • Optionales Ausschließen von Tests, Doku oder Notebooks, um das gepackte Repo kleiner und leichter analysierbar zu machen.
  • \n
  • Packen des gefilterten Repos in einen einzigen Long-Context-Prompt und Senden an Nemotron 3 Super über OpenRouter.
  • \n
  • Rückgabe strukturierter Ergebnisse zu Repo-bezogenen Aufgaben.
  • \n
\n

Lass uns das Schritt für Schritt aufbauen.

\n

Schritt 1: Abhängigkeiten installieren

\n

Bevor wir den Repo-Analyzer bauen, brauchen wir eine minimale Umgebung für drei Kernaufgaben: UI-Rendering, Repository-Verarbeitung und Modellinteraktion. Da wir Nemotron nicht lokal ausführen, bleibt das Setup schlank und fokussiert.

\n

Installiere alles mit:

\n
!pip -q install gradio openai tiktoken gitpython orjson pandas
\n

In diesem Projekt verwenden wir:

\n
    \n
  • openai für die Interaktion mit der OpenRouter-API (Nemotron-Endpunkt)
  • \n
  • gradio zum Bauen der interaktiven UI
  • \n
  • tiktoken zur Abschätzung des Token-Verbrauchs vor dem Senden von Prompts
  • \n
  • orjson für schnelles JSON-Parsing der Modellausgaben
  • \n
  • pandas zur Darstellung strukturierter Ergebnisse als Tabellen
  • \n
  • gitpython zum Klonen und Arbeiten mit Repositories
  • \n
\n

Im Gegensatz zu lokalen LLM-Setups (die CUDA, vLLM oder Quantisierung erfordern) vermeidet dieser Ansatz GPU-Abhängigkeiten vollständig und bleibt Colab-freundlich.

\n

Schritt 2: OpenRouter-Client initialisieren

\n

In diesem Schritt richten wir Nemotron 3 Super über OpenRouter ein und definieren ein paar Konstanten für die Repo-Verarbeitung. Da das Modell nicht lokal läuft, ist dieser Client die zentrale Schnittstelle, um große, gepackte Prompts zu senden und strukturierte Analyseergebnisse zu empfangen.

\n
OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
MODEL = "nvidia/nemotron-3-super-120b-a12b:free"
WORKDIR = "/content/repo_ui"
if not OPENROUTER_API_KEY:
    raise ValueError("Set OPENROUTER_API_KEY in environment before running.")
client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=OPENROUTER_API_KEY,
)
enc = tiktoken.get_encoding("cl100k_base")
\n

An dieser Stelle kommuniziert die Anwendung mit Nemotron 3 Super über OpenRouter – mittels des openai-Python-Clients mit benutzerdefinierter base_url und einem API-Schlüssel in OPENROUTER_API_KEY. 

\n

Das ausgewählte Modell zeigt auf den gehosteten Endpunkt von OpenRouter, wodurch wir ein sehr großes Modell nutzen können, ohne lokale GPU-Infrastruktur zu benötigen. 

\n

Zur Verwaltung der Kontextgröße initialisieren wir einen Tokenizer (tiktoken mit cl100k_base), um vorab zu schätzen, wie viele Tokens das gepackte Repo verbraucht. Das ist wichtig, da das Modell ein endliches Kontextfenster hat. 

\n

Ein paar praktische Hinweise:

\n
    \n
  • Der kostenlose Endpunkt von OpenRouter protokolliert sowohl Prompts als auch Ausgaben. Nutze dieses Setup daher nur mit öffentlichen Repositories.
  • \n
  • Das verfügbare Kontextfenster (~262K Tokens) ist kleiner als das theoretische Maximum des Modells. Große Repos erfordern daher ggf. Filtering (z. B. Ausschluss von Tests, Doku oder Notebooks). Sende außerdem keine proprietären oder sensiblen Codes.
  • \n
  • Da alles über eine Remote-API läuft, bleibt das Setup leichtgewichtig und unabhängig von speicherstarken GPUs wie H100.
  • \n
\n

Schritt 3: Repo-Filter und Prompt-Regeln

\n

Jetzt brauchen wir zwei Schutzmechanismen: Dateifilterung und Prompt-Grounding. 

\n

Ersteres verhindert, dass wir Kontext an irrelevante Artefakte wie Build-Outputs, Notebooks, Bilder oder Binärdateien verschwenden. 

\n

Zweiteres stellt sicher, dass das Modell nach dem Packen des Repos an der tatsächlichen Codebasis verankert bleibt, statt in allgemeine Software-Ratschläge abzudriften. 

\n

Zusammen machen diese Regeln die Repo-Analyse deutlich effizienter.

\n
BASE_IGNORE_DIRS = {
    ".git", ".github", "__pycache__", "node_modules", ".venv", "venv",
    "dist", "build", ".mypy_cache", ".pytest_cache", ".idea", ".vscode"
}
IGNORE_SUFFIXES = {
    ".png", ".jpg", ".jpeg", ".gif", ".webp", ".pdf", ".zip", ".gz",
    ".tar", ".mp4", ".mov", ".onnx", ".pt", ".bin", ".parquet", ".feather",
    ".ico", ".svg", ".lock"
}
MAX_FILE_BYTES = 250_000
SYSTEM_PROMPT = """You are a codebase analysis model.
You are given the complete repository contents in one prompt.
Rules:
1. Use only the provided repository content.
2. Do not invent files, modules, libraries, or dependencies.
3. Every substantial claim must cite one or more file paths.
4. Clearly separate direct observations from engineering inferences or recommendations.
5. For modernization or improvement suggestions, explicitly distinguish:
   - repo-grounded findings
   - general engineering recommendations
6. Return valid JSON only.
7. If uncertain, say so explicitly.
8. For anything involving security, dead code, dependency replacement, or testing gaps,
   present findings as likely candidates unless directly proven by repository content.
"""
\n

Die folgenden drei Filtervariablen steuern die Vorverarbeitung des Repositories:

\n
    \n
  • BASE_IGNORE_DIRS schließt Verzeichnisse aus, die für die Codeanalyse irrelevant oder zu laut sind – etwa .git, node_modules, Build-Ordner und Editor-Caches. 
  • \n
  • IGNORE_SUFFIXES filtert Dateitypen heraus, die für Repo-Reasoning selten hilfreich sind: Bilder, Archive, Videos, Model-Checkpoints und andere Artefakte. 
  • \n
  • MAX_FILE_BYTES ist eine zusätzliche Sicherheitsgrenze, die zu große Dateien überspringt und so hilft, im praktischen Kontextbudget zu bleiben.
  • \n
\n

Der SYSTEM_PROMPT ist genauso wichtig wie die Dateifilter. 

\n

Da das Modell das Repo als einen langen Prompt sieht, brauchen wir klare Verhaltensregeln, um das Erfinden von Modulen, das Fehlinterpretieren von Abstraktionen oder zu allgemeine Empfehlungen zu vermeiden. 

\n

Diese Anweisungen zwingen das Modell, in der bereitgestellten Codebasis verankert zu bleiben, Dateipfade für wichtige Aussagen zu zitieren und harte Evidenz von allgemeinem Urteil zu trennen. 

\n

Schritt 4: Task-Schemas und Sicherheitsnetze

\n

Nachdem Dateifilter und Systemprompt stehen, definieren wir nun Repo-weite Aufgaben, jeweils mit einem strukturierten JSON-Schema. 

\n

Das bringt zwei Vorteile: Das Modell weiß exakt, welches Output-Format erwartet wird, und die App kann die Ergebnisse zuverlässig parsen und in aufgabenspezifischen Tabellen darstellen. 

\n
TASK_DEFS: Dict[str, Dict[str, Any]] = {
    "Architecture Overview": {
        "description": "Explain the architecture, major modules, likely entrypoints, dependencies, and architectural issues.",
        "schema": {
            "repo_overview": {
                "summary": "",
                "primary_languages": [],
                "likely_entrypoints": []
            },
            "modules": [
                {
                    "name": "",
                    "responsibility": "",
                    "key_files": [],
                    "evidence_paths": []
                }
            ],
            "dependencies": [
                {
                    "from_module": "",
                    "to_module": "",
                    "relationship": "",
                    "evidence_paths": []
                }
            ],
            "issues": [
                {
                    "type": "",
                    "description": "",
                    "severity": "low|medium|high",
                    "evidence_paths": []
                }
            ],
            "unknowns_and_uncertainties": []
        },
    },
    ...
}
\n

Das TASK_DEFS-Dictionary ist das zentrale Register für alle Analysemodi, die die App unterstützt. 

\n

Jede Aufgabe enthält eine natürlichsprachliche Beschreibung, was zu tun ist, und ein strenges Schema, das die erwartete JSON-Ausgabe definiert. 

\n

So kann dasselbe gepackte Repo sehr unterschiedliche Arten von Reasoning unterstützen – von Architekturkartierung über Duplikatanalyse bis zu Onboarding-Guides oder Erkennung von Testlücken.

\n

Beispielsweise verlangt Architecture Overview vom Modell, Folgendes zu identifizieren:

\n
    \n
  • eine High-Level-Zusammenfassung des Repos,
  • \n
  • wichtige Module,
  • \n
  • Abhängigkeitsbeziehungen,
  • \n
  • und Architekturprobleme.
  • \n
\n

Weitere Aufgaben im vollständigen Code umfassen:

\n
    \n
  • Code Duplication – Fokus auf wiederholter Logik und zugehörigen Refactor-Möglichkeiten,
  • \n
  • Improvement Opportunities – identifiziert Potenziale für Wartbarkeit und Modularität,
  • \n
  • Refactor Roadmap – verwandelt Findings in einen priorisierten Engineering-Plan,
  • \n
  • Testing Gaps – hebt untertestete Bereiche hervor,
  • \n
  • Onboarding Guide – erklärt, wie neue Engineers an die Codebasis herangehen sollten,
  • \n
  • Library Modernization – deckt veraltete oder überlappende Bibliotheken auf,
  • \n
  • Code Quality Hotspots – zeigt fragile oder fehleranfällige Bereiche,
  • \n
  • sowie Security bzw. Config Hygiene – markiert riskante Defaults oder operative Probleme.
  • \n
\n
FULL_REVIEW_TASKS = [
    "Architecture Overview",
    "Improvement Opportunities",
    "Code Quality Hotspots",
    "Testing Gaps",
    "Refactor Roadmap",
]
TASK_CHOICES = [
    "Architecture Overview",
    "Code Duplication",
    "Improvement Opportunities",
    "Refactor Roadmap",
    "Testing Gaps",
    "Onboarding Guide",
    "Library Modernization",
    "Code Quality Hotspots",
    "Security / Config Hygiene",
    "Full Review",
]
def safe_str(value: Any) -> str:
    if value is None:
        return ""
    if isinstance(value, str):
        return value.strip()
    return str(value).strip()
\n

Die Liste FULL_REVIEW_TASKS definiert ein gebündeltes Multi-Step-Review. 

\n

Statt alle Aufgaben zu starten, läuft eine kuratierte Teilmenge für einen breiten Engineering-Blick auf das Repo. TASK_CHOICES wird im Gradio-Dropdown genutzt, damit die Nutzerin entweder eine einzelne Aufgabe oder das Bündel wählen kann.

\n

Der letzte Teil, safe_str(), löst ein überraschend häufiges Stabilitätsproblem. Während der Entwicklung trat oft folgender Fehler auf:

\n
'NoneType' object has no attribute 'strip'
\n

Das passiert etwa, wenn:

\n
    \n
  • ein Gradio-Eingabefeld None zurückgibt,
  • \n
  • ein Task-Wert fehlt,
  • \n
  • eine Modellantwort leeren Inhalt hat,
  • \n
  • oder ein Parser einen unerwarteten Null-Wert erhält.
  • \n
\n

Durch Normalisieren all dieser Werte via safe_str() wird die App deutlich robuster. 

\n

Schritt 5: Repo klonen und packen

\n

Bevor wir etwas ans Modell schicken, müssen wir ein GitHub-Repo in einen einzigen strukturierten Prompt verwandeln. 

\n

Dieser Schritt übernimmt die komplette Aufnahme-Pipeline: Repo klonen, irrelevante Dateien filtern, nur nutzbaren Text extrahieren und alles in ein Format packen, das das Modell in einem Rutsch konsumieren kann.

\n

Anders als bei klassischen RAG-Pipelines zerteilen oder embedden wir nicht. Stattdessen komprimieren wir das komplette Repo in einen einzigen Long-Context-Input – daher sind Selektivität und Struktur hier entscheidend für Performance und Qualität.

\n
def clone_repo(repo_url: str, branch: str = "main") -> str:
    repo_url = safe_str(repo_url)
    branch = safe_str(branch)
    if not repo_url:
        raise ValueError("Repository URL is empty.")
    if os.path.exists(WORKDIR):
        shutil.rmtree(WORKDIR)
    cmd = ["git", "clone", "--depth", "1"]
    if branch:
        cmd += ["--branch", branch]
    cmd += [repo_url, WORKDIR]
    subprocess.run(cmd, check=True, capture_output=True, text=True)
    return WORKDIR
def is_probably_text(path: Path) -> bool:
    try:
        with open(path, "rb") as f:
            chunk = f.read(2048)
        if b"\x00" in chunk:
            return False
        chunk.decode("utf-8")
        return True
    except Exception:
        return False
def iter_repo_files(root: str, exclude_tests: bool, exclude_docs: bool, exclude_notebooks: bool):
    root = Path(root)
    for path in root.rglob("*"):
        if not path.is_file():
            continue
        rel = path.relative_to(root)
        parts = set(rel.parts)
        rel_str = str(rel).lower()
        if any(part in BASE_IGNORE_DIRS for part in rel.parts):
            continue
        if exclude_tests and ("test" in parts or "tests" in parts or "/tests/" in f"/{rel_str}/"):
            continue
        if exclude_docs and ("docs" in parts or rel_str.endswith(".md") or rel_str.endswith(".rst")):
            continue
        if exclude_notebooks and rel_str.endswith(".ipynb"):
            continue
        if path.suffix.lower() in IGNORE_SUFFIXES:
            continue
        try:
            if path.stat().st_size > MAX_FILE_BYTES:
                continue
        except Exception:
            continue
        if not is_probably_text(path):
            continue
        yield path
def repo_tree_string(root: str, exclude_tests: bool, exclude_docs: bool, exclude_notebooks: bool) -> str:
    root = Path(root)
    lines: List[str] = []
    for path in sorted(root.rglob("*")):
        rel = path.relative_to(root)
        rel_str = str(rel).lower()
        parts = set(rel.parts)
        if any(part in BASE_IGNORE_DIRS for part in rel.parts):
            continue
        if path.is_file() and path.suffix.lower() in IGNORE_SUFFIXES:
            continue
        if exclude_tests and ("test" in parts or "tests" in parts or "/tests/" in f"/{rel_str}/"):
            continue
        if exclude_docs and ("docs" in parts or rel_str.endswith(".md") or rel_str.endswith(".rst")):
            continue
        if exclude_notebooks and rel_str.endswith(".ipynb"):
            continue
        lines.append(f"{rel}/" if path.is_dir() else str(rel))
    return "\n".join(lines)
def pack_repo(root: str, exclude_tests: bool, exclude_docs: bool, exclude_notebooks: bool):
    root = Path(root)
    tree = repo_tree_string(root, exclude_tests, exclude_docs, exclude_notebooks)
    files: List[Tuple[str, int, str]] = []
    total_tokens = len(enc.encode(tree))
    for path in iter_repo_files(root, exclude_tests, exclude_docs, exclude_notebooks):
        rel = path.relative_to(root)
        try:
            content = path.read_text(encoding="utf-8", errors="ignore")
        except Exception:
            continue
        block = f"\n=== FILE START: {rel} ===\n{content}\n=== FILE END: {rel} ===\n"
        tok = len(enc.encode(block))
        files.append((str(rel), tok, block))
        total_tokens += tok
    return tree, files, total_tokens
def build_repo_context(tree: str, files: List[Tuple[str, int, str]]) -> str:
    joined_files = "".join(block for _, _, block in files)
    return f"""=== REPO TREE START ===
{tree}
=== REPO TREE END ===
=== REPOSITORY FILES START ===
{joined_files}
=== REPOSITORY FILES END ===
"""
\n

Dieser Schritt definiert die komplette Aufnahme- und Packing-Pipeline, wobei jede Funktion eine klare Aufgabe hat:

\n
    \n
  • clone_repo()-Funktion: Klont ein GitHub-Repo in ein temporäres Arbeitsverzeichnis via Shallow Clone (--depth 1), um die Historie nicht mitzuladen. Eingaben wie Repo-URL und Branch werden mit safe_str() normalisiert. Ausgabe ist der lokale Pfad (WORKDIR).
  • \n
  • iter_repo_files()-Funktion: Hauptfilterlage. Sie durchsucht rekursiv das Repo und liefert nur signalstarke Dateien. Systemverzeichnisse, nicht unterstützte Dateitypen, zu große Dateien sowie optional Tests, Doku und Notebooks werden ausgeschlossen. 
  • \n
  • is_probably_text()-Funktion: Leichter Sicherheitscheck beim Filtern. Liest einen kleinen Chunk, prüft auf Nullbytes (typisch für Binärdateien) und auf erfolgreiche UTF-8-Dekodierung. Nur solche Dateien werden als Text akzeptiert.
  • \n
  • repo_tree_string()-Funktion: Baut eine String-Repräsentation der Repo-Struktur mit denselben Filterregeln. So versteht das Modell erst das Layout, bevor es Inhalte liest.
  • \n
  • pack_repo()-Funktion: Kern des Packings. Sie liest die Inhalte aller gefilterten Dateien ein und umschließt jede Datei mit Markern wie === FILE START === und === FILE END ===. Zudem wird der Tokenverbrauch mit tiktoken geschätzt und aufsummiert. 
  • \n
  • build_repo_context()-Funktion: Fügt alles zu einem strukturierten Prompt zusammen. Zuerst der Repo-Baum, danach die Datei-Inhalte. So erfasst das Modell erst die Struktur, dann die Implementierungsdetails.
  • \n
\n

Das Ergebnis ist ein rohes GitHub-Repo, transformiert in einen einzigen, strukturierten Long-Context-Input. 

\n

Schritt 6: Task-Prompting und JSON-Parsing

\n

Nachdem das Repo gefiltert und in einen einzigen Long-Context-Input gepackt ist, müssen wir dem Modell sagen, welche Analyse es durchführen und wie es die Ergebnisse formatieren soll. 

\n

Statt immer dieselbe generische Anweisung zu senden, erzeugen wir einen aufgabenspezifischen Prompt mit dem Tasknamen, der Beschreibung und dem exakten erwarteten JSON-Schema.

\n
def build_task_prompt(task_name: str, repo_context: str) -> str:
    task_name = safe_str(task_name)
    if not task_name:
        raise ValueError("Task name is empty.")
    if task_name not in TASK_DEFS:
        raise ValueError(f"Unknown task: {task_name}")
    task_def = TASK_DEFS[task_name]
    return f"""Analyze this full repository.
Task Name:
{task_name}
Task Description:
{task_def["description"]}
Return JSON matching this schema exactly:
{json.dumps(task_def["schema"], indent=2)}
{repo_context}
"""
def try_parse_json(text: str) -> Dict[str, Any]:
    text = safe_str(text)
    if not text:
        raise ValueError("Model returned empty text.")
    try:
        return orjson.loads(text)
    except Exception:
        start = text.find("{")
        end = text.rfind("}")
        if start != -1 and end != -1 and end > start:
            return orjson.loads(text[start:end+1])
        raise ValueError("Could not parse model output as JSON.")
\n

Der obige Code definiert zwei eng verwandte Funktionen als Brücke zwischen gepacktem Repo und aufgabenspezifischem Analyse-Output.

\n
    \n
  • build_task_prompt()-Funktion: Baut den finalen Prompt für das Modell. Sie normalisiert den Tasknamen mit safe_str(), validiert die Existenz in TASK_DEFS und fügt dann Beschreibung, JSON-Schema sowie den kompletten Repo-Kontext ein.
  • \n
\n
    \n
  • try_parse_json()-Funktion: Verarbeitet die Modellausgabe nach der Inferenz. Long-Context-Modelle umschließen JSON in der Praxis manchmal mit zusätzlichem Text oder Formatierungsartefakten. Die Funktion versucht erst ein direktes orjson.loads() und extrahiert andernfalls das JSON zwischen der ersten öffnenden und letzten schließenden Klammer. Durch striktes Prompting plus tolerantes Parsing bleibt die App deutlich stabiler.
  • \n
\n

Im nächsten Schritt verbinden wir diese Logik mit dem eigentlichen Modellaufruf und beginnen, strukturierte Repo-Analyseergebnisse zu erzeugen.

\n

Schritt 7: Modellausführung

\n

Jetzt führen wir die Analyse aus, indem wir den Prompt über OpenRouter an Nemotron 3 Super senden. 

\n

Statt Teile des Repos zu zerschneiden oder abzurufen, schicken wir die gesamte gefilterte Codebasis in einer einzigen Anfrage und lassen das Modell global über Dateien, Module und Abhängigkeiten schlussfolgern.

\n
def call_model(prompt: str, max_tokens: int = 5000) -> str:
    prompt = safe_str(prompt)
    if not prompt:
        raise ValueError("Prompt is empty.")
    resp = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": prompt},
        ],
        temperature=0.2,
        max_tokens=max_tokens,
        extra_headers={
            "HTTP-Referer": "https://colab.research.google.com",
            "X-Title": "Whole Repo Analysis Demo"
        }
    )
    if not getattr(resp, "choices", None):
        raise ValueError("Model response has no choices.")
    message = getattr(resp.choices[0], "message", None)
    if message is None:
        raise ValueError("Model response has no message.")
    content = getattr(message, "content", None)
    content = safe_str(content)
    if not content:
        raise ValueError("Model returned empty content.")
    return content
\n

Die Funktion call_model() führt den eigentlichen Modellaufruf aus und extrahiert die Antwort. Die Pipeline im Überblick:

\n
    \n
  • Eingabevalidierung: Normalisiert den Prompt, um ungültige API-Anfragen zu vermeiden.
  • \n
  • Aufbau der Modellanfrage: OpenAI-kompatibler Client mit OpenRouter als Backend. Enthält Systemprompt (Grounding, JSON-Output, Evidenzpflicht) und die User-Nachricht mit Task-Prompt und Repo-Kontext. temperature ist auf 0.2 gesetzt für deterministischere Ausgaben; max_tokens begrenzt die Antwortgröße.
  • \n
\n
    \n
  • Antwortvalidierung: Prüft choices, extrahiert die message und normalisiert den Inhalt. Fehlende Inhalte lösen Fehler aus. 
  • \n
\n
    \n
  • Leere Antworten behandeln: Verhindert, dass leere Strings downstream Fehler verursachen (z. B. beim JSON-Parsing).
  • \n
\n

Damit können wir zum Inferenz-Teil übergehen und sehen, wie die Analyse über das gehostete OpenRouter-Modell läuft.

\n

Schritt 8: Helferfunktionen

\n

Als Nächstes brauchen wir ein paar kleine Helfer, die UI und Ausgabehandling vereinfachen. 

\n
def to_df(items, columns=None):
    if items is None:
        return pd.DataFrame(columns=columns or [])
    if isinstance(items, dict):
        return pd.DataFrame([items])
    if not items:
        return pd.DataFrame(columns=columns or [])
    return pd.DataFrame(items)
def get_tasks_to_run(task: str) -> List[str]:
    task = safe_str(task)
    if not task:
        raise ValueError("Task is empty.")
    return FULL_REVIEW_TASKS if task == "Full Review" else [task]
def visible_for_task(task: str) -> Dict[str, bool]:
    task = safe_str(task)
    if task == "Full Review":
        return {
            "modules": True,
            "deps": True,
            "issues": True,
            "dups": False,
            "refactor": False,
            "improvements": True,
            "roadmap": True,
            "testing": True,
            "onboarding": False,
            "libs": False,
            "hotspots": True,
            "security": False,
        }
    return {
        "modules": task == "Architecture Overview",
        "deps": task == "Architecture Overview",
        "issues": task == "Architecture Overview",
        "dups": task == "Code Duplication",
        "refactor": task == "Code Duplication",
        "improvements": task == "Improvement Opportunities",
        "roadmap": task == "Refactor Roadmap",
        "testing": task == "Testing Gaps",
        "onboarding": task == "Onboarding Guide",
        "libs": task == "Library Modernization",
        "hotspots": task == "Code Quality Hotspots",
        "security": task == "Security / Config Hygiene",
    }
def render_markdown_summary(repo_url: str, total_tokens: int, files, results: Dict[str, Dict[str, Any]]) -> str:
    repo_url = safe_str(repo_url)
    lines = [
        "# Whole-Repo Analysis Report",
        f"**Repository:** {repo_url}",
        f"**Files included:** {len(files)}",
        f"**Estimated input tokens:** {total_tokens}",
        "",
    ]
    if "Architecture Overview" in results:
        overview = results["Architecture Overview"].get("repo_overview", {})
        lines += [
            "## Architecture",
            overview.get("summary", "No summary returned."),
            "",
            f"**Primary languages:** {', '.join(overview.get('primary_languages', [])) or 'N/A'}",
            f"**Likely entrypoints:** {', '.join(overview.get('likely_entrypoints', [])) or 'N/A'}",
            "",
        ]
    if "Improvement Opportunities" in results:
        lines += [
            "## Improvement Opportunities",
            f"- Identified: {len(results['Improvement Opportunities'].get('improvement_opportunities', []))}",
            "",
        ]
    if "Code Quality Hotspots" in results:
        lines += [
            "## Code Quality Hotspots",
            f"- Identified: {len(results['Code Quality Hotspots'].get('quality_hotspots', []))}",
            "",
        ]
    if "Testing Gaps" in results:
        lines += [
            "## Testing Gaps",
            f"- Identified: {len(results['Testing Gaps'].get('testing_gaps', []))}",
            "",
        ]
    if "Code Duplication" in results:
        dup = results["Code Duplication"]
        lines += [
            "## Duplication & Refactor Summary",
            f"- Duplication candidates: {len(dup.get('duplication_candidates', []))}",
            f"- Design smells: {len(dup.get('design_smells', []))}",
            f"- High risk files: {len(dup.get('high_risk_files', []))}",
            f"- Refactor recommendations: {len(dup.get('refactor_recommendations', []))}",
            "",
        ]
    if "Library Modernization" in results:
        lines += [
            "## Library Modernization",
            f"- Recommendations: {len(results['Library Modernization'].get('library_recommendations', []))}",
            "",
        ]
    if "Security / Config Hygiene" in results:
        lines += [
            "## Security / Config Hygiene",
            f"- Findings: {len(results['Security / Config Hygiene'].get('security_and_config_findings', []))}",
            "",
        ]
    if "Refactor Roadmap" in results:
        lines += [
            "## Refactor Roadmap",
            f"- Planned items: {len(results['Refactor Roadmap'].get('refactor_roadmap', []))}",
            "",
        ]
    if "Onboarding Guide" in results:
        onboarding = results["Onboarding Guide"].get("onboarding_guide", {})
        lines += [
            "## Onboarding Guide",
            onboarding.get("summary", "No summary returned."),
            "",
            f"**Recommended reading order:** {', '.join(onboarding.get('recommended_reading_order', [])) or 'N/A'}",
            f"**Entrypoints:** {', '.join(onboarding.get('entrypoints', [])) or 'N/A'}",
            "",
        ]
    return "\n".join(lines)
def hidden_update():
    return gr.update(value=pd.DataFrame(), visible=False)
def visible_update(df):
    return gr.update(value=df, visible=True)
def empty_outputs(stats, status_message):
    return (
        status_message,
        "",
        *all_hidden(),
        "{}",
        json.dumps(stats, indent=2),
    )
\n

Diese Helfer erledigen zusammen den Großteil der Präsentationslogik der App.

\n
    \n
  • to_df()-Funktion: Konvertiert Modellausgaben in pandas-DataFrames für saubere Gradio-Tabellen. 
  • \n
  • get_tasks_to_run()-Funktion: Entscheidet, ob eine einzelne Aufgabe oder das Bündel Full Review läuft.
  • \n
  • visible_for_task()-Funktion: Steuert, welche Report-Tabellen in der UI sichtbar sind, je nach gewählter Aufgabe.
  • \n
  • render_markdown_summary()-Funktion: Erzeugt statt rohem JSON eine lesbare Zusammenfassung mit Repo-Metadaten, Tokenzahl und Highlights.
  • \n
  • hidden_update() und visible_update(): Kleine Gradio-Helfer, um Komponenten zu verstecken/anzeigen. Wichtig, da Gradio für jede Output-Komponente Rückgabewerte erwartet.
  • \n
  • empty_outputs()-Funktion: Für Frühabbrüche und Fehlerzustände: selbst wenn etwas schiefgeht, liefert die App gültige Outputs für alle UI-Elemente. 
  • \n
\n

Diese Helfer ändern nicht die Kernlogik, machen daraus aber eine nutzbare Anwendung.

\n

Schritt 9: End-to-End-Repo-Analyse

\n

Diese Funktion orchestriert den gesamten Workflow. Sie validiert Eingaben, bereitet das Repo auf, führt eine oder mehrere Analysen aus und liefert strukturierte Ausgaben an die Gradio-UI.

\n
def analyze_repo(repo_url, branch, task, token_budget, exclude_tests, exclude_docs, exclude_notebooks):
    repo_url = safe_str(repo_url)
    branch = safe_str(branch)
    task = safe_str(task)
    try:
        if not repo_url:
            stats = {"repo_url": "", "branch": branch}
            return empty_outputs(stats, "Error: Repository URL is empty.")
        if not task:
            stats = {"repo_url": repo_url, "branch": branch}
            return empty_outputs(stats, "Error: Task is empty.")
        repo_path = clone_repo(repo_url, branch)
        tree, files, total_tokens = pack_repo(repo_path, exclude_tests, exclude_docs, exclude_notebooks)
        stats = {
            "repo_url": repo_url,
            "branch": branch,
            "files_included": len(files),
            "estimated_input_tokens": total_tokens,
            "token_budget": token_budget,
            "fits_budget": total_tokens <= token_budget,
            "sample_files": [f[0] for f in files[:30]],
            "tasks_requested": get_tasks_to_run(task),
        }
        if total_tokens > token_budget:
            msg = (
                f"Repo too large for current budget.\n\n"
                f"Estimated tokens: {total_tokens}\n"
                f"Budget: {token_budget}\n\n"
                f"Try excluding tests/docs/notebooks or use a smaller repo."
            )
            return empty_outputs(stats, msg)
        repo_context = build_repo_context(tree, files)
        tasks_to_run = get_tasks_to_run(task)
        results: Dict[str, Dict[str, Any]] = {}
        for task_name in tasks_to_run:
            prompt = build_task_prompt(task_name, repo_context)
            raw_text = call_model(prompt)
            results[task_name] = try_parse_json(raw_text)
        report_md = render_markdown_summary(repo_url, total_tokens, files, results)
        vis = visible_for_task(task)
        arch_json = results.get("Architecture Overview", {})
        dup_json = results.get("Code Duplication", {})
        imp_json = results.get("Improvement Opportunities", {})
        roadmap_json = results.get("Refactor Roadmap", {})
        testing_json = results.get("Testing Gaps", {})
        onboarding_json = results.get("Onboarding Guide", {})
        lib_json = results.get("Library Modernization", {})
        hotspot_json = results.get("Code Quality Hotspots", {})
        security_json = results.get("Security / Config Hygiene", {})
        modules_df = to_df(arch_json.get("modules", []), ["name", "responsibility", "key_files", "evidence_paths"])
        deps_df = to_df(arch_json.get("dependencies", []), ["from_module", "to_module", "relationship", "evidence_paths"])
        issues_df = to_df(arch_json.get("issues", []), ["type", "description", "severity", "evidence_paths"])
        dups_df = to_df(dup_json.get("duplication_candidates", []), ["summary", "files_involved", "why_it_looks_duplicate", "confidence"])
        refactor_df = to_df(dup_json.get("refactor_recommendations", []), ["title", "description", "target_files", "expected_benefit"])
        improvements_df = to_df(imp_json.get("improvement_opportunities", []), ["title", "description", "why_it_matters", "affected_files", "suggested_change", "expected_impact", "effort", "evidence_paths"])
        roadmap_df = to_df(roadmap_json.get("refactor_roadmap", []), ["priority", "title", "description", "target_files", "impact", "effort", "why_now"])
        testing_df = to_df(testing_json.get("testing_gaps", []), ["module_or_file", "why_it_needs_tests", "likely_missing_cases", "recommended_test_type", "evidence_paths"])
        onboarding_df = to_df(onboarding_json.get("onboarding_guide", {}), ["summary", "recommended_reading_order", "entrypoints", "important_directories", "debugging_start_points", "common_confusion_points"])
        libs_df = to_df(lib_json.get("library_recommendations", []), ["current_library_or_pattern", "where_used", "suggested_alternative", "why_better", "migration_difficulty", "confidence", "evidence_paths", "recommendation_type"])
        hotspots_df = to_df(hotspot_json.get("quality_hotspots", []), ["file_or_module", "risk_level", "why_it_is_a_hotspot", "evidence_paths", "recommended_cleanup"])
        security_df = to_df(security_json.get("security_and_config_findings", []), ["title", "severity", "description", "affected_files", "evidence_paths", "recommended_fix", "confidence"])
        status = (
            f"Done.\n\n"
            f"Files included: {len(files)}\n"
            f"Estimated input tokens: {total_tokens}\n"
            f"Tasks run: {', '.join(tasks_to_run)}"
        )
        return (
            status,
            report_md,
            visible_update(modules_df) if vis["modules"] else hidden_update(),
            visible_update(deps_df) if vis["deps"] else hidden_update(),
            visible_update(issues_df) if vis["issues"] else hidden_update(),
            visible_update(dups_df) if vis["dups"] else hidden_update(),
            visible_update(refactor_df) if vis["refactor"] else hidden_update(),
            visible_update(improvements_df) if vis["improvements"] else hidden_update(),
            visible_update(roadmap_df) if vis["roadmap"] else hidden_update(),
            visible_update(testing_df) if vis["testing"] else hidden_update(),
            visible_update(onboarding_df) if vis["onboarding"] else hidden_update(),
            visible_update(libs_df) if vis["libs"] else hidden_update(),
            visible_update(hotspots_df) if vis["hotspots"] else hidden_update(),
            visible_update(security_df) if vis["security"] else hidden_update(),
            json.dumps(results, indent=2),
            json.dumps(stats, indent=2),
        )
    except subprocess.CalledProcessError as e:
        stats = {"repo_url": repo_url, "branch": branch}
        stderr = safe_str(getattr(e, "stderr", "")) or safe_str(e)
        return empty_outputs(stats, f"Git clone failed:\n{stderr}")
    except Exception as e:
        stats = {"repo_url": repo_url, "branch": branch}
        return empty_outputs(stats, f"Error: {safe_str(e)}")
\n

Diese Funktion orchestriert die gesamte Pipeline – vom rohen Repo bis zum strukturierten Output.

\n
    \n
  • Eingaben und Repo-Vorbereitung: Das Repo wird geklont, gepackt und der Tokenbedarf geschätzt. Überschreitet es das Budget, erfolgt ein früher Abbruch.
  • \n
  • Task-Ausführung (Kernlogik): Für jede angeforderte Aufgabe wird ein spezifischer Prompt gebaut, das Modell aufgerufen und die JSON-Ausgabe geparst. Alles landet im gemeinsamen results-Dictionary.
  • \n
  • Aufbereitung für die UI: Die JSON-Ausgaben werden mit to_df() in DataFrames überführt und UI-Komponenten zugeordnet. visible_for_task() steuert die Sichtbarkeit.
  • \n
  • Reporting und Fehlerbehandlung: Ein Markdown-Report fasst die wichtigsten Punkte zusammen, vollständige JSONs bleiben für Debugging verfügbar. Alle Fehler (Klonen, leere Antworten, Parsing) laufen über empty_outputs(), um die UI stabil zu halten.
  • \n
\n

Schritt 10: Die Gradio-UI bauen

\n

Zum Schluss verpacken wir alles in eine einfache Gradio-Oberfläche.

\n
with gr.Blocks(title="Nemotron Repo Analyzer") as demo:
    gr.Markdown("# Nemotron Repo Analyzer")
    # gr.Markdown("Analyze a public GitHub repository without RAG using a single packed prompt.")
    with gr.Tab("Analyze"):
        with gr.Row():
            repo_url = gr.Textbox(
                label="GitHub Repo URL",
                value="https://github.com/AashiDutt/Nemotron-3-Nano.git"
            )
            branch = gr.Textbox(label="Branch", value="main")
        with gr.Row():
            task = gr.Dropdown(
                choices=TASK_CHOICES,
                value="Full Review",
                label="Task"
            )
            token_budget = gr.Slider(
                minimum=40000,
                maximum=180000,
                step=5000,
                value=120000,
                label="Token Budget"
            )
        with gr.Row():
            exclude_tests = gr.Checkbox(value=False, label="Exclude tests")
            exclude_docs = gr.Checkbox(value=False, label="Exclude docs / markdown")
            exclude_notebooks = gr.Checkbox(value=True, label="Exclude notebooks")
        analyze_btn = gr.Button("Analyze Repo", variant="primary")
        status = gr.Textbox(label="Status", lines=7)
    with gr.Tab("Report"):
        report_md = gr.Markdown()
        modules_df = gr.Dataframe(label="Modules", visible=False)
        deps_df = gr.Dataframe(label="Dependencies", visible=False)
        issues_df = gr.Dataframe(label="Architecture Issues", visible=False)
        dups_df = gr.Dataframe(label="Duplication Candidates", visible=False)
        refactor_df = gr.Dataframe(label="Refactor Recommendations", visible=False)
        improvements_df = gr.Dataframe(label="Improvement Opportunities", visible=False)
        roadmap_df = gr.Dataframe(label="Refactor Roadmap", visible=False)
        testing_df = gr.Dataframe(label="Testing Gaps", visible=False)
        onboarding_df = gr.Dataframe(label="Onboarding Guide", visible=False)
        libs_df = gr.Dataframe(label="Library Modernization", visible=False)
        hotspots_df = gr.Dataframe(label="Code Quality Hotspots", visible=False)
        security_df = gr.Dataframe(label="Security / Config Hygiene", visible=False)
    with gr.Tab("Raw JSON"):
        all_results_json_box = gr.Code(label="All Task Results JSON", language="json")
        stats_box = gr.Code(label="Run Stats", language="json")
    analyze_btn.click(
        analyze_repo,
        inputs=[repo_url, branch, task, token_budget, exclude_tests, exclude_docs, exclude_notebooks],
        outputs=[
            status,
            report_md,
            modules_df,
            deps_df,
            issues_df,
            dups_df,
            refactor_df,
            improvements_df,
            roadmap_df,
            testing_df,
            onboarding_df,
            libs_df,
            hotspots_df,
            security_df,
            all_results_json_box,
            stats_box,
        ]
    )
demo.launch(debug=True, share=True)
\n

Der gr.Blocks()-Container definiert das App-Layout, die obere Markdown-Zeile setzt Titel und Kontext. Die Oberfläche ist in drei Tabs gegliedert, um den Ablauf übersichtlich zu halten.

\n
    \n
  • Der Analyze-Tab ist die Eingabeschicht. Er sammelt Repo-URL und Branch, lässt die Aufgabe wählen und das Token-Budget konfigurieren. Außerdem gibt es Schalter zum Ausschließen von Tests, Doku oder Notebooks, um Prompt-Größe und Rauschen zu steuern. Der Button „Analyze Repo“ startet die Pipeline, und das Statusfeld zeigt Fortschritt, Fehler oder Abschluss an.
  • \n
  • Der Report-Tab ist die Ausgabeschicht. Er rendert eine Markdown-Zusammenfassung für den schnellen Überblick, gefolgt von strukturierten DataFrames zur Detailprüfung. 
  • \n
  • Der Raw JSON-Tab dient Debugging und Transparenz. Er zeigt die vollständigen strukturierten Ausgaben des Modells plus Lauf-Metadaten. Praktisch zum Validieren, Debuggen oder Export in nachgelagerte Systeme.
  • \n
\n

So werden die Nutzereingaben direkt mit analyze_repo() verbunden und dessen Ausgaben zurück in die UI gemappt. Die Reihenfolge der Outputs ist entscheidend, da jeder Rückgabewert einem bestimmten UI-Element zugeordnet wird.

\n

Zum Starten der App verwenden wir:

\n
demo.launch(debug=True, share=True)
\n

Das Flag debug=True zeigt Laufzeitfehler während der Entwicklung, share=True erzeugt einen öffentlichen Link – so kannst du die App ohne Deployment teilen.

\n

Fazit

\n

In diesem Tutorial habe ich gezeigt, wie du mit NVIDIA Nemotron 3 Super, OpenRouter und Gradio einen Analyzer für Codebasen aufbaust. 

\n

Statt auf Chunks, Embeddings und Retrieval zu setzen, klont die App ein öffentliches GitHub-Repo, filtert und packt die Codebasis in einen einzigen Prompt und lässt das Modell über das gesamte Repository auf einmal schlussfolgern.

\n

Dieser Whole-Context-Ansatz eignet sich für Aufgaben, die einen globalen Blick benötigen – etwa Architektur-Erklärung, Onboarding, Testlücken-Analyse, Verbesserungspotenziale und Refactor-Planung.

\n

Von hier aus könntest du das Projekt erweitern, zum Beispiel:

\n
    \n
  • Einen „Preview Fit“-Button hinzufügen, der die Tokenzahl vor der Analyse schätzt,
  • \n
  • Teilfehlerbehandlung für Full Review ergänzen,
  • \n
  • Downloadbare Markdown-Reports unterstützen,
  • \n
  • Oder Repo-Prompt-Packing gegen eine RAG-Baseline auf demselben Repository vergleichen.
  • \n

FAQs

Verwendet dieses Tutorial das volle 1M-Token-Kontextfenster von Nemotron 3 Super?

Nein. NVIDIA dokumentiert bis zu 1M Tokens für offizielle Nemotron 3 Super Deployments, aber OpenRouters kostenloser gehosteter Endpunkt bietet aktuell 262.144 Kontext – für dieses Tutorial völlig ausreichend.

Warum läuft Nemotron 3 Super nicht lokal?

Nemotron 3 Super ist ein großes Reasoning-Modell (etwa 85 GB via Ollama) und für ein Tutorial wie dieses über einen gehosteten Endpunkt deutlich einfacher zu nutzen.

Welche Nemotron-Aufgabe ist für diese App am zuverlässigsten?

In der Praxis sind die stärksten Aufgaben:

\n
    \n
  • Architecture Overview
  • \n
  • Improvement Opportunities
  • \n
  • Testing Gaps
  • \n
  • Onboarding Guide
  • \n
  • Refactor Roadmap
  • \n
\n

Diese profitieren am meisten davon, das gesamte Repository auf einmal zu sehen.

\n

Sollte ich standardmäßig Full Review verwenden?

Für kleinere öffentliche Repos: ja. Für größere Repos ist eine Einzelaufgabe meist stabiler.


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.

Themen
Künstliche Intelligenz
Große Sprachmodelle

Top DataCamp Courses

Kurs

Agentische Systeme mit LangChain entwerfen

3 Std.
14.8K
Lerne die grundlegenden Komponenten von LangChain-Agenten kennen und entwickle eigene Chat-Agenten.
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

OLS-Regression: Die wichtigsten Ideen erklärt

Gewinne Vertrauen in die OLS-Regression, indem du ihre theoretischen Grundlagen beherrschst. Erforsche, wie du einfache Implementierungen in Excel, R und Python durchführst.
Josef Waples's photo

Josef Waples

8 Min.

Tutorial

Python-Arrays

Python-Arrays mit Code-Beispielen. Lerne noch heute, wie du mit Python NumPy Arrays erstellen und ausdrucken kannst!
DataCamp Team's photo

DataCamp Team

3 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 Switch Case Statement: Ein Leitfaden für Anfänger

Erforsche Pythons match-case: eine Anleitung zu seiner Syntax, Anwendungen in Data Science und ML sowie eine vergleichende Analyse mit dem traditionellen switch-case.
Matt Crabtree's photo

Matt Crabtree

5 Min.

Mehr AnzeigenMehr Anzeigen