mvest soll nicht an einen einzelnen LLM-Anbieter gebunden sein. Gründe: unterschiedliche Stärken pro Modell, deutliche Kostendifferenzen, Provider-Ausfälle, und für den Critic-Agent ist Provider-Diversität ein zentraler Verifikations-Hebel (siehe ADR-0003).
Pydantic AI (ADR-0004) bringt Multi-Provider-Support nativ mit. Dennoch braucht es eine eigene Konfigurations-Schicht, damit pro Agent eindeutig festgelegt werden kann, welches Modell zum Einsatz kommt, und damit Wechsel ohne Code-Änderung möglich sind.
Wir nutzen Pydantic AIs eingebaute Provider-Abstraktion als technische Basis und ergänzen sie um eine eigene Konfigurations-Schicht:
| Provider | Beispiel-Modelle | Use-Case-Profil |
|---|---|---|
| Anthropic | Opus 4.7, Sonnet 4.6, Haiku 4.5 | Strategy (Opus), allgemeine Agents (Sonnet), günstige Screener (Haiku). Sehr starkes Tool-Use. |
| OpenAI | GPT-5, GPT-4o, GPT-4o-mini | Critic-Agent (anderer Provider als Strategy), Research mit JSON-Mode, günstige Screener (mini). |
| Gemini Pro, Gemini Flash | Critic-Alternative, lange Kontexte für Filings, günstige Massen-Inferenz mit Flash. | |
| Groq | Llama-3.x, Mixtral | Sehr schnelle Inference für Screener / einfache Filterungen. |
| Mistral | Mistral Large, Codestral | Europäischer Provider, optional. |
| Ollama (lokal) | Llama-3.x, Qwen | Dev-Loop ohne API-Cost, optional. Nicht für Produktiv-Empfehlungen. |
Modellauswahl pro Agent erfolgt in einer Konfigurationsdatei (config/agents.yaml),
nicht im Code:
# config/agents.yaml
agents:
screener:
primary: { provider: anthropic, model: claude-haiku-4-5 }
fallback: { provider: openai, model: gpt-4o-mini }
research:
primary: { provider: anthropic, model: claude-sonnet-4-6 }
fallback: { provider: openai, model: gpt-4o }
analyst:
primary: { provider: anthropic, model: claude-sonnet-4-6 }
fallback: { provider: google, model: gemini-pro }
strategy:
primary: { provider: anthropic, model: claude-opus-4-7 }
fallback: { provider: openai, model: gpt-5 }
critic:
# MUSS anderer Provider als strategy sein
primary: { provider: openai, model: gpt-5 }
fallback: { provider: google, model: gemini-pro }
constraint: provider_must_differ_from: strategy
risk_assessment:
primary: { provider: anthropic, model: claude-haiku-4-5 }
fallback: { provider: openai, model: gpt-4o-mini }
Ein AgentModelFactory liest die Konfiguration und erzeugt
Pydantic-AI-Model-Instanzen:
class AgentModelSpec(BaseModel):
provider: ProviderId # enum: anthropic | openai | google | ...
model: str # modell-id-string (provider-spezifisch)
class AgentConfig(BaseModel):
primary: AgentModelSpec
fallback: AgentModelSpec | None = None
constraint: dict | None = None # z. B. provider_must_differ_from
def build_model(spec: AgentModelSpec) -> pydantic_ai.Model:
match spec.provider:
case ProviderId.ANTHROPIC: return AnthropicModel(spec.model)
case ProviderId.OPENAI: return OpenAIModel(spec.model)
...
fallback definiert ist → Retry mit Fallback-Model; used_model wird im Decision-Log markiert. Falls kein Fallback definiert → Agent-Step schlägt fehl, Coordinator entscheidet (Skip mit Markierung vs. Abort des Runs).
Beim App-Start wird die Konfiguration validiert. Constraint
provider_must_differ_from: strategy beim Critic prüft, dass Provider von Critic
und Strategy verschieden sind — andernfalls Start-Abbruch mit klarer Fehlermeldung.
.env (gitignored)..env.example listet alle möglichen Keys mit leerem Wert.config/pricing.yaml gepflegt (manuell aktualisierbar, fern von Code-Logik).used_model tracken, Critic-Constraint muss auch im Fallback gelten). Mitigation: Constraint-Check gilt für Primary und Fallback.