Python · agentes de IA · Amazon
Dê dados de produtos ao vivo a um agente de IA
Um modelo de linguagem só sabe o que estava nos dados de treinamento. Pergunte quanto custa um produto hoje e ele vai chutar ou dizer que não sabe. A solução é deixar o modelo pedir a resposta ao seu código enquanto escreve: você descreve algumas “ferramentas” – buscar em uma loja, consultar um produto – e, quando o modelo precisa de dados ao vivo, ele pede ao seu programa que execute uma delas.
Seu programa então chama a everydata.io, obtém os dados atuais da página ao vivo e os devolve ao modelo, que responde com preços e disponibilidade reais. Isso funciona da mesma forma com OpenAI, Anthropic e a maioria dos outros provedores; só o formato de empacotamento muda um pouco. Este guia mantém as ferramentas independentes de provedor e mostra os dois formatos.
Atualizado em · por everydata.io
O que você terá no final
- Duas funções Python que buscam resultados de busca e detalhes de produtos da Amazon ao vivo.
- Uma descrição neutra das ferramentas, convertida em definições de ferramentas no estilo OpenAI e no estilo Anthropic.
- Um loop funcional de pergunta e resposta para cada provedor.
O que você precisa
- Python 3.10 ou mais recente,
pip install requestse o SDK do seu provedor de modelos (pip install openaioupip install anthropic). - Uma chave de API gratuita da everydata.io – crie uma aqui – em
EVERYDATA_API_KEY. - Uma chave de API do seu provedor de modelos em
OPENAI_API_KEYouANTHROPIC_API_KEY.
01Como o tool calling funciona, em quatro passos
- Você envia a pergunta junto com uma lista de ferramentas: um nome, uma frase sobre o que cada uma faz e quais entradas ela recebe.
- Em vez de responder imediatamente, o modelo diz “por favor, execute
search_amazoncom a keyword ‘usb c charger 20w’”. - Seu código executa essa função – aqui: uma requisição à everydata.io – e envia o resultado de volta.
- O modelo lê o resultado e escreve a resposta final. Se precisar de mais, ele pede outra ferramenta antes.
O modelo nunca vê sua chave da everydata.io e nunca chama a API por conta própria. É o seu código que decide o que de fato é executado.
02Escreva as funções que buscam os dados
São funções Python comuns. Cada uma faz uma requisição e mantém apenas os campos de que o modelo precisa – uma página de produto completa tem dezenas de campos, e cada campo extra custa tokens sem melhorar a resposta.
"""Two tools an AI model can call: search Amazon and look up one product – live, through everydata.io."""
import json
import os
import re
import time
import requests
API = "https://api.everydata.io"
KEY = os.environ["EVERYDATA_API_KEY"]
def _get(path: str, params: dict) -> dict:
"""One everydata.io request. Errors come back as data, so the model can explain them instead of crashing."""
retried = False
for _ in range(4):
try:
resp = requests.get(f"{API}{path}", params=params, headers={"x-api-key": KEY}, timeout=60)
except requests.RequestException as exc:
return {"error": "network", "detail": str(exc)[:300]}
if resp.status_code == 429:
# Too many requests at once. The answer says how long to wait: "... Try again in 12 seconds."
wait = re.search(r"in (\d+) seconds", resp.text)
time.sleep(int(wait.group(1)) + 1 if wait else 60)
continue
if resp.status_code >= 500 and not retried:
retried = True # the page could not be loaded – try once more (5xx answers are not counted)
time.sleep(5)
continue
if resp.status_code >= 400:
return {"error": resp.status_code, "detail": resp.text[:300]}
return resp.json()
return {"error": 429, "detail": "Still rate limited after waiting. Try again in a minute."}
def search_amazon(keyword: str, domainCode: str = "com") -> dict:
data = _get("/amz/amazon-search-by-keyword-asin",
{"keyword": keyword, "domainCode": domainCode, "excludeSponsored": "true"})
if "error" in data:
return data
keep = ("asin", "productDescription", "price", "currency", "productRating", "countReview")
# Only the first five results and only the fields the model needs: fewer tokens, better answers.
return {"results": [{k: p.get(k) for k in keep} for p in data.get("searchProductDetails", [])[:5]]}
def get_amazon_product(asin: str, domainCode: str = "com") -> dict:
data = _get("/amz/amazon-lookup-product-by-asin", {"asin": asin, "domainCode": domainCode})
if "error" in data:
return data
keep = ("productTitle", "asin", "price", "retailPrice", "currency",
"productRating", "countReview", "warehouseAvailability", "features")
return {k: data.get(k) for k in keep}
FUNCTIONS = {"search_amazon": search_amazon, "get_amazon_product": get_amazon_product}
def run_tool(name: str, args: dict) -> str:
"""Run the tool the model asked for and return the result as a JSON string."""
fn = FUNCTIONS.get(name)
if fn is None:
return json.dumps({"error": f"unknown tool {name}"})
try:
return json.dumps(fn(**args), ensure_ascii=False)
except Exception as exc: # wrong arguments, unexpected data … – tell the model instead of crashing
return json.dumps({"error": "tool failed", "detail": f"{type(exc).__name__}: {exc}"[:300]})Isto é o que o endpoint de busca retorna antes de a função reduzir o resultado (exemplo documentado, abreviado):
{
"responseStatus": "PRODUCT_FOUND_RESPONSE",
"responseMessage": "Product successfully found!",
"domainCode": "com",
"keyword": "laptop",
"numberOfProducts": 26,
"searchProductDetails": [
{
"productDescription": "Acer Aspire 14 AI Copilot+ PC - Intel Core Ultra 5 226V & 14” WUXGA Touch - NPU: Up to 40 Tops - GPU: Up to 53 Tops | Intel Arc 130V | 16GB LPDDR5X | 1TB Gen 4 SSD | Wi-Fi 6E | A14-52MT-59DP",
"asin": "B0FGJ14KRX",
"countReview": 20,
"price": 548.44,
"productRating": "4.0 out of 5 stars",
"currency": "USD"
}
],
"currentPage": 1,
"lastPage": 20
}03Descreva as ferramentas uma vez e empacote por provedor
A descrição é o que o modelo lê para decidir quando usar uma ferramenta, então escreva pensando no modelo: o que ela retorna e quando é útil. As entradas são descritas em JSON Schema, que todos os grandes provedores entendem. Só o envelope ao redor muda.
# Add to tools.py – one description per tool, written for the model, in plain JSON Schema.
TOOL_SPECS = [
{
"name": "search_amazon",
"description": "Search Amazon right now. Returns up to 5 products with ASIN, title, current price, "
"currency and rating. Use it when the user asks what is available or what something costs.",
"parameters": {
"type": "object",
"properties": {
"keyword": {"type": "string", "description": "What to search for, e.g. 'usb c charger 20w'"},
"domainCode": {"type": "string", "description": "Amazon marketplace: com, de, co.uk, fr, it, es …"},
},
"required": ["keyword"],
},
},
{
"name": "get_amazon_product",
"description": "Live details of one Amazon product by ASIN: title, price, list price, availability, "
"rating and key features. A price of 0 means the page shows no price right now.",
"parameters": {
"type": "object",
"properties": {
"asin": {"type": "string", "description": "10-character Amazon product ID, e.g. B07ZPKN6YR"},
"domainCode": {"type": "string", "description": "Amazon marketplace: com, de, co.uk, fr, it, es …"},
},
"required": ["asin"],
},
},
]
# OpenAI-style (Chat Completions): the schema goes under "function" -> "parameters".
OPENAI_TOOLS = [{"type": "function", "function": spec} for spec in TOOL_SPECS]
# Anthropic-style (Messages API): the same schema is called "input_schema".
ANTHROPIC_TOOLS = [
{"name": s["name"], "description": s["description"], "input_schema": s["parameters"]}
for s in TOOL_SPECS
]Os nomes dos parâmetros são os mesmos da API (keyword, asin, domainCode), então os argumentos do modelo podem ser passados diretamente para as funções do passo 2.
04Execute com uma API no estilo Anthropic
O loop envia a pergunta, executa cada ferramenta que o modelo pede, devolve os resultados como blocos tool_result e para quando o modelo responde em texto.
import anthropic
from tools import ANTHROPIC_TOOLS, run_tool
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY
MAX_ROUNDS = 5 # hard limit on tool rounds – every tool call is one everydata.io request
messages = [{"role": "user", "content": "What does a 20W USB-C charger cost on amazon.com right now?"}]
for _ in range(MAX_ROUNDS):
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
tools=ANTHROPIC_TOOLS,
messages=messages,
)
if response.stop_reason != "tool_use":
break
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": block.id, "content": run_tool(block.name, block.input)}
for block in response.content
if block.type == "tool_use"
],
})
else:
raise SystemExit(f"Stopped after {MAX_ROUNDS} tool rounds without a final answer. "
"Ask a more specific question or raise MAX_ROUNDS.")
print("".join(block.text for block in response.content if block.type == "text"))05Execute com uma API no estilo OpenAI
O mesmo loop, com formatos de mensagem diferentes: os pedidos de ferramenta chegam em tool_calls, com os argumentos como string JSON, e cada resultado volta como uma mensagem com o papel tool. Muitos outros provedores e servidores de modelos locais também aceitam esse formato no estilo OpenAI.
import json
import os
from openai import OpenAI
from tools import OPENAI_TOOLS, run_tool
client = OpenAI() # reads OPENAI_API_KEY
MODEL = os.environ["OPENAI_MODEL"] # any chat model that supports function calling
MAX_ROUNDS = 5 # hard limit on tool rounds – every tool call is one everydata.io request
messages = [{"role": "user", "content": "What does a 20W USB-C charger cost on amazon.com right now?"}]
for _ in range(MAX_ROUNDS):
response = client.chat.completions.create(model=MODEL, messages=messages, tools=OPENAI_TOOLS)
message = response.choices[0].message
if not message.tool_calls:
break
messages.append(message)
for call in message.tool_calls:
try:
args = json.loads(call.function.arguments)
except json.JSONDecodeError:
result = json.dumps({"error": "arguments were not valid JSON"})
else:
result = run_tool(call.function.name, args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": result})
else:
raise SystemExit(f"Stopped after {MAX_ROUNDS} tool rounds without a final answer. "
"Ask a more specific question or raise MAX_ROUNDS.")
print(message.content)06Antes de colocar na frente dos usuários
- Mantenha o limite rígido de rodadas de ferramentas. Cada chamada de ferramenta é uma requisição à everydata.io, então um loop sem limite pode consumir sua cota. Quando o limite é atingido, os scripts acima param com uma mensagem clara em vez de imprimir meia resposta.
- Retorne erros como dados (
_geterun_toolfazem isso). Assim, o modelo pode dizer ao usuário que uma página não pôde ser carregada, em vez de falhar em silêncio._getespera quando você envia requisições demais de uma vez (429) e tenta mais uma vez um carregamento de página que falhou (5xx); respostas 5xx não são descontadas da sua cota. - Reduza os resultados. Cinco resultados de busca com seis campos cada costumam bastar para uma boa resposta.
- Informe ao modelo, na descrição da ferramenta, que um preço
0significa “nenhum preço exibido no momento”, para que ele não apresente o produto como gratuito.
Quer as mesmas ferramentas no Claude Desktop, no Claude Code ou no ChatGPT em vez do seu próprio app? Empacote-as em um servidor MCP – veja Crie uma ferramenta MCP para dados da web ao vivo.
Endpoints usados neste guia
Search Products · API Amazon
Product Details by ASIN · API Amazon
O que mais você pode fazer com esses dados
Qualquer página de produto da Amazon em JSON estruturado: título, marca, preço, nota, imagens, bullets, especificações, variações e avaliações. Por URL ou ASIN.
Resultados de busca da Amazon por palavra-chave em JSON: ASINs, preços, notas, selo Prime, volume de vendas e patrocinados por página, em qualquer marketplace.
Monitore preços da Amazon em tempo real em 15+ marketplaces. Preço da buy box, preço de tabela, estoque e histórico de preços – uma chamada JSON por ASIN.
Teste com seus próprios dados
O plano gratuito inclui 100 requisições por mês, válidas em todas as plataformas – o suficiente para seguir este guia do início ao fim.