Implemente um loop de chamada de ferramenta LLM no Jitterbit Studio
Introdução
A chamada de função de uma única rodada (cobrindo em Roteie respostas LLM para operações do Studio usando chamada de função) lida com 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 agentes requerem 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, elaborar um resumo usando ambos os resultados.
Um loop de chamada de ferramenta lida com isso retornando cada resultado da ferramenta para o LLM como uma mensagem de papel 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 um limite máximo de iterações seja alcançado.
Este guia se baseia em:
- Roteie respostas LLM para operações do Studio usando chamada de função para definição de esquema de ferramenta e o padrão de despacho de uma única rodada. Leia esse guia primeiro.
- Construa um chat LLM de múltiplas rodadas com histórico de conversa se você estiver combinando o loop de chamada de ferramenta com memória de conversa persistente entre as interações 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 as iterações:
| Variável | Propósito |
|---|---|
InAndOut |
O corpo da solicitação atual do LLM (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 acumuladas de troca de ferramentas de todas as rodadas anteriores como uma string de array JSON. Cresce a cada iteração. |
tools_resp |
A resposta mais recente do LLM que continha uma seleção de tool_calls. Usada para extrair a mensagem do assistente para acumulação. |
call_llm_again |
Definido como true quando uma chamada de ferramenta está em andamento; definido como false quando o modelo retorna uma resposta final. |
Parte 1: Inicializar as variáveis de 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 um passo 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 da ferramenta e como construir os esquemas, veja Roteie as respostas LLM para operações do Studio usando chamadas de função.
Nota
Salve as mensagens base em non_tool_messages_json antes do início do loop. Cada iteração combina essas mensagens base com as trocas de ferramenta acumuladas para reconstruir o array completo de mensagens. Não atualize non_tool_messages_json dentro do loop.
Parte 2: Analisar a resposta da chamada da ferramenta
Encadeie uma operação de Chamada LLM (HTTP v2 POST para o endpoint de Conclusões de Chat da OpenAI, com InAndOut como corpo da solicitação). Para configuração de conexão e autenticação, veja Chame uma API REST usando o conector HTTP v2.
Em caso de sucesso, adicione um passo 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 único que a API atribui a cada chamada de ferramenta. Ele deve ser retornado na mensagem de resultado da ferramenta em 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 populado (que é, no mínimo, [...], comprimento 3 ou mais) de um valor ausente ou vazio.
Tratando 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 quaisquer chamadas após a primeira são ignoradas. Para lidar com cada chamada, faça uma das seguintes opções:
- Desative chamadas de ferramentas paralelas na solicitação LLM para que o modelo retorne no máximo uma chamada por resposta. Para as APIs de Chat Completions da OpenAI e Azure OpenAI, defina
parallel_tool_callscomofalseno corpo da solicitação construído em Parte 1. - Itere sobre o array
tool_calls, despachando a ferramenta e anexando uma mensagem de assistente/toolpara cada entrada antes da próxima chamada LLM. A API LLM requer um resultadotoolcorrespondente para cadatool_call_idna mensagem do assistente.
Parte 3: Despachar para a operação da ferramenta
Use uma declaração Case para despachar para a operação da ferramenta correta com base em function_name, seguindo o mesmo padrão de Roteie respostas LLM para operações do Studio usando chamadas 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 individuais de argumento.
Parte 4: Anexar o resultado da ferramenta e reconstruir a solicitação
Após a operação da ferramenta definir function_resp, construa o array de mensagens atualizado para a próxima chamada LLM. Esta etapa requer manipulação complexa de JSON: analisando as mensagens acumuladas, anexando novas entradas e re-serializando o resultado. Implemente isso como um passo 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 um passo de script, defina a linguagem do 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 estejam 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 da ferramenta retornar dados estruturados (por exemplo, um objeto JSON), serialize-o para uma string antes de atribuí-lo a function_resp. O campo content da mensagem de papel tool deve ser uma string.
Parte 5: Controle do loop
Envolva a chamada do LLM, o despachante e as etapas de reconstrução da solicitação em um controlador de Script Jitterbit 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 parser de resposta da Parte 2 em caso de sucesso. A operação Tool Dispatcher executa a declaraçã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ções 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 se resolve em uma ou duas rodadas; atingir consistentemente o limite sinaliza um problema de design do prompt ou resultado da ferramenta, em vez de uma necessidade de um limite maior.
Alternativa: Construção de mensagens em Script Jitterbit
Se preferir evitar JavaScript, a acumulação de mensagens na Parte 4 pode ser implementada inteiramente em Script Jitterbit usando concatenação de strings. O Agente de RH implementa essa abordagem, usando gpt.registeredTools como uma variável de projeto para armazenar os esquemas de ferramentas serializados (equivalente a toolsJson neste guia), com call_llm_again controlando o loop.
A abordagem de Script Jitterbit constrói as mensagens do assistente e do 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 a substituição do 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 em Parte 4 quando possível; reserve a abordagem Jitterbit Script para projetos onde o JavaScript não está disponível.
Verifique a integração
-
Implante e execute a operação do controlador com uma mensagem de usuário que requer 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 de usuário que requer duas chamadas de ferramenta sequenciais (por exemplo, procurar um contato e depois criar um ticket para esse contato). Confirme que
loop_countatinge 2, depois inspecione as mensagens acumuladas.tools_messagesé uma string de array JSON serializado, então registre-a após a conclusão do loop 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 pelo seu resultadotool. Otool_call_idde cada mensagemtooldeve corresponder aoidna entradatool_callsda mensagem do assistente anterior. -
Envie uma mensagem que não requer nenhuma chamada de ferramenta (por exemplo, uma pergunta geral que o modelo pode 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 anterior do LLM. Um desvio geralmente significa quetools_respfoi atualizado antes que o ID fosse extraído. -
Se o loop atingir o limite de iterações, 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 ao pedido do usuário.