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 Agent | O que faz | Equivalente em infra |
|---|---|---|
| Agent | Sistema autônomo que toma ações | Controller (Kubernetes controller, autoscaler) |
| Tool | Função que o agent pode chamar | API endpoint, CLI command |
| Observation | Resultado de uma ação | Output do comando, response da API |
| Reasoning | LLM decidindo próximo passo | Logic no controller loop |
| Agent loop | Ciclo: observar → pensar → agir → repetir | Control loop (reconcile loop no K8s) |
| Planning | Decompor tarefa em steps | Pipeline de CI/CD com stages |
| Guardrails | Limites do que o agent pode fazer | RBAC, 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
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.
| Componente | Tokens típicos por chamada | Observação |
|---|---|---|
| System prompt | ~200 input tokens | Reaparece em toda iteração |
| Tool definitions (5 tools) | ~1000 input tokens | Schema grande pesa |
| Histórico | ~500-3000 input tokens | Cresce a cada volta do loop |
| Resposta / decisão do modelo | ~100-500 output tokens | Tool 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ário | Agent | Script/Automation |
|---|---|---|
| Tarefa bem definida, steps fixos | Overkill | Ideal |
| Tarefa com decisões baseadas em contexto | Ideal | Rígido demais |
| Precisa lidar com edge cases imprevistos | Ideal | Quebraria |
| Precisa ser auditável passo a passo | Mais difícil | Logs claros |
| Custo importa muito | Mais caro (tokens) | Barato |
| Confiabilidade 99.99% | Ainda não | Possí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
- How AI Agents Work (Neo Kim, System Design Newsletter)
- Azure OpenAI function calling
- ReAct: Synergizing Reasoning and Acting in Language Models