Ir para o conteúdo

Construir um chat multi-turno com LLM e histórico de conversa no Jitterbit Studio

Introdução

Uma conversa com múltiplas trocas requer mais do que enviar uma única mensagem do usuário a um LLM: o modelo precisa do histórico completo da troca para responder coerentemente a perguntas de acompanhamento e manter o contexto entre as trocas. Este guia cobre o padrão de baixo nível para construir e persistir um array de histórico de conversa no Studio, usando Cloud Datastore como armazenamento de sessão e SumCSV para acumular mensagens entre execuções.

Este guia é diferente de Como criar um agente de IA contextual, que cobre a arquitetura geral do agente. O foco aqui é nos padrões de script e transformação que construem o array messages: como formatar cada mensagem como uma linha CSV, como persistir e recuperar o histórico completo entre execuções de operação, e como lidar com campos opcionais como tool_call_id ao combinar histórico de conversa com function calling.

Este guia é baseado em:

Nota

Este guia gerencia o histórico de conversa explicitamente, o que você precisa ao chamar o LLM pelo conector HTTP v2. Se você enviar prompts através de um conector LLM nativo em vez disso, e esse conector oferecer uma configuração Armazenar contexto de chat entre operações, ele pode reter o contexto do chat para você: essa configuração mantém histórico entre operações que compartilham o mesmo chatId em grupos de agentes em nuvem, e agentes privados retêm contexto de chat na memória automaticamente.

Padrão de design

O padrão de histórico de conversa adiciona duas etapas em torno de uma chamada LLM padrão: uma etapa de recuperação de histórico antes da solicitação e uma etapa de atualização de histórico após a resposta.

%%{init: {"themeVariables": {"lineColor": "#6b7280", "edgeLabelBackground": "#f5f5f5"}}}%% flowchart LR A["Query Cloud Datastore
Retrieve history by session key"] --> B["Script
Build messages CSV"] --> C["Transformation
Convert CSV to JSON
call LLM"] --> D["Script
Append user + assistant
Update CDS"]

Cada mensagem do usuário e resposta do assistente é armazenada no Cloud Datastore como uma linha em uma string CSV, identificada por um identificador de sessão (normalmente uma ID de canal do Slack ou ID de usuário). Antes de cada chamada LLM, o histórico completo é recuperado e montado no array messages. Após o LLM responder, a mensagem atual do usuário e a resposta do assistente são ambas anexadas ao histórico, e a string atualizada é gravada de volta no Cloud Datastore.

Parte 1: Configurar o armazenamento do Cloud Datastore

Crie um armazenamento de chave com um campo ConversationHistory para manter a string de histórico CSV para cada sessão. Siga Armazenar e recuperar estado de sessão usando Cloud Datastore para as etapas completas de configuração. Para adicionar campos, no portal Harmony navegue até Menu do portal Harmony > Console de Gerenciamento > Cloud Datastore, abra o armazenamento de chave, e adicione um campo chamado ConversationHistory com o tipo Texto Grande. Os campos integrados Key, Alternative Key, e Value estão sempre presentes e não precisam ser adicionados.

Nota

Os campos Texto Grande suportam até 25.000 bytes por item. Para conversas de longa duração, implemente uma estratégia de truncamento: retenha apenas os N últimos intercâmbios antes de gravar de volta no Cloud Datastore, ou resuma as trocas anteriores usando o próprio LLM antes de armazenar.

Parte 2: Recuperar histórico e construir o array de mensagens

Recuperar o histórico

O primeiro passo na operação consulta o Cloud Datastore pelo registro de sessão, usando o identificador de sessão (por exemplo, um ID de canal do Slack) como filtro de chave. Após a atividade Query Items, leia o resultado em uma etapa de script:

<trans>
$historyMessages = "";
if(Source.json.pagination.totalItems > 0,
    $historyMessages = Source.json.items.item[0].ConversationHistory
);
</trans>

Isso define historyMessages como a string CSV armazenada ou como uma string vazia se for a primeira vez na sessão.

Construir o array de mensagens

Na próxima etapa de script, monte o array completo de mensagens como uma string CSV usando SumCSV. Cada chamada a SumCSV produz uma linha CSV a partir de um array de valores de campo:

<trans>
// System message (always first, not stored in history)
message = Array();
message[0] = "system";
message[1] = $systemPrompt;
$messages = SumCSV(message);

// Append stored conversation history (all prior user/assistant pairs)
if(length(trim($historyMessages)) > 0,
    $messages = $messages + "\n" + $historyMessages
);

// Append the current user message
message[0] = "user";
message[1] = $userInput;
$messages = $messages + "\n" + SumCSV(message);
</trans>

messages agora contém uma string CSV com uma linha por mensagem. A mensagem do sistema sempre vem primeiro; o histórico armazenado segue na ordem em que foi acumulado; a mensagem atual do usuário é a última.

Dica

Armazene a chave de sessão em uma variável global antes dessa operação para que a atualização do Cloud Datastore na Parte 4 use a mesma chave.

Parte 3: Converter o array de mensagens para JSON e chamar o LLM

Configurar o schema de fonte de transformação

Adicione uma transformação à operação e defina seus dados de origem para a variável messages, analisada como CSV. Defina um schema de origem CSV com duas colunas:

  • role (string)
  • content (string)

Mapear o array de mensagens para a solicitação do LLM

Mapeie as colunas CSV para o corpo da solicitação do Chat Completions do OpenAI. O caminho de destino para cada entrada de mensagem é json/messages/item:

  • json/messages/item/role → coluna de origem role
  • json/messages/item/content → coluna de origem content

Se a operação também adiciona resultados de chamadas de ferramentas ao histórico (para uso com o padrão de loop de chamadas de ferramentas), o CSV pode incluir colunas adicionais. Use Unmap no script de campo de destino para omitir o campo quando a coluna estiver vazia, em vez de enviar um valor nulo ou uma string vazia:

// In the target script for json/messages/item/tool_call_id
if(length(trim(tool_call_id)) == 0, Unmap(), tool_call_id)

Aplique o mesmo padrão a name se incluir uma coluna tool_name:

// In the target script for json/messages/item/name
if(length(trim(tool_name)) == 0, Unmap(), tool_name)

Isso garante que o campo esteja ausente do objeto JSON serializado quando vazio. A API do OpenAI exige que tool_call_id e name estejam ausentes (não nulos ou vazios) para mensagens padrão de user e assistant.

Conectar ao endpoint do LLM

Coloque a atividade HTTP v2 POST direcionada ao endpoint do Chat Completions do OpenAI após a transformação. Para configuração de conexão e autenticação, consulte Usar o OpenAI para processar dados em uma operação do Studio.

Parte 4: Adicionar a resposta e atualizar o Cloud Datastore

Após a chamada do LLM, uma etapa de script extrai a resposta do assistente, adiciona tanto a mensagem do usuário quanto a resposta do assistente ao histórico armazenado e escreve a string atualizada de volta ao Cloud Datastore.

Construir a string de histórico atualizada

<trans>
// Extract the assistant response
$assistantReply = TrimChars(GetJSONString($jitterbit.response, "/choices/0/message/content"), "\"");

// Build the two new rows to append
userMessage = Array();
userMessage[0] = "user";
userMessage[1] = $userInput;

assistantMessage = Array();
assistantMessage[0] = "assistant";
assistantMessage[1] = $assistantReply;

newExchange = SumCSV(userMessage) + "\n" + SumCSV(assistantMessage);

// Combine prior history with the new exchange
$updatedHistory = trim($historyMessages);
if(length($updatedHistory) > 0,
    $updatedHistory = $updatedHistory + "\n"
);
$updatedHistory = $updatedHistory + newExchange;
</trans>

updatedHistory contém todas as trocas anteriores mais a mensagem atual do usuário e a resposta do assistente. A mensagem do sistema não está incluída: ela é preparada dinamicamente no início de cada turno na Parte 2 e não precisa ser armazenada.

Escrever o histórico atualizado no Cloud Datastore

Use o padrão query-insert-update de Armazenar e recuperar estado de sessão usando Cloud Datastore para escrever updatedHistory no campo ConversationHistory, usando a mesma chave de identificador de sessão usada na Parte 2.

Nota

A atividade Update Items faz a correspondência do registro a atualizar pelo seu campo Key. Se o resultado da Query da Parte 2 retornou totalItems = 0, a operação Insert deve ser executada. Estruture a cadeia pós-atualização para corresponder ao padrão no guia do Cloud Datastore.

Abordagem alternativa: GetInstance()

A função GetInstance fornece uma forma alternativa de atribuir papéis de mensagem quando as mensagens já estão armazenadas como um array estruturado (por exemplo, recuperadas de um array JSON ou de uma tabela de banco de dados com uma linha por mensagem). Em uma transformação que itera sobre instâncias de mensagem, use TargetInstanceCount para obter o índice da linha atual e atribuir papéis com base na posição par/ímpar:

<trans>
// Assign alternating user/assistant roles based on row position (1-based)
$role = if(TargetInstanceCount() % 2 == 1, "user", "assistant");
</trans>

Mapeie role para json/messages/item/role. Esta abordagem funciona quando todos os turnos são armazenados com seus valores de conteúdo em ordem cronológica e a atribuição de papel pode ser derivada apenas da posição. Não oferece suporte a uma mensagem do sistema como entrada distinta primeira ou metadados de papel por mensagem.

A abordagem SumCSV na Parte 2 é mais flexível para o padrão de agente conversacional: oferece suporte a uma mensagem do sistema explícita, permite qualquer papel por linha e armazena o histórico completo como um único campo do Cloud Datastore sem exigir um esquema com uma linha por mensagem.

Verificar a integração

  1. Implante e execute a operação com uma chave de sessão que ainda não existe no armazenamento. Forneça uma mensagem inicial do usuário.

  2. Nos logs da operação, confirme que o resultado da Query mostra totalItems = 0 e que a operação Insert foi executada para criar o registro de sessão.

  3. No Management Console > Cloud Datastore, abra o armazenamento e confirme que existe um registro com a chave esperada e que ConversationHistory contém uma linha de usuário e uma linha de assistente.

  4. Execute a operação novamente com a mesma chave de sessão e uma pergunta de acompanhamento que se refira à primeira troca.

  5. Confirme que a resposta do LLM reflete o contexto anterior. No Cloud Datastore, confirme que ConversationHistory agora contém dois pares de usuário/assistente.

  6. Se a resposta do LLM ignorar o contexto anterior, use WriteToOperationLog para registrar messages antes da transformação e confirme que os turnos anteriores aparecem na string CSV.

  7. Se a transformação gerar um erro de incompatibilidade de esquema, confirme que a contagem de colunas do esquema de origem CSV corresponde ao número de valores passados para cada chamada SumCSV. Todas as linhas devem ter o mesmo número de colunas.