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.
Inhalt
Tool Calling mit NCA: KI Agents im eigenen PHP Stack
Never Code Alone arbeitet täglich mit Symfony AI. Wir nutzen Tool Calling intensiv und wissen deshalb, wo der Weg vom ersten AsTool Attribut bis zum belastbaren Feature wirklich lang wird. Ein Tool ist am Ende ein ganz normaler Service, der getestet, statisch geprüft und sauber injiziert gehört. Dazu kennen wir das Ökosystem drumherum aus der Praxis: Symfony AI Mate, das PHP Framework Neuron AI und Symfony selbst seit über 15 Jahren.
Wir liefern schnell. Das Setup kommt fertig: Docker für eine reproduzierbare Umgebung, ein Proxy wie LiteLLM davor für Budgets, Routing und Verbrauch pro Team, dazu CI/CD Pipelines mit PHPStan, Psalm und PHPUnit, die jedes Tool bei jedem Commit prüfen. So bekommt ein bestehendes Projekt zuverlässige AI Features auf Enterprise Level, ohne dass jemand monatelang evaluiert.
Damit das im Team hält, kommt die Schulung dazu. Wir zeigen an eurer eigenen Codebase, wie ein Tool geschnitten, beschrieben, validiert und getestet wird, und übergeben den Betrieb an eure Entwickler. Rund um das Thema helfen wir beim Vibe Coding Consulting, beim Weg vom Prototyp zur Produktion, beim Codebase Audit für KI generierten Code und beim Aufbau DSGVO konformer self hosted KI mit Ollama.
Zuverlässige AI Features im Projekt: Docker, Proxy, Pipeline und Schulung
Finde das passende Angebot für dein Projekt
Anfrage-Konfiguration
Starten Sie Ihre Anfrage
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.
Anfrage-Konfiguration
Worauf liegt dein Fokus?
Wähle die Expertise, die dein Projekt jetzt am dringendsten benötigt.
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?
| Baustein | Wofür gedacht | Beispiel aus Symfony AI |
|---|---|---|
| Tool Calling | Das Modell ruft eigene PHP Methoden auf und entscheidet selbst wann | WeatherTool im Cookbook, MovieSearch in der Demo |
| Structured Output | Die Antwort kommt als typsicheres PHP Objekt zurück | Der movies Agent liefert Antwort und Filmliste strukturiert |
| RAG mit Store | Passende Dokumente werden per Vektorsuche in den Kontext geholt | SimilaritySearch über den indexierten Symfony Blog |
| Memory | Fakten und Verlauf wandern in den System Prompt | Der blog Agent nutzt eine eigene Memory Klasse |
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.
Easing patterns like retrieval augmented generation and tool calling
NCA 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.
NCA 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.
Was ist Tool Calling in Symfony AI 2026?
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.
Welches Paket brauche ich für Tool Calling 2026?
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.
Wie sicher ist Tool Calling 2026 im Produktivbetrieb?
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.
Funktioniert Tool Calling 2026 auch mit lokalen Modellen?
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.
Was kostet Tool Calling 2026 an Tokens?
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.
Wie beschreibe ich ein Tool richtig?
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.
Kann eine Klasse mehrere Tools enthalten?
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.
Was passiert, wenn ein Tool eine Exception wirft?
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.
Wie teste ich Tools ohne API Kosten?
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.
Wie hindere ich den Agent daran, sich Antworten auszudenken?
Ü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.
Kann ein Agent einen anderen Agent als Tool nutzen?
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.
Wie unterscheidet sich Tool Calling von MCP?
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. Beides lässt sich kombinieren, weil MCP Server im Agent wieder als Tools auftauchen.