20 % de remise sur toutes les offres payantes avec le code

Python · Agents IA · Amazon

Donner des données produits en direct à un agent IA

Un modèle de langage ne connaît que ce qui figurait dans ses données d'entraînement. Demandez-lui combien coûte un produit aujourd'hui, il devinera ou dira qu'il ne sait pas. La solution : laisser le modèle interroger votre code pendant qu'il rédige sa réponse. Vous décrivez quelques « outils » – rechercher dans une boutique, consulter un produit – et quand le modèle a besoin de données en direct, il demande à votre programme d'en exécuter un.

Votre programme appelle alors everydata.io, obtient les données actuelles de la page en direct et les renvoie au modèle, qui répond avec de vrais prix et une vraie disponibilité. Le principe est le même avec OpenAI, Anthropic et la plupart des autres fournisseurs ; seul l'emballage diffère légèrement. Ce guide garde des outils indépendants du fournisseur et montre les deux emballages.

Mis à jour le · par everydata.io

Ce que vous aurez à la fin

  • Deux fonctions Python qui récupèrent en direct les résultats de recherche et les détails produits Amazon.
  • Une description d'outils neutre, convertie en définitions d'outils au format OpenAI et au format Anthropic.
  • Une boucle question-réponse fonctionnelle pour chaque fournisseur.

Ce dont vous avez besoin

  • Python 3.10 ou plus récent, pip install requests et le SDK de votre fournisseur de modèle (pip install openai ou pip install anthropic).
  • Une clé API everydata.io gratuite – créez-la ici – dans EVERYDATA_API_KEY.
  • Une clé API de votre fournisseur de modèle dans OPENAI_API_KEY ou ANTHROPIC_API_KEY.

01Le tool calling en quatre étapes

  • Vous envoyez la question avec une liste d'outils : un nom, une phrase décrivant ce que fait chacun, et les entrées qu'il accepte.
  • Au lieu de répondre directement, le modèle répond « exécute search_amazon avec le keyword 'usb c charger 20w' ».
  • Votre code exécute cette fonction – ici, une requête vers everydata.io – et renvoie le résultat.
  • Le modèle lit le résultat et rédige la réponse finale. S'il lui faut davantage, il demande d'abord un autre outil.

Le modèle ne voit jamais votre clé everydata.io et n'appelle jamais l'API lui-même. C'est votre code qui décide de ce qui s'exécute réellement.

02Écrivez les fonctions qui récupèrent les données

Ce sont de simples fonctions Python. Chacune effectue une requête et ne garde que les champs utiles au modèle – une page produit complète compte des dizaines de champs, et chaque champ superflu coûte des tokens sans améliorer la réponse.

tools.py
"""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]})

Voici ce que renvoie l'endpoint de recherche avant que la fonction ne le réduise (exemple documenté abrégé) :

Exemple de réponse · GET /amz/amazon-search-by-keyword-asin
{
  "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
}

03Décrivez les outils une fois, puis adaptez l'emballage à chaque fournisseur

La description est ce que lit le modèle pour décider quand utiliser un outil : rédigez-la donc pour lui, en indiquant ce que l'outil renvoie et quand il est utile. Les entrées sont décrites en JSON Schema, que tous les grands fournisseurs comprennent. Seule l'enveloppe diffère.

tools.py (suite)
# 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
]

Les noms de paramètres correspondent à ceux de l'API (keyword, asin, domainCode) : les arguments du modèle peuvent être transmis tels quels aux fonctions de l'étape 2.

04Exécutez-le avec une API de type Anthropic

La boucle envoie la question, exécute chaque outil demandé par le modèle, renvoie les résultats sous forme de blocs tool_result et s'arrête quand le modèle répond en texte.

ask_claude.py
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"))

05Exécutez-le avec une API de type OpenAI

Même boucle, formats de messages différents : les demandes d'outils arrivent dans tool_calls, avec les arguments sous forme de chaîne JSON, et chaque résultat repart dans un message de rôle tool. De nombreux autres fournisseurs et serveurs de modèles locaux acceptent aussi ce format de type OpenAI.

ask_openai.py
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)

06Avant de le mettre entre les mains des utilisateurs

  • Conservez la limite stricte du nombre de tours d'outils. Chaque appel d'outil correspond à une requête everydata.io : une boucle sans limite peut épuiser votre quota. Quand la limite est atteinte, les scripts ci-dessus s'arrêtent avec un message clair au lieu d'afficher une réponse à moitié écrite.
  • Renvoyez les erreurs sous forme de données (_get et run_tool le font). Le modèle peut alors expliquer à l'utilisateur qu'une page n'a pas pu être chargée, au lieu d'échouer en silence. _get patiente lorsque vous envoyez trop de requêtes à la fois (429) et réessaie une fois un chargement de page échoué (5xx) ; les réponses 5xx ne sont pas décomptées de votre quota.
  • Réduisez les résultats. Cinq résultats de recherche de six champs chacun suffisent généralement pour une bonne réponse.
  • Indiquez au modèle, dans la description de l'outil, qu'un prix de 0 signifie « aucun prix affiché pour l'instant », pour qu'il ne présente pas le produit comme gratuit.

Vous voulez les mêmes outils dans Claude Desktop, Claude Code ou ChatGPT plutôt que dans votre propre application ? Encapsulez-les dans un serveur MCP – voir Créer un outil MCP pour des données web en direct.

Endpoints utilisés dans ce guide

GET/amz/amazon-search-by-keyword-asin

Search Products · API Amazon

GET/amz/amazon-lookup-product-by-asin

Product Details by ASIN · API Amazon

Tous les paramètres dans la référence de l’API

Essayez avec vos propres données

L’offre gratuite comprend 100 requêtes par mois, utilisables sur toutes les plateformes – de quoi suivre ce guide de bout en bout.