Zum Inhalt springen

Implementieren Sie eine LLM-Tool-Calling-Schleife in Jitterbit Studio

Einführung

Single-Round-Funktionsaufrufe (behandelt unter Leiten Sie LLM-Responses an Studio-Operationen mithilfe von Funktionsaufrufen weiter) verarbeiten pro Request eine Toolauswahl: das Modell wählt eine Funktion, eine Studio-Operation führt diese aus, und die Interaktion endet. Viele agentenbasierte Workflows erfordern mehr als einen Toolaufruf, um eine einzelne Benutzeranfrage zu beantworten: Das Modell muss möglicherweise einen CRM-Datensatz abfragen, dann Kontoinformationen abrufen und anschließend eine Zusammenfassung mit beiden Ergebnissen erstellen.

Eine Tool-Calling-Schleife verarbeitet dies, indem jedes Tool-Ergebnis als tool-Rolle-Nachricht an das LLM zurückgegeben und das Modell mit der erweiterten Konversation erneut aufgerufen wird. Die Schleife wiederholt sich, bis das Modell eine Klartext-Response ohne Tool-Aufrufe erzeugt oder eine maximale Iterationslimit erreicht ist.

Diese Anleitung baut auf:

Designmuster

Die Schleife fügt zwei Schritte zum Single-Round-Funktionsaufrufs-Muster hinzu: das Anfügen des Tool-Ergebnisses zum Messages-Array und das erneute Aufrufen des LLM. Die Schleife wiederholt sich, solange die Modell-Response ein tool_calls-Array enthält.

%%{init: {"themeVariables": {"lineColor": "#6b7280", "edgeLabelBackground": "#f5f5f5"}}}%% flowchart LR A["Script
Build initial request
(base messages + tools)"] --> B["HTTP v2
LLM call"] B --> C{"tool_calls
in response?"} C -->|Yes| D["Script
Extract function
name and ID"] D --> E["Dispatcher
Run tool
operation"] E --> F["Script
Append tool result
rebuild request"] F --> B C -->|No| G["Script
Extract final
response"]

Fünf globale Variablen führen den Status über Iterationen hinweg:

Variable Zweck
InAndOut Der aktuelle LLM-Request-Body (vor jedem Aufruf aktualisiert).
non_tool_messages_json Die Basis-Nachrichten (Systemaufforderung und Benutzernachricht) als JSON-Array-String. Bleibt über alle Iterationen hinweg konstant.
tools_messages Die kumulierten Tool-Austausch-Nachrichten aus allen vorherigen Runden als JSON-Array-String. Wächst mit jeder Iteration.
tools_resp Die neueste LLM-Response, die eine tool_calls-Auswahl enthielt. Wird verwendet, um die Assistent-Nachricht zur Akkumulation zu extrahieren.
call_llm_again Wird auf true gesetzt, wenn ein Tool-Aufruf läuft; wird auf false gesetzt, wenn das Modell eine endgültige Response zurückgibt.

Teil 1: Initialisieren Sie die Schleifenvariablen

Vor dem ersten LLM-Aufruf erstellen Sie die Basis-Nachrichten und den initialen Request-Body und setzen alle Schleifenstatus-Variablen zurück. Fügen Sie einen Script-Schritt am Anfang der Operation hinzu:

// Escape and build the base messages as a JSON array string
systemMsg = "{\"role\":\"system\",\"content\":\""
    + Replace($systemPrompt, "\"", "\\\"") + "\"}";
userMsg   = "{\"role\":\"user\",\"content\":\""
    + Replace($userMessage, "\"", "\\\"") + "\"}";

$non_tool_messages_json = "[" + systemMsg + "," + userMsg + "]";

// Build the full initial request body
$InAndOut = "{\"model\":\"gpt-4o\","
    + "\"messages\":[" + systemMsg + "," + userMsg + "],"
    + "\"tools\":" + $toolsJson + ","
    + "\"tool_choice\":\"auto\"}";

// Initialize loop state
$tools_messages = "";
$tools_resp     = "";
$call_llm_again = false;
$loop_count     = 0;

toolsJson ist eine Projektvariable, die das serialisierte Tool-Schema-Array enthält. Für das Tool-Definitionsformat und die Konstruktion der Schemas siehe Leiten Sie LLM-Responses an Studio-Operationen mithilfe von Funktionsaufrufen weiter.

Hinweis

Speichern Sie die Basis-Nachrichten in non_tool_messages_json, bevor die Schleife beginnt. Jede Iteration kombiniert diese Basis-Nachrichten mit den kumulierten Tool-Austausche, um das vollständige Messages-Array zu rekonstruieren. Aktualisieren Sie non_tool_messages_json nicht innerhalb der Schleife.

Teil 2: Analysieren Sie die Tool-Call-Response

Verketten Sie eine LLM-Anrufe-Operation (HTTP v2 POST zum OpenAI-Chat-Completions-Endpoint mit InAndOut als Request-Body). Für Verbindungs- und Authentifizierungseinrichtung siehe Rufen Sie eine REST-API mit dem HTTP-v2-Connector auf.

Bei Erfolg fügen Sie einen Script-Schritt hinzu, um die Response zu lesen und die Schleifenkontrollvariablen zu setzen:

// Check whether the model selected a tool
toolCallsVal = GetJSONString($jitterbit.response, "/choices/0/message/tool_calls");
hasToolCalls  = (toolCallsVal != "null" && Length(toolCallsVal) > 2);

If(hasToolCalls,
    $tool_call_id       = TrimChars(GetJSONString($jitterbit.response,
                              "/choices/0/message/tool_calls/0/id"), "\"");
    $function_name      = TrimChars(GetJSONString($jitterbit.response,
                              "/choices/0/message/tool_calls/0/function/name"), "\"");
    $function_arguments = GetJSONString($jitterbit.response,
                              "/choices/0/message/tool_calls/0/function/arguments");
    $tools_resp         = $jitterbit.response;
    $call_llm_again     = true;
,
    $call_llm_again = false;
    $final_response = TrimChars(GetJSONString($jitterbit.response,
                          "/choices/0/message/content"), "\"");
);

tool_call_id ist ein eindeutiger Identifier, den die API jedem Tool-Aufruf zuweist. Er muss in der Tool-Ergebnis-Nachricht in Teil 4 genau wie erhalten zurückgegeben werden, oder die API lehnt den Request ab.

Hinweis

tool_calls fehlt in der Response, wenn das Modell Klartext zurückgibt. Der GetJSONString-Aufruf gibt in diesem Fall "null" zurück, und die Längenkontrolle > 2 unterscheidet korrekt ein gefülltes Array (das mindestens [...] mit einer Länge von 3 oder mehr ist) von einem fehlenden oder leeren Wert.

Behandlung mehrerer Tool-Aufrufe

Das obige Skript liest tool_calls/0, den ersten Tool-Aufruf in der Antwort. Ein LLM kann mehrere Tool-Aufrufe in einer einzelnen Antwort zurückgeben (parallele Tool-Aufrufe), und alle Aufrufe nach dem ersten werden ignoriert. Um jeden Aufruf zu verarbeiten, führen Sie einen der folgenden Schritte durch:

  • Deaktivieren Sie parallele Tool-Aufrufe in der LLM-Anfrage, damit das Modell höchstens einen Aufruf pro Antwort zurückgibt. Für die OpenAI- und Azure OpenAI-Chat-Completion-APIs setzen Sie parallel_tool_calls im Anfragebody auf false, der in Teil 1 erstellt wird.
  • Durchlaufen Sie das Array tool_calls, senden Sie jeden Tool und fügen Sie vor dem nächsten LLM-Aufruf pro Eintrag eine tool-Resultatmeldung hinzu. Verwenden Sie GetJSONString mit einem inkrementierenden Index (z. B. /choices/0/message/tool_calls/1/id), um jeden zusätzlichen Aufruf zu lesen. Die LLM-API erfordert ein entsprechendes tool-Resultat für jede tool_call_id in der Assistentenmeldung, die einmal versendet wird.

Teil 3: Senden an den Tool-Vorgang

Verwenden Sie eine Case-Anweisung, um basierend auf function_name an den korrekten Tool-Vorgang zu senden. Folgen Sie dabei dem gleichen Muster wie unter LLM-Antworten mithilfe von Function Calling an Studio-Vorgänge weiterleiten. Jeder Zielvorgang führt die angeforderte Funktion aus und setzt function_resp auf die Ergebniszeichenfolge.

function_arguments enthält die Parameterauswahl des Modells als JSON-Zeichenfolge. Analysieren Sie diese mit JSONParser in jedem Zielvorgang, um einzelne Argumentwerte zu extrahieren.

Teil 4: Append-Tool-Resultat und Anfrage neu erstellen

Nachdem der Tool-Vorgang function_resp setzt, erstellen Sie das aktualisierte Messages-Array für den nächsten LLM-Aufruf. Dieser Schritt erfordert komplexe JSON-Manipulation: Analysieren Sie die kumulierten Meldungen, fügen Sie neue Einträge an und serialisieren Sie das Ergebnis neu. Implementieren Sie dies als JavaScript-Skriptschritt:

var tool_resp      = JSON.parse($tools_resp);
var prev_tool_msgs = $tools_messages ? JSON.parse($tools_messages) : [];
var base_msgs      = JSON.parse($non_tool_messages_json);

// The assistant message that selected the tool (must precede the tool result)
var assistant_msg = tool_resp.choices[0].message;

// The tool result returned by the Studio operation
var tool_result_msg = {
    "role": "tool",
    "tool_call_id": $tool_call_id,
    "content": String($function_resp)
};

// Accumulate all tool exchanges: assistant selection followed by tool result
prev_tool_msgs.push(assistant_msg);
prev_tool_msgs.push(tool_result_msg);

// Rebuild the full request with base messages + all accumulated tool exchanges
var req = JSON.parse($InAndOut);
req["messages"] = base_msgs.concat(prev_tool_msgs);
$tools_messages = JSON.stringify(prev_tool_msgs);
$InAndOut = JSON.stringify(req);

Um JavaScript in einem Skriptschritt zu verwenden, setzen Sie die Skriptsprache im Script-Editor auf JavaScript, bevor Sie Inhalte hinzufügen.

Hinweis

Die Assistentenmeldung (tool_resp.choices[0].message) muss unmittelbar vor ihrer entsprechenden tool-Resultatmeldung im Array stehen. Die OpenAI-API erfordert, dass die tool_calls-Auswahl und das entsprechende tool-Resultat nebeneinander und in der gleichen Reihenfolge wie die Aufrufe erfolgt sind. Eine nicht übereinstimmende oder fehlende tool_call_id verursacht einen 400-Fehler.

Tipp

Wenn der Tool-Vorgang strukturierte Daten zurückgibt (z. B. ein JSON-Objekt), serialisieren Sie diese in eine Zeichenfolge, bevor Sie sie function_resp zuweisen. Das Inhaltsfeld content der tool-Rollenmeldung muss eine Zeichenfolge sein.

Teil 5: Loop steuern

Schließen Sie den LLM-Aufruf, den Dispatcher und die Anfrageneuerstellung in einen Jitterbit-Script-Controller mithilfe einer While-Schleife ein. Platzieren Sie diesen Controller am Anfang der Vorgangskette:

maxIterations = 5;

// First LLM call (always runs before the loop)
RunOperation("<TAG>operation:LLM Call</TAG>");

// Loop until the model returns a final response or the limit is reached
while($call_llm_again == true && $loop_count < maxIterations,
    $loop_count = $loop_count + 1;
    RunOperation("<TAG>operation:Tool Dispatcher</TAG>");
    RunScript("<TAG>script:Append Tool Result</TAG>");
    RunOperation("<TAG>operation:LLM Call</TAG>");
);

If($loop_count >= maxIterations && $call_llm_again == true,
    WriteToOperationLog("Tool-calling loop reached " + maxIterations
        + " iterations without a final response. Last function: " + $function_name)
);

Der Vorgang LLM Call verwendet InAndOut als Anfragebody und führt den Parser für Teil 2 bei Erfolg aus. Der Vorgang Tool Dispatcher führt die Teil 3-Case-Anweisung aus. Append Tool Result ist das Teil 4-JavaScript-Skript, hier als benannte Skriptkomponente mit RunScript referenziert.

Durch das Setzen eines maximalen Iterationslimits werden Endlosschleifen vermieden, wenn ein Tool konsistent einen Fehler zurückgibt und das Modell darauf reagiert, indem es denselben Tool erneut anfordert.

Tipp

Beginnen Sie mit einem Limit von 5 Iterationen. Die meisten Workflows werden in einer oder zwei Runden aufgelöst. Wenn Sie das Limit konsistent erreichen, deutet dies auf ein Problem mit dem Prompt-Design oder dem Tool-Resultat hin, nicht auf einen Bedarf für ein höheres Limit.

Alternative: Jitterbit-Script-Meldungserstellung

Wenn Sie JavaScript vermeiden möchten, kann die Meldungsansammlung in Teil 4 vollständig in Jitterbit Script mithilfe von Stringverkettung implementiert werden. Der HR Agent implementiert diesen Ansatz, wobei gpt.registeredTools als Projektvariable die serialisierten Tool-Schemas hält (entsprechend toolsJson in diesem Leitfaden), wobei call_llm_again die Schleife steuert.

Der Jitterbit-Script-Ansatz erstellt die Assistent- und Tool-Ergebnismeldungen, indem JSON-Strings direkt verkettet werden, anstatt JSON.parse und JSON.stringify zu verwenden. Das akkumulierte Array wird verwaltet, indem die Assistentenmeldung aus tools_resp mit GetJSONString extrahiert und das Tool-Ergebnis als formatierter String angefügt wird:

// Extract the full assistant message object from the previous response
assistantMsgJson = GetJSONString($tools_resp, "/choices/0/message");

// Build the tool result message
escapedResp = Replace($function_resp, "\"", "\\\"");
toolResultJson = "{\"role\":\"tool\",\"tool_call_id\":\""
    + $tool_call_id + "\",\"content\":\"" + escapedResp + "\"}";

// Accumulate: start a new array or append to the existing one
If(Length($tools_messages) == 0,
    $tools_messages = "[" + assistantMsgJson + "," + toolResultJson + "]"
,
    $tools_messages = Left($tools_messages, Length($tools_messages) - 1)
        + "," + assistantMsgJson + "," + toolResultJson + "]"
);

// Rebuild the request by replacing the messages field
// (omitted: requires parsing and replacing the messages array in $InAndOut)

Das vollständige Ersetzen des Felds messages in InAndOut erfordert das Ersetzen des vorhandenen Array-Werts innerhalb des JSON-Strings, was fehleranfällig ist, wenn der Nachrichteninhalt Sonderzeichen enthält. Verwende den JavaScript-Ansatz in Teil 4, wenn möglich. Reserviere den Jitterbit-Script-Ansatz für Projekte, in denen JavaScript nicht verfügbar ist.

Integration überprüfen

  1. Stelle die Controller-Operation bereit und führe sie aus, indem du eine Benutzermeldung verwendest, die genau einen Tool-Aufruf erfordert. Bestätige in den Operationsprotokollen, dass loop_count auf 1 erhöht wird und dass die Tool-Operation ausgeführt wurde und ein Ergebnis zurückgegeben hat.

  2. Bestätige in den Protokollen, dass der zweite LLM-Aufruf das erweiterte Messages-Array empfangen hat (protokolliere InAndOut mit WriteToOperationLog vor dem Aufruf des LLM) und dass er eine einfache Textantwort ohne tool_calls zurückgegeben hat.

  3. Sende eine Benutzermeldung, die zwei aufeinanderfolgende Tool-Aufrufe erfordert (z. B. einen Kontakt nachschlagen und dann ein Ticket für diesen Kontakt erstellen). Bestätige, dass loop_count 2 erreicht, und überprüfe anschließend die akkumulierten Meldungen. tools_messages ist ein serialisierter JSON-Array-String, daher protokolliere ihn nach Abschluss der Schleife, um seinen Inhalt zu sehen:

    WriteToOperationLog("tools_messages: " + $tools_messages);
    

    Bestätige in der protokollierten Zeichenfolge, dass vier Objekte vorhanden sind, die zwischen "role":"assistant" und "role":"tool" wechseln (zwei von jedem): Jede Runde fügt die Assistentenmeldung, die das Tool ausgewählt hat, gefolgt von dessen tool-Ergebnis ein. Die tool_call_id jeder tool-Meldung sollte mit der id in der vorherigen tool_calls-Angabe der Assistentenmeldung übereinstimmen.

  4. Sende eine Meldung, die keinen Tool-Aufruf erfordert (z. B. eine allgemeine Frage, die das Modell aus seinem eigenen Wissen beantworten kann). Bestätige, dass die Schleife nicht ausgeführt wird (der ursprüngliche LLM-Aufruf gibt keine tool_calls zurück, call_llm_again bleibt false und loop_count bleibt 0).

  5. Falls die API einen 400-Fehler zurückgibt, der auf eine ungültige tool_call_id hinweist, protokolliere den Wert von tool_call_id und vergleiche ihn mit dem Feld id in choices[0].message.tool_calls[0] aus der vorherigen LLM-Antwort. Eine Nichtübereinstimmung bedeutet normalerweise, dass tools_resp aktualisiert wurde, bevor die ID extrahiert wurde.

  6. Falls die Schleife das Iterationslimit erreicht, protokolliere InAndOut zu Beginn jeder Runde, um die akkumulierten Meldungen zu überprüfen. Ein wiederholter Tool-Aufruf für dieselbe Funktion mit denselben Argumenten deutet darauf hin, dass die Tool-Operation ein Fehlerergebnis zurückgibt, das das Modell erneut versucht, oder dass die Tool-Beschreibung nicht der Benutzeranfrage entspricht.