Le Function Calling de GPT-6 Astra est le moteur des agents IA : le modèle décide quel outil appeler et quels arguments passer ; votre runtime exécute l'effet de bord ; le résultat retourne au modèle pour continuer le raisonnement. Quand cette boucle est correcte, Astra peut interroger des API, lire des bases de données et exécuter une logique personnalisée de façon autonome. Quand elle est défectueuse, la boucle se bloque ou les droits des outils dépassent ce qui était prévu. Ce qu'est le Function Calling est couvert dans la comparaison de protocoles ; comment obtenir une clé API est dans le tutoriel API. Cet article couvre les trois patterns d'outils qui font passer de « ça tourne » à « c'est déployable en sécurité ».
Étape 1 : Définir un schéma d'outil
Un schéma d'outil est un objet JSON qui indique au modèle le nom de l'outil, ce qu'il fait et les paramètres qu'il accepte. Voici un outil qui appelle une API météo HTTP :
import json
from openai import OpenAI
client = OpenAI()
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a city via HTTP API.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g. Tokyo, London, Shanghai",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit. Default celsius.",
},
},
"required": ["city"],
"additionalProperties": False,
},
}
]
La description doit dire ce que fait l'outil — pas seulement répéter son nom. Le modèle lit ce champ pour décider d'appeler l'outil. Les champs requis vont dans required ; les optionnels dans properties, mais pas dans required. additionalProperties: false réduit les clés inattendues. Plusieurs outils vont dans la même liste ; le modèle en choisit un ou plusieurs selon la conversation. En cas d'appels parallèles, la boucle doit traiter tous les résultats avant de continuer.
Étape 2 : La boucle d'outils complète
Le pattern : envoyer la requête → vérifier les éléments function_call dans output → exécuter → ajouter les résultats à input → renvoyer, jusqu'à ce qu'il n'y ait plus de function_call. La boucle doit être un while True — un if unique ne reçoit jamais la réponse finale du modèle.
import json
import requests
from openai import OpenAI
client = OpenAI()
def call_weather_api(city: str, unit: str = "celsius") -> dict:
"""Replace with your real HTTP endpoint."""
url = "https://api.example.com/weather"
r = requests.get(url, params={"city": city, "unit": unit}, timeout=5)
r.raise_for_status()
return r.json() # e.g. {"city": "Tokyo", "temp": 22, "condition": "sunny"}
# Initial request — model may call tools
response = client.responses.create(
model="gpt-6-astra",
input="What is the weather in Tokyo right now?",
tools=tools,
reasoning={"effort": "low"},
)
# Tool loop: keep going until model stops calling tools
while True:
tool_calls = [o for o in response.output if o.type == "function_call"]
if not tool_calls:
break # model is done — final answer is in response.output_text
tool_results = []
for call in tool_calls:
args = json.loads(call.arguments)
if call.name == "get_weather":
result = call_weather_api(**args)
else:
result = {"error": f"Unknown tool: {call.name}"}
tool_results.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result, ensure_ascii=False),
})
# Append history + results and continue
response = client.responses.create(
model="gpt-6-astra",
input=response.output + tool_results,
tools=tools,
reasoning={"effort": "low"},
)
print(response.output_text)
Détails d'implémentation. ① response.output est une liste de types mélangés : ne filtrer que type == "function_call". ② Pour continuer, passer response.output + tool_results en input. Envoyer seulement tool_results fait perdre l'historique de conversation. ③ La call_id du résultat doit correspondre à celle de l'appel d'outil. ④ Sérialiser les valeurs de retour avec json.dumps — ne pas passer de dict Python brut.
name contre une allowlist connue. Vérifier les types d'arguments. Ajouter un timeout et une allowlist de domaines pour les outils HTTP. Les outils DB n'exécutent que des instructions SQL prédéfinies — ne jamais concaténer la sortie du modèle dans une chaîne SQL. Les outils qui écrivent sur disque ou exécutent des commandes shell doivent tourner sur un Mac distant jetable.
Étape 3 : Un outil de base de données en lecture seule
Trois exigences pour un outil DB sûr : connexion en lecture seule (pas d'INSERT, UPDATE, DELETE), requêtes paramétrées (pas d'injection SQL), SQL allowlist (pas de SQL arbitraire).
import sqlite3
from typing import Any
# Only these parameterized queries can be executed — no raw user SQL
ALLOWED_QUERIES: dict[str, str] = {
"get_order": "SELECT id, status, total, created_at FROM orders WHERE id = ?",
"list_orders": "SELECT id, status, total FROM orders WHERE user_id = ? LIMIT 20",
}
def query_db(query_name: str, params: list[Any]) -> list[dict]:
"""Execute an allowlisted, parameterized read-only query.
Never accepts raw SQL strings from the model.
"""
sql = ALLOWED_QUERIES.get(query_name)
if sql is None:
raise ValueError(f"Query '{query_name}' not in allowlist")
con = sqlite3.connect("orders.db", check_same_thread=False)
con.row_factory = sqlite3.Row
try:
rows = con.execute(sql, params).fetchall()
return [dict(r) for r in rows]
finally:
con.close()
# Schema exposed to the model
db_tool = {
"type": "function",
"name": "query_db",
"description": "Query the orders DB. Only read-only allowlisted queries.",
"parameters": {
"type": "object",
"properties": {
"query_name": {
"type": "string",
"enum": list(ALLOWED_QUERIES.keys()),
"description": "Name of the allowlisted query",
},
"params": {
"type": "array",
"items": {"type": ["string", "number"]},
"description": "Positional parameters for the query",
},
},
"required": ["query_name", "params"],
},
}
En production : pool de connexions, timeout de requête, limite de lignes dans chaque SQL, masquage des colonnes sensibles. Le résultat renvoyé au modèle doit être un résumé, pas un dump brut de table — chaque ligne retournée coûte des tokens d'entrée à la prochaine requête.
Outils de fonctions vs outils hébergés
| Dimension | Outils de fonctions (type: function) | Outils hébergés (exploités par OpenAI) |
|---|---|---|
| Lieu d'exécution | Votre runtime (local / Mac distant) | Infrastructure OpenAI |
| Idéal pour | API privées, BDD internes, logique métier | Recherche web, interpréteur de code, shell, Computer Use |
| Effort de développement | Schéma + fonction d'exécution + boucle à écrire | Nommer l'outil dans la liste, OpenAI gère l'exécution |
| Facturation | Tokens uniquement | Tokens + frais d'outil par appel |
| Isolation | Les outils en écriture nécessitent un environnement isolé | OpenAI gère le sandbox ; vous gérez la sécurité des données |
Les deux types peuvent coexister dans la même requête. Les résultats des outils hébergés entrent automatiquement dans l'historique de conversation, sans soumission manuelle. Les résultats des outils de fonctions doivent être soumis manuellement via function_call_output.
Matrice de décision
| Si vous avez besoin de | Choisissez | Pourquoi |
|---|---|---|
| Appeler une API REST interne | Outil de fonction | Réseau privé inaccessible depuis l'infra OpenAI |
| Interroger une BDD interne (lecture seule) | Outil de fonction + SQL allowlist | Données privées ; gérer les droits d'accès soi-même |
| Recherche web en temps réel | Outil hébergé web_search_preview | Crawling et classement gérés par OpenAI |
| Faire exécuter du Python au modèle | Outil hébergé code_interpreter | Exécution dans le sandbox OpenAI |
| Le modèle pilote un bureau / navigateur | Outil hébergé computer_use + Mac isolé | Bureau visible nécessaire ; isolation pour éviter les accidents |
| Commandes shell avec écriture disque | Outil de fonction + Mac distant isolé | Les droits en écriture doivent rester sur un hôte jetable |
Configurations recommandées
A — Développeur solo / prototype : machine locale, outils de fonctions pour HTTP (pas d'écriture disque). Écrire le schéma, stabiliser la boucle, loguer les call_id. Passer aux outils multiples seulement après un loop mono-outil fiable.
B — Petite équipe / produit : outils de fonctions derrière des services internes avec auth par token. Outil DB en lecture seule + pool de connexions + timeout. Outils hébergés activés selon le type de tâche. Logique d'exécution couverte par des tests unitaires.
C — Entreprise : tous les appels d'outils via une API gateway avec log d'audit. DB : réplica en lecture seule + row-level security. Computer Use sur des Mac nodes jetables et isolés. Compte et livraison dans le centre d'aide ; coût mensuel des nodes sur la page tarifs.
Erreurs courantes
- Boucle exécutée une seule fois (if au lieu de while) : la réponse finale du modèle n'arrive jamais.
- Résultats soumis sans historique : passer seulement tool_results en input fait perdre le contexte.
- Sortie du modèle directement concaténée en SQL/shell : une allowlist + requêtes paramétrées éliminent cette surface d'attaque.
- Pas de timeout sur les outils HTTP : un service en aval qui se bloque gèle toute la session agent.
- Grands dumps renvoyés par les outils : les lignes consomment des tokens et peuvent déclencher l'uplift 272K.
Plan d'action : 7 étapes
- Confirmer d'abord que le chemin texte Responses fonctionne (voir le tutoriel API).
- Écrire le premier schéma d'outil. Description en phrase verbale, required rempli, additionalProperties: false.
- Implémenter la fonction d'exécution. Timeout pour les HTTP, allowlist + paramétrage pour les DB.
- Envoyer la requête Responses avec tools, afficher response.output et confirmer la présence de function_call.
- Implémenter la boucle while True. call_id alignées, function_call_output soumis, historique inclus dans input.
- Ajouter les outils hébergés si nécessaire. Leurs résultats ne nécessitent pas de soumission manuelle.
- Déplacer les outils en écriture sur un Mac distant isolé et détruire le workspace en fin de session.
FAQ
L'API Responses est-elle obligatoire pour Function Calling ?
Oui. Appels d'outils, outils hébergés et boucles async exigent Responses. Un outil hébergé sur Chat Completions renvoie 400.
Combien d'outils peut-on définir par requête ?
Pas de petite limite stricte, mais plus d'outils = plus de tokens. Ne passer que les outils utiles à la tâche en cours.
Comment prévenir l'injection SQL ?
Requêtes paramétrées avec ? combinées à une SQL allowlist. Ne jamais concaténer la sortie du modèle dans du SQL.
Outil de fonction ou outil hébergé ?
API privée, BDD interne, logique custom → outil de fonction. Recherche web, code, Computer Use → outil hébergé. Les deux peuvent coexister.
Peut-on streamer la réponse finale après des appels d'outils ?
Oui. Stabiliser d'abord la boucle sans streaming, puis ajouter stream=True.
Conclusion
Les trois fondations du Function Calling GPT-6 Astra : un schéma avec une description claire, une boucle while qui soumet l'historique complet avec chaque résultat, et des outils DB avec allowlist et requêtes paramétrées — les outils en écriture sur un Mac isolé. Les nodes Mac distants se trouvent sur les pages location et tarifs ; les questions de compte au centre d'aide.
Pour aller plus loin
- Comment appeler l'API GPT-6 Astra : Python, de la clé à la première requête →
- Qu'est-ce que Function Calling : OpenAI, Gemini, Claude comparés →
- Qu'est-ce que GPT-6 Astra : sortie, prix et agents →
Outils appelant de vraies API ? Exécutez-les sur un Cloud Mac isolé
Appeler un endpoint HTTP public en local est correct. Un outil qui écrit sur le disque, modifie un dépôt ou pilote un navigateur ne doit pas partager votre bureau quotidien. Les Mac distants isolent un espace de travail par session.