Zum Inhalt springen

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

Einführung

Die einmalige Funktionsaufrufverarbeitung (behandelt in LLM-Antworten an Studio-Operationen mit Funktionsaufrufen weiterleiten) behandelt eine Tool-Auswahl pro Anfrage: Das Modell wählt eine Funktion aus, eine Studio-Operation führt sie aus, und die Interaktion endet. Viele agentische Workflows erfordern mehr als einen Tool-Aufruf, um eine einzige Benutzeranfrage zu beantworten: Das Modell muss möglicherweise einen CRM-Datensatz abfragen, dann Kontodetails abrufen und schließlich eine Zusammenfassung unter Verwendung beider Ergebnisse erstellen.

Eine Tool-Calling-Schleife behandelt dies, indem sie jedes Tool-Ergebnis als tool-Rollen-Nachricht an das LLM zurückgibt und das Modell erneut mit dem erweiterten Gespräch aufruft. Die Schleife wiederholt sich, bis das Modell eine Antwort im Klartext ohne Tool-Aufrufe produziert oder ein maximales Iterationslimit erreicht ist.

Dieser Leitfaden baut auf:

Entwurfsmuster

Die Schleife fügt dem einmaligen Funktionsaufrufmuster zwei Schritte hinzu: das Anhängen des Tool-Ergebnisses an das Nachrichten-Array und das erneute Aufrufen des LLM. Die Schleife wiederholt sich, solange die Modellantwort 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 tragen den Status über Iterationen hinweg:

Variable Zweck
InAndOut Der aktuelle LLM-Anforderungstext (vor jedem Aufruf aktualisiert).
non_tool_messages_json Die Basisnachrichten (Systemaufforderung und Benutzernachricht) als JSON-Array-String. Bleibt über alle Iterationen hinweg konstant.
tools_messages Die angesammelten Tool-Austauschnachrichten aus allen vorherigen Runden als JSON-Array-String. Wächst mit jeder Iteration.
tools_resp Die aktuellste LLM-Antwort, die eine tool_calls-Auswahl enthielt. Wird verwendet, um die Assistenten-Nachricht zur Ansammlung zu extrahieren.
call_llm_again Wird auf true gesetzt, wenn ein Tool-Aufruf in Bearbeitung ist; wird auf false gesetzt, wenn das Modell eine endgültige Antwort zurückgibt.

Teil 1: Initialisieren der Schleifenvariablen

Vor dem ersten LLM-Aufruf die Basisnachrichten und den anfänglichen Anfragekörper erstellen und alle Schleifenstatusvariablen zurücksetzen. Fügen Sie einen Skriptschritt zu Beginn des Vorgangs 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 Format der Tool-Definition und wie man die Schemata erstellt, siehe Route LLM-Antworten zu Studio-Vorgängen mithilfe von Funktionsaufrufen.

Hinweis

Speichern Sie die Basisnachrichten in non_tool_messages_json, bevor die Schleife beginnt. Jede Iteration kombiniert diese Basisnachrichten mit den angesammelten Tool-Austauschen, um das vollständige Nachrichtenarray wiederherzustellen. Aktualisieren Sie non_tool_messages_json nicht innerhalb der Schleife.

Teil 2: Analysieren der Tool-Aufrufantwort

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

Bei Erfolg fügen Sie einen Skriptschritt hinzu, um die Antwort zu lesen und die Schleifensteuerungsvariablen 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 eine eindeutige Kennung, die die API jedem Tool-Aufruf zuweist. Sie muss in der Tool-Ergebnisnachricht in Teil 4 genau so zurückgegeben werden, wie sie empfangen wurde, oder die API wird die Anfrage ablehnen.

Hinweis

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

Verarbeitung 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 einzigen Antwort zurückgeben (parallele Tool-Aufrufe), und alle Aufrufe nach dem ersten werden ignoriert. Um jeden Aufruf zu verarbeiten, tun Sie eines der Folgenden:

  • 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 Completions APIs setzen Sie parallel_tool_calls im Anfragekörper auf false, wie in Teil 1 beschrieben.
  • Iterieren Sie über das tool_calls-Array, indem Sie das Tool aufrufen und für jeden Eintrag ein Assistenten-/tool-Nachrichtenpaar anhängen, bevor der nächste LLM-Aufruf erfolgt. Die LLM-API erfordert ein passendes tool-Ergebnis für jede tool_call_id in der Assistenten-Nachricht.

Teil 3: An das Tool-Operation dispatchen

Verwenden Sie eine Case Anweisung, um an die richtige Tool-Operation basierend auf function_name zu dispatchen, und folgen Sie dem gleichen Muster wie in LLM-Antworten an Studio-Operationen weiterleiten mit Funktionsaufruf. Jede Zieloperation führt die angeforderte Funktion aus und setzt function_resp auf den Ergebnisstring.

function_arguments enthält die Parameterauswahlen des Modells als JSON-String. Parsen Sie es mit JSONParser innerhalb jeder Zieloperation, um die einzelnen Argumentwerte zu extrahieren.

Teil 4: Das Tool-Ergebnis anhängen und die Anfrage neu aufbauen

Nachdem die Tool-Operation function_resp gesetzt hat, bauen Sie das aktualisierte Nachrichten-Array für den nächsten LLM-Aufruf auf. Dieser Schritt erfordert komplexe JSON-Manipulation: das Parsen der angesammelten Nachrichten, das Anhängen neuer Einträge und das erneute Serialisieren des Ergebnisses. Implementieren Sie es als JavaScript-Skript-Schritt:

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 Skript-Schritt zu verwenden, setzen Sie die Skriptsprache im Skripteditor auf JavaScript, bevor Sie Inhalte hinzufügen.

Hinweis

Die Assistenten-Nachricht (tool_resp.choices[0].message) muss unmittelbar vor der entsprechenden tool-Ergebnismeldung im Array erscheinen. Die OpenAI-API erfordert, dass die Auswahl der tool_calls und das entsprechende tool-Ergebnis benachbart und in der gleichen Reihenfolge wie die Aufrufe gemacht werden. Eine nicht übereinstimmende oder fehlende tool_call_id verursacht einen 400-Fehler.

Tipp

Wenn die Tool-Operation strukturierte Daten zurückgibt (zum Beispiel ein JSON-Objekt), serialisieren Sie es in einen String, bevor Sie es function_resp zuweisen. Das content-Feld der tool-Rollen-Nachricht muss ein String sein.

Teil 5: Die Schleife steuern

Wickeln Sie den LLM-Aufruf, den Dispatcher und die Schritte zum Wiederaufbau der Anfrage in einen Jitterbit-Skript-Controller mit einer While-Schleife ein. Platzieren Sie diesen Controller am Anfang der Operationenkette:

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 LLM Call-Operation verwendet InAndOut als Anfragekörper und führt den Teil 2 Antwortparser bei Erfolg aus. Die Tool Dispatcher-Operation führt die Teil 3 Case-Anweisung aus. Append Tool Result ist das Teil 4 JavaScript-Skript, das hier als benannte Skriptkomponente unter Verwendung von RunScript referenziert wird.

Das Festlegen einer maximalen Iterationsgrenze verhindert, dass Schleifen außer Kontrolle geraten, wenn ein Tool ständig einen Fehler zurückgibt und das Modell reagiert, indem es dasselbe Tool erneut anfordert.

Tipp

Beginnen Sie mit einer Grenze von 5 Iterationen. Die meisten Workflows lösen sich in ein oder zwei Runden; das ständige Erreichen der Grenze signalisiert ein Problem mit dem Prompt-Design oder dem Tool-Ergebnis und nicht die Notwendigkeit einer höheren Grenze.

Alternative: Jitterbit-Skript-Nachrichtenaufbau

Wenn Sie JavaScript vermeiden möchten, kann die Nachrichtenakkumulation im Teil 4 vollständig in Jitterbit-Skript unter Verwendung von String-Verkettung implementiert werden. Der HR Agent implementiert diesen Ansatz, indem er gpt.registeredTools als Projektvariable verwendet, um die serialisierten Tool-Schemas (entsprechend toolsJson in diesem Leitfaden) zu halten, wobei call_llm_again die Schleife steuert.

Der Jitterbit-Skript-Ansatz erstellt die Nachrichten des Assistenten und des Tool-Ergebnisses, indem er JSON-Strings direkt verkettet, anstatt JSON.parse und JSON.stringify zu verwenden. Das akkumulierte Array wird durch das Extrahieren der Assistenten-Nachricht aus tools_resp unter Verwendung von GetJSONString und das Anhängen des Tool-Ergebnisses als formatierten String aufrechterhalten:

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

Die vollständige Ersetzung des messages-Felds in InAndOut erfordert die Ersetzung des vorhandenen Array-Werts innerhalb des JSON-Strings, was anfällig ist, wenn der Nachrichteninhalt Sonderzeichen enthält. Verwenden Sie den JavaScript-Ansatz in Teil 4, wenn möglich; reservieren Sie den Jitterbit-Skriptansatz für Projekte, in denen JavaScript nicht verfügbar ist.

Integration überprüfen

  1. Bereitstellen und ausführen der Steuerungsoperation mit einer Benutzer-Nachricht, die genau einen Tool-Aufruf erfordert. Bestätigen Sie in den Betriebsprotokollen, 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 Nachrichten-Array erhalten hat (protokollieren Sie InAndOut mit WriteToOperationLog, bevor Sie den LLM aufrufen) und dass er eine Antwort im Klartext ohne tool_calls zurückgegeben hat.

  3. Senden Sie eine Benutzer-Nachricht, die zwei aufeinanderfolgende Tool-Aufrufe erfordert (zum Beispiel, einen Kontakt suchen und dann ein Ticket für diesen Kontakt erstellen). Bestätigen Sie, dass loop_count 2 erreicht, und überprüfen Sie dann die angesammelten Nachrichten. tools_messages ist ein serialisierter JSON-Array-String, protokollieren Sie ihn, nachdem die Schleife abgeschlossen ist, um seinen Inhalt zu sehen:

    WriteToOperationLog("tools_messages: " + $tools_messages);
    

    Bestätigen Sie im protokollierten String, dass es vier Objekte gibt, abwechselnd "role":"assistant" und "role":"tool" (jeweils zwei): Jede Runde fügt die Assistenten-Nachricht hinzu, die das Tool ausgewählt hat, gefolgt von ihrem tool-Ergebnis. Die tool_call_id jeder tool-Nachricht sollte mit der id im vorhergehenden Eintrag der tool_calls-Nachricht des Assistenten übereinstimmen.

  4. Senden Sie eine Nachricht, die keinen Tool-Aufruf erfordert (zum Beispiel eine allgemeine Frage, die das Modell aus seinem eigenen Wissen beantworten kann). Bestätigen Sie, dass die Schleife nicht ausgeführt wird (der anfängliche 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 dem Hinweis auf eine ungültige tool_call_id zurückgibt, protokolliere den Wert von tool_call_id und vergleiche ihn mit dem id-Feld in choices[0].message.tool_calls[0] aus der vorherigen LLM-Antwort. Eine Abweichung bedeutet typischerweise, dass tools_resp aktualisiert wurde, bevor die ID extrahiert wurde.

  6. Wenn die Schleife die Iterationsgrenze erreicht, protokolliere InAndOut zu Beginn jeder Runde, um die angesammelten Nachrichten zu überprüfen. Ein wiederholter Toolaufruf 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 Toolbeschreibung nicht mit der Anfrage des Benutzers übereinstimmt.