Implementar um loop de chamada de ferramenta LLM no Jitterbit Studio
Introdução
A chamada de função de uma única rodada (abordada em Rotear respostas LLM para operações do Studio usando chamada de função) processa uma seleção de ferramenta por solicitação: o modelo escolhe uma função, uma operação do Studio a executa e a interação termina. Muitos fluxos de trabalho com agentes exigem mais de uma chamada de ferramenta para responder a uma única solicitação do usuário: o modelo pode precisar consultar um registro de CRM, depois buscar detalhes da conta e, em seguida, redigir um resumo usando ambos os resultados.
Um loop de chamada de ferramenta processa isso retornando cada resultado de ferramenta ao LLM como uma mensagem com função tool e chamando o modelo novamente com a conversa estendida. O loop se repete até que o modelo produza uma resposta em texto simples sem chamadas de ferramenta ou até que um limite máximo de iterações seja atingido.
Este guia se baseia em:
- Rotear respostas LLM para operações do Studio usando chamada de função para definição de esquema de ferramenta e o padrão de envio de uma única rodada. Leia esse guia primeiro.
- Criar um chat LLM multi-turno com histórico de conversa se você estiver combinando o loop de chamada de ferramenta com memória de conversa persistente entre turnos do usuário.
Padrão de design
O loop adiciona duas etapas ao padrão de chamada de função de uma única rodada: anexar o resultado da ferramenta ao array de mensagens e chamar o LLM novamente. O loop se repete enquanto a resposta do modelo incluir um array tool_calls.
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"]
Cinco variáveis globais mantêm o estado entre iterações:
| Variável | Finalidade |
|---|---|
InAndOut |
O corpo da solicitação LLM atual (atualizado antes de cada chamada). |
non_tool_messages_json |
As mensagens base (prompt do sistema e mensagem do usuário) como uma string de array JSON. Permanece constante em todas as iterações. |
tools_messages |
As mensagens de troca de ferramenta acumuladas de todas as rodadas anteriores como uma string de array JSON. Cresce a cada iteração. |
tools_resp |
A resposta LLM mais recente que continha uma seleção tool_calls. Usada para extrair a mensagem do assistente para acumulação. |
call_llm_again |
Definida como true quando uma chamada de ferramenta está em andamento; definida como false quando o modelo retorna uma resposta final. |
Parte 1: Inicializar as variáveis do loop
Antes da primeira chamada LLM, construa as mensagens base e o corpo da solicitação inicial e redefina todas as variáveis de estado do loop. Adicione uma etapa de script no início da operação:
// 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 é uma variável de projeto que contém o array de esquema de ferramenta serializado. Para o formato de definição de ferramenta e como construir os esquemas, consulte Rotear respostas LLM para operações do Studio usando chamada de função.
Nota
Salve as mensagens base em non_tool_messages_json antes do loop começar. Cada iteração combina essas mensagens base com as trocas de ferramenta acumuladas para reconstruir o array de mensagens completo. Não atualize non_tool_messages_json dentro do loop.
Parte 2: Analisar a resposta da chamada de ferramenta
Encadeie uma operação LLM Call (HTTP v2 POST para o endpoint OpenAI Chat Completions, com InAndOut como corpo da solicitação). Para configuração de conexão e autenticação, consulte Chamar uma API REST usando o conector HTTP v2.
Em caso de sucesso, adicione uma etapa de script para ler a resposta e definir as variáveis de controle do loop:
// 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 é um identificador exclusivo que a API atribui a cada chamada de ferramenta. Ele deve ser retornado na mensagem de resultado da ferramenta na Parte 4, exatamente como recebido, ou a API rejeitará a solicitação.
Nota
tool_calls está ausente da resposta quando o modelo retorna texto simples. A chamada GetJSONString retorna "null" nesse caso, e a verificação de comprimento > 2 distingue corretamente um array preenchido (que tem no mínimo [...], comprimento 3 ou mais) de um valor ausente ou vazio.
Tratamento de múltiplas chamadas de ferramenta
O script acima lê tool_calls/0, a primeira chamada de ferramenta na resposta. Um LLM pode retornar várias chamadas de ferramenta em uma única resposta (chamadas de ferramenta paralelas), e qualquer chamada após a primeira é ignorada. Para tratar cada chamada, faça um dos seguintes:
- Desabilite as chamadas de ferramenta paralelas na solicitação do LLM para que o modelo retorne no máximo uma chamada por resposta. Para as APIs OpenAI e Azure OpenAI Chat Completions, defina
parallel_tool_callscomofalseno corpo da solicitação criado na Parte 1. - Itere sobre o array
tool_calls, despachando cada ferramenta e anexando uma mensagem de resultadotoolpor entrada antes da próxima chamada do LLM. UseGetJSONStringcom um índice incremental (por exemplo,/choices/0/message/tool_calls/1/id) para ler cada chamada adicional. A API do LLM requer um resultadotoolcorrespondente para cadatool_call_idna mensagem do assistente, que é enviada uma vez.
Parte 3: Despachar para a operação de ferramenta
Use uma instrução Case para despachar para a operação de ferramenta correta com base em function_name, seguindo o mesmo padrão da Rotear respostas do LLM para operações do Studio usando chamada de função. Cada operação de destino executa a função solicitada e define function_resp como a string de resultado.
function_arguments contém as seleções de parâmetros do modelo como uma string JSON. Analise-a usando JSONParser dentro de cada operação de destino para extrair valores de argumentos individuais.
Parte 4: Anexar o resultado da ferramenta e reconstruir a solicitação
Após a operação de ferramenta definir function_resp, crie o array de mensagens atualizado para a próxima chamada do LLM. Esta etapa requer manipulação JSON complexa: análise das mensagens acumuladas, anexação de novas entradas e re-serialização do resultado. Implemente-a como uma etapa de script JavaScript:
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);
Para usar JavaScript em uma etapa de script, defina a linguagem de script como JavaScript no editor de Script antes de adicionar conteúdo.
Nota
A mensagem do assistente (tool_resp.choices[0].message) deve aparecer imediatamente antes de sua mensagem de resultado tool correspondente no array. A API OpenAI requer que a seleção tool_calls e o resultado tool correspondente sejam adjacentes e na mesma ordem em que as chamadas foram feitas. Um tool_call_id incompatível ou ausente causa um erro 400.
Dica
Se a operação de ferramenta retornar dados estruturados (por exemplo, um objeto JSON), serialize-os para uma string antes de atribuir a function_resp. O campo content da mensagem com função tool deve ser uma string.
Parte 5: Controlar o loop
Envolva a chamada do LLM, o despachador e as etapas de reconstrução da solicitação em um controlador Jitterbit Script usando um loop While. Coloque este controlador no topo da cadeia de operações:
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)
);
A operação LLM Call usa InAndOut como o corpo da solicitação e executa o analisador de resposta da Parte 2 em caso de sucesso. A operação Tool Dispatcher executa a instrução Case da Parte 3. Append Tool Result é o script JavaScript da Parte 4, referenciado aqui como um componente de script nomeado usando RunScript.
Definir um limite máximo de iteração evita loops descontrolados quando uma ferramenta retorna consistentemente um erro e o modelo responde solicitando a mesma ferramenta novamente.
Dica
Comece com um limite de 5 iterações. A maioria dos fluxos de trabalho é resolvida em uma ou duas rodadas; atingir consistentemente o limite sinaliza um problema de design de prompt ou resultado de ferramenta, em vez de uma necessidade de um limite mais alto.
Alternativa: Construção de mensagens em Jitterbit Script
Se preferir evitar JavaScript, o acúmulo de mensagens na Parte 4 pode ser implementado inteiramente em Jitterbit Script usando concatenação de strings. O HR Agent implementa essa abordagem, usando gpt.registeredTools como uma variável de projeto para manter os esquemas de ferramenta serializados (equivalente a toolsJson neste guia), com call_llm_again controlando o loop.
A abordagem Jitterbit Script constrói as mensagens do assistente e resultado da ferramenta concatenando strings JSON diretamente, em vez de usar JSON.parse e JSON.stringify. O array acumulado é mantido extraindo a mensagem do assistente de tools_resp usando GetJSONString e anexando o resultado da ferramenta como uma string formatada:
// 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)
A substituição completa do campo messages em InAndOut requer substituir o valor do array existente dentro da string JSON, o que é frágil se o conteúdo da mensagem contiver caracteres especiais. Use a abordagem JavaScript na Parte 4 quando possível; reserve a abordagem Jitterbit Script para projetos onde JavaScript não está disponível.
Verificar a integração
-
Implante e execute a operação do controlador com uma mensagem do usuário que exija exatamente uma chamada de ferramenta. Confirme nos logs da operação que
loop_countincrementa para 1 e que a operação da ferramenta foi executada e retornou um resultado. -
Nos logs, confirme que a segunda chamada LLM recebeu o array de mensagens estendido (registre
InAndOutusandoWriteToOperationLogantes de chamar o LLM) e que retornou uma resposta final em texto simples semtool_calls. -
Envie uma mensagem do usuário que exija duas chamadas de ferramenta sequenciais (por exemplo, procurar um contato e depois criar um ticket para esse contato). Confirme que
loop_countatinge 2 e inspecione as mensagens acumuladas.tools_messagesé uma string de array JSON serializada, então registre-a após o loop ser concluído para ver seu conteúdo:WriteToOperationLog("tools_messages: " + $tools_messages);Na string registrada, confirme que há quatro objetos, alternando
"role":"assistant"e"role":"tool"(dois de cada): cada rodada anexa a mensagem do assistente que selecionou a ferramenta seguida por seu resultadotool. Otool_call_idde cada mensagemtooldeve corresponder aoidna entradatool_callsda mensagem do assistente anterior. -
Envie uma mensagem que não exija nenhuma chamada de ferramenta (por exemplo, uma pergunta geral que o modelo possa responder com seu próprio conhecimento). Confirme que o loop não é executado (a chamada LLM inicial não retorna
tool_calls,call_llm_againpermanecefalseeloop_countpermanece 0). -
Se a API retornar um erro 400 citando um
tool_call_idinválido, registre o valor detool_call_ide compare-o com o campoidemchoices[0].message.tool_calls[0]da resposta LLM anterior. Uma incompatibilidade normalmente significa quetools_respfoi atualizado antes da extração do ID. -
Se o loop atingir o limite de iteração, registre
InAndOutno início de cada rodada para inspecionar as mensagens acumuladas. Uma chamada de ferramenta repetida para a mesma função com os mesmos argumentos indica que a operação da ferramenta está retornando um resultado de erro que o modelo está tentando novamente, ou que a descrição da ferramenta não corresponde à solicitação do usuário.