Zum Inhalt springen

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

Einführung

Single-Round-Funktionsaufrufe (behandelt in LLM-Antworten mithilfe von Funktionsaufrufen zu Studio-Operationen weiterleiten) verarbeiten pro Anfrage eine Toolauswahl: Das Modell wählt eine Funktion aus, eine Studio-Operation führt sie aus, und die Interaktion endet. Viele agentengesteuerte Workflows erfordern mehr als einen Tool-Aufruf, um eine einzelne Benutzeranfrage zu beantworten: Das Modell muss möglicherweise einen CRM-Datensatz abfragen, dann Kontodaten abrufen und dann eine Zusammenfassung mit beiden Ergebnissen erstellen.

Eine Tool-Calling-Schleife verarbeitet dies, indem jedes Tool-Ergebnis als tool-Rollennachricht an das LLM zurückgegeben wird und das Modell erneut mit der erweiterten Konversation aufgerufen wird. Die Schleife wiederholt sich, bis das Modell eine Nur-Text-Antwort ohne Tool-Aufrufe erzeugt oder eine maximale Iterationsbegrenzung erreicht wird.

Diese Anleitung baut auf Folgendem auf:

Designmuster

Die Schleife fügt dem Single-Round-Funktionsaufrufen-Muster zwei Schritte hinzu: Anhängen des Tool-Ergebnisses an das Messages-Array und erneutes Aufrufen des LLM. Die Schleife wiederholt sich, solange die Modellantworte ein tool_calls-Array enthält.

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-Anfragekörper (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 gesammelten Tool-Austausch-Nachrichten aus allen vorherigen Runden als JSON-Array-String. Wächst mit jeder Iteration.
tools_resp Die neueste LLM-Antwort, die eine tool_calls-Auswahl enthielt. Wird verwendet, um die Assistenten-Nachricht zur Akkumulation zu extrahieren.
call_llm_again Auf true gesetzt, wenn ein Tool-Aufruf läuft; auf false gesetzt, wenn das Modell eine endgültige Antwort zurückgibt.

Teil 1: Initialisieren Sie die Schleifenvariablen

Vor dem ersten LLM-Aufruf erstellen Sie die Basis-Nachrichten und den anfänglichen Anfragekörper und setzen Sie alle Schleifenstatsvariablen zurück. Fügen Sie einen Skriptschritt 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 LLM-Antworten mithilfe von Funktionsaufrufen zu Studio-Operationen weiterleiten.

Hinweis

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

Teil 2: Analysieren Sie die Tool-Call-Antwort

Verketten Sie einen LLM-Aufruf (HTTP v2 POST zum OpenAI Chat Completions-Endpunkt mit InAndOut als Anfragekörper). Für Verbindungs- und Authentifizierungseinrichtung siehe Rufen Sie eine REST-API mithilfe des HTTP v2-Connectors auf.

Fügen Sie bei Erfolg einen Skriptschritt hinzu, um die Antwort zu lesen und die Schleifenkontrollvariablen festzulegen:

// 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 Bezeichner, den die API jedem Tool-Aufruf zuweist. Er muss in der Tool-Ergebnisnachricht in Teil 4 genau wie empfangen zurückgegeben werden, sonst lehnt die API die Anfrage ab.

Hinweis

tool_calls fehlt in der Antwort, wenn das Modell Nur-Text 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 eines der folgenden Verfahren durch:

  • Deaktivieren Sie parallele Tool-Aufrufe in der LLM-Anfrage, damit das Modell maximal einen Aufruf pro Antwort zurückgibt. Für die OpenAI- und Azure OpenAI Chat Completions APIs setzen Sie parallel_tool_calls auf false im Anfragekörper, der in Teil 1 erstellt wird.
  • Durchlaufen Sie das Array tool_calls, versenden Sie jedes Tool und fügen Sie vor dem nächsten LLM-Aufruf eine tool-Ergebnismeldung pro Eintrag an. 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 passendes tool-Ergebnis für jede tool_call_id in der Assistentenmeldung, die einmal gesendet wird.

Teil 3: Versand zur Tool-Operation

Verwenden Sie eine Case-Anweisung, um basierend auf function_name zur korrekten Tool-Operation zu versenden. Folgen Sie dabei dem gleichen Muster wie unter LLM-Antworten mithilfe von Function Calling zu Studio-Operationen weiterleiten. Jede Zieloperation 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 innerhalb jeder Zieloperation, um einzelne Argumentwerte zu extrahieren.

Teil 4: Tool-Ergebnis anfügen und Anfrage neu erstellen

Nachdem die Tool-Operation function_resp setzt, erstellen Sie das aktualisierte Nachrichtenarray für den nächsten LLM-Aufruf. Dieser Schritt erfordert komplexe JSON-Manipulation: Analysieren Sie die gesammelten Nachrichten, fügen Sie neue Einträge an und serialisieren Sie das Ergebnis erneut. 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-Ergebnismeldung im Array stehen. Die OpenAI-API erfordert, dass die tool_calls-Auswahl und das entsprechende tool-Ergebnis nebeneinander und in der gleichen Reihenfolge wie die Aufrufe stehen. Eine nicht übereinstimmende oder fehlende tool_call_id verursacht einen 400-Fehler.

Tipp

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

Teil 5: Schleife steuern

Umhüllen Sie den LLM-Aufruf, den Dispatcher und die Schritte zum Neuerstellen der Anfrage in einem Jitterbit Script-Controller mit einer While-Schleife. Platzieren Sie diesen Controller am Anfang der Operationskette:

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)
);

Die Operation LLM Call verwendet InAndOut als Anfragekörper und führt den Parser aus Teil 2 bei Erfolg aus. Die Operation Tool Dispatcher führt die Case-Anweisung aus Teil 3 aus. Append Tool Result ist das JavaScript-Skript aus Teil 4, das hier als benannte Skriptkomponente mit RunScript referenziert wird.

Das Festlegen einer maximalen Iterationsbegrenzung verhindert Endlosschleifen, wenn ein Tool konsistent einen Fehler zurückgibt und das Modell darauf reagiert, indem es das gleiche Tool erneut anfordert.

Tipp

Beginnen Sie mit einer Begrenzung von 5 Iterationen. Die meisten Workflows werden in einer oder zwei Runden gelöst. Wenn Sie die Begrenzung konsistent erreichen, deutet dies auf ein Problem mit dem Prompt-Design oder dem Tool-Ergebnis hin, nicht auf die Notwendigkeit einer höheren Begrenzung.

Alternative: Jitterbit Script-Nachrichtenerstellung

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

Der Jitterbit-Script-Ansatz erstellt die Nachrichten des Assistenten und des Tool-Ergebnisses, indem JSON-Strings direkt verkettet werden, anstatt JSON.parse und JSON.stringify zu verwenden. Das akkumulierte Array wird verwaltet, indem die Assistentennachricht 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 Feldes messages in InAndOut erfordert das Ersetzen des vorhandenen Array-Werts innerhalb der JSON-Zeichenkette, was fehleranfällig ist, wenn der Nachrichteninhalt Sonderzeichen enthält. Verwenden Sie den JavaScript-Ansatz in Teil 4, wenn möglich. Reservieren Sie den Jitterbit-Script-Ansatz für Projekte, in denen JavaScript nicht verfügbar ist.

Integration überprüfen

  1. Stellen Sie die Controller-Operation bereit und führen Sie sie aus, indem Sie eine Benutzernachricht verwenden, die genau einen Tool-Aufruf erfordert. Bestätigen Sie 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ätigen Sie in den Protokollen, dass der zweite LLM-Aufruf das erweiterte Messages-Array erhalten hat (protokollieren Sie InAndOut mit WriteToOperationLog vor dem LLM-Aufruf) und dass er eine einfache Textantwort ohne tool_calls zurückgegeben hat.

  3. Senden Sie eine Benutzernachricht, die zwei aufeinanderfolgende Tool-Aufrufe erfordert (z. B. einen Kontakt nachschlagen und dann ein Ticket für diesen Kontakt erstellen). Bestätigen Sie, dass loop_count 2 erreicht, und überprüfen Sie dann die akkumulierten Nachrichten. tools_messages ist eine serialisierte JSON-Array-Zeichenkette. Protokollieren Sie sie nach Abschluss der Schleife, um ihren Inhalt anzuzeigen:

    WriteToOperationLog("tools_messages: " + $tools_messages);
    

    Bestätigen Sie in der protokollierten Zeichenkette, dass vier Objekte vorhanden sind, die zwischen "role":"assistant" und "role":"tool" wechseln (zwei von jedem): Jede Runde fügt die Assistentennachricht an, die das Tool ausgewählt hat, gefolgt von seinem tool-Ergebnis. Die tool_call_id jeder tool-Nachricht sollte mit der id im Eintrag tool_calls der vorherigen Assistentennachricht übereinstimmen.

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

  5. Wenn die API einen 400-Fehler mit Verweis auf eine ungültige tool_call_id zurückgibt, protokollieren Sie den Wert von tool_call_id und vergleichen Sie 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. Wenn die Schleife das Iterationslimit erreicht, protokollieren Sie InAndOut am Anfang jeder Runde, um die akkumulierten Nachrichten 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 mit der Benutzeranfrage übereinstimmt.