~ / tutoriais /agente-de-ofertas-claude-tool-use-rerank $ _

Hands-on: construindo um agente de ofertas em 80 linhas com Claude, tool use e um reranker

Lucas Souza Lucas Souza 7 min de leitura Tutoriais
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_KEY no 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 pgvector para 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.

Lucas Souza
Lucas Souza

{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
Tutoriais

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.

· 10 min
Anatomia de um Agent Harness: state, tool execution, feedback loops e guardrails
Tutoriais

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.

· 14 min
Chatbot não é agente: o teste dos 3 turnos que separa brinquedo de produto
Notícias

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.

· 11 min
LLM-as-a-Judge: avaliação automatizada do seu agente de ofertas sem abrir planilha
Tutoriais

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.

· 11 min

VirguIA

beer & code assistant

conectando…

Não foi possível iniciar o chat agora.

tocando