Nous utilisons des cookies. Les cookies et le stockage du navigateur vous gardent connecté et mémorisent vos préférences. Avec votre accord, nous mesurons aussi l’utilisation du site. Vous pouvez à tout moment. Consultez notre politique relative aux cookies.

Guide : agents avec appel d'outils

Premiers pas

Guide : agents avec appel d'outils

Construisez une boucle d'agent avec appels d'outils parallèles, limite d'étapes, sortie structurée et maîtrise des coûts, avec le format tools d'OpenAI.

Un agent est une boucle : le modèle lit la conversation, décide d'appeler un ou plusieurs de vos outils, vous les exécutez et renvoyez les résultats, et le modèle continue jusqu'à pouvoir répondre. api14 prend cela en charge avec le format tools d'OpenAI sur tous les modèles compatibles avec les outils : le même code fonctionne donc avec OpenAI, Anthropic, Google et d'autres fournisseurs en changeant l'identifiant du modèle.

Ce guide construit un petit agent avec une limite d'étapes, une exécution parallèle des outils, une réponse finale structurée et un contrôle des coûts. Le code utilise le paquet officiel openai avec l'URL de base https://api14.com/api/v1.

Définir les outils

Chaque outil est un nom, une description que le modèle lit, et un JSON Schema pour ses arguments. Soignez des descriptions précises : c'est la seule documentation du modèle.

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api14.com/api/v1", api_key=os.environ["API14_API_KEY"])

def search_orders(customer_email: str) -> list:
    return [{"id": "A-1001", "status": "shipped"}]  # votre requête de base de données

def get_shipment(order_id: str) -> dict:
    return {"order_id": order_id, "carrier": "UPS", "eta": "2026-10-09"}  # votre appel d'API

TOOLS = {"search_orders": search_orders, "get_shipment": get_shipment}

tools = [
    {"type": "function", "function": {
        "name": "search_orders",
        "description": "List a customer's orders with their status.",
        "parameters": {"type": "object", "properties": {"customer_email": {"type": "string"}},
                       "required": ["customer_email"]},
    }},
    {"type": "function", "function": {
        "name": "get_shipment",
        "description": "Carrier and estimated delivery date for one order.",
        "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
                       "required": ["order_id"]},
    }},
]

La boucle avec une limite d'étapes

Chaque itération est une requête facturée séparément : plafonnez donc toujours le nombre d'étapes. Renvoyez les erreurs d'outil au modèle sous forme de contenu au lieu de planter ; les modèles s'en sortent en général en corrigeant les arguments.

MAX_STEPS = 8

def run_tool(call):
    try:
        args = json.loads(call.function.arguments)
        result = TOOLS[call.function.name](**args)
    except Exception as e:  # JSON invalide, outil inconnu, ou votre fonction a échoué
        result = {"error": str(e)}
    return {"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)}

def agent(task, model="openai/gpt-5-mini"):
    messages = [
        {"role": "system", "content": "You are a support agent. Use tools to look things up. Be brief."},
        {"role": "user", "content": task},
    ]
    spent = 0.0
    for step in range(MAX_STEPS):
        r = client.chat.completions.create(model=model, messages=messages, tools=tools, max_tokens=1000)
        spent += r.usage.cost
        msg = r.choices[0].message
        if not msg.tool_calls:
            return msg.content, spent
        messages.append(msg)
        messages.extend(run_tool(call) for call in msg.tool_calls)
    raise RuntimeError(f"No answer after {MAX_STEPS} steps (spent ${spent:.4f})")

print(agent("Where is my order? My email is ada@example.com"))

Envoyez tools à chaque requête de la boucle. De nombreux fournisseurs rejettent un message tool quand la requête n'a pas de définitions d'outils.

Appels d'outils parallèles

Avec parallel_tool_calls (activé par défaut quand c'est pris en charge), un modèle peut demander plusieurs appels dans un même tour, par exemple get_shipment pour trois commandes. Exécutez-les en parallèle et ajoutez un message tool par appel, dans n'importe quel ordre : tool_call_id relie chaque résultat à son appel. En Node :

import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api14.com/api/v1", apiKey: process.env.API14_API_KEY });
const MAX_STEPS = 8;

async function agent(messages, tools, impl, model = "openai/gpt-5-mini") {
  for (let step = 0; step < MAX_STEPS; step++) {
    const res = await client.chat.completions.create({ model, messages, tools, max_tokens: 1000 });
    const msg = res.choices[0].message;
    if (!msg.tool_calls?.length) return msg.content;

    messages.push(msg);
    const results = await Promise.all(msg.tool_calls.map(async (call) => {
      let content;
      try {
        content = JSON.stringify(await impl[call.function.name](JSON.parse(call.function.arguments)));
      } catch (err) {
        content = JSON.stringify({ error: String(err) });
      }
      return { role: "tool", tool_call_id: call.id, content };
    }));
    messages.push(...results);
  }
  throw new Error(`No answer after ${MAX_STEPS} steps`);
}

Si vos outils ont des effets de bord qui doivent se produire dans l'ordre (créer, puis mettre à jour), définissez parallel_tool_calls: false pour que le modèle demande un seul appel par tour.

Sortie finale structurée

Quand la réponse de l'agent alimente d'autres codes, demandez un JSON conforme à un schéma. Un schéma simple et portable consiste en un appel supplémentaire après que la boucle a réuni les faits (messages est ici la conversation de la boucle ; renvoyez-la depuis agent() avec la réponse) :

schema = {
    "type": "object",
    "properties": {
        "order_id": {"type": "string"},
        "status": {"type": "string", "enum": ["processing", "shipped", "delivered", "unknown"]},
        "eta": {"type": ["string", "null"]},
        "reply": {"type": "string"},
    },
    "required": ["order_id", "status", "eta", "reply"],
    "additionalProperties": False,
}

final = client.chat.completions.create(
    model="openai/gpt-5-mini",
    messages=messages + [{"role": "user", "content": "Summarize the result for our ticket system."}],
    tools=tools,
    tool_choice="none",
    response_format={"type": "json_schema", "json_schema": {"name": "ticket", "strict": True, "schema": schema}},
)
ticket = json.loads(final.choices[0].message.content)

tool_choice: "none" conserve tools dans la requête (nécessaire car l'historique contient des messages d'outil) tout en empêchant de nouveaux appels. Validez toujours le JSON analysé. Voir Sorties structurées.

Choisir un modèle

  • Capacité : le modèle doit lister tools dans supported_parameters. Vérifiez avec GET /models, voir Modèles. Pour l'étape finale, cherchez structured_outputs ou response_format.
  • Routage : ajoutez provider: { "require_parameters": true } (en Python, via extra_body) pour que les requêtes n'aillent qu'aux fournisseurs qui prennent en charge tous les paramètres envoyés. Voir Routage des fournisseurs.
  • Taille : les petits modèles comme openai/gpt-5-mini gèrent de façon fiable et économique quelques outils bien décrits. Passez à un modèle plus grand quand l'agent a beaucoup d'outils, des plans longs en plusieurs étapes, ou choisit sans cesse le mauvais outil.
  • Testez avec vos outils : lancez les mêmes 20 à 50 tâches réelles sur deux ou trois modèles candidats et comparez le taux de réussite, le nombre d'étapes par tâche et le usage.cost total.
curl -s "https://api14.com/api/v1/models?category=text" \
  | jq -r '.data[] | select(.supported_parameters | index("tools")) | "\(.id)  \(.pricing.prompt)"'

Maîtriser les coûts

Un agent peut faire de nombreuses requêtes pour une seule tâche, et toute la conversation est renvoyée à chaque étape. Limitez les dépenses à plusieurs niveaux :

  • Limite d'étapes (MAX_STEPS) et max_tokens par requête, comme ci-dessus.
  • Budget par tâche : additionnez usage.cost sur les étapes et arrêtez-vous quand il dépasse votre seuil.
  • Résultats d'outils courts : ne renvoyez que les champs dont le modèle a besoin. Les grosses sorties d'outils sont payées en tokens d'entrée à chaque étape suivante.
  • Limite quotidienne en USD par clé : créez une clé dédiée à l'agent dans Clés d'API et définissez une limite quotidienne. Quand elle est atteinte, les requêtes renvoient 429 daily_limit_reached jusqu'au prochain jour UTC ; traitez ce code en arrêtant l'agent, pas en réessayant.
  • Liste de modèles autorisés : restreignez la clé aux modèles que vous avez testés ; tout autre renvoie 403 model_not_allowed.
  • Cache de prompt : gardez le prompt système et les définitions d'outils identiques et en premier dans chaque requête, pour bénéficier des réductions sur le préfixe en cache. Voir Cache de prompt.

Voir Appel d'outils, Limites de débit et Erreurs.