Olá a todos, Leo aqui do agntdev.com. Espero que todos estejam tendo uma semana produtiva!
Hoje quero explorar algo em que tenho pensado muito recentemente, especialmente enquanto trabalhava em alguns projetos pessoais que envolvem fluxos de trabalho de agentes mais complexos e passos múltiplos. Falamos muito sobre como construir agentes, sobre os LLMs em si e as coisas interessantes que podem fazer. Mas e quanto ao aspecto menos glamoroso, mas absolutamente crucial, de garantir que nossos agentes funcionem de fato de maneira confiável e eficiente ao longo do tempo?
Em particular, estou falando de observabilidade dos agentes. Não se trata apenas de registro; trata-se de realmente entender o que seu agente está fazendo, por que está fazendo isso e identificar problemas antes que se amplifiquem. Em um mundo onde os agentes interagem com APIs externas, tomam decisões baseadas em entradas dinâmicas e podem operar por longos períodos, agir às cegas é uma receita para o desastre. Aprendi isso da maneira mais difícil, como explicarei.
O “Bug Misterioso” Que Me Ensinou Tudo
Alguns meses atrás, estava desenvolvendo um agente assistente pessoal. Vamos chamá-lo de “Projeto Chronos.” Sua tarefa era monitorar meu calendário, feeds de notícias e canais específicos do Slack, então sugerir proativamente horários para reuniões, resumir atualizações importantes ou até mesmo redigir respostas iniciais para perguntas comuns. Coisa bem padrão na superfície.
Eu o construí, testei alguns cenários e parecia funcionar bem. Configurei para funcionar durante a noite, pensando em acordar com um resumo perfeitamente elaborado. Em vez disso, acordei… nada. Ou melhor, um resumo parcial que parava abruptamente, seguido por uma mensagem de erro enigmática nos logs do sistema que dizia essencialmente “algo deu errado.”
O depuração disso foi um pesadelo. Chronos precisava fazer várias coisas: recuperar eventos do calendário, interrogar uma API de notícias, interagir com uma API do Slack, processar os dados e, em seguida, gerar um resumo. Qual etapa falhou? Por quê? Ele tentou todas as etapas? Era um limite de requisição da API? Um prompt malformado? Um tempo limite? Não tinha ideia.
Meus logs iniciais eram básicos: “Iniciou a etapa X,” “Concluiu a etapa Y,” e então a saída final ou um erro. Isso não era suficiente. Era como tentar diagnosticar um problema no carro sabendo apenas que ele ligou e depois parou, sem qualquer informação sobre a temperatura do motor, pressão do combustível ou falhas elétricas.
Aquela experiência destacou o ponto: se você está sério sobre o desenvolvimento de agentes, precisa de uma sólida observabilidade desde o primeiro dia. Não é um pensamento secundário; é um componente fundamental.
Além do Registro Básico: O Que Significa “Observabilidade” para os Agentes?
Para mim, a observabilidade dos agentes se divide em algumas áreas-chave, cada uma fornecendo uma perspectiva diferente sobre o funcionamento do seu agente:
1. Rastreio da Execução Passo-a-Passo
Este é o ponto mais crítico. Você precisa saber exatamente o que seu agente está fazendo em cada fase de sua execução. Pense nisso como uma trilha detalhada de migalhas. Para o Projeto Chronos, eu precisava ver:
- Quando começou a recuperar eventos do calendário.
- Os parâmetros usados para a chamada da API do calendário (por exemplo, intervalo de datas).
- A resposta bruta da API do calendário.
- Como ele processou essa resposta.
- O prompt exato enviado ao LLM para resumir as informações do calendário.
- A resposta do LLM.
- Qualquer ferramenta chamada, com suas entradas e saídas.
- Mensagens de erro, não apenas “algo falhou,” mas um erro específico com contexto (por exemplo, “A API do calendário retornou 401 Não Autorizado para o usuário X”).
Esse nível de detalhe é valioso para recriar problemas e entender os pontos decisórios. Meus logs iniciais diziam apenas “Recuperando dados do calendário…” e depois “Resumindo os dados do calendário…” sem nada no meio. Não foi útil quando a recuperação dos dados em si falhou silenciosamente.
2. Rastreio de Prompts e Respostas
O LLM é o cérebro do seu agente. Se você não sabe quais prompts ele está recebendo e quais respostas está dando, está agindo às cegas. Isso inclui:
“`html
- O prompt completo enviado ao LLM (sistema, usuário e qualquer descrição da chamada de função).
- A temperatura, top_p e outros parâmetros de geração.
- A resposta bruta do LLM, incluindo qualquer chamada a ferramentas que ele decidiu fazer.
- Uso de tokens (entrada, saída, total) para rastrear custos e analisar desempenho.
Isso é crucial para a engenharia de prompts. Se um agente fornece respostas sem sentido, ver o prompt exato que recebeu ajuda a entender se o contexto da entrada estava errado ou se o próprio prompt estava mal estruturado.
3. Monitoramento das Chamadas às Ferramentas
Os agentes frequentemente interagem com ferramentas externas ou APIs. Cada interação é um ponto de falha ou comportamento inesperado em potencial. Você deve registrar:
- Qual ferramenta foi chamada.
- Os argumentos exatos passados para a ferramenta.
- A saída bruta da ferramenta.
- Quaisquer erros retornados pela ferramenta ou durante sua execução.
No caso do Chronos, se ele tentou chamar a API do Slack para postar um resumo, eu precisava saber o canal que ele mirou, o conteúdo da mensagem e se a API retornou, por exemplo, um erro 403 Forbidden. Minha configuração anterior apenas me dizia “Tentou postar no Slack.”
4. Snapshot do Estado
Muitos agentes mantêm um estado interno – uma planilha, uma memória, uma lista de fatos que coletaram. Capturar periodicamente esse estado pode ser incrivelmente útil para depuração. Se um agente ficar preso em um ciclo ou tomar uma decisão ruim, ver seus “pensamentos” internos em vários momentos pode revelar onde sua compreensão saiu do caminho.
Isso diz respeito menos ao registro de cada variação de variável e mais à captura de estados-chave para a tomada de decisões. Para o Chronos, isso poderia ser “Compreensão atual do programa do usuário” ou “Pontos-chave dos feeds de notícias até agora.”
Abordagens Práticas: Construindo a Observabilidade
Ok, então como implementamos isso sem nos afogar nos logs? Aqui estão algumas estratégias práticas e trechos de código.
Estratégia 1: Registro Estruturado com Contexto
Esqueça as instruções `print()`. Use uma biblioteca de registro adequada (como o módulo `logging` do Python). Fundamental, enriqueça suas mensagens de log com dados estruturados (JSON, dicionários) em vez de apenas strings simples. Isso torna os logs analisáveis, pesquisáveis e muito mais úteis.
Aqui está um exemplo simplificado em Python:
“““python
import logging
import json
import uuid
from datetime import datetime
# Configuração básica do logger (em um aplicativo real, você o configuraria de forma mais robusta)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter(‘%(levelname)s: %(message)s’)
handler.setFormatter(formatter)
logger.addHandler(handler)
def log_agent_step(agent_id: str, step_name: str, status: str, details: dict = None):
log_data = {
“timestamp”: datetime.now().isoformat(),
“agent_id”: agent_id,
“step_name”: step_name,
“status”: status, # por exemplo, “iniciado”, “completado”, “falhou”
“details”: details if details is not None else {}
}
logger.info(json.dumps(log_data))
class MyAgent:
def __init__(self, agent_id: str = None):
self.agent_id = agent_id if agent_id else str(uuid.uuid4())
self.memory = [] # Memória interna simples
def _fetch_calendar_events(self, user_id: str, date_range: str):
log_agent_step(self.agent_id, “fetch_calendar_events”, “iniciado”,
{“user_id”: user_id, “date_range”: date_range})
try:
# Simula uma chamada API
if “error” in date_range:
raise ValueError(“Erro simulado na API do calendário”)
events = [
{“title”: “Team Sync”, “time”: “10:00 AM”},
{“title”: “Client Meeting”, “time”: “02:00 PM”}
]
log_agent_step(self.agent_id, “fetch_calendar_events”, “completado”,
{“num_events”: len(events), “data_preview”: events[0]})
self.memory.append(f”Eventos do calendário: {events}”)
return events
except Exception as e:
log_agent_step(self.agent_id, “fetch_calendar_events”, “falhou”,
{“error”: str(e), “traceback”: “…”}) # Na vida real, capture traceback
raise
def _summarize_with_llm(self, prompt_text: str):
log_agent_step(self.agent_id, “summarize_with_llm”, “iniciado”,
{“prompt_length”: len(prompt_text), “prompt_preview”: prompt_text[:100]})
try:
# Simula uma chamada LLM
if “fail_llm” in prompt_text:
raise RuntimeError(“Erro simulado na API LLM”)
response = f”Resumo LLM de: {prompt_text[:50]}…”
token_usage = {“input”: len(prompt_text) // 4, “output”: len(response) // 4}
log_agent_step(self.agent_id, “summarize_with_llm”, “completado”,
{“response_length”: len(response), “token_usage”: token_usage,
“llm_response_preview”: response[:100]})
self.memory.append(f”Resumo produzido pelo LLM: {response}”)
return response
except Exception as e:
log_agent_step(self.agent_id, “summarize_with_llm”, “falhou”,
{“error”: str(e), “traceback”: “…”})
raise
def run_daily_briefing(self, user_id: str):
log_agent_step(self.agent_id, “run_daily_briefing”, “iniciado”, {“user_id”: user_id})
try:
calendar_data = self._fetch_calendar_events(user_id, “hoje”)
news_summary = self._summarize_with_llm(“Resuma as principais notícias de hoje…”)
final_briefing_prompt = (
f”Crie um briefing diário baseado em:\n”
f”Calendário: {json.dumps(calendar_data)}\n”
f”Notícias: {news_summary}”
)
final_briefing = self._summarize_with_llm(final_briefing_prompt)
log_agent_step(self.agent_id, “run_daily_briefing”, “completado”,
{“final_briefing_length”: len(final_briefing)})
return final_briefing
except Exception as e:
log_agent_step(self.agent_id, “run_daily_briefing”, “falhou”,
{“error”: str(e), “current_memory”: self.memory}) # Captura memória em caso de falha
raise
# Exemplo de uso
if __name__ == “__main__”:
agent = MyAgent()
print(f”\n— Execução do Agente {agent.agent_id} (Caso de Sucesso) —“)
try:
briefing = agent.run_daily_briefing(“leo_g”)
print(f”Briefing: {briefing[:100]}…”)
except Exception as e:
print(f”Execução do agente falhou: {e}”)
agent_fail = MyAgent()
print(f”\n— Execução do Agente {agent_fail.agent_id} (Caso de Falha do Calendário) —“)
try:
# Simula uma falha do calendário passando “error” em date_range
agent_fail._fetch_calendar_events(“leo_g”, “error_today”)
except Exception as e:
print(f”Execução do agente falhou como esperado: {e}”)
agent_llm_fail = MyAgent()
print(f”\n— Execução do Agente {agent_llm_fail.agent_id} (Caso de Falha LLM) —“)
try:
# Simula uma falha LLM
agent_llm_fail._summarize_with_llm(“fail_llm_please”)
except Exception as e:
print(f”Execução do agente falhou como esperado: {e}”)
“`
Nota como `log_agent_step` captura o ID do agente, o nome do passo, o estado e um dicionário de detalhes pertinentes. Isso torna fácil filtrar os logs por ID do agente, rastrear uma execução única ou procurar todos os passos “falhados”.
Estratégia 2: Observabilidade Centralizada com uma Biblioteca/Serviço Dedicado
Para agentes mais complexos ou ambientes de produção, você rapidamente superará o simples registro em arquivo. É aqui que ferramentas especializadas se destacam. Bibliotecas como `LangSmith` da LangChain (ou similares para outros frameworks) oferecem rastreamento integrado, visualização e depuração para aplicações LLM.
Mesmo que você não esteja usando LangChain, o conceito é transferível. Você pode construir seu próprio wrapper em torno da execução do seu agente que envia eventos estruturados para um serviço de registro (Datadog, Splunk, ELK stack ou até mesmo um simples bucket S3 com processamento Lambda). A chave é padronizar o esquema dos eventos.
Meu projeto Chronos melhorado agora utiliza uma classe personalizada `TraceManager` que envolve operações críticas. Esse manager envia eventos estruturados para um banco de dados local para desenvolvimento e para um serviço de registro em nuvem em produção. Isso me permite ver toda uma “trilha” de cada execução do agente, com passos aninhados e todos os dados associados (prompt, respostas, input/output das ferramentas, erros).
Estratégia 3: Capturar Chamadas LLM e Ferramentas
Muitos SDKs LLM permitem definir callbacks ou interceptores para chamadas API. Use-os! Em vez de registrar manualmente antes e depois de cada prompt LLM, você pode ter um único interceptor que registra automaticamente:
- O preciso endpoint API atingido.
- Headers e corpo da solicitação (especialmente o prompt).
- Headers e corpo da resposta (a conclusão).
- Latência.
- Quaisquer exceções.
Da mesma forma, envolva as chamadas às suas ferramentas. Se você tiver uma ferramenta `search_web`, o wrapper deve registrar a consulta de pesquisa, o motor de busca utilizado e os primeiros N resultados retornados, junto com quaisquer erros.
Recomendações Práticas para o Seu Próximo Projeto de Agente
- Projete para a Observabilidade Primeiro: Não trate isso como uma questão secundária. Pense no que você precisaria para depurar antes de escrever seu primeiro passo do agente.
- Abrace o Registro Estruturado: Esqueça `print()` e `console.log()` para o código de produção. Use uma biblioteca de registro apropriada e retorne dados estruturados (JSON) para cada evento significativo.
- Rastreie Tudo que é Importante: Registre o início e o fim de cada passo importante, todos os prompts e respostas LLM (incluindo parâmetros e contagens de tokens), e cada chamada às ferramentas com seus inputs e outputs.
- Capture o Estado em Caso de Falha: Quando um agente falha, registre seu estado interno ou memória naquele momento. Isso fornece um contexto crucial para entender por que falhou.
- Use IDs Específicos para os Agentes: Atribua um ID único a cada execução do agente (por exemplo, um UUID). Isso permite que você filtre e rastreie facilmente um único caminho de execução em seus logs.
- Visualize Suas Trilhas: Se possível, use ou construa uma ferramenta que possa visualizar esses logs estruturados como uma sequência de eventos. Ver o fluxo torna a depuração infinitamente mais fácil em comparação a garimpar texto bruto. LangSmith faz isso maravilhosamente, mas até mesmo um script personalizado pode renderizar uma linha do tempo HTML simples.
- Monitore os Custos: O uso de tokens LLM é um custo direto. Registre. Isso ajuda você a entender para onde seu dinheiro está indo e a otimizar seus prompts.
Construir agentes é empolgante, mas construir agentes confiáveis é onde está o verdadeiro trabalho (e o verdadeiro valor). E a confiabilidade começa com o conhecimento do que está acontecendo nos bastidores. Minha dolorosa experiência com o Projeto Chronos me ensinou bem essa lição. Não espere o seu “bug misterioso” para se convencer. Comece a registrar de maneira inteligente hoje.
Quais são suas estratégias de observabilidade preferidas para agentes? Entre em contato nos comentários ou nas mídias sociais. Estou sempre interessado em ouvir como outros enfrentam esses desafios!
🕒 Published: