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çõesResponseToAgent: 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:
- 👤 Usuário inicia enviando uma mensagem
- 🤖 Agent/LLM analisa e toma uma decisão:
- Executar Action → Chama uma função específica
- Responder diretamente → Envia resposta imediata
- ⚙️ Action executada retorna um de dois tipos:
- 📋 ResponseToAgent → Instrução volta para o LLM processar
- ✉️ ResponseToUser → Mensagem vai direto para o usuário
- 🔄 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-sucedidaERROR: "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árioinstruction(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 à respostafunctions(List[SystemFunction]): Funções do sistema a serem executadasdetail(str): Detalhes adicionais sobre o statusstate_updates(Dict[str, Any]): Apenas para observabilidade - cria evento no LangFuse mostrando estado atualmemory_updates(Dict[str, Any]): Apenas para observabilidade - cria evento no LangFuse mostrando memória atualmetadata(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:
- TOOL_RESPONSE:
instructionvai para o histórico do LLM (contexto futuro) - ASSISTANT:
messagevai diretamente para o usuário
- TOOL_RESPONSE:
- Flag
back_to_user=Trueativada 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 executadasdetail(str): Detalhes adicionais sobre o statusmetadata(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 encerramentosession_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ênciaexternal_skill_name(str, opcional): Nome da skill externa para onde transferirexternal_skill_id(str, opcional): ID (UUID) da skill externa para onde transferirsession_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.
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 redirecionamentoreason(str): Motivo do redirecionamentosession_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ário | ResponseToUser | ResponseToAgent |
|---|---|---|
| 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
instructionclaro sobre o resultado da action - Use
messageclara e completa para o usuário - Separe responsabilidades:
instructionpara LLM,messagepara 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
instructionem 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