ADR-0004: Agent-Framework

Status
Proposed
Datum
2026-05-15
Decider
Malte (mit Claude)
Verwandt
ADR-0003 (Architektur), ADR-0005 (Provider), ADR-0006 (Tools)
Tags
framework, agents, dependency

Kontext

ADR-0003 legt eine Multi-Agent-DAG fest mit typisierten Inputs/Outputs, Tool-Use, Provider-Diversität und einer Coordinator-Orchestrierung. Wir brauchen ein Framework, das diese Anforderungen abdeckt, ohne große Mengen an unbenötigter Komplexität mitzubringen.

Entscheidungstreiber

Betrachtete Optionen

Option A — Pydantic AI

Agent-Framework vom Pydantic-Team. Type-safe, Multi-Provider built-in (Anthropic, OpenAI, Google, Groq, Mistral, Ollama …), MCP-Support, async-first.

Modelle sind über einen einheitlichen Model-Typ austauschbar (pro Agent-Instanz konfigurierbar).

Outputs werden direkt gegen Pydantic-Schemas validiert — passt zu unserem Verifikations-Modell aus ADR-0003.

Tools werden als typisierte Python-Funktionen registriert; MCP-Server lassen sich einbinden.

Sehr schlanke API, keine DSL, kein Graph-Compiler — Workflow ist normaler Python-Code.

Dependency-Injection für Per-Run-Context (passt zum Coordinator-Pattern).

Jüngeres Projekt als LangChain, aber von einem etablierten Team (Pydantic) und in aktiver Entwicklung.

Kein eingebauter Graph-Compiler — DAG-Orchestrierung ist Python-Code, nicht deklarativ.

Option B — LangGraph (auf LangChain)

State-Machine-/Graph-Compiler für Multi-Agent-Workflows.

Mächtige Graph-Primitiven (Conditional Edges, Cycles, Human-in-the-Loop).

Großes Ökosystem.

Bringt LangChain als transitive Dependency — viel Code, viele Abstraktionen, häufige API-Brüche.

Type-Safety eingeschränkt; viele Schnittstellen sind dict-basiert.

Für unsere DAG-Komplexität (lineare Pipeline mit zwei parallelen Forks) Overkill.

Option C — CrewAI

Multi-Agent-Framework mit Rollen-/Team-Metapher.

Niedrige Lernkurve, ansprechende Konzepte (Crew, Tasks, Agents).

Strukturiert Workflows als „Tasks", Steuerung über Roles — weniger explizit als ein DAG. Schwerer mappbar auf unsere Verifikations-Schichten.

Provider-Abstraktion weniger sauber als bei Pydantic AI.

Option D — Eigene leichte Orchestrierung über Anthropic + OpenAI SDKs

Direkter Aufruf der Provider-SDKs, eigene Abstraktion drüber.

Maximale Kontrolle, keine Abhängigkeit.

Wir bauen Multi-Provider-Adapter, Tool-Use-Normalisierung, MCP-Anbindung und Pydantic-Schema-Routing selbst.

Das ist nicht-trivialer Aufwand und genau das, was Pydantic AI bereits sauber implementiert hat.

Eigener Code muss gepflegt werden — kein Free-Updates wenn neue Provider/Modelle dazukommen.

Entscheidung

Wir wählen Option A: Pydantic AI als Agent-Framework.

Konkrete Nutzung

Beispiel-Skizze (Pseudo-Code)

research_agent = Agent(
    model=research_model,           # konfigurierbar pro Agent
    deps_type=RunDeps,
    result_type=ResearchReport,
    system_prompt=RESEARCH_PROMPT,
    tools=[tavily_search, tavily_extract, sec_edgar_filing],
)

analyst_agent = Agent(
    model=analyst_model,
    deps_type=RunDeps,
    result_type=QuantSnapshot,
    tools=[get_eod_prices, compute_indicators, get_fundamentals],
)

# Coordinator (deterministische Python-Logik, kein LLM)
async def coordinate(watchlist: Watchlist, trigger: TriggerType) -> RunSummary:
    shortlist = await screener_agent.run(...)
    research, quant = await asyncio.gather(
        research_agent.run(shortlist.tickers, deps=deps),
        analyst_agent.run(shortlist.tickers, deps=deps),
    )
    strategy = await strategy_agent.run(research=research.data, quant=quant.data, deps=deps)
    critic, risk = await asyncio.gather(
        critic_agent.run(strategy.data, deps=deps),
        risk_agent.run(strategy.data, deps=deps),
    )
    return persist_run_summary(...)

Konsequenzen

Positiv

Negativ / Trade-offs