Blog
Decision Models: Die KI, die nicht schreibt, sondern entscheidet
Ein Modell, das kein Wort schreibt, sondern aus einer festen Liste wählt und jeder Option eine Wahrscheinlichkeit gibt. Wir binden Jev über die TypeSafe-Bridge an Symfony AI an, triagieren Tickets gegen einen Threshold im Code und sichern Tool-Calls mit einem Workflow-Guard ab.
Aktualisiert symfony ai decision-models 13 Min. Lesezeit
Von David Gorges
Geschäftsführer
Ein Modell, das nicht schreibt
Ein Sprachmodell erzeugt Text, Token für Token. Ein Decision Model erzeugt gar nichts. Es bekommt einen State, also Text oder JSON, dazu eine oder mehrere Fragen mit festgelegten Antwortmöglichkeiten, und liefert für jede Frage genau eine Antwort aus dieser Liste, mit einer Wahrscheinlichkeit pro Option. Kein Fließtext, nichts zu parsen, keine Antwort außerhalb des Schemas.
TypeSafe AI, das Unternehmen des früheren OpenAI-Forschers Diogo Almeida, hat die Kategorie mit Jev benannt und drei Fragetypen definiert, die andere Implementierungen übernommen haben:
- Choice: eine Option aus bis zu 255 benannten Möglichkeiten. Zurück kommen die Wahl, eine Wahrscheinlichkeit je Option und eine Konfidenz.
- Score: eine Einstufung auf einer geordneten Skala, etwa von "ruhig" bis "sehr wütend", mit Wahrscheinlichkeit je Stufe.
- Noul: ja oder nein, als Wahrscheinlichkeit zwischen 0 und 1.
Mehrere Fragen zum selben State gehen in einem einzigen Aufruf raus. Weil dabei keine Ausgabetoken entstehen, können Dienste wie Jev nur die Eingabe abrechnen; prüf trotzdem das Preismodell des jeweiligen Anbieters. Jev antwortet laut TypeSafe in 70 bis 500 Millisekunden. Laya, ein offenes Modell mit 322 Millionen Parametern, braucht auf einer Laptop-GPU 21 Millisekunden pro Entscheidung. Für einen normalen Aufruf von GPT-6 Luna werden etwa 1,6 Sekunden genannt. Hardware und Aufgaben unterscheiden sich, die Zahlen sind also kein Vergleich unter gleichen Bedingungen.
TypeSafe nennt Jev ein "System One Model", nach Daniel Kahnemans schnellem, intuitivem Denken. Der Name geht auf den Ökonomen William Stanley Jevons zurück. Sein Paradoxon: Wird eine Ressource effizienter und damit billiger nutzbar, steigt ihr Verbrauch, statt zu sinken. Darauf wettet das Unternehmen. Wenn eine Entscheidung nur Bruchteile eines Cents kostet, treffen Anwendungen sehr viel mehr davon.
Warum es bis 2026 gedauert hat
Die technische Idee ist nicht neu. Ein Klassifikator auf BERT-Basis macht seit Jahren nichts anderes, und wer ein Sprachmodell nach einem Buchstaben fragt und die Logprobs liest, bekommt ebenfalls eine Verteilung über Optionen. Neu ist die Kombination aus drei Dingen.
Erstens die Zero-Shot-Fähigkeit. Du formulierst Frage und Optionen zur Laufzeit in natürlicher Sprache und brauchst kein Trainingsset pro Aufgabe.
Zweitens die Kalibrierung. TypeSafe trainiert Jev mit einem Verfahren namens Reinforcement Learning for Calibrated Decisions. Die Wahrscheinlichkeiten werden gegen tatsächliche Ergebnisse optimiert, nicht gegen menschliche Vorlieben. Gibt das Modell einer Antwort die Wahrscheinlichkeit 0,9, soll sie bei vergleichbaren Vorhersagen in etwa neun von zehn Fällen stimmen.
Drittens der Bedarf. Structured Output hat uns schema-gültigen Text gebracht, aber keine brauchbare Konfidenz. LLM-as-a-Judge ist in Evals langsam und teuer. Und Agenten vervielfachen die kleinen Urteile pro Aufgabe: Welches Tool? Welches Modell? Ist die Aufgabe erledigt? Darf das ausgeführt werden?
Der Markt hat die Lücke sofort bestätigt. Innerhalb von 24 Stunden nutzten laut Vercel knapp 13 Prozent der zahlenden Teams im AI Gateway das Modell, doppelt so viele wie bei der GPT-5.6-Familie und sechsmal so viele wie bei Fable 5.1. TypeSafe selbst spricht von bis zu 194-fach schnelleren und 445-fach günstigeren Workflows und räumt ein, dass diese Zahlen am oberen Rand dessen liegen, was in der Praxis zu erwarten ist.
Innerhalb von vier Wochen haben AWS, Cloudflare, OpenAI und Microsoft eigene Decision Models veröffentlicht. Diese Geschichte und die Fragen der Skeptiker stehen in einem eigenen Beitrag: Decision Models: Wer was gestartet hat und was offen ist. Hier schreiben wir Code.
Symfony AI: die TypeSafe-Bridge
Symfony AI unterstützt Jev seit Version 0.14.0. Die Bridge lebt im Paket symfony/ai-type-safe-platform und ist bewusst eine Platform-Bridge, kein Agent-Modell: Jev nimmt keine Messages entgegen, ruft keine Tools auf und streamt nicht. Statt Messages schickst du eine Evaluation aus State und Fragen.
composer require symfony/ai-bundle symfony/ai-type-safe-platform
ai:
platform:
typesafe:
api_key: '%env(TYPESAFE_API_KEY)%'
Das kanonische Beispiel ist die Ticket-Triage im Support, weil sie alle drei Fragetypen auf einem State zeigt. Die Zahlen in den Kommentaren sind beispielhaft:
<?php
declare(strict_types=1);
use Symfony\AI\Platform\Bridge\TypeSafe\Evaluation;
use Symfony\AI\Platform\Bridge\TypeSafe\Factory;
use Symfony\AI\Platform\Bridge\TypeSafe\Question\ChoiceQuestion;
use Symfony\AI\Platform\Bridge\TypeSafe\Question\NoulQuestion;
use Symfony\AI\Platform\Bridge\TypeSafe\Question\ScoreQuestion;
$platform = Factory::createPlatform($_ENV['TYPESAFE_API_KEY']);
$ticket = <<<'TXT'
Betreff: Rechnung doppelt abgebucht
Ihr habt mir den September zweimal berechnet. Ich brauche das Geld
bis Freitag zurück, sonst platzt meine Miete. Das ist das zweite Mal.
TXT;
$result = $platform->invoke('jev-1.13.0', new Evaluation($ticket, [
'department' => new ChoiceQuestion(
'Welches Team soll das Ticket bearbeiten?',
[
'billing' => 'Zahlungen, Rechnungen, Abonnements',
'technical' => 'Fehler, Integrationen, Ausfälle',
'sales' => 'Preise, Verträge, Angebote',
],
),
'frustration' => new ScoreQuestion(
'Wie verärgert ist die Person?',
[
'Ruhig, nennt nur Fakten',
'Verärgert, aber sachlich',
'Sehr wütend, deutliche Sprache',
],
),
'is_urgent' => new NoulQuestion(
'Braucht die Nachricht binnen eines Tages eine Antwort?',
),
]));
$answers = $result->asObject();
$department = $answers->getChoice('department');
// "billing"
$department->getChoice();
// ['billing' => 0.97, 'technical' => 0.02, 'sales' => 0.01]
$department->getProbabilities();
// nahe 2: die mit Wahrscheinlichkeiten gewichtete Stufe
$answers->getScore('frustration')->getScore();
// 0.93
$answers->getNoul('is_urgent')->getProbability();
// true: expliziter Threshold statt Standardwert 0.5
$answers->getNoul('is_urgent')->isTrue(0.9);
Welches Team soll das Ticket bearbeiten?
Braucht die Nachricht binnen eines Tages eine Antwort?
erreicht Threshold 0,90Der Threshold gehört in den Code
Der Teil, der ein Decision Model von einem weiteren LLM-Aufruf unterscheidet, steht nicht im Prompt, sondern danach: ein Threshold in getestetem Code. Erreicht die Wahrscheinlichkeit der gewählten Option den Threshold, läuft das Ticket automatisch durch; darunter entscheidet ein Mensch. Beides wird mit Version der Fragen und Modell-ID geloggt, damit sich jede Entscheidung Monate später auf den genauen Wortlaut der Fragen zurückführen lässt.
Jeder Threshold ist ein Trade-off, und den verstehst du am besten, wenn du ihn ausprobierst. Unten liegen 50 ausgedachte Entscheidungen, jede dort eingezeichnet, wo die Wahrscheinlichkeit liegt, die das Modell ihr gegeben hat. Verschieb den Threshold und sieh zu, wie viele Fälle automatisch durchlaufen, wie viele falsche Antworten dabei mitrutschen und wie viele bei einem Menschen landen.
Threshold 0,80
- Automatisch
- 86 % 43 / 50
- Falsch, aber automatisch
- 4 / 43
- An einen Menschen
- 14 % 7 / 50
In einem Messenger-Handler sieht das so aus. Drei Dinge sind hier Absicht; wähl eine Erläuterung unter dem Code, um zu sehen, wo sie stehen:
<?php
declare(strict_types=1);
namespace App\Support\Triage;
use Psr\Log\LoggerInterface;
use Symfony\AI\Platform\Bridge\TypeSafe\Evaluation;
use Symfony\AI\Platform\Bridge\TypeSafe\Question\ChoiceQuestion;
use Symfony\AI\Platform\Bridge\TypeSafe\Question\NoulQuestion;
use Symfony\AI\Platform\PlatformInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
final readonly class TriageTicketHandler
{
/** Darunter entscheidet ein Mensch. Am eigenen Testset prüfen. */
private const float MIN_PROBABILITY = 0.85;
private const float URGENT_THRESHOLD = 0.9;
private const string MODEL = 'jev-1.13.0';
private const string QUESTIONS_VERSION = 'triage-v3';
public function __construct(
#[Autowire(service: 'ai.platform.typesafe')]
private PlatformInterface $platform,
private TicketRepository $tickets,
private TicketRouter $router,
private LoggerInterface $logger,
) {
}
public function __invoke(TriageTicket $message): void
{
$ticket = $this->tickets->get($message->ticketId);
$evaluation = new Evaluation($ticket->body(), [
'department' => new ChoiceQuestion(
'Welches Team soll das Ticket bearbeiten?',
Department::asOptions(),
),
'is_urgent' => new NoulQuestion(
'Braucht die Nachricht binnen eines Tages eine Antwort?',
),
]);
$answers = $this->platform
->invoke(self::MODEL, $evaluation)
->asObject();
$department = $answers->getChoice('department');
$choice = $department->getChoice();
$probabilities = $department->getProbabilities();
$urgent = $answers->getNoul('is_urgent');
$this->logger->info('ticket.triage', [
'ticket' => $ticket->id(),
'questions' => self::QUESTIONS_VERSION,
'model' => self::MODEL,
'department' => $probabilities,
'urgent' => $urgent->getProbability(),
]);
if ($probabilities[$choice] < self::MIN_PROBABILITY) {
$this->router->toHumanReview($ticket, 'Unter dem Threshold');
return;
}
$this->router->assign(
$ticket,
Department::from($choice),
urgent: $urgent->isTrue(self::URGENT_THRESHOLD),
);
}
}
-
Thresholds mit Namen Zeilen 18–22, 63–67
Die Thresholds sind Konstanten mit Namen, und die Prüfung nimmt die Wahrscheinlichkeit der gewählten Option: dieselbe Zahl wie der Regler oben und die Kalibrierung unten. Ein Wert, der im Codebeispiel beruhigend aussieht, gehört erst in Produktion, wenn deine eigenen Fälle ihn tragen.
-
Eine Liste der Optionen Zeilen 40, 71
Die Optionen der Choice-Frage werden aus demselben Enum erzeugt, das der Code später mit
Department::from()wieder einliest. Es gibt keine zweite Liste, die auseinanderlaufen könnte. -
Die ganze Verteilung loggen Zeilen 55–61
Das Log hält die vollständige Verteilung fest, nicht nur die Wahl. Erst daran lässt sich später prüfen, ob das Modell kalibriert bleibt, wenn sich deine Tickets ändern.
Tool-Freigabe als Workflow-Guard
Der zweite Einsatzort ist die Freigabe. Ein Tool-Call ist der Moment, in dem ein Agent handelt. Bevor er das darf, stellen wir einem Decision Model zwei Fragen zum geplanten Call. Strands Agents nennt das eine Intervention vor dem Tool-Call. In Symfony ist der natürliche Ort dafür ein Guard der Workflow-Komponente, weil die Transition dann für jeden Aufrufer gleich geschützt ist:
<?php
declare(strict_types=1);
namespace App\Agent;
use Symfony\AI\Platform\Bridge\TypeSafe\Evaluation;
use Symfony\AI\Platform\Bridge\TypeSafe\Question\NoulQuestion;
use Symfony\AI\Platform\PlatformInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\Workflow\Event\GuardEvent;
#[AsEventListener(event: 'workflow.agent_task.guard.execute_tool')]
final readonly class ToolCallGuard
{
private const float MIN_SAFE = 0.95;
private const float MIN_ON_TASK = 0.9;
private const string MODEL = 'jev-1.13.0';
public function __construct(
#[Autowire(service: 'ai.platform.typesafe')]
private PlatformInterface $platform,
) {
}
public function __invoke(GuardEvent $event): void
{
/** @var AgentTask $task */
$task = $event->getSubject();
// Der State ist JSON: Aufgabe, geplanter Tool-Call, Argumente.
$evaluation = new Evaluation($task->proposedToolCall(), [
'is_safe' => new NoulQuestion(
'Ist dieser Aufruf ohne unumkehrbare Folgen'
.' für Daten oder Dritte?',
),
'on_task' => new NoulQuestion(
'Dient dieser Aufruf der gestellten Aufgabe?',
),
]);
$answers = $this->platform
->invoke(self::MODEL, $evaluation)
->asObject();
$safe = $answers->getNoul('is_safe')->getProbability();
$onTask = $answers->getNoul('on_task')->getProbability();
if ($safe < self::MIN_SAFE || $onTask < self::MIN_ON_TASK) {
$event->setBlocked(true, sprintf(
'Decision Model: safe=%.2f, on_task=%.2f',
$safe,
$onTask,
));
}
}
}
Eine Warteschlange für Freigaben entsteht dadurch noch nicht. Dein Workflow muss den Pfad zur menschlichen Prüfung vorsehen, und der Tool-Call darf erst nach einer erlaubten Transition stattfinden.
Prüf die Anbieterkompatibilität vor einem Wechsel. Die hier dokumentierte Bridge richtet sich an TypeSafe und liefert die Modell-IDs jev-latest, jev-preview und jev-1.13.0 mit. Liquid d1, Cloudflares Clef, Jeeves, Laya und Strands Decider bieten ähnliche Schnittstellen; daraus folgt noch nicht, dass die Bridge unverändert mit ihnen funktioniert. Wer was anbietet, steht in unserem Überblick über Decision Models.
Auch OpenAIs eigener Endpunkt /v1/decisions braucht eine passende Integration. Ähnliche Fragetypen machen ihn nicht zum direkten Ersatz für die TypeSafe-Bridge.
Weil der Aufruf in den Beispielen hinter PlatformInterface steckt, hast du eine geeignete Schnittstelle für einen anderen Anbieter oder ein selbst gehostetes Modell. Prüf dessen API-Verhalten und kalibrier die Thresholds neu, bevor du wechselst.
Wo wir Decision Models einsetzen würden
Unser wichtigstes Architekturmuster heißt seit Längerem "Das Modell schlägt vor, der Code entscheidet". Bisher kostete der Vorschlag einen vollen LLM-Aufruf mit Structured Output. Ein Decision Model kann diese kleinen Urteile beschleunigen und Wahrscheinlichkeiten liefern, die der Code gegen getestete Thresholds prüft. Wo unsere Agenten das Sprachmodell heute fragen, ob etwas relevant, sicher oder erledigt ist, würden wir ein Decision Model dagegen testen; das Schreiben bleibt beim LLM.
- Dokumenteneingang: Jede Datei bekommt beim Eintreffen ihre Pipeline und einen Risikowert, bevor ein LLM auch nur ein Feld extrahiert. Routinefälle brauchen das LLM gar nicht.
- Freigaben: Unsere Agenten liefern Entwürfe, Menschen geben frei. Ein kalibriertes "Braucht es einen Menschen?" könnte die Warteschlange dort verkürzen, wo automatische Bearbeitung bereits erlaubt ist. Welcher Anteil sicher automatisch durchlaufen kann, hängt von Aufgabe und Testergebnissen ab.
- Interviews und Umfragen bewerten: Score-Fragen gegen eine Rubrik liefern Stufe und Verteilung. Ob sie den bisherigen generativen Evaluator ersetzen können, muss für den jeweiligen Einsatz geprüft werden.
- Evals: LLM-as-a-Judge in der Testsuite durch ein Decision Model ersetzen, damit die Evals bei jedem Deploy laufen können.
- Modell-Routing: Vor jedem generativen Aufruf entscheidet ein kleines Modell, ob das günstige oder das große Modell dran ist. Bei ausreichendem Volumen kann das die Rechnung deutlich senken.
Die Regel dahinter ist einfach: Decision Model für das Ja oder Nein, Sprachmodell für die Prosa, getesteter Code für die Konsequenzen.
Was du jetzt tun kannst
-
Ein Testset bauen.
Starte mit 200 anonymisierten Fällen aus deinem Eingang oder deiner Triage, mit der richtigen Antwort, und erweitere das Set um seltene und kostspielige Fehler. Ohne das sind Thresholds geraten.
-
Kalibrierung messen, nicht nur Accuracy.
Teil die Entscheidungen nach der Wahrscheinlichkeit der gewählten Option in Buckets und vergleich die Accuracy jedes Buckets mit dieser Wahrscheinlichkeit: Liegt dein 0,9-Bucket nur sieben von zehn Mal richtig, ist dein Threshold zu optimistisch. Wieg das Ergebnis gegen Fehlerkosten und Freigabeanforderungen ab, bevor du etwas automatisierst.
-
Den Aufruf hinter eine Schnittstelle legen.
Das Ökosystem ist jung, und die hier beschriebenen Preise sind noch nicht gefestigt. Was heute Jev ist, kann im Dezember Strands Decider auf eigener Hardware sein.
-
Über personenbezogene Daten lokal entscheiden.
Laya läuft auf einer CPU, Strands Decider auf einem MacBook. Gehostete Modelle bleiben für alles, was das Haus verlassen darf.
-
Die Verteilung loggen.
Nicht nur die Wahl, sondern alle Wahrscheinlichkeiten, dazu Version der Fragen und Modell-ID. Daran erkennst du Drift, bevor es Kunden tun.
TypeSafe hat sein Modell nach dem Jevons-Paradoxon benannt, weil billigere Urteile zu sehr viel mehr automatisierten Urteilen führen werden. Deshalb sind der Threshold, der Guard und das Log keine Kür, sondern der Teil, der aus einer Demo ein System macht, das du verantworten kannst.
David Gorges ist Geschäftsführer von Dekkode in Hamburg; wir bauen Symfony-Anwendungen und KI-Funktionen. Wenn du ein Decision Model hinter einen Threshold gelegt hast, erzähl uns, wie es gelaufen ist: schreib uns. Den nächsten Beitrag bekommst du über den RSS-Feed des Blogs.
Quellen
15 Quellen
-
[AI Bundle][Platform] Add TypeSafe bridge
symfony/ai PR #2549
-
symfony/ai Releases
GitHub
-
TypeSafe Dokumentation
TypeSafe
-
TypeSafe-Konfidenzsemantik
TypeSafe
-
Jev (AI model)
Wikipedia
-
Laya
brainfunctioncollapse.com
-
Introducing Strands Decider
Strands Labs (AWS) · 1. Oktober 2026
-
Introducing Clef: our open-source decision models
Cloudflare · 1. Oktober 2026
-
Decision models
Liquid AI
-
PostHog Jeeves
GitHub
-
OpenAI Decisions API
OpenAI · offizieller Leitfaden, geprüft am 7. Oktober 2026
-
Typesafe AI raises $870M at $7.5B
Hacker News · Erfahrungsberichte, keine Messung
Aktualisiert am 10. Oktober 2026.