Aufbau zuverlässiger Datenanalyse-Agenten: Lehren aus dem KDD Cup

Lehren aus dem KDD Cup zeigen, dass zuverlässige Datenanalyse-Agenten von rigoroser Evaluation, robuster Tool-Orchestrierung und einem sorgfältigen Umgang mit mehrdeutigen realen Daten abhängen. Wettbewerbsartige Benchmarks decken Fehlermodi frühzeitig auf und fördern iterative Verfeinerung, transparente Argumentation und Validierungsschleifen, die Prototypen in verlässliche Analyseassistenten verwandeln.

Vorlesen ist in diesem Browser nicht verfügbar
Aufbau zuverlässiger Datenanalyse-Agenten: Lehren aus dem KDD Cup

Tags

Kurze Zusammenfassung

Lehren aus dem KDD Cup zeigen, dass zuverlässige Datenanalyse-Agenten von rigoroser Evaluation, robuster Tool-Orchestrierung und einem sorgfältigen Umgang mit mehrdeutigen realen Daten abhängen. Wettbewerbsartige Benchmarks decken Fehlermodi frühzeitig auf und fördern iterative Verfeinerung, transparente Argumentation und Validierungsschleifen, die Prototypen in verlässliche Analyseassistenten verwandeln.

Zuverlässige Datenanalyse-Agenten bauen: Lehren aus dem KDD Cup

Datenanalyse-Agenten befinden sich im aktuellen KI-Schub in einer unbequemen Position. Ein Chatbot, der einen plausiblen Absatz erzeugt, hat Erfolg, wenn der Absatz nützlich ist. Ein Analyse-Agent, der eine plausible Zahl erzeugt, hat nur dann Erfolg, wenn die Zahl korrekt ist – und Korrektheit in der Datenarbeit ist keine Frage des Stils. Sie ist eine Frage von Joins, Filtern, Typen, NULL-Zulässigkeit und Zeitzonen.

Der KDD Cup, die seit Langem bestehende Data-Mining-Wettbewerbsreihe der SIGKDD-Community, ist ein nützlicher Rahmen für dieses Problem. Wettbewerbsaufgaben sind auf ganz spezifische Weise adversarial: Sie geben Ihnen einen Datensatz, den Sie nicht entworfen haben, eine Frage, die Sie nicht geschrieben haben, und eine Bewertungsfunktion, über die Sie nicht verhandeln können. Der NVIDIA AI Blog-Beitrag „Building Reliable Data Analytics Agents: Lessons from the KDD Cup“ nutzt dieses Setting, um zu untersuchen, was nötig ist, damit einem Agenten echte analytische Arbeit anvertraut werden kann, statt dass er sie nur zu erledigen scheint.

Dieser Artikel greift dasselbe Thema auf – Zuverlässigkeit als zentrales Engineering-Problem – und macht daraus eine Bauanleitung: was zu installieren ist, wie die Pipeline zu strukturieren ist und wie sie zu testen ist. Eine Klarstellung vorab: Die Quelle ist ein technischer Erfahrungsbericht eines Anbieters, und die folgenden Implementierungsmuster sind allgemeine, reproduzierbare Praxis, die von diesem Thema motiviert ist. Sie sind keine Aussagen über interne Details eines bestimmten dort beschriebenen Systems.

Warum Analyse-Agenten anders scheitern

Die meisten Agentenfehler in dialogorientierten Umgebungen sind ärgerlich. Die meisten Agentenfehler in analytischen Umgebungen sind still. Ein Agent, der einen Spaltennamen halluziniert, bekommt meist einen Fehler. Ein Agent, der zwei Tabellen über den falschen Schlüssel verknüpft, oft nicht. Er liefert eine Zahl zurück, die Zahl sieht vernünftig aus, und die Zahl ist falsch.

Drei Fehlermodi dominieren:

Schema-Drift und Halluzination. Das Modell nimmt eine Spalte namens user_id an, während die Tabelle uid hat. Ohne Introspektion füllt das Modell Lücken mit plausiblen Namen.

Semantisch falsche, aber syntaktisch gültige Abfragen. Ein Inner Join verwirft stillschweigend Zeilen, die ein Left Join behalten hätte. Eine WHERE-Klausel auf einer Spalte, die NULL-Werte zulässt, entfernt Datensätze, die der Analyst erwartet hätte zu sehen.

Nicht-Reproduzierbarkeit. Dieselbe Frage zweimal gestellt, erzeugt zwei verschiedene Abfragen und zwei verschiedene Antworten. Es gibt kein Artefakt zum Auditieren.

Jede der folgenden Praktiken existiert, um eine dieser Lücken zu schließen. Die vereinende Idee ist einfach: Behandeln Sie den Agenten als Pipeline mit verifizierbaren Stufen, nicht als Prompt, der Antworten ausgibt.

Anforderungen

Um mitzumachen, benötigen Sie:

  • Python 3.11 oder neuer.
  • Eine Unix-ähnliche Shell (macOS, Linux oder WSL). Die Befehle setzen bash oder zsh voraus.
  • Zugriff auf ein beliebiges instruktionsbefolgendes LLM über eine HTTP-API oder einen lokalen Server. Die folgenden Beispiele sind anbieterneutral und verweisen auf eine Modellkennung, die Sie angeben.
  • Genug RAM, um Ihr Arbeitsdataset zu halten, oder eine Datenbank-Engine, die auf die Festplatte auslagern kann. Für das Gerüst selbst ist keine GPU erforderlich.

Der Stack ist bewusst langweilig: Pandas für In-Memory-Daten, DuckDB für SQL über Dateien ohne Server, Pydantic für typisierte Tool-Verträge, Tenacity für Retry-Logik und pytest für Regressionstests.

Schritt-für-Schritt-Installation

Beginnen Sie mit der Erstellung einer isolierten Umgebung, damit die Abhängigkeiten des Agenten nicht mit Ihrem System-Python kollidieren.

python3 -m venv .venv

Aktivieren Sie sie. Unter Windows verwenden Sie stattdessen .venv\Scripts\activate.

source .venv/bin/activate

Aktualisieren Sie pip, bevor Sie etwas installieren, da ältere Versionen Abhängigkeiten weniger vorhersehbar auflösen.

python -m pip install --upgrade pip

Installieren Sie die Kernabhängigkeiten. DuckDB führt analytisches SQL direkt gegen CSV- und Parquet-Dateien aus, was während der Entwicklung eine ganze Klasse von Fehlern beseitigt: „der Agent hat auf die falsche Datenbank gezeigt“.

pip install "pandas>=2.0" duckdb "pydantic>=2" pyyaml tenacity pytest openai

Erstellen Sie das Projektgerüst. Tools, Validierungsprüfungen und Evaluationen in getrennten Verzeichnissen zu halten, macht offensichtlich, wohin ein neues Stück Logik gehört.

mkdir -p analytics_agent/tools analytics_agent/checks analytics_agent/evals data artifacts
touch analytics_agent/__init__.py

Schreiben Sie eine Konfigurationsdatei, anstatt Modellnamen und Pfade fest zu codieren. Das ist die einzelne Änderung, die die Reproduzierbarkeit am meisten verbessert, weil jeder Lauf durch ein Artefakt beschrieben wird, das Sie committen können.

cat > config.yaml <<'YAML'
model:
  id: "<your-model-id>"
  temperature: 0
  max_repair_attempts: 3
data:
  source: "data/orders.csv"
  sample_rows: 20
limits:
  max_rows_returned: 5000
  statement_timeout_seconds: 30
YAML

Exportieren Sie Ihre API-Zugangsdaten als Umgebungsvariablen, damit sie niemals ins Repository gelangen. Committen Sie niemals Schlüssel in eine Konfigurationsdatei.

export ANALYTICS_AGENT_API_KEY="<your-key>"

Setzen Sie temperature in der Konfiguration auf 0. Das garantiert keine Determinismus über Anbieter hinweg, aber es entfernt eine Varianzquelle und macht Regressionen leichter zuzuordnen.

Den Agenten im tatsächlichen Schema verankern

Die erste Regel eines zuverlässigen Analyse-Agenten: Er darf niemals raten, wie die Daten aussehen. Bevor eine Frage beantwortet wird, profilieren Sie die Quelle und legen das Profil in den Prompt-Kontext.

import duckdb

con = duckdb.connect()
con.execute("CREATE VIEW t AS SELECT * FROM read_csv_auto('data/orders.csv')")

print(con.execute("DESCRIBE t").df())
print(con.execute("SUMMARIZE t").df())

DESCRIBE liefert Spaltennamen und -typen; SUMMARIZE ergänzt NULL-Anzahlen, Distinct-Anzahlen und grundlegende Verteilungen. Beides an das Modell zu übergeben kostet ein paar Hundert Tokens und beseitigt die meisten Spalten-Halluzinationsfehler.

Stichproben Sie auch echte Zeilen, aber deterministisch – ein fester Seed oder ein ORDER BY mit einem LIMIT – damit dieselbe Frage bei jedem Lauf denselben Kontext erzeugt.

Die Tool-Oberfläche einschränken

Ein Agent ist nur so zuverlässig wie die Aktionen, die er ausführen kann. Definieren Sie eine kleine, typisierte Menge von Tools und lehnen Sie alles andere ab.

from pydantic import BaseModel, Field

class DescribeTable(BaseModel):
    table: str = Field(..., description="Name of a registered view.")

class SampleRows(BaseModel):
    table: str
    limit: int = Field(20, ge=1, le=100)

class RunSQL(BaseModel):
    sql: str = Field(..., description="A read-only SELECT or WITH statement.")
    rationale: str = Field(..., description="Why this query answers the sub-question.")

Vier Tools – describe, sample, run SQL und ein Tool für die endgültige Antwort – decken überraschend viel analytische Arbeit ab. Jedes zusätzliche Tool vervielfacht den Raum der Möglichkeiten, in denen der Agent schiefgehen kann.

Ausführung sicher und deterministisch machen

Generiertes SQL muss read-only und begrenzt sein. Keyword-Filterung ist ein nützliches erstes Gate, aber seien Sie ehrlich über ihre Grenzen: Sie ist keine Sicherheitsgrenze. Kombinieren Sie sie mit einer read-only Datenbankverbindung.

FORBIDDEN = ("insert", "update", "delete", "drop", "alter", "create", "attach", "copy")

def safe_sql(sql: str) -> str:
    stripped = sql.strip().rstrip(";")
    lowered = stripped.lower()
    if not lowered.startswith(("select", "with")):
        raise ValueError("Only SELECT and WITH statements are permitted.")
    if any(kw in lowered for kw in FORBIDDEN):
        raise ValueError("Statement contains a forbidden keyword.")
    return stripped

Selbst ein SELECT kann teuer sein, also setzen Sie ein Statement-Timeout und eine harte Zeilenobergrenze. Ein Agent, der 500.000 Zeilen zurückgibt, hat die Frage nicht beantwortet; er hat das Problem stromabwärts verlagert.

Jedes Ergebnis validieren, bevor es den Nutzer erreicht

Dies ist der Schritt, den die meisten Teams überspringen, und er ist der wichtigste. Führen Sie nach jeder Abfrage programmatische Assertions auf dem Ergebnis aus. Fehler sollten eine Reparaturschleife auslösen, keine selbstsichere Antwort.

def assert_sane(df, min_rows=1, max_null_fraction=0.2, unique_key=None):
    assert len(df) >= min_rows, f"Expected >= {min_rows} rows, got {len(df)}"
    if unique_key:
        assert df[unique_key].is_unique, f"{unique_key} is not unique after the join"
    for col in df.columns:
        frac = df[col].isna().mean()
        assert frac <= max_null_fraction, f"{col} is {frac:.0%} null"

Die unique_key-Prüfung ist besonders wertvoll. Sie fängt den klassischen Many-to-Many-Join ab, der Zeilenanzahlen aufbläht und stillschweigend jede Summe in einer nachgelagerten Aggregation verdoppelt.

Umhüllen Sie die Sequenz generate-validate-repair mit einer begrenzten Retry-Schleife. Geben Sie dem Modell den exakten Assertion-Fehler und lassen Sie es überarbeiten; begrenzen Sie die Versuche, damit ein verwirrter Agent nicht endlos schleift.

from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=8),
    retry=retry_if_exception_type(ValueError),
)
def generate_and_execute(question, schema_context):
    sql = safe_sql(propose_sql(question, schema_context))
    df = con.execute(sql).df()
    assert_sane(df)
    return df

Tracing und Replay

Hängen Sie für jeden Lauf einen strukturierten Datensatz an eine JSONL-Datei an: die Frage, den Hash des Schema-Kontexts, das generierte SQL, das Validierungsergebnis, die endgültige Antwort und das Timing.

import json, time, hashlib

def log_event(path, **fields):
    fields["ts"] = time.time()
    with open(path, "a") as f:
        f.write(json.dumps(fields) + "\n")

log_event("artifacts/trace.jsonl", question=q, sql=sql, rows=len(df))

Das ist unspektakulär, und es ist der Unterschied zwischen Debuggen durch Nachdenken und Debuggen durch Raten. Wenn eine Antwort drei Wochen später angefochten wird, ist der Trace das Artefakt, das den Streit klärt.

Anwendungsbeispiele

Ein minimaler Kommandozeilen-Einstiegspunkt macht den Agenten in CI und im Terminal testbar. Halten Sie die Schnittstelle eng.

python -m analytics_agent.run \
  --config config.yaml \
  --question "What was the monthly revenue trend for the top three regions in the last year?"

Führen Sie ihn gegen eine lokale Parquet-Datei statt CSV aus, indem Sie data.source in der Konfiguration ändern – die Profiling- und Tooling-Schichten ändern sich nicht.

python -m analytics_agent.run --config config.local.yaml --question "Which customers have not ordered in 90 days?"

Für interaktive Exploration halten Sie den Profiling-Schritt warm und verwenden Sie die DuckDB-Verbindung wieder, damit wiederholte Fragen die Datei nicht erneut einlesen.

Zuverlässigkeit statt Bauchgefühl bewerten

Zuverlässigkeit ist eine messbare Eigenschaft, kein Gefühl. Erstellen Sie ein Golden Set aus zwanzig bis fünfzig Fragen mit Antworten, die Sie manuell oder durch eine unabhängige Abfrage verifiziert haben. Bewerten Sie jeden Lauf auf drei Achsen: Hat er die richtige Antwort produziert, hat er sie mit einer gültigen Abfrage produziert und hat er sie innerhalb des Kostenbudgets produziert.

pytest analytics_agent/evals -q --tb=short

Ein nützlicher Test prüft Determinismus explizit – führen Sie dieselbe Frage zweimal aus und vergleichen Sie die resultierenden DataFrames.

def test_repeatable(agent, question):
    a = agent.ask(question)
    b = agent.ask(question)
    assert a.equals(b), "Agent output is not reproducible at temperature 0"

Verfolgen Sie die Pass-Rate über die Zeit. Ein Abfall nach einer Prompt- oder Modelländerung ist ein Signal; ohne das Eval-Set ist er eine unsichtbare Regression, die Monate später als falsche Dashboard-Zahl auftaucht.

Offene Grenzen, die es wert sind, benannt zu werden

Drei Einschränkungen widersetzen sich einfachen Lösungen. Erstens: Von einem LLM generiertes SQL kann auf Ihrer Stichprobe korrekt und bei Edge Cases falsch sein, die die Stichprobe nie enthielt; daher ist die Abdeckung des Eval-Sets wichtiger als seine Größe. Zweitens: Keyword-basierte SQL-Filterung ist Defense in Depth, nicht Defense; Isolation gehört auf die Datenbankebene. Drittens: „Zuverlässig“ ist domänenrelativ – eine Pass-Rate von 95 % ist hervorragend für explorative Analyse und inakzeptabel für regulatorisches Reporting. Legen Sie den Schwellenwert fest, bevor Sie bauen, nicht danach.

Fazit

Die Lehre, die das KDD-Cup-Setting konkret macht, ist, dass Analyse-Agenten wie Data-Science-Einreichungen bewertet werden, nicht wie Chat-Antworten: gegen zurückgehaltene Fragen, ohne Teilpunkte für Selbstsicherheit. Zuverlässigkeit kommt aus dem Gerüst um das Modell – Schema-Verankerung vor der Generierung, eine schmale typisierte Tool-Oberfläche, read-only und begrenzte Ausführung, programmatische Validierung jedes Ergebnisses, begrenzte Reparaturschleifen und eine Regression-Suite, die Drift abfängt. Keine dieser Komponenten ist exotisch, und keine ist optional, wenn die Ausgabe eine Zahl ist, auf deren Grundlage jemand handeln wird. Beginnen Sie mit dem Profiling-Schritt und den Validierungs-Assertions; sie liefern den größten Zuverlässigkeitsgewinn für den geringsten Aufwand, und alles andere baut darauf auf.

Quelle: „Building Reliable Data Analytics Agents: Lessons from the KDD Cup“, NVIDIA AI Blog — https://developer.nvidia.com/blog/building-reliable-data-analytics-agents-lessons-from-the-kdd-cup

Quellen