Python · Agenti AI · Amazon
Dai a un agente AI dati prodotto live
Un modello linguistico conosce solo ciò che c'era nei suoi dati di addestramento. Chiedigli quanto costa oggi un prodotto e tirerà a indovinare, oppure dirà che non lo sa. La soluzione è lasciare che il modello chieda la risposta al tuo codice mentre scrive: descrivi alcuni “tool” – cercare in un negozio, consultare un prodotto – e quando il modello ha bisogno di dati live chiede al tuo programma di eseguirne uno.
Il tuo programma chiama quindi everydata.io, ottiene i dati attuali dalla pagina live e li restituisce al modello, che risponde con prezzi e disponibilità reali. Funziona allo stesso modo con OpenAI, Anthropic e la maggior parte degli altri provider; cambia solo leggermente il formato. Questa guida mantiene i tool indipendenti dal provider e mostra entrambi i formati.
Aggiornato il · di everydata.io
Cosa avrai alla fine
- Due funzioni Python che recuperano risultati di ricerca e dettagli prodotto Amazon live.
- Una descrizione neutra dei tool, convertita in definizioni di tool in stile OpenAI e in stile Anthropic.
- Un loop domanda-risposta funzionante per ciascun provider.
Cosa ti serve
- Python 3.10 o superiore,
pip install requestse l'SDK del tuo provider di modelli (pip install openaioppurepip install anthropic). - Una chiave API gratuita di everydata.io – creala qui – in
EVERYDATA_API_KEY. - Una chiave API del tuo provider di modelli in
OPENAI_API_KEYoANTHROPIC_API_KEY.
01Come funziona il tool calling, in quattro passaggi
- Invii la domanda insieme a un elenco di tool: un nome, una frase su cosa fa ciascuno e quali input accetta.
- Invece di rispondere subito, il modello replica “esegui
search_amazoncon la parola chiave ‘usb c charger 20w’”. - Il tuo codice esegue quella funzione – qui: una richiesta a everydata.io – e rimanda il risultato.
- Il modello legge il risultato e scrive la risposta finale. Se gli serve altro, prima chiede un altro tool.
Il modello non vede mai la tua chiave everydata.io e non chiama mai l'API direttamente. È il tuo codice a decidere cosa viene eseguito davvero.
02Scrivi le funzioni che recuperano i dati
Sono semplici funzioni Python. Ognuna fa una richiesta e conserva solo i campi che servono al modello – una pagina prodotto completa ha decine di campi, e ogni campo in più costa token senza migliorare la risposta.
"""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]})Ecco cosa restituisce l'endpoint di ricerca prima che la funzione lo riduca (esempio documentato, abbreviato):
{
"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
}03Descrivi i tool una volta, poi adattali a ogni provider
La descrizione è ciò che il modello legge per decidere quando usare un tool, quindi scrivila per il modello: cosa restituisce e quando è utile. Gli input sono descritti in JSON Schema, che tutti i principali provider comprendono. Cambia solo l'involucro attorno.
# 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
]I nomi dei parametri corrispondono all'API (keyword, asin, domainCode), quindi gli argomenti del modello possono essere passati direttamente alle funzioni del passaggio 2.
04Eseguilo con un'API in stile Anthropic
Il loop invia la domanda, esegue ogni tool richiesto dal modello, restituisce i risultati come blocchi tool_result e si ferma quando il modello risponde con del testo.
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"))05Eseguilo con un'API in stile OpenAI
Stesso loop, formato dei messaggi diverso: le richieste di tool arrivano in tool_calls, con gli argomenti come stringa JSON, e ogni risultato torna indietro come messaggio con ruolo tool. Molti altri provider e server di modelli locali accettano anch'essi questo formato in stile 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)06Prima di metterlo davanti agli utenti
- Mantieni il limite rigido sui round di tool. Ogni chiamata a un tool è una richiesta a everydata.io, quindi un loop senza limite può esaurire la tua quota. Quando il limite viene raggiunto, gli script sopra si fermano con un messaggio chiaro invece di stampare mezza risposta.
- Restituisci gli errori come dati (
_geterun_toollo fanno). Il modello può così dire all'utente che una pagina non è stata caricata invece di fallire in silenzio._getattende quando invii troppe richieste insieme (429) e riprova una volta un caricamento di pagina fallito (5xx); le risposte 5xx non vengono conteggiate nella tua quota. - Riduci i risultati. Cinque risultati di ricerca con sei campi ciascuno di solito bastano per una buona risposta.
- Spiega al modello, nella descrizione del tool, che un prezzo pari a
0significa “nessun prezzo mostrato al momento”, così non presenta il prodotto come gratuito.
Vuoi gli stessi tool in Claude Desktop, Claude Code o ChatGPT invece che nella tua app? Racchiudili in un server MCP – vedi Crea un tool MCP per dati web live.
Endpoint usati in questa guida
Search Products · API Amazon
Product Details by ASIN · API Amazon
Cos'altro puoi fare con questi dati
Qualsiasi pagina prodotto Amazon in JSON: titolo, marchio, prezzo, valutazione, immagini, punti elenco, specifiche, varianti e recensioni. Per URL o ASIN.
Risultati di ricerca Amazon per parola chiave in JSON: ASIN, prezzi, valutazioni, flag Prime, volume di vendita e annunci sponsorizzati, su ogni marketplace.
Monitora i prezzi Amazon in tempo reale su oltre 15 marketplace: prezzo della buy box, di listino, disponibilità e storico prezzi. Una chiamata JSON per ASIN.
Provalo con i tuoi dati
Il piano gratuito include 100 richieste al mese, utilizzabili su ogni piattaforma – abbastanza per seguire questa guida dall'inizio alla fine.