Réponse rapide
Comment ajouter la recherche web à un modèle OpenAI avec function calling ?
Déclarez une fonction search_web avec un schéma JSON, et quand le modèle l'appelle, envoyez la requête au https://llmwebsearch.com/api/v1/search de LLMWebSearch. Renvoyez les sources numérotées, avec les 3 meilleures pages en Markdown, sous forme de function_call_output ; le modèle répond et les cite. Une requête coûte 1 crédit, à partir de 1,20 $US/1K requêtes depuis octobre 2026. Sans KYC ; paiement en crypto.
Ce qu'il vous faut
| Packages | openai et requests (ou openai-agents pour la version Agents SDK) |
|---|---|
| Clés | OPENAI_API_KEY pour le modèle, LLMWEBSEARCH_API_KEY pour la recherche |
| Modèle | Tout modèle qui prend en charge le function calling, défini dans OPENAI_MODEL |
| API utilisée | Responses API : client.responses.create avec tools et input |
| Facturation de notre côté | Un crédit paie une recherche : jusqu'à 20 résultats classés, avec les pages des 3 premiers lues et renvoyées en Markdown. Lire 4 à 10 pages coûte 2 crédits. Un appel qui se termine sur une erreur ne coûte rien |
À quoi ressemble le schéma de l'outil ?
Un function tool a un name, un description et un JSON Schema parameters. Gardez la description concrète : ce que l'outil renvoie, et que les sources doivent être citées par numéro. Deux paramètres suffisent pour la plupart des agents : la query, et combien de meilleurs résultats récupérer en entier (read).
Le modèle ne voit jamais votre clé API. Il propose seulement des arguments ; votre code décide comment appeler l'API et quoi renvoyer.
Comment fonctionne la boucle d'appels ?
Envoyez le message de l'utilisateur avec les outils. Si la réponse contient des items de type function_call, analysez le JSON arguments de chacun, exécutez la recherche, et ajoutez un function_call_output avec le même call_id. Appelez ensuite le modèle à nouveau avec la liste d'entrée plus longue. Quand une réponse ne contient aucun appel de fonction, son texte est la réponse.
La boucle ci-dessous s'arrête après cinq tours, une garde contre un modèle qui chercherait sans fin. Le guide du function calling d'OpenAI décrit ces mêmes items function_call et function_call_output, consulté le 22 octobre 2026.
import json
import os
import requests
from openai import OpenAI
client = OpenAI()
MODEL = os.environ["OPENAI_MODEL"] # any model that supports function calling
SEARCH = "https://llmwebsearch.com/api/v1/search"
tools = [
{
"type": "function",
"name": "search_web",
"description": "Search the live web. Returns numbered sources with URLs; the first `read` include page text. Cite sources as [1], [2].",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "What to search for, as a person would type it."},
"read": {"type": "integer", "minimum": 0, "maximum": 10, "description": "How many top results to read in full."},
},
"required": ["query"],
"additionalProperties": False,
},
}
]
def search_web(query, read=3):
resp = requests.post(
SEARCH,
headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
json={"q": query, "max_results": 5, "read": read, "max_chars": 5000},
timeout=60,
)
if resp.status_code != 200:
return "Search failed: " + resp.json().get("error", {}).get("code", str(resp.status_code))
parts = []
for i, r in enumerate(resp.json()["results"], start=1):
text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
return "\n\n".join(parts)
input_list = [{"role": "user", "content": "How should I size a Postgres connection pool?"}]
for _ in range(5):
response = client.responses.create(model=MODEL, tools=tools, input=input_list)
calls = [item for item in response.output if item.type == "function_call"]
if not calls:
print(response.output_text)
break
input_list += response.output
for item in calls:
args = json.loads(item.arguments)
output = search_web(args["query"], int(args.get("read", 3)))
input_list.append({"type": "function_call_output", "call_id": item.call_id, "output": output})
Existe-t-il une version plus courte avec le Agents SDK ?
Oui. Le OpenAI Agents SDK transforme une fonction Python en outil avec un décorateur et exécute la boucle d'appels à votre place. Sa documentation des outils, consultée le 22 octobre 2026, montre le décorateur @tool de agents.decorators, avec la description tirée de la docstring et la description de chaque argument depuis sa section Args. La même page mentionne aussi la forme @function_tool.
Runner.run_sync exécute l'agent jusqu'à ce qu'il produise une réponse finale, en appelant search_web autant de fois que le modèle le demande.
import os
import requests
from agents import Agent, Runner
from agents.decorators import tool
@tool
def search_web(query: str, read: int = 3) -> str:
"""Search the live web and return numbered sources with their URLs.
Args:
query: What to search for, as a person would type it.
read: How many top results to read in full, from 0 to 10.
"""
resp = requests.post(
"https://llmwebsearch.com/api/v1/search",
headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
json={"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000},
timeout=60,
)
resp.raise_for_status()
parts = []
for i, r in enumerate(resp.json()["results"], start=1):
text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
return "\n\n".join(parts)
agent = Agent(
name="Researcher",
instructions="Answer from search_web results only and cite them as [1], [2].",
tools=[search_web],
)
result = Runner.run_sync(agent, "How should I size a Postgres connection pool?")
print(result.final_output)
Pourquoi utiliser un outil API plutôt que la recherche web intégrée d'OpenAI ?
Surtout pour maîtriser ce que le modèle lit et ce que cela coûte. Notre outil transmet à votre code les sources brutes, avec les URL et le texte des pages, pour que vous décidiez ce qui entre dans le contexte, que vous puissiez le journaliser et le stocker. Nous n'entraînons aucun modèle sur vos requêtes ni sur ce que renvoie l'API, et nous n'ajoutons aucune règle de notre cru contre la conservation des résultats ; les droits des sites dont vous lisez les pages s'appliquent toujours.
Le prix est aussi fixe par requête au lieu de dépendre de la quantité de contenu récupéré que le modèle lit. La comparaison des outils intégrés met côte à côte les prix par appel d'OpenAI, Anthropic, Google et d'autres, avec les dates.
Comment empêcher le modèle de trop chercher ?
Dites-le dans les instructions : cherchez une fois, lisez les sources, ne cherchez à nouveau que si elles se contredisent ou passent à côté du sujet. Plafonnez le nombre de tours dans votre propre boucle, comme le fait le script ci-dessus avec cinq.
Gardez max_chars modeste. Trois pages de 5 000 caractères font environ 4 000 tokens par requête, et chaque tour les ajoute à l'entrée de l'appel suivant au modèle.
OpenAI function calling : questions fréquentes
Est-ce que cela fonctionne avec l'API Chat Completions ?
Oui, avec l'ancien format tools de Chat Completions, où la fonction se trouve dans un objet function et les résultats reviennent sous forme de messages tool. La fonction de recherche elle-même ne change pas.
Le modèle peut-il lire une page qu'il trouve dans une source ?
Ajoutez une seconde fonction, read_urls, qui envoie des URL à https://llmwebsearch.com/api/v1/extract. Lire des pages dont vous avez déjà les URLs coûte 1 crédit par groupe de 3 pages entamé, renvoyées avec leur contenu, jusqu'à 20 URLs par appel ; les URLs refusées ou en échec ne sont pas comptées.
Quel modèle dois-je utiliser ?
N'importe quel modèle qui prend en charge le function calling. Essayez-en deux ou trois sur vos propres questions : la réponse dépend autant des sources que du modèle, et un modèle plus petit coûte moins cher par tour.
Sources (2)
- Function calling — OpenAI API docs (read 2026-10-22)
- Outils (OpenAI Agents SDK) — OpenAI Agents SDK docs (read 2026-10-22)