Capstone: agent Python completo com Claude SDK
⏱ 18 min·⭐ 80 XP
Pré-requisitos (0/1)0%
- ⬜📓 Jupyter pra engenharia: notebook reprodutível(Python para Engenheiros)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Projeto: triage assistant
Agente que recebe texto livre de cliente (bug report, feature request, dúvida), classifica (bug/feature/question), cria ticket em Linear, notifica Slack, retorna resumo. Aplicação de tudo da trilha Python.
Stack
| Camada | O que ela resolve | O que quebra sem ela |
|---|---|---|
| Validação de entrada e saída | Recusa pedido malformado e restringe o que o modelo pode devolver | Categoria inventada atravessa o sistema e vira ticket errado |
| Laço de agente com teto | Encerra por sucesso ou por limite | Ferramenta que devolve vazio faz o laço girar até a fatura chegar |
| Rastro por turno | Mostra qual ferramenta foi chamada, com que argumento | Você fica sabendo QUE deu errado, nunca onde |
| Conjunto de avaliação | Transforma "melhorou" em número | Cada mudança de prompt é aposta, e a regressão passa |
| Respostas gravadas no teste | Suíte determinística e sem custo | Teste lento e caro deixa de ser executado — e some |
| Teto de gasto | Limita o prejuízo de um defeito | Um laço com erro em produção cobra em dinheiro real |
CamadaValidação de entrada e saída
O que ela resolveRecusa pedido malformado e restringe o que o modelo pode devolver
O que quebra sem elaCategoria inventada atravessa o sistema e vira ticket errado
CamadaLaço de agente com teto
O que ela resolveEncerra por sucesso ou por limite
O que quebra sem elaFerramenta que devolve vazio faz o laço girar até a fatura chegar
CamadaRastro por turno
O que ela resolveMostra qual ferramenta foi chamada, com que argumento
O que quebra sem elaVocê fica sabendo QUE deu errado, nunca onde
CamadaConjunto de avaliação
O que ela resolveTransforma "melhorou" em número
O que quebra sem elaCada mudança de prompt é aposta, e a regressão passa
CamadaRespostas gravadas no teste
O que ela resolveSuíte determinística e sem custo
O que quebra sem elaTeste lento e caro deixa de ser executado — e some
CamadaTeto de gasto
O que ela resolveLimita o prejuízo de um defeito
O que quebra sem elaUm laço com erro em produção cobra em dinheiro real
- +
- v2 (schemas)
- (endpoint)
- SDK + agent loop
- pra Linear/Slack
- pra tracing
- pra testes
- +
🗺️ Anatomia do Triage Assistant — as quatro camadas do projeto
Entrada
Webhook de ticket novo
Validação com Pydantic
Fila para não bloquear o webhook
Decisão
Prompt classifica urgência
Roteador escolhe a fila certa
Confiança baixa → escala para humano
Execução
Chama API do sistema de tickets
Atualiza status e tag
Registra a decisão tomada
Observabilidade
Log estruturado por decisão
Métrica de acerto vs correção humana
Alerta se a fila de escalonamento crescer
Código (esqueleto)
# src/schemas.py
from pydantic import BaseModel, Field
from typing import Literal
class TriageInput(BaseModel):
text: str = Field(min_length=10, max_length=5000)
client_id: str
class TicketCategory(BaseModel):
kind: Literal["bug", "feature", "question"]
priority: Literal["low", "med", "high"]
summary: str = Field(max_length=200)
# src/tools.py
from anthropic import Anthropic
async def create_linear_ticket(title: str, description: str, team: str) -> str:
"""Cria ticket no Linear e retorna url."""
...
async def notify_slack(channel: str, text: str) -> None:
"""Envia mensagem no Slack."""
...
TOOLS = [
{
"name": "create_linear_ticket",
"description": "Create a Linear issue...",
"input_schema": TicketInput.model_json_schema(),
},
{
"name": "notify_slack",
"description": "Send Slack message...",
"input_schema": SlackInput.model_json_schema(),
},
]
# src/agent.py
async def run_agent(input: TriageInput) -> TicketCategory:
client = Anthropic()
messages = [{"role": "user", "content": input.text}]
for _ in range(5): # max_steps
resp = client.messages.create(
model="claude-sonnet-4",
max_tokens=1024,
tools=TOOLS,
messages=messages,
)
if resp.stop_reason == "tool_use":
for block in resp.content:
if block.type == "tool_use":
result = await execute_tool(block.name, block.input)
messages.append({"role": "assistant", "content": resp.content})
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
}],
})
continue
# Final answer
return TicketCategory.model_validate_json(resp.content[0].text)
raise Exception("max steps reached")
# src/main.py
from fastapi import FastAPI
app = FastAPI()
@app.post("/triage")
async def triage(input: TriageInput) -> TicketCategory:
return await run_agent(input)Quiz rápido
No capstone, o resultado da classificação é um modelo com campos `Literal` em vez de texto livre. Por que isso é decisivo num agente?
Observability
from langfuse.decorators import observe
@observe(name="triage_agent")
async def run_agent(input: TriageInput) -> TicketCategory:
# cada API call é span-automático
...
# Langfuse UI mostra:
# - trace por request
# - cada turn do loop como span
# - tool calls com input/output
# - latency, tokens, costTestes
# Mock do LLM — VCR ou Anthropic replay
import pytest
from unittest.mock import patch
@pytest.mark.asyncio
async def test_agent_classifies_bug():
with patch("src.agent.Anthropic") as mock:
mock.return_value.messages.create.return_value = ... # canned response
result = await run_agent(TriageInput(text="app crasha ao abrir", client_id="c1"))
assert result.kind == "bug"✅
Este capstone exercita a trilha inteira. Ao terminar, você tem um agent FUNCIONAL em produção — type-safe, observado, testado. Esse é o nível que um engenheiro de IA sério entrega.
Perguntas frequentes
❓ Como estruturar um agente em Python?
Com esquemas validados na fronteira, ferramentas como funções tipadas, o laço explícito com teto de passos, e registro por chamada. A estrutura importa mais que o arcabouço: com esses quatro elementos, trocar de biblioteca é refatoração local.
❓ O que testar num agente?
As ferramentas isoladas com teste comum, e o laço com o modelo substituído por dublê que devolve sequência fixa de pedidos — incluindo respostas ruins. Assim a lógica fica coberta sem pagar inferência nem depender de saída não determinística.
❓ O que falta num agente que "já funciona"?
Quase sempre quatro coisas: teto de gasto, registro de trajetória, conjunto de avaliação e caminho de degradação quando a ferramenta falha. Sem elas, ele funciona na demonstração e produz surpresa na fatura e no incidente.
Fixando
Quiz rápido
Por que instrumentar o laço do agente com tracing, e não apenas registrar a resposta final?
Quiz rápido
Como testar o agente sem chamar o modelo de verdade a cada execução da suíte?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…