Réponse rapide
Comment ajouter une recherche web en direct à LangChain ou LlamaIndex ?
Enveloppez l'appel /search de LLMWebSearch dans un @tool LangChain, un retriever LangChain ou un FunctionTool LlamaIndex. Chacun renvoie des sources classées avec les 3 meilleures pages en Markdown et leurs URL, prêtes pour les citations. Aucun package de notre part à installer : le code ci-dessous constitue l'intégration. Une recherche coûte 1 crédit, à partir de 1,20 $US/1K requêtes en octobre 2026. Sans KYC ; payez en crypto.
À quoi sert chaque wrapper
| Outil LangChain | Un agent appelle search_web lorsqu'il décide qu'il a besoin du web |
|---|---|
| Retriever LangChain | Une chaîne récupère des documents frais à chaque question, sans les demander |
| FunctionTool LlamaIndex | La même fonction search_web, pour les agents LlamaIndex |
| Packages | langchain-core ou llama-index-core, plus requests |
| Clé | LLMWEBSEARCH_API_KEY dans l'environnement |
| Facturation | 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 |
Comment créer un outil de recherche pour un agent LangChain ?
Décorez une fonction avec @tool de langchain_core.tools. LangChain lit le nom, les arguments typés et la docstring pour décrire l'outil au modèle, gardez donc la docstring courte et précise. La fonction renvoie une chaîne avec des sources numérotées, ce qui convient à tous les modèles de chat et garde les URL à côté du texte.
Appelez-la directement avec .invoke() pour la tester, puis passez-la dans la liste tools de votre agent.
import os
import requests
from langchain_core.tools import tool
API = "https://llmwebsearch.com/api/v1/search"
HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
@tool
def search_web(query: str, read: int = 3) -> str:
"""Search the live web. Returns numbered sources with URLs; the first `read` include page text."""
body = {"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000}
resp = requests.post(API, headers=HEADERS, json=body, 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)
print(search_web.invoke({"query": "postgres connection pool size"})[:500])
Comment utiliser le web en direct comme retriever ?
Sous-classez BaseRetriever et renvoyez un Document par page lue. Le texte de la page va dans page_content ; l'URL, le titre, le rang et la date vont dans metadata, où les chaînes qui citent leurs sources les recherchent. Les résultats sans contenu sont ignorés, car un retriever doit fournir un texte que le modèle peut citer.
Un retriever s'exécute à chaque question. C'est adapté à une chaîne de question-réponse sur des sujets d'actualité, et superflu pour un chat où la plupart des tours n'ont pas besoin de recherche ; utilisez l'outil dans ce cas.
import os
from typing import List
import requests
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_core.retrievers import BaseRetriever
class LLMWebSearchRetriever(BaseRetriever):
"""Live web search results as Documents, one per page that was read."""
max_results: int = 5
read: int = 3
max_chars: int = 6000
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun) -> List[Document]:
resp = requests.post(
"https://llmwebsearch.com/api/v1/search",
headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
json={"q": query, "max_results": self.max_results, "read": self.read, "max_chars": self.max_chars},
timeout=60,
)
resp.raise_for_status()
docs = []
for r in resp.json()["results"]:
if r["content_status"] != "ok":
continue
meta = {"source": r["url"], "title": r["title"], "position": r["position"], "published_at": r.get("published_at")}
docs.append(Document(page_content=r["content"], metadata=meta))
return docs
retriever = LLMWebSearchRetriever(read=3)
for doc in retriever.invoke("pgbouncer transaction mode limitations"):
print(doc.metadata["position"], doc.metadata["source"], len(doc.page_content))
Comment donner la même recherche à un agent LlamaIndex ?
Écrivez une fonction simple et enveloppez-la avec FunctionTool.from_defaults de llama_index.core.tools. LlamaIndex construit le nom et la description de l'outil à partir du nom, de la signature et de la docstring de la fonction. L'appel de l'outil renvoie un ToolOutput dont l'content est le texte que l'agent lira.
La fonction est la même que celle de LangChain, une équipe qui utilise les deux frameworks peut donc la partager.
import os
import requests
from llama_index.core.tools import FunctionTool
def search_web(query: str, read: int = 3) -> str:
"""Search the live web and return numbered sources; the first `read` include their page text."""
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)
search_tool = FunctionTool.from_defaults(fn=search_web)
print(search_tool.metadata.name)
print(search_tool.call(query="postgres connection pool size").content[:300])
Comment garder la fenêtre de contexte sous contrôle ?
Définissez max_chars sur l'appel API plutôt que de tronquer ensuite : moins de données circulent et le framework ne conserve jamais la version longue. 5 000 caractères par page et 3 pages font environ 4 000 tokens, confortable pour la plupart des modèles.
Pour les chaînes de retrieval qui combinent le web en direct avec vos propres documents, donnez aux documents web un poids plus faible ou une section de prompt distincte. Les pages web changent et peuvent être fausses ; vos propres documents sont généralement la référence.
Puis-je stocker ce que le retriever renvoie dans une base de données vectorielle ?
Oui, en ce qui nous concerne. 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. Stockez l'URL et published_at avec chaque chunk, pour que l'agent puisse citer la page et que vous puissiez supprimer les chunks qui vieillissent.
La plupart des équipes mettent en cache les résultats web des questions fréquentes pendant un jour ou une semaine. Cela réduit les recherches répétées, et chaque recherche évitée est un crédit économisé.
LangChain et LlamaIndex : questions fréquentes
Existe-t-il un package langchain-llmwebsearch ?
Non. Nous ne publions aucun package. Le code de cette page est assez court pour vous appartenir : copiez-le, changez les valeurs par défaut et gardez-le dans votre dépôt.
Puis-je rendre l'outil async ?
Oui. Remplacez requests par httpx.AsyncClient et définissez la fonction avec async def. Le décorateur @tool de LangChain décore directement les fonctions async ; dans LlamaIndex, passez la version async à FunctionTool.from_defaults en tant que async_fn.
Comment lire une page que l'agent a trouvée ailleurs ?
Ajoutez un second outil qui envoie des URLs à 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. La référence extract montre la requête.