Eliminierung technischer Schulden mit PHPStan, Rector PHP und PHPUnit. Über 20 Jahre Praxiserfahrung in skalierbaren Backends.
- Aktualisiert:
- Autor:
- Roland Golla
Was ist Tool Calling in Symfony AI?
Tool Calling in Symfony AI bedeutet: Das Sprachmodell ruft deine eigenen PHP Methoden auf. Du markierst eine Klasse mit dem Attribut AsTool, legst sie in eine Toolbox und übergibst diese dem Agent. Ab dann entscheidet das Modell selbst, wann es dein Werkzeug braucht, mit welchen Argumenten es aufgerufen wird und was es mit dem Ergebnis anstellt. Alle Beispiele auf dieser Seite stammen aus dem offiziellen Cookbook, dem Symfony AI Repository und der Symfony AI Demo.
composer require symfony/ai-platform symfony/ai-agent
namespace App\Tool;
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
#[AsTool('weather', 'Fetches the current weather for a given city')]
final class WeatherTool
{
/**
* @param string $city The name of the city to look up
* @param string $units "metric" or "imperial"
*/
public function __invoke(string $city, string $units = 'metric'): array
{
// Call a weather API, query a database, etc.
return [
'city' => $city,
'temperature' => '22°C',
'condition' => 'sunny',
];
}
}
Mehr Code braucht ein erstes Tool nicht. Name und Beschreibung im Attribut sagen dem Modell, was das Werkzeug tut, die Parameterbeschreibungen kommen aus den @param Einträgen im PHPDoc. Der Rückgabewert muss kein String sein: Arrays, Objekte, Skalare und DateTimeInterface wandelt der ToolResultConverter selbst um, ein eigenes json_encode ist also überflüssig.
Tool Calling mit NCA: KI Agents im eigenen PHP Stack
Gesetzliche Konformität & Inklusion. Optimierung von Performance und Conversion durch radikal nutzerzentriertes, universelles Design.
Skalierbare KI-Systeme mit echtem Code Ownership. CI/CD, Backup-Strategien und Infrastruktur, die mit deinem Team wächst.
Wie funktioniert Tool Calling in Symfony AI?
Der Ablauf ist immer gleich. Du legst deine Tools in eine Toolbox und übergibst sie dem Agent. Symfony AI schickt die Tool Beschreibungen als JSON Schema mit dem Prompt an das Modell. Braucht das Modell ein Werkzeug, antwortet es nicht mit Text, sondern mit einem Tool Call. Symfony AI ruft deine Methode auf, hängt das Ergebnis als Nachricht an und fragt das Modell erneut. Das läuft so lange, bis eine Antwort steht.
use App\Tool\WeatherTool;
use Symfony\AI\Agent\Agent;
use Symfony\AI\Agent\Toolbox\Toolbox;
use Symfony\AI\Platform\Bridge\OpenAi\Factory;
use Symfony\Component\HttpClient\HttpClient;
$platform = Factory::createPlatform($apiKey, HttpClient::create());
$toolbox = new Toolbox([new WeatherTool()]);
$agent = new Agent($platform, 'gpt-5-mini', toolbox: $toolbox);
Danach reicht eine ganz normale Nachricht. Der Agent findet die verfügbaren Tools selbst, entscheidet über den Aufruf, führt ihn aus und baut das Ergebnis in seine Antwort ein.
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;
$messages = new MessageBag(
Message::ofUser('What is the weather like in Paris right now?'),
);
$result = $agent->call($messages);
echo $result->getContent();
// "The current weather in Paris is 22°C and sunny."
Ein Detail lohnt den zweiten Blick: Der Agent kennt den Parameter maxToolCalls, der standardmäßig bei 50 liegt. Ein Modell kann Tool Calls verketten, das Ergebnis des einen führt zum nächsten Aufruf. Ohne Deckel wird daraus im schlechtesten Fall eine Endlosschleife auf deiner Kreditkarte. In der Produktion setzen wir diesen Wert bewusst niedrig und loggen jeden Ausreißer. Tool Calling funktioniert übrigens auch mit Streaming: Die Aufrufe laufen im Hintergrund weiter, während die Antwort Token für Token beim Nutzer ankommt.
Fertige Tools aus dem Symfony AI Ökosystem
Nicht jedes Werkzeug musst du selbst schreiben. Die Agent Component bringt fertige Tools mit, jedes als eigenes Composer Paket: Wikipedia, YouTube, SimilaritySearch, Brave und SerpApi. Die Installation ist ein Einzeiler.
composer require symfony/ai-wikipedia-tool
Danach landet das Tool zusammen mit deinen eigenen in derselben Toolbox. Das Modell sieht schlicht eine Liste von Werkzeugen und trifft seine Wahl.
use Symfony\AI\Agent\Bridge\Wikipedia\Wikipedia;
$toolbox = new Toolbox([
new WeatherTool(),
new Wikipedia(HttpClient::create()),
]);
Genau hier zahlt sich Zurückhaltung aus. Jedes Tool wird als JSON Schema mit jedem Aufruf mitgeschickt, und je größer die Auswahl, desto häufiger greift ein Modell daneben. Drei gut beschriebene Werkzeuge schlagen zehn halbgare.
Beispiel aus der Symfony AI Demo: die Filmsuche als Tool
Die offizielle Demo Anwendung zeigt den Alltagsfall: ein Tool, das ein eigenes Repository durchsucht. Es ist ein ganz normaler Symfony Service mit Constructor Injection. Der PHPDoc Kommentar über der Methode ist kein Beiwerk, aus ihm entsteht die Beschreibung des Parameters, an der sich das Modell orientiert.
namespace App\Movies;
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
#[AsTool(
name: 'movie_search',
description: 'Search the movie collection by title, director or cast member. Returns the matching movies, each with a "slug" you must reuse to reference a movie in your answer. Pass an empty query to list the whole collection.',
)]
final class MovieSearch
{
public function __construct(
private readonly MovieRepository $movies,
) {
}
/**
* @param string $query Search term matched against title, director and cast; pass an empty string to list all movies
*
* @return list<Movie> the matching movies
*/
public function __invoke(string $query = ''): array
{
$needle = mb_strtolower(trim($query));
$results = [];
foreach ($this->movies->findAll() as $movie) {
if ('' !== $needle && !$this->matches($movie, $needle)) {
continue;
}
$results[] = $movie;
}
return $results;
}
}
Kein Vector Store, keine Magie, nur ein Treffer im Speicher. Auffällig ist die lange Beschreibung im Attribut: Sie sagt dem Modell nicht nur, was das Tool tut, sondern auch, wie es die Ergebnisse weiterverwenden soll. Der Rückgabewert darf ein Array oder ein JsonSerializable Objekt sein, Symfony AI wandelt das selbst in JSON um. Eine Klasse kann außerdem mehrere Tools tragen: Das Wikipedia Tool aus dem Repository trägt AsTool zweimal und verweist über den Parameter method auf die Methoden search und article.
Tools im AI Bundle konfigurieren
In einer Symfony Anwendung übernimmt das AI Bundle die Verdrahtung. Du definierst deinen Agent in der Konfiguration und listest die Tools auf, die er nutzen darf. Die Toolbox baut das Bundle daraus selbst, inklusive Dependency Injection. Das ist der Auszug aus der offiziellen Demo Konfiguration:
# config/packages/ai.yaml
ai:
platform:
openai:
api_key: '%env(OPENAI_API_KEY)%'
agent:
movies:
platform: 'ai.platform.openai'
model: 'gpt-4.1'
prompt: |
You are a friendly movie expert helping users discover films from a curated collection.
To find movies you always use the 'movie_search' tool - never rely on your own memory and never
invent movies.
tools:
- 'App\Movies\MovieSearch'
blog:
platform: 'ai.platform.openai'
model: 'gpt-4.1'
tools:
- 'Symfony\AI\Agent\Bridge\SimilaritySearch\SimilaritySearch'
- service: 'clock'
name: 'clock'
description: 'Provides the current date and time.'
method: 'now'
Zwei Wege stehen nebeneinander. Eine Klasse mit AsTool Attribut wird einfach als Klassenname eingetragen. Ein bestehender Service ohne Attribut, hier die Symfony Clock, bekommt Name, Beschreibung und Methode direkt in der Konfiguration. Der System Prompt ist dabei Teil der Absicherung: Der Satz, nie auf das eigene Gedächtnis zu vertrauen und nie Filme zu erfinden, hält das Modell in den Werkzeugen. Für einzelne Aufrufe lässt sich die Auswahl zusätzlich einschränken, indem beim call nur bestimmte Tool Namen übergeben werden.
Guardrails: Argumente validieren statt vertrauen
Die Argumente eines Tool Calls kommen aus einem Sprachmodell. Sie sind damit Nutzereingaben, nicht mehr und nicht weniger. Symfony AI kennt dafür das Schema Attribut direkt am Methodenargument. Das offizielle Wetter Tool zeigt es an der Vorhersage, die nur zwischen einem und sechzehn Tagen erlaubt ist:
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
use Symfony\AI\Platform\Contract\JsonSchema\Attribute\Schema;
#[AsTool(name: 'weather_current', description: 'get current weather for a location', method: 'current')]
#[AsTool(name: 'weather_forecast', description: 'get weather forecast for a location', method: 'forecast')]
final class OpenMeteo
{
/**
* @param float $latitude the latitude of the location
* @param float $longitude the longitude of the location
*/
public function forecast(
float $latitude,
float $longitude,
#[Schema(minimum: 1, maximum: 16)]
int $days = 7,
): array {
// Abruf der Wetterdaten
}
}
Ein Punkt wird dabei gern übersehen: Das Schema allein beschreibt nur, es erzwingt nichts. Erst der ValidateToolCallArgumentsListener weist einen Aufruf mit falschen Argumenten zurück, bevor deine Methode überhaupt läuft. Im AI Bundle ist dieser Listener automatisch registriert, im Standalone Betrieb musst du ihn selbst am Event Dispatcher anmelden. Bei Backed Enums als Argumenttyp passiert die Prüfung ganz ohne Attribut, weil das Schema die erlaubten Werte bereits kennt.
Events: in die Tool Kette eingreifen
Symfony AI feuert rund um jeden Tool Call Events. Damit lässt sich die Kette steuern, ohne ein einziges Tool anzufassen. Das offizielle Beispiel bricht nach dem Wetter Tool sofort ab und gibt das Ergebnis strukturiert zurück, statt es noch einmal durch das Modell zu schicken:
$openMeteo = new OpenMeteo(http_client());
$toolbox = new Toolbox([$openMeteo], logger: logger());
$eventDispatcher = new EventDispatcher();
$agent = new Agent($platform, 'gpt-5-mini', toolbox: $toolbox, eventDispatcher: $eventDispatcher);
$eventDispatcher->addListener(ToolCallsExecuted::class, static function (ToolCallsExecuted $event): void {
foreach ($event->getToolResults() as $toolCallResult) {
if (str_starts_with($toolCallResult->getToolCall()->getName(), 'weather_')) {
$event->setResult(new ObjectResult($toolCallResult->getResult()));
}
}
});
Das zweite Event ist für die Absicherung noch wichtiger. ToolCallRequested feuert vor der Ausführung. Ein Listener kann den Aufruf dort mit deny und einer Begründung blockieren oder mit setResult ein eigenes Ergebnis unterschieben, ohne dass deine Methode je läuft. Genau das ist der Platz für eine menschliche Freigabe bei Tools, die löschen, stornieren oder Geld bewegen. Welche Tools eine Bestätigung brauchen, hinterlegst du sauber im Metadata Feld des AsTool Attributs, statt Namen im Listener zu pflegen. Für Laufzeitfehler gibt es zusätzlich die FaultTolerantToolbox, die Exceptions abfängt und dem Modell eine lesbare Fehlermeldung statt eines Absturzes liefert.
Tool Calling, Structured Output oder RAG?
Tool Calling lokal mit Ollama und DSGVO
Tool Calling ist der Punkt, an dem echte Daten ins Spiel kommen. Ein Tool liest aus der Datenbank, und das Ergebnis geht als Nachricht an das Modell. Läuft dieses Modell bei einem US Anbieter, verlässt der Inhalt der Abfrage das Haus. Genau deshalb testen wir Tool Setups zuerst gegen lokale Modelle über Ollama. Symfony AI unterstützt Ollama als Platform, der Wechsel ist eine Zeile in der Konfiguration.
Für Tool Calling reichen kleinere Modelle oft aus, weil die Aufgabe klar umrissen ist: Werkzeug wählen, Argumente füllen, Ergebnis formulieren. Wie gut ein Modell mit Werkzeugen umgeht, ist allerdings sehr unterschiedlich. Wir haben die Kandidaten in unserem Überblick zu lokalen Modellen für Tool Calling und MCP eingeordnet.
Für Unternehmen mit Compliance Anforderungen kombinieren wir das mit gehosteter Inferenz in Deutschland. Unser Infrastruktur Partner Conversis aus Duisburg betreibt die Server dafür. Wichtig bleibt die Grundregel: In der Entwicklung arbeitet der Agent gegen eine lokale Umgebung mit Testdaten, echte Daten kommen erst dazu, wenn die Inferenz im eigenen Netzwerk läuft. Mehr dazu auf unserer Seite zu Self Hosted KI für Unternehmen.
Tools testen: der Teil, den fast alle überspringen
Ein Tool ist eine gewöhnliche PHP Klasse. Es gibt also keinen Grund, es nicht zu testen. Die Methode selbst prüfst du mit einem ganz normalen Unit Test in PHPUnit: Repository mocken, Argument reinreichen, Rückgabe prüfen. Kein API Key, keine Wartezeit, keine Kosten.
Für die Ebene darüber liefert Symfony AI den MockAgent. Er beantwortet definierte Eingaben mit festen Ergebnissen und zählt dabei mit, wie oft und womit er aufgerufen wurde. So sieht das im Test des Repositories aus:
$responses = ['hello' => 'Hi there!'];
$agent = new MockAgent($responses);
$messages = new MessageBag(Message::ofUser('hello'));
$result = $agent->call($messages);
$this->assertInstanceOf(TextResult::class, $result);
$this->assertSame('Hi there!', $result->getContent());
Damit lässt sich der Service testen, der den Agent nutzt, ohne dass ein Modell antworten muss. Eine unbekannte Eingabe quittiert der MockAgent mit einer Exception, was Lücken in den Testdaten sofort sichtbar macht. Zusammen mit PHPStan in der CI/CD Pipeline bleibt ein Agent Feature genauso überprüfbar wie jeder andere Teil der Anwendung.
Tool Calling in Symfony und Sulu CMS Projekten
In gewachsenen Symfony Anwendungen steckt die interessante Logik längst in Services. Tool Calling macht daraus ohne Umbau die Fähigkeiten eines Agents. Der Weg dorthin ist kurz, weil ein Tool nichts weiter braucht als ein Attribut und eine gute Beschreibung. Die Demo zeigt das an einem Repository, das vorher schon da war.
Wer weiter gehen will, veröffentlicht Tools per MCP und macht sie damit auch außerhalb der eigenen Anwendung nutzbar. Für die Entwicklungsseite deckt Symfony AI Mate genau diesen Weg ab. NCA berät zu beiden Richtungen und betreibt selbst einen MCP Server für Sulu CMS.
Neu in Symfony AI 0.14: was sich beim Tool Calling ändert
- MapToolArguments: Ein neues Attribut erlaubt flache DTOs als Tool Argumente. Statt einer langen Parameterliste nimmt die Methode ein Objekt entgegen.
- Tool Execution Strategies: Mehrere Tool Calls lassen sich gebündelt ausführen, statt sie strikt nacheinander abzuarbeiten.
- Execution Cancellation: Ein laufender Agent lässt sich abbrechen. Wichtig bei langen Tool Ketten, die niemand mehr braucht.
- MCP Client Bridge: Der Agent spricht jetzt direkt mit entfernten MCP Servern und bindet deren Werkzeuge ein. Die Gegenseite, einen eigenen Server, baust du mit dem Symfony MCP Bundle.
- Server Tools bei Anthropic: Werkzeuge, die direkt beim Anbieter laufen, lassen sich über die Option server_tools anfordern.
Easing patterns like retrieval augmented generation and tool calling
Sofort AI Features im bestehenden PHP Legacy liefern: paralleles Symfony, saubere CI/CD Pipeline, statische Analyse und sicheres Refactoring. Euer Team übernimmt.
Mehr erfahren
Symfony AI Structured Output liefert typsichere PHP Objekte aus LLM Antworten. Beispiel: Land eingeben, Hauptstadt zurückbekommen. Mit Ollama und Symfony.
Mehr erfahren
Model Inference in Symfony AI: Mit einem invoke Aufruf jedes KI Modell ansprechen. Anbieter wechseln ohne Code Änderung, von OpenAI bis Ollama lokal.
Mehr erfahrenNCA Erfahrung mit Tool Calling
Die meisten Tool Setups scheitern nicht an der Technik, sondern an der Beschreibung. Ein knapper Name und ein vager Satz reichen nicht, das Modell benutzt das Werkzeug dann falsch, und niemand merkt es, weil trotzdem eine plausible Antwort herauskommt. Die Demo macht es richtig vor: Die Beschreibung von movie_search sagt nicht nur, was das Tool tut, sondern auch, wie das Ergebnis weiterverwendet werden soll. Wir gehen deshalb jedes Tool wie eine öffentliche API an.
Der zweite Punkt ist das Blast Radius Denken. Bevor ein schreibendes Tool live geht, klären wir, was im schlimmsten Fall passiert, wenn das Modell es zur falschen Zeit aufruft. Daraus entstehen Freigabe Listener, enge Validierung und getrennte Agents für lesende und schreibende Aufgaben.
Der dritte Punkt ist Tempo. Docker Setup, Proxy davor, Pipeline drumherum und ein Workshop im Team: Damit steht das erste belastbare AI Feature in Tagen statt in Quartalen, und euer Team kann das nächste selbst bauen. Mehr dazu in unserem Bereich Vibe Coding Best Practices und im Vibe Coding Consulting.
Roland Golla ist Entwickler aus Leidenschaft – seit über 20 Jahren. Er hat hunderte Projekte begleitet, von Legacy-Refactoring bis KI-Integration. Bei Vibe Coding verbindet er das Beste aus beiden Welten: Die Geschwindigkeit von KI-generiertem Code mit der Qualität professioneller Softwareentwicklung. Kein Bullshit, keine Agentur-Floskeln – direkte Hilfe von jemandem, der selbst täglich im Code steckt.
Häufige Fragen zu Tool Calling in Symfony AI
Die Fragen, die uns in Beratungsgesprächen zu Tool Calling immer wieder begegnen, kurz und ohne Umwege beantwortet.
Version 0.14.0 vom 25. September 2026 bringt das Attribut MapToolArguments für DTOs als Argumente, gebündelte Tool Ausführung, Abbruch laufender Agents und eine MCP Client Bridge. Dazu kommen Sicherheitsfixes: Einschränkungen der Tool Auswahl pro Aufruf werden jetzt auch während der Ausführung durchgesetzt.
Tool Calling bedeutet, dass ein Sprachmodell eigene PHP Methoden aufrufen kann. Du markierst eine Klasse mit dem Attribut AsTool und legst sie in eine Toolbox, die dem Agent übergeben wird. Symfony AI beschreibt das Werkzeug als JSON Schema, das Modell fordert den Aufruf an, das Ergebnis fließt zurück in die Konversation.
Tool Calling steckt in der Agent Component, installiert über composer require symfony/ai-platform symfony/ai-agent. In einer Symfony Anwendung kommt zusätzlich das AI Bundle dazu, das Platform, Agent und Store per Konfiguration verdrahtet und die Toolbox automatisch aus deinen Service Definitionen baut.
Es ist so sicher, wie du es baust. Argumente kommen aus einem Sprachmodell und sind damit Nutzereingaben. Validierung über das Schema Attribut plus den ValidateToolCallArgumentsListener, eine Freigabe für schreibende Tools über das Event ToolCallRequested und ein niedriger maxToolCalls Wert sind das Minimum. Nutze mindestens Version 0.14, weil dort Einschränkungen pro Aufruf erst wirklich durchgesetzt werden.
Ja. Über Ollama laufen Modelle lokal und beherrschen Tool Calling. Die Qualität schwankt allerdings deutlich stärker als bei großen Cloud Modellen, gerade bei vielen Tools gleichzeitig. Für sensible Daten ist der lokale Weg trotzdem meist der richtige.
Jedes Tool wird als JSON Schema mit jedem Aufruf mitgeschickt, und jeder Tool Call erzeugt eine zusätzliche Runde beim Modell. Viele Tools und lange Ketten treiben die Kosten schnell hoch. Wenige, klar geschnittene Tools und eine Begrenzung über maxToolCalls halten das im Rahmen. Seit 0.14 lässt sich ein laufender Agent zudem abbrechen.
Wie eine öffentliche API. Der Name sagt, was passiert, die Beschreibung sagt, wann das Werkzeug gebraucht wird, und der PHPDoc Kommentar erklärt jeden Parameter mit einem Beispiel. Vage Beschreibungen führen dazu, dass das Modell rät und trotzdem plausibel klingende Antworten liefert.
Ja. Du setzt das AsTool Attribut mehrfach auf dieselbe Klasse und verweist über den Parameter method auf verschiedene Methoden. Das Wetter Tool aus dem Repository macht genau das mit current und forecast, das Wikipedia Tool mit search und article.
MapToolArguments ist ein Attribut, das mit Version 0.14 dazugekommen ist. Es erlaubt, die Argumente eines Tools als flaches DTO entgegenzunehmen statt als lange Liste einzelner Parameter. Das hält die Signatur übersichtlich, besonders bei Werkzeugen mit vielen Eingaben.
Ohne Absicherung bricht der Agent Aufruf ab. Die FaultTolerantToolbox fängt Exceptions und gibt dem Modell eine lesbare Fehlermeldung zurück, damit es reagieren kann. Für fachliche Fehler lohnt eine eigene Exception, die das ToolExecutionExceptionInterface implementiert und die Meldung selbst formuliert.
Die Tool Klasse selbst testest du wie jeden Service mit PHPUnit, Abhängigkeiten werden gemockt. Für die Ebene darüber liefert Symfony AI den MockAgent: Er beantwortet definierte Eingaben mit festen Ergebnissen und zählt Aufrufe mit. So läuft die komplette Testsuite ohne externen Aufruf.
Über den System Prompt und über Grenzen. Eine klare Anweisung, Fragen ausschließlich mit den Tools zu beantworten und im Zweifel zu sagen, dass keine Information vorliegt, hilft deutlich. Zusätzlich lässt sich pro Aufruf einschränken, welche Tools überhaupt angeboten werden.
Ja, über den Subagent. Ein bestehender Agent wird als Werkzeug registriert und vom übergeordneten Agent aufgerufen. Das eignet sich, um Spezialwissen zu kapseln oder viele Einzeltools hinter einer fachlichen Fassade zu verstecken, damit der Hauptagent nicht in Optionen ertrinkt.
Tool Calling passiert innerhalb deiner Anwendung: Der Agent ruft PHP Methoden im selben Prozess auf. MCP ist ein Protokoll, mit dem Werkzeuge über Prozessgrenzen hinweg angeboten werden. Seit Version 0.14 bringt der Agent eine eigene MCP Client Bridge mit, entfernte MCP Server tauchen damit direkt als Tools auf.