ToolCallingAgent gegen CodeAgent bei lokalen Qwen2.5-14B-Modellen
ToolCallingAgent gegen CodeAgent in smolagents auf einem M4-Mac: Geschwindigkeit und Routinggenauigkeit mit lokalen Qwen2.5 14B-Instruct- und Coder-Modellen bei verschiedenen Temperaturen.
Quellen: ToolCallingAgent vs. CodeAgent: Which Performs Better on a Local LLM?
Die Einrichtung lokaler Agenten macht Schwachstellen schnell sichtbar. Der hier verwendete Rechner war ein Standard-Apple M4 Mac mit 16 GB einheitlicher Speicher, ohne Cloud-Inferenzfunktionen und ohne zusätzliches VRAM als Ersatz – daher kann bereits ein mittlerer Kontextumfang zum Swapping führen. Zwei Agententypen aus smolagents, nämlich ToolCallingAgent und CodeAgent, wurden jeweils gegen zwei lokal bereitgestellte Modelle getestet: qwen2.5-coder:14b und qwen2.5:14b-instruct. Beide Modelle haben eine Größe von 14 B und eine gemeinsame Basistruktur und wurden auf demselben Rechner ausgeführt.
- Qwen2.5:14b-instruct ist das Allzweckmodell der 14B-Reihe, das anhand umfangreicher Anweisungs- und Dialogdaten optimiert wurde, sodass es Befehle gut befolgt.
- qwen2.5-coder:14b verwendet denselben Kern, wurde jedoch zusätzlich mit großen Mengen an Codedaten vorgetrainiert und anschließend darauf ausgerichtet feingetuned.
Angesichts eines so begrenzten Hardware-Budgets wurde von keiner der beiden Konfigurationen eine herausragende Leistung erwartet. Die interessantere Frage war, an welcher Stelle jede Konfiguration versagt, und ob die Wahl einer anderen Art von Agent diese Grenzen verringern könnte.
Die Konfiguration
Bei einer Temperaturabtastung bei 0, 0,1, 0,3 und 0,6 wurden beide Agententypen mit einer einzigen Aufgabe getestet: Die fünf besten Pizzerien in Seattle finden, die Wartezeiten dort überprüfen und nur jene empfehlen, die unter dem Schwellenwert des Kunden liegen. Die Funktionen, die die Wartezeiten berechnen und mit dem Schwellenwert vergleichen, sind deterministisch.
@tool
def get_wait_time(restaurant_name: str) -> int:
"""Current wait in minutes for a restaurant. Deterministic, not a live lookup."""
@tool
def restaurants_in_wait_limit(wait_time: int, wait_threshold: int) -> bool:
"""True if the wait is within the customer's threshold."""
Das deterministische Design ist beabsichtigt. Eine Echtzeitabfrage würde es einem Modell ermöglichen, eine plausible Wartezeit zu schätzen und weiterzumachen. Ein erzwungener Funktionsaufruf prüft das Wesentliche: ob der Agent auf das Tool vertraut anstatt einen Wert selbst zu erfinden, und ob dies auch dann noch gilt, wenn sich die Temperatur oder der Agenttyp ändert.
Drei Aspekte wurden überwacht: die Latenz, ob eine Ausführung abgeschlossen wurde, sowie die Routungsgenauigkeit, definiert als der Anteil der gesamten Toolkette, der bei jedem Aufruf korrekt ausgeführt wird. Betrachten Sie die Zahlen als Probe, da es sich hier nicht um einen formalen Benchmark handelt.
Wie sich die beiden Agenten unterscheiden
Mit ToolCallingAgent erzeugt jeder Schritt einen einzigen JSON-Funktionsaufruf. Das Modell liefert die Argumente, das Framework führt das Tool aus, und anschließend beginnt der nächste Schritt. Fünf Restaurants und zwei Funktionen ergeben insgesamt zehn separate Aufrufe, wobei jeder davon eine neue Gelegenheit darstellt, die strukturierte Ausgabe zu verfälschen.
CodeAgent wählt einen anderen Ansatz und erzeugt einen Python-Codeausschnitt, der in einem Schritt ausgeführt wird. Eine Schleife kann jedes Restaurant abdecken und beide darin enthaltenen Funktionen aufrufen. Eine korrekte Schleife ist für alle Fälle in diesem Ausführungsschritt korrekt, sodass die Entscheidung nur einmal statt zehn Mal getroffen wird.
Ergebnisse
Auf Qwen2.5:14b-instruct
Sowohl die Agenten leisteten gute Arbeit. CodeAgent hielt bei jeder Temperatur eine Routing-Genauigkeit von 1,00, während ToolCallingAgent zwischen 0,95 und 1,00 lag. In Bezug auf die Korrektheit zeigte sich kein nennenswerter Unterschied.
Die Geschwindigkeit zeigte ein anderes Bild. ToolCallingAgent arbeitete etwa doppelt so langsam – etwa fünf Minuten pro Ausführung gegenüber etwa zweieinhalb Minuten für CodeAgent. Der Grund liegt in der Struktur: ToolCallingAgent muss bei jedem Schritt eine neue Modellaufrufkosten tragen, indem er den Gesprächskontext erneut sendet und jedes Mal von vorne generiert. CodeAgent hingegen zahlt nur einmal. Auf einem lokalen Rechner, bei dem jede Generierung ohnehin langsam ist, kann dieser Aufwand nicht ignoriert werden.
Auf Qwen2.5-coder:14b
Hier trennten sich die beiden Agenten stark voneinander. ToolCallingAgent erreichte bei keiner Temperatur eine Routinggenauigkeit von 50 Prozent und blieb zwischen 0,40 und 0,50. Er arbeitete zwar schneller als beim Instruct-Modell, doch das war kein gutes Zeichen. Er war schnell, weil er aufgab – sobald der Kontext verloren ging, hörte er auf, Aufrufe zu senden, und beendete die Arbeit früher, obwohl weniger erledigt wurde.
CodeAgent gewann mit einer Routiergenauigkeit von 1,00 haushoch und erreichte damit die besten Werte. Das ist zu erwarten, da es das Programmiermodell um Python bittet und niemals um strukturiertes JSON.
Was man daraus ableiten kann
Der Vorteil von CodeAgent bei Schleifen ist real. Im instruct-Modell, bei dem beide Agenten bei der Routierung 1,00 erreichten, machte das Bündeln der Aufgaben CodeAgent doppelt so schnell wie ToolCallingAgent.
Qwen2.5:14b-instruct wirkt wie ein zuverlässiger Allrounder, der sich sowohl mit strukturierten JSON-Aufrufen als auch mit einfachem Code wohlfühlt. Qwen2.5-coder:14b eignet sich besser für anspruchsvolle Programmieraufgaben, da es unzuverlässig sein kann bei der Erstellung strukturierter JSON-Daten sowie bei der Aufrechterhaltung dieser Qualität über mehrere Aufrufe hinweg.
Eine offene Frage
Es bleibt eine Nachfrage offen. Zwei Faktoren unterscheiden die Agenten: CodeAgent gruppiert seine Arbeit in ein einziges Programm und verwendet Python anstelle strukturierter JSON-Daten. Ein nächstes Experiment könnte diese Unterschiede aufzeigen, indem CodeAgent strukturierte Toolaufrufe ausstößt, während er weiterhin in Batch-Modus ausgeführt wird. Das Ergebnis würde zeigen, ob der Vorteil hauptsächlich von Schleifen oder davon herrührt, dass Modelle Code leichter erzeugen können als eine lange Reihe von JSON-Aufrufen.