Skip to main content

Response Objects

O módulo response_objects fornece estruturas padronizadas e type-safe para retornos de Actions. Centraliza todos os tipos de resposta e define como elas são processadas pela engrenagem de conversação.

Visão Geral

O sistema de resposta funciona com dois objetos principais que determinam o fluxo da conversação:

  • ResponseToUser: Resposta direta ao usuário que interrompe o ciclo de chamada de ações
  • ResponseToAgent: Resposta que retorna ao LLM para nova decisão

🔄 Ciclo de Interação: User → Agent → Action

Antes de detalhar os objetos de resposta, é importante entender como funciona o ciclo de interação no sistema:

🎯 Pontos-Chave do Fluxo:

  1. 👤 Usuário inicia enviando uma mensagem
  2. 🤖 Agent/LLM analisa e toma uma decisão:
    • Executar Action → Chama uma função específica
    • Responder diretamente → Envia resposta imediata
  3. ⚙️ Action executada retorna um de dois tipos:
    • 📋 ResponseToAgent → Instrução volta para o LLM processar
    • ✉️ ResponseToUser → Mensagem vai direto para o usuário
  4. 🔄 Ciclo continua até uma resposta final ser enviada

💡 Implicações Práticas:

  • ResponseToAgent: Mantém o LLM no controle, permite múltiplas actions sequenciais
  • ResponseToUser: Quebra o ciclo, envia resposta definitiva ao usuário
  • Contexto preservado: Cada action contribui para o histórico do LLM
  • Flexibilidade: Permite fluxos simples (1 action) ou complexos (múltiplas actions)

Enums e Classes Base

ResponseStatus

Enum que define o status da resposta.

Valores:

  • SUCCESS: "success" - Operação bem-sucedida
  • ERROR: "error" - Erro na operação

Quando utilizar:

  • Para indicar o resultado da operação
  • Permitir diferentes comportamentos baseados no status

Exemplo:


# Resposta de sucesso (padrão)
status = ResponseStatus.SUCCESS

# Resposta com erro
status = ResponseStatus.ERROR


ResponseToUser

Descrição e Objetivo

Objeto estruturado que envia mensagem diretamente para o usuário, ativando a flag back_to_user=True. Isso faz com que o Conversation Engine crie uma mensagem direta para o usuário e pare o ciclo atual de interação, sem retornar ao LLM.

Parâmetros

Obrigatórios:

  • message (str): Mensagem que será enviada diretamente ao usuário
  • instruction (str): Instrução para o histórico do LLM (crítico para manter contexto)

Opcionais:

  • status (ResponseStatus): Status da resposta (padrão: SUCCESS)
  • files (List[FileContent]): Arquivos anexados à resposta
  • functions (List[SystemFunction]): Funções do sistema a serem executadas
  • detail (str): Detalhes adicionais sobre o status
  • state_updates (Dict[str, Any]): Apenas para observabilidade - cria evento no LangFuse mostrando estado atual
  • memory_updates (Dict[str, Any]): Apenas para observabilidade - cria evento no LangFuse mostrando memória atual
  • metadata (Dict[str, Any]): Metadados customizados

Quando Utilizar

Use ResponseToUser quando:

  • Quiser enviar resposta predefinida e formatada ao usuário
  • Não precisar de processamento adicional do LLM
  • Quiser finalizar o ciclo de chamada de ações imediatamente
  • Tiver mensagem completa e pronta para o usuário
  • Quiser controle total sobre a mensagem final

Resultado Esperado

  • Duas mensagens criadas pelo Conversation Engine:
    1. TOOL_RESPONSE: instruction vai para o histórico do LLM (contexto futuro)
    2. ASSISTANT: message vai diretamente para o usuário
  • Flag back_to_user=True ativada no sistema
  • Quebra do loop de chamada de ações
  • Controle não retorna ao LLM
  • Ciclo de interação finalizado

⚠️ Importância Crítica do instruction

O campo instruction não é verdadeiramente opcional na prática. Ele é essencial para:

  • Manter contexto do LLM sobre o resultado da action
  • Permitir decisões futuras baseadas no que aconteceu
  • Garantir continuidade do fluxo conversacional
  • Debugging e auditoria do que cada tool executou

Problema comum sem instruction adequado:

# ❌ PROBLEMÁTICO - LLM perde contexto
return ResponseToUser(
message="Agora me informe seu CPF"
# instruction não fornecido -> LLM não sabe se telefone foi validado
)

Histórico que o LLM vê:

TOOL_CALL: validar_telefone({"telefone": "11999999999"})
TOOL_RESPONSE: "Agora me informe seu CPF" # ← LLM não sabe o resultado!
ASSISTANT: "Agora me informe seu CPF"
USER: "123.456.789-00"

Solução correta:

# ✅ CORRETO - LLM mantém contexto
return ResponseToUser(
instruction="Telefone 11999999999 validado com sucesso. Prosseguir para coleta de CPF.",
message="✅ Telefone validado!\n\nAgora me informe seu CPF:"
)

Histórico que o LLM vê:

TOOL_CALL: validar_telefone({"telefone": "11999999999"})
TOOL_RESPONSE: "Telefone 11999999999 validado com sucesso. Prosseguir para coleta de CPF."
ASSISTANT: "✅ Telefone validado!\n\nAgora me informe seu CPF:"
USER: "123.456.789-00"

Exemplos

# Resposta com validação bem-sucedida
def confirmar_agendamento(data: str, horario: str, protocolo: str):
return ResponseToUser(
instruction=f"Agendamento criado com sucesso: protocolo {protocolo} para {data} às {horario}h. Cliente confirmado e notificado.",
message=f"✅ Agendamento confirmado para {data} às {horario}h!\n\nProtocolo: {protocolo}\nVocê receberá um lembrete 1 hora antes do compromisso."
)

# Resposta com arquivos anexados
def enviar_comprovante(numero_protocolo: str, arquivo_pdf: str):
return ResponseToUser(
instruction=f"Comprovante protocolo {numero_protocolo} gerado e enviado com sucesso. Arquivo: {arquivo_pdf}. Cliente pode prosseguir.",
message="📄 Seu comprovante foi gerado com sucesso!",
files=[
FileContent(
fileId=arquivo_pdf,
fileName=f"comprovante_{numero_protocolo}.pdf",
fileType="application/pdf"
)
]
)

# Resposta com validação bem-sucedida
def finalizar_cadastro(dados_usuario: dict):
return ResponseToUser(
instruction=f"Cadastro do usuário {dados_usuario.get('nome')} finalizado com sucesso. Dados validados e conta ativada. Cliente pode utilizar todos os serviços.",
message="🎉 Cadastro realizado com sucesso!\n\nSua conta está ativa e você já pode utilizar todos os nossos serviços.",
state_updates={
"usuario_cadastrado": True,
"etapa_atual": "completo"
},
memory_updates={
"dados_cadastro": dados_usuario,
"data_cadastro": datetime.now().isoformat()
}
)

# Resposta com transferência para humano
def solicitar_suporte_complexo(motivo: str):
return ResponseToUser(
instruction=f"Transferindo cliente para atendimento humano. Motivo: {motivo}. Prioridade alta para suporte especializado.",
message="🤝 Entendo que você precisa de um atendimento mais especializado.\n\nVou transferir você para um de nossos especialistas que poderá ajudar melhor com sua solicitação.",
functions=[
TransferToHuman(
reason=f"Solicitação complexa: {motivo}",
session_info={
"priority": "alta",
"category": "suporte_especializado"
}
)
]
)

# Resposta de erro formatada
def erro_validacao_documento():
return ResponseToUser(
instruction="Falha na validação do documento: critérios não atendidos (legibilidade, validade, autenticidade). Solicitar novo documento ao cliente.",
status=ResponseStatus.ERROR,
message="❌ Documento inválido\n\nO documento enviado não atende aos critérios necessários:\n• Deve estar legível\n• Deve estar dentro da validade\n• Deve ser um documento oficial\n\nPor favor, envie um novo documento.",
detail="Falha na validação automática do documento"
)

ResponseToAgent

Descrição e Objetivo

Objeto estruturado que retorna uma instrução para o LLM, permitindo que o agente tome uma nova decisão baseada na informação fornecida. O Conversation Engine adiciona a instrução ao contexto e solicita um novo completion do LLM.

Parâmetros

Obrigatórios:

  • instruction (str): Instrução que será enviada de volta ao LLM

Opcionais:

  • status (ResponseStatus): Status da resposta (padrão: SUCCESS)
  • functions (List[SystemFunction]): Funções do sistema a serem executadas
  • detail (str): Detalhes adicionais sobre o status
  • metadata (Dict[str, Any]): Metadados customizados

Quando Utilizar

Use ResponseToAgent quando:

  • Precisar que o LLM processe a informação retornada
  • Quiser que o agente tome decisões baseadas no resultado
  • A Action for intermediária no fluxo de conversação
  • Precisar combinar resultados com outras informações
  • Quiser manter o fluxo de chamada de ações ativo

Resultado Esperado

  • Instrução adicionada ao contexto do LLM
  • Novo completion solicitado ao modelo
  • LLM pode fazer novas chamadas de ações baseado na informação
  • Fluxo continua até o LLM decidir responder diretamente

Exemplos


# Resultado de consulta para análise do LLM
def consultar_saldo_conta(numero_conta: str):
saldo = buscar_saldo_no_banco(numero_conta)

return ResponseToAgent(
instruction=f"Saldo atual da conta {numero_conta}: R$ {saldo:.2f}. Com base nesta informação, analise se o cliente pode prosseguir com a operação solicitada."
)

# Validação que requer decisão do LLM
def validar_idade_usuario(idade: int, servico: str):
if idade >= 18:
return ResponseToAgent(
instruction=f"Usuário tem {idade} anos, está apto para o serviço '{servico}'. Prossiga com o atendimento normalmente."
)
else:
return ResponseToAgent(
instruction=f"Usuário tem {idade} anos, é menor de idade. Para o serviço '{servico}', verifique se precisa de autorização dos responsáveis ou se há restrições específicas.",
state_updates={"usuario_menor_idade": True}
)

# Resultado de API externa para análise
def consultar_cep(cep: str):
try:
endereco = api_viacep(cep)
return ResponseToAgent(
instruction=f"Endereço encontrado para o CEP {cep}: {endereco['logradouro']}, {endereco['bairro']}, {endereco['localidade']}-{endereco['uf']}. Continue o atendimento com essas informações."
)
except Exception as e:
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction=f"Não foi possível consultar o CEP {cep}. Solicite ao usuário que informe o endereço completo manualmente.",
detail=str(e)
)

# Análise de documento que requer interpretação
def analisar_documento(tipo_documento: str, conteudo: str):
resultado_analise = processar_documento(tipo_documento, conteudo)

return ResponseToAgent(
instruction=f"Análise do {tipo_documento} concluída:\n" +
f"• Status: {resultado_analise['status']}\n" +
f"• Campos encontrados: {resultado_analise['campos']}\n" +
f"• Observações: {resultado_analise['observacoes']}\n\n" +
f"Com base nesta análise, prossiga conforme necessário.",
memory_updates={
"ultimo_documento": {
"tipo": tipo_documento,
"status": resultado_analise['status'],
"data_analise": datetime.now().isoformat()
}
}
)

# Integração com sistema externo
def criar_protocolo_atendimento(categoria: str, descricao: str):
protocolo = sistema_externo.criar_protocolo(categoria, descricao)

return ResponseToAgent(
instruction=f"Protocolo de atendimento criado com sucesso: #{protocolo['numero']}.\n" +
f"Categoria: {protocolo['categoria']}\n" +
f"Prioridade: {protocolo['prioridade']}\n\n" +
f"Informe ao usuário o número do protocolo e continue o atendimento.",
state_updates={
"protocolo_ativo": protocolo['numero'],
"categoria_atendimento": categoria
}
)

Classes de Conveniência

System Functions

EndChat

Finaliza a conversa.

Parâmetros:

  • reason (str): Motivo do encerramento
  • session_info (dict, opcional): Informações da sessão

Exemplo:


def finalizar_atendimento(motivo: str):
return ResponseToUser(
message="Atendimento finalizado. Obrigado por entrar em contato!",
functions=[EndChat(reason=motivo)]
)

TransferToHuman

Transfere para atendimento humano ou para uma skill externa específica.

Parâmetros:

  • reason (str): Motivo da transferência
  • external_skill_name (str, opcional): Nome da skill externa para onde transferir
  • external_skill_id (str, opcional): ID (UUID) da skill externa para onde transferir
  • session_info (dict, opcional): Informações adicionais da sessão.
  • user_id (str, opcional): ID de um operador específico para o usuário

Exemplos:

# Transferência para skill externa específica
def transferir_para_pagamentos(user_id: str):
return ResponseToUser(
instruction="Cliente solicitou suporte para pagamentos. Transferindo para skill externa de Pagamentos.",
message="Vou transferir você para nossa equipe de Pagamentos que poderá ajudar melhor com sua solicitação.",
functions=[
TransferToHuman(
reason="Cliente precisa de suporte especializado em pagamentos",
external_skill_name="Pagamentos",
external_skill_id="123e4567-e89b-12d3-a456-426614174000",
user_id=user_id,
session_info={"priority": "alta"}
)
]
)

# Transferência genérica para atendimento humano
def transferir_para_especialista():
return ResponseToUser(
instruction="Caso complexo que requer atendimento humano especializado. Cliente transferido.",
message="Vou transferir você para um especialista.",
functions=[
TransferToHuman(
reason="Necessita atendimento especializado",
session_info={"priority": "alta"}
)
]
)

RedirectToSkill

Redireciona deterministicamente para outra habilidade interna do agente.

Aviso

Este método só pode ser utilizado dentro de um ResponseToAgent e não em ResponseToUser.

Parâmetros:

  • skill_name (str): Nome da skill interna para redirecionamento
  • reason (str): Motivo do redirecionamento
  • session_info (dict, opcional): Informações da sessão

Quando utilizar:

  • Transferir automaticamente para skill específica baseado em lógica determinística
  • Encadear skills para fluxos complexos
  • Delegar processamento para skill especializada

Exemplo:

def processar_nome_pirata(name: str):
pirate_name = gerar_nome_pirata(name)

return ResponseToAgent(
instruction=f"Nome pirata '{pirate_name}' gerado para {name}. Agora informe o usuário de forma criativa.",
functions=[
RedirectToSkill(
skill_name="Comunicação Criativa",
reason="Apresentar resultado de forma envolvente"
)
],
memory_updates={
"pirate_name_generated": pirate_name,
"original_name": name,
"generation_timestamp": datetime.now().isoformat()
}
)

Comparação: Quando Usar Cada Tipo

CenárioResponseToUserResponseToAgent
Confirmação de agendamento✅ Resposta final e formatada❌ Desnecessário
Consulta de saldo❌ LLM pode usar a info✅ Para análise e decisão
Erro de validação✅ Resposta direta clara❌ LLM pode confundir
Resultado de API❌ LLM deve processar✅ Para análise contextual
Transferência para humano✅ Mensagem de despedida❌ Ação imediata
Redirecionamento para skill interna❌ Use RedirectToSkill✅ Use RedirectToSkill
Upload de arquivo✅ Confirmação direta❌ Ação simples
Análise de documento❌ LLM deve interpretar✅ Para próximas decisões
Finalização de processo✅ Mensagem de sucesso❌ Processo finalizado

Boas Práticas

✅ Faça

Para ResponseToUser:

  • SEMPRE forneça instruction claro sobre o resultado da action
  • Use message clara e completa para o usuário
  • Separe responsabilidades: instruction para LLM, message para usuário
  • Documente o resultado da operação no instruction

Para ResponseToAgent:

  • Forneça contexto suficiente para o LLM decidir
  • Use linguagem clara nas instruções
  • Mantenha instruções concisas mas informativas

❌ Evite

  • NUNCA omita instruction em ResponseToUser (LLM perde contexto)
  • Não misture os propósitos (user vs agent)
  • Não use ResponseToUser quando precisar de análise do LLM
  • Não use ResponseToAgent para respostas finais simples
  • Não inclua informações sensíveis em metadados
  • Não crie instruções ambíguas para o LLM