Challenge 3.1: Tool Calling
Was wenn ein LLM nicht nur Text generieren, sondern auch Aktionen ausführen könnte — z.B. das Wetter abfragen, eine Berechnung durchführen oder eine Datenbank abfragen?
OVERVIEW
Abschnitt betitelt „OVERVIEW“Das LLM erkennt, dass es das Wetter nicht aus eigenem Wissen beantworten kann. Es generiert einen Tool Call — eine strukturierte Anfrage an eine Funktion. Die Funktion wird ausgefuehrt, das Ergebnis geht zurück ans LLM, und das LLM formuliert daraus eine natuerliche Antwort.
Ohne Tools: Das LLM kann nur reden, nicht handeln. Es hat kein aktuelles Wissen, kann nicht rechnen, nicht auf Datenbanken zugreifen. Jede Frage nach aktuellen Daten wird mit einer Halluzination oder “Das weiss ich nicht” beantwortet.
Mit Tools: Das LLM wird zum Agenten. Es erkennt, welche Funktion es braucht, ruft sie mit den richtigen Parametern auf und verarbeitet das Ergebnis. Es kann jetzt Wetter abfragen, rechnen, Dateien lesen — alles, wofuer Du ein Tool definierst.
WALKTHROUGH
Abschnitt betitelt „WALKTHROUGH“Schicht 1: Ein Tool definieren mit tool()
Abschnitt betitelt „Schicht 1: Ein Tool definieren mit tool()“Ein Tool hat drei Teile: eine Beschreibung (damit das LLM weiss, wann es das Tool nutzen soll), ein Input-Schema (damit das LLM weiss, welche Parameter es schicken muss) und eine Execute-Funktion (die die eigentliche Arbeit macht):
import { tool } from 'ai';import { z } from 'zod';
const weatherTool = tool({ description: 'Get the weather in a location', // ← Wann soll das LLM dieses Tool nutzen? inputSchema: z.object({ // ← Welche Parameter braucht es? location: z.string().describe('The city name'), // ← .describe() hilft dem LLM }), execute: async ({ location }) => ({ // ← Was passiert bei Aufruf? location, temperature: 22, condition: 'sunny', }),});Drei kritische Details:
descriptionbeeinflusst, ob das LLM das Tool waehlt. Je praeziser, desto besser..describe()an Zod-Feldern erklärt dem LLM, was der Parameter bedeutet.executeist async — Du kannst echte API-Calls, Datenbankabfragen oder beliebigen Code ausführen.
Schicht 2: Tool mit generateText nutzen
Abschnitt betitelt „Schicht 2: Tool mit generateText nutzen“Tools werden als Objekt an generateText uebergeben. Die Keys sind die Tool-Namen, die das LLM sieht:
import { generateText } from 'ai';import { anthropic } from '@ai-sdk/anthropic';
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), tools: { // ← Tools als Objekt weather: weatherTool, // ← Key = Tool-Name für das LLM }, prompt: 'Wie ist das Wetter in Berlin?',});
console.log(result.text);// → "In Berlin sind es aktuell 22 Grad und es ist sonnig."Achtung:
result.textkann leer sein, wenn das LLM nur einen Tool Call macht und danach keinen Text generiert. Das passiert, wennmaxStepsnicht gesetzt ist (Default: 1) — dann stoppt der Call nach dem ersten Tool-Ergebnis, ohne dass das LLM eine Antwort formuliert. SetzemaxSteps: 2oder hoeher, damit das LLM nach dem Tool Call noch antworten kann.
Das LLM entscheidet selbststaendig, ob es ein Tool nutzt. Bei der Frage “Wie ist das Wetter?” erkennt es, dass es weather aufrufen muss. Bei “Was ist TypeScript?” würde es direkt antworten.
Schicht 3: Tool Choice — Steuerung der Tool-Nutzung
Abschnitt betitelt „Schicht 3: Tool Choice — Steuerung der Tool-Nutzung“Mit toolChoice steuerst Du, wie das LLM mit Tools umgeht:
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), tools: { weather: weatherTool }, toolChoice: 'auto', // ← LLM entscheidet (default) prompt: 'Wie ist das Wetter in Berlin?',});| toolChoice | Verhalten |
|---|---|
'auto' | LLM entscheidet selbst ob ein Tool noetig ist (default) |
'required' | LLM MUSS mindestens ein Tool nutzen |
'none' | Keine Tools — LLM antwortet nur mit Text |
{ type: 'tool', toolName: 'weather' } | Erzwingt ein bestimmtes Tool |
Schicht 4: Tool Calls und Tool Results im Result
Abschnitt betitelt „Schicht 4: Tool Calls und Tool Results im Result“Nach der Ausfuehrung findest Du alle Tool Calls und ihre Ergebnisse im Result-Objekt:
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), tools: { weather: weatherTool }, prompt: 'Wie ist das Wetter in Berlin?',});
// Tool Calls des letzten Schrittsfor (const toolCall of result.toolCalls) { console.log('Tool:', toolCall.toolName); // → "weather" console.log('Args:', toolCall.args); // → { location: "Berlin" }}
// Tool Results des letzten Schrittsfor (const toolResult of result.toolResults) { console.log('Result:', toolResult.result); // → { location: "Berlin", temperature: 22, ... }}result.toolCalls und result.toolResults enthalten die Daten des letzten Schritts. Für Multi-Step Szenarien (kommt in Challenge 3.3) nutzt Du result.steps.
Datei: challenge-3-1.ts
Aufgabe: Baue ein Calculator-Tool mit drei Operationen (add, subtract, multiply) und nutze es mit generateText.
import { generateText, tool } from 'ai';import { anthropic } from '@ai-sdk/anthropic';import { z } from 'zod';
// TODO 1: Definiere ein Calculator-Tool// - description: Beschreibung die dem LLM erklärt, wann es den Rechner nutzen soll// - inputSchema mit:// - operation: z.enum(['add', 'subtract', 'multiply']).describe(...)// - a: z.number().describe('First number')// - b: z.number().describe('Second number')// - execute: Fuehrt die Berechnung aus und gibt das Ergebnis zurück
// TODO 2: Nutze das Tool mit generateText// - model: anthropic('claude-sonnet-4-5-20250514')// - tools: { calculator: calculatorTool }// - prompt: 'Was ist 42 mal 17?'
// TODO 3: Logge result.text, result.toolCalls und result.toolResultsCheckliste:
- Calculator-Tool mit
tool()definiert - Zod-Schema mit
operation,aundb— alle mit.describe() -
executefuehrt die richtige Operation aus (switch/case oder if/else) - Tool mit
generateTextintegriert -
result.textzeigt die natuerliche Antwort des LLM -
result.toolCallszeigt den Tool-Aufruf
Lösung anzeigen
import { generateText, tool } from 'ai';import { anthropic } from '@ai-sdk/anthropic';import { z } from 'zod';
const calculatorTool = tool({ description: 'Perform a math calculation with two numbers', inputSchema: z.object({ operation: z.enum(['add', 'subtract', 'multiply']).describe('The math operation to perform'), a: z.number().describe('The first number'), b: z.number().describe('The second number'), }), execute: async ({ operation, a, b }) => { switch (operation) { case 'add': return { operation, a, b, result: a + b }; case 'subtract': return { operation, a, b, result: a - b }; case 'multiply': return { operation, a, b, result: a * b }; } },});
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), tools: { calculator: calculatorTool }, prompt: 'Was ist 42 mal 17?',});
console.log('Antwort:', result.text);console.log('Tool Calls:', result.toolCalls);console.log('Tool Results:', result.toolResults);Erklärung: Das LLM erkennt die Rechenaufgabe, ruft calculator mit { operation: 'multiply', a: 42, b: 17 } auf, bekommt { result: 714 } zurück und formuliert daraus eine natuerliche Antwort wie “42 mal 17 ist 714.”
Ausfuehren: npx tsx challenge-3-1.ts
Erwarteter Output (ungefaehr):
Antwort: 42 mal 17 ist 714.Tool Calls: [{ toolName: 'calculator', args: { operation: 'multiply', a: 42, b: 17 } }]Tool Results: [{ result: { operation: 'multiply', a: 42, b: 17, result: 714 } }]COMBINE
Abschnitt betitelt „COMBINE“Uebung: Kombiniere ein Tool mit einem System Prompt aus Challenge 1.6. Gib dem LLM eine Rolle und ein Tool gleichzeitig.
- Definiere einen System Prompt: “Du bist ein freundlicher Mathe-Tutor. Erklaere jeden Rechenweg Schritt für Schritt.”
- Nutze das Calculator-Tool aus der TRY-Uebung
- Stelle eine Rechenaufgabe — das LLM soll das Tool nutzen UND den Rechenweg erklären
- Vergleiche: Wie ändert sich die Antwort mit vs. ohne System Prompt?
Optional Stretch Goal: Definiere ein zweites Tool (unitConverter), das Einheiten umrechnet (z.B. km in Meilen). Gib dem LLM beide Tools und stelle eine Frage, die beides erfordert: “Ich fahre 100 km — wie viele Meilen sind das und wie lange dauert es bei 60 km/h?”