Hands-on: construindo um agente de ofertas em 80 linhas com Claude, tool use e um reranker
Você abre quinze abas. Compara preço, frete, vendedor, reputação. Volta na primeira aba para conferir se aquela oferta ainda existe. Cansa.
Agente de ofertas resolve isso. Mas a maioria dos tutoriais para por aí: prompt bonito, busca genérica, output em texto solto. Em produção isso quebra na primeira semana.
Neste tutorial você monta um esqueleto que funciona em 80 linhas de Python: agent loop com Claude usando tool use, busca na web, reranqueamento com Cohere Rerank v3.5 e saída em JSON estruturado validado via strict tool use. É a base que a gente expande no workshop — você sai daqui com algo que roda hoje.
TL;DR
- O que é: agente de ofertas que faz busca → rerank → síntese → JSON estruturado em ~80 linhas de Python.
- Stack/Modelos:
claude-sonnet-4-6,rerank-v3.5(Cohere), Tavily para busca. - Custo/Acesso: chave Anthropic (paga) + Cohere (free tier generoso) + Tavily (1.000 buscas grátis/mês).
- Pré-requisito mental: entender que agente é um loop, não uma única chamada.
Por que esse stack importa
Tem três coisas acontecendo aqui que valem ser entendidas separadas.
Tool use é a primitiva do agente. Quando você define uma tool, o Claude responde com stop_reason: "tool_use" e blocos tool_use no content. Seu código executa o que pediram, devolve um tool_result, e o ciclo recomeça até o modelo decidir que terminou. É só isso. Ler a doc oficial de tool use por dez minutos economiza meses de fricção.
Reranker existe porque busca densa erra na última milha. Você pega 10 candidatos do Tavily ou do web_search nativo. Os 3 primeiros raramente são os 3 melhores para sua intenção. O rerank-v3.5 da Cohere reordena com base na intenção do query — score normalizado de 0 a 1, contexto de 4.096 tokens, suporte a 100+ idiomas (changelog). Atenção: a Cohere alerta que o score não é linear — 0.9 não vale duas vezes mais que 0.45.
JSON estruturado vira contrato com produto. Output em texto solto é veneno para integração. Com strict: true na definição da tool, o Claude valida o schema antes de devolver. Você nunca mais vai parsear regex de markdown achando que isso é "AI engineering".
Pré-requisitos
- [ ] Python 3.11+
- [ ]
pip install anthropic cohere httpx - [ ]
ANTHROPIC_API_KEY,COHERE_API_KEY,TAVILY_API_KEYno ambiente - [ ] Saber o que é um while loop. Sério.
Se quiser trocar Tavily por SerpAPI, Brave ou pelo web_search nativo do Anthropic (lançado em maio de 2025 a $10/1k buscas), o esqueleto continua igual — só muda a função tavily_search.
Mão na massa — o agente em 80 linhas
Passo 1: Definir as duas ferramentas
O agente vai ter exatamente duas ferramentas: uma para buscar candidatos na web, outra para devolver a oferta final em JSON.
SEARCH_TOOL = {
"name": "search_web",
"description": "Busca ofertas na web. Devolve snippets re-ranqueados.",
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
}
EXTRACT_TOOL = {
"name": "extract_product",
"description": "Devolve a melhor oferta verificável encontrada.",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"price_brl": {"type": "number"},
"store": {"type": "string"},
"url": {"type": "string"},
"reason": {"type": "string"},
},
"required": ["title", "price_brl", "store", "url", "reason"],
"additionalProperties": False,
},
}
strict: true em EXTRACT_TOOL é o que transforma a tool em "saída JSON garantida". Sem isso, o Claude pode improvisar campos.
Passo 2: Wrapper de busca + rerank
A busca crua devolve dez resultados. O rerank corta para os cinco que realmente importam dado o query.
import os, httpx, cohere
co = cohere.ClientV2()
TAVILY = os.environ["TAVILY_API_KEY"]
def tavily_search(query: str, k: int = 10) -> list[str]:
r = httpx.post("https://api.tavily.com/search", json={
"api_key": TAVILY, "query": query, "max_results": k,
}, timeout=30).json()
return [f"{x['title']} | {x['url']}\n{x['content']}" for x in r["results"]]
def rerank(query: str, docs: list[str], k: int = 5) -> list[str]:
out = co.rerank(model="rerank-v3.5", query=query, documents=docs, top_n=k)
return [docs[r.index] for r in out.results]
O rerank-v3.5 é multilíngue por padrão. Se sua busca volta um snippet em inglês e a query é em português, o reranker entende.
Passo 3: O agent loop
Esse é o coração. Cinco linhas conceituais: chama, lê, decide, executa, repete.
import json, anthropic
claude = anthropic.Anthropic()
SYSTEM = (
"Você é um agente de ofertas. Use search_web (1 ou 2 vezes) para reunir "
"candidatos, depois chame extract_product UMA vez com a melhor oferta. "
"Nunca invente preço — só use o que apareceu no contexto."
)
def find_best(query: str) -> dict:
msgs = [{"role": "user", "content": f"Quero a melhor oferta de: {query}"}]
while True:
r = claude.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048,
system=SYSTEM,
tools=[SEARCH_TOOL, EXTRACT_TOOL],
messages=msgs,
)
msgs.append({"role": "assistant", "content": r.content})
if r.stop_reason != "tool_use":
raise RuntimeError("Agente parou sem extrair produto.")
results = []
for block in r.content:
if block.type != "tool_use":
continue
if block.name == "extract_product":
return block.input
if block.name == "search_web":
snippets = tavily_search(block.input["query"])
top = rerank(query, snippets)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": "\n\n".join(top),
})
msgs.append({"role": "user", "content": results})
if __name__ == "__main__":
print(json.dumps(
find_best("teclado mecânico 60% RGB hot-swap"),
indent=2, ensure_ascii=False,
))
Repara no truque: quando o Claude chama extract_product, a função retorna direto. Esse é o sinal de "terminei". A tool dupla papel: contrato de saída + sinal de parada.
Passo 4: Rodar
python agent.py
Saída esperada (formato, não conteúdo):
{
"title": "Keychron K6 Pro Hot-Swap RGB",
"price_brl": 899.00,
"store": "Mercado Livre",
"url": "https://...",
"reason": "Hot-swap, RGB per-key, ANSI 65%, melhor preço entre os 5 candidatos."
}
Limitações e pontos de atenção
Preço alucina. O LLM lê "R$ 899,00" no snippet e às vezes devolve 89.90. No workshop a gente trata isso com regex pós-processamento + um segundo passo de verificação visitando a URL. Para um esqueleto, deixa registrado: nunca confie em campo numérico vindo direto de LLM sem checar contra a fonte.
Sem evals, é fé. Esse agente não tem teste. Para produção você precisa de um conjunto de queries com gabarito ("teclado X custa entre Y e Z na loja W") e medir taxa de acerto a cada mudança no prompt ou modelo.
Snippet ≠ página. Tavily devolve um resumo. O preço real pode estar atrás de um clique, num modal de frete, ou variar com CEP. Para sério, plugue um web_fetch na ponta.
Custo escala com loop. Cada iteração é uma chamada. Se o agente entrar em loop infinito (gera, eu já vi), o boleto aparece. Limite com um contador de iterações ou com max_uses na tool de busca.
Próximos passos no workshop
Esse esqueleto é o ponto zero. No workshop Construindo Agentes de IA com Claude, a gente expande para:
- Comparação multi-loja com ranking ponderado (preço + reputação + frete).
- Cache semântico com
pgvectorpara queries repetidas. - Evals automatizadas com LLM-as-a-judge.
- Empacotamento como API HTTP com filas e observabilidade.
Se você quer construir produto real com IA — não prompt mágico, engenharia de verdade —, garanta sua vaga aqui. As turmas são pequenas e fecham rápido.
FAQ
Posso trocar Claude por outro modelo?
Pode. O agent loop é o mesmo padrão para qualquer LLM com tool use (OpenAI, Gemini, Mistral). O que muda é a sintaxe dos blocos. O Claude tem strict tool use que economiza muito retry de validação.
Por que não usar o web_search nativo do Anthropic e pular o Tavily?
Pode usar. A vantagem do server tool é zero infra. A desvantagem é menos controle sobre os snippets e menos espaço para encaixar o reranker. Como tutorial didático, o tool client deixa o loop visível. Em produção, escolha pela conta.
O reranker vale o custo a mais? Em busca curta (top-3) com queries claras, ganho marginal. Em queries ambíguas e com top-K maior, o reranker é a diferença entre o agente acertar e errar.
E se o agente entrar em loop? Adicione um contador. Cinco iterações são suficientes para 95% dos casos. Se passar disso, retorne erro e investigue o prompt.
Conclusão
Em 80 linhas você tem agent loop + tool use + busca + rerank + JSON estruturado. Isso é mais do que a maioria dos "tutoriais de agente" de Twitter entrega.
O próximo passo não é adicionar mais ferramentas. É medir. Sem evals, qualquer agente parece mágico até o primeiro cliente real reclamar.
Quer construir esse tipo de coisa com gente que está fazendo isso em produção? Entra no Clã Beer & Code — é pra isso que a gente existe.
{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.
Você também pode gostar
Renderização que converte: do JSON do agente ao card clicável
O agente devolve JSON impecável e o front mostra texto cru. Conexão zero. Aqui a gente fecha o ciclo: do schema Zod ao card de produto clicável com structured outputs do Claude e generative UI no Vercel AI SDK 5.
Anatomia de um Agent Harness: state, tool execution, feedback loops e guardrails
Harness é o software que envolve o LLM e separa um demo bonito de um agente que aguenta produção. Quebro a anatomia em cinco peças obrigatórias: estado persistente, roteador de ferramentas, validação de I/O, loop de raciocínio e limites de segurança. É o mapa mental que abre a série de posts sobre engenharia de agentes.
Chatbot não é agente: o teste dos 3 turnos que separa brinquedo de produto
Três perguntas simples sobre um produto real — preço hoje, reviews recentes, disponibilidade no CEP — quebram qualquer chatbot cru. O que separa brinquedo de produto não é o modelo. É o harness: a camada que transforma um LLM em agente confiável, com tool use, estado e validação contra o mundo real.
LLM-as-a-Judge: avaliação automatizada do seu agente de ofertas sem abrir planilha
Como montar um juiz LLM que pontua cada resposta do agente contra uma rubrica objetiva: preço correto, link válido, sentimento de review coerente. Você sai do achismo e transforma iteração em ciclo mensurável.