Terça-feira, 14h. Seu colega mostra um demo: ele pede pro “agent” verificar o status de 5 servidores, identificar qual tem mais CPU usage, e criar um ticket pra investigação. O agent faz tudo sozinho. Sem scripts. Sem runbooks.

Seu primeiro pensamento: “Isso é só um LLM chamando APIs, certo?”

Sim. E não. O conceito é simples. A parte trabalhosa é fazer isso funcionar com segurança e previsibilidade em produção. É aí que mora a engenharia de verdade.

O mapa pro profissional de infra

Conceito AgentO que fazEquivalente em infra
AgentSistema autônomo que toma açõesController (Kubernetes controller, autoscaler)
ToolFunção que o agent pode chamarAPI endpoint, CLI command
ObservationResultado de uma açãoOutput do comando, response da API
ReasoningLLM decidindo próximo passoLogic no controller loop
Agent loopCiclo: observar → pensar → agir → repetirControl loop (reconcile loop no K8s)
PlanningDecompor tarefa em stepsPipeline de CI/CD com stages
GuardrailsLimites do que o agent pode fazerRBAC, policies

O que é um AI agent (sem buzzwords)

Um agent é um LLM + tools + loop. Ponto.

  • O LLM decide o que fazer baseado no contexto
  • As tools executam ações no mundo (APIs, databases, file system)
  • O loop repete o ciclo até a tarefa estar completa

Se você já trabalhou com Kubernetes controllers, o pattern é idêntico:

Kubernetes Controller:
1. Observe estado atual (kubectl get)
2. Compare com estado desejado (spec)
3. Tome ação pra reconciliar (create/update/delete)
4. Repita

AI Agent:
1. Observe contexto (user request + resultados anteriores)
2. Raciocine sobre o que falta (LLM pensa)
3. Tome ação (chame uma tool)
4. Observe resultado
5. Repita até concluir

O agent loop desmontado

Agent loopFluxo Observe, Think, Act, Resultado, com condições de parada.AGENT LOOPObserve(input)Think(LLM)Act(tool)ResultadoObservação da toolCondição de parada:- Tarefa completa- Limite de iterações atingido- Erro irrecuperável

Implementação básica

import os
import json
from openai import AzureOpenAI

deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT"]

client = AzureOpenAI(
    azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
    api_key=os.environ["AZURE_OPENAI_KEY"],
    api_version="2024-06-01"
)

# Definir tools disponíveis
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_server_metrics",
            "description": "Retorna métricas de CPU, memória e disco de um servidor",
            "parameters": {
                "type": "object",
                "properties": {
                    "hostname": {"type": "string", "description": "Nome do servidor"}
                },
                "required": ["hostname"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "create_ticket",
            "description": "Cria um ticket de investigação no sistema de ITSM",
            "parameters": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
                    "description": {"type": "string"}
                },
                "required": ["title", "severity", "description"]
            }
        }
    }
]

# Implementação das tools (a parte que executa de verdade)
def execute_tool(name, arguments):
    if name == "get_server_metrics":
        # Na vida real: query Azure Monitor, Prometheus, etc.
        return {"cpu": 87.5, "memory": 62.0, "disk": 45.0}
    elif name == "create_ticket":
        # Na vida real: POST pro ServiceNow, Jira, etc.
        return {"ticket_id": "INC-2024-4521", "status": "created"}

def run_agent(user_request, max_iterations=10):
    """Loop principal do agent."""
    messages = [
        {"role": "system", "content": 
         "Você é um agente de operações de infraestrutura. "
         "Use as ferramentas disponíveis pra completar a tarefa. "
         "Quando terminar, responda com o resultado final."},
        {"role": "user", "content": user_request}
    ]
    
    for i in range(max_iterations):
        # Think: LLM decide o que fazer
        response = client.chat.completions.create(
            model=deployment_name,
            messages=messages,
            tools=tools,
            temperature=0
        )
        
        choice = response.choices[0]
        message = choice.message
        
        # Se o modelo decidiu responder (tarefa completa)
        if choice.finish_reason == "stop":
            return message.content
        
        # Se o modelo quer chamar uma tool
        if choice.finish_reason == "tool_calls" and message.tool_calls:
            # Adicionar a decisão do modelo ao histórico
            messages.append(message)
            
            # Executar cada tool chamada
            for tool_call in message.tool_calls:
                args = json.loads(tool_call.function.arguments)
                result = execute_tool(tool_call.function.name, args)
                
                # Adicionar resultado ao histórico
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "name": tool_call.function.name,
                    "content": json.dumps(result)
                })
            continue

        raise RuntimeError(f"finish_reason inesperado: {choice.finish_reason}")
    
    return "Limite de iterações atingido sem completar a tarefa."

ReAct: o pattern mais comum

ReAct (Reasoning + Acting) é o padrão em que o agent alterna entre decidir o próximo passo e chamar tools. No paper original, esse raciocínio aparece em texto. Em produção, muita gente não expõe esse texto inteiro ao usuário. Guarda um scratchpad interno, logs estruturados ou só a sequência de tool calls.

Exemplo didático:

User: "Qual servidor da pool web-prod está com mais CPU?"

Agent (pensando): "Preciso verificar métricas de cada servidor na pool web-prod. 
                   Vou consultar web-prod-01, web-prod-02 e web-prod-03."

Agent (ação): get_server_metrics("web-prod-01") → {"cpu": 45.2, ...}
Agent (ação): get_server_metrics("web-prod-02") → {"cpu": 87.5, ...}
Agent (ação): get_server_metrics("web-prod-03") → {"cpu": 23.1, ...}

Agent (pensando): "web-prod-02 está com 87.5% de CPU, acima dos outros. 
                   Vale abrir um ticket."

Agent (ação): create_ticket(title="CPU alta em web-prod-02", 
                            severity="medium", ...)

Agent (resposta final): "web-prod-02 está com 87.5% de CPU. 
                         Criei o ticket INC-2024-4521 pra investigação."

Cada iteração do loop consome tokens. Um agent que precisa de 5 tool calls volta ao modelo pelo menos 6 vezes: uma pra cada decisão e outra pra fechar a resposta. Isso bate direto em latência e custo.

Tools: o vocabulário do agent

Tools são o que dão poder ao agent. Sem tools, é só um chatbot. Com tools, vira automação inteligente.

Princípios de boas tool definitions

1. Nomes descritivos e sem ambiguidade

Ruim:  "query" (query o quê?)
Bom:   "get_server_metrics" (claro o que faz)

2. Descriptions que o LLM entende

Ruim:  "Gets stuff from the monitoring system"
Bom:   "Retorna métricas atuais (CPU %, memória %, disco %) de um servidor 
        pelo hostname. Use quando precisar verificar saúde de um servidor."

3. Parameters com constraints claros

{
    "severity": {
        "type": "string",
        "enum": ["low", "medium", "high", "critical"],
        "description": "low: informativo. medium: investigar em 24h. "
                      "high: investigar imediatamente. critical: incidente ativo."
    }
}

4. Documente quando usar E quando NÃO usar

"Use esta ferramenta para verificar status de servidores em produção. 
 NÃO use para servidores de desenvolvimento (use get_dev_server_status)."

Quanto custa um agent em produção?

Dá pra estimar sem chute. Conte tokens por chamada e multiplique pelo preço do modelo.

ComponenteTokens típicos por chamadaObservação
System prompt~200 input tokensReaparece em toda iteração
Tool definitions (5 tools)~1000 input tokensSchema grande pesa
Histórico~500-3000 input tokensCresce a cada volta do loop
Resposta / decisão do modelo~100-500 output tokensTool call ou resposta final

Se você assumir um modelo na faixa de US$5 por milhão de input tokens e US$15 por milhão de output tokens, um fluxo com 5 iterações e algo perto de 8000 input + 1500 output tokens sai por volta de US$0.0625 por tarefa.

Em 1000 execuções por dia, isso dá algo perto de US$62.50/dia. Prompt caching, respostas mais curtas e menos iterações derrubam esse número. Tool schema inchado e histórico longo fazem o contrário.

Guardrails: quando o agent pode matar produção

Um agent com acesso a kubectl delete ou az vm deallocate pode causar desastre. Guardrails são essenciais.

Tipos de guardrails

1. Tool-level permissions

# Classificar tools por risco
SAFE_TOOLS = ["get_server_metrics", "list_pods", "get_logs", "create_ticket"]
APPROVAL_REQUIRED = ["restart_service", "scale_resource"]
FORBIDDEN = ["delete_namespace", "deallocate_vm"]

def execute_with_guardrails(tool_name, args, user_role):
    if tool_name in FORBIDDEN:
        return {"error": "Ação bloqueada por policy"}
    
    if tool_name in APPROVAL_REQUIRED:
        approval = request_human_approval(tool_name, args)
        if not approval:
            return {"error": "Ação não aprovada"}
    
    return execute_tool(tool_name, args)

2. Iteration limits

MAX_ITERATIONS = 10  # Nunca deixe o agent rodar infinitamente
MAX_COST = 0.50      # Budget máximo por task em dólares

3. Output validation

# Validar que o agent não está hallucinating tool calls
def validate_tool_call(tool_name, args):
    if tool_name not in KNOWN_TOOLS:
        raise ValueError(f"Tool desconhecida: {tool_name}")
    
    # Validar que hostname existe antes de executar
    if "hostname" in args:
        if not is_valid_hostname(args["hostname"]):
            return {"error": f"Servidor {args['hostname']} não encontrado"}

Agent vs automation script: quando usar qual

CenárioAgentScript/Automation
Tarefa bem definida, steps fixosOverkillIdeal
Tarefa com decisões baseadas em contextoIdealRígido demais
Precisa lidar com edge cases imprevistosIdealQuebraria
Precisa ser auditável passo a passoMais difícilLogs claros
Custo importa muitoMais caro (tokens)Barato
Confiabilidade 99.99%Ainda nãoPossível

Regra prática: se você consegue cobrir quase tudo com script, escreva o script. Agent faz mais sentido quando o espaço de decisão é grande demais pra modelar com if/else sem virar um monstro.

O que levar pra segunda-feira

  • Agent = LLM + tools + loop. É um controller pattern que você já conhece, com um LLM no lugar da lógica hardcoded.
  • Tools são o que definem o poder do agent. Um agent é tão bom quanto as ferramentas que tem acesso.
  • Guardrails não são opcionais em produção. Classifique tools por risco, implemente aprovação humana pra ações destrutivas, limite iterações.
  • Custo escala com complexidade da tarefa. Cada iteração do loop custa tokens. Poucas voltas ficam baratas. Histórico longo e muitas tools fazem a conta subir rápido.
  • Não use agents onde um script resolve. Agents adicionam incerteza. Use-os onde a flexibilidade justifica o trade-off.

O próximo post entra em como projetar um AI agent do zero: escolhas de arquitetura, tool design e estratégias de planning.

Leitura complementar