Sans KYC — pas de pièce d'identité, pas de selfie, pas de documents. Payez en crypto : BTC, USDT, USDC, ETH, SOL, LTC
Langue: Français
Obtenir une clé API

Docs · LangChain · LlamaIndex

LangChain et LlamaIndex : un chargeur de pages web et un outil read_url

Deux façons d'utiliser l'API dans une stack IA Python : charger des pages comme documents pour la recherche, ou donner à un agent un outil read_url. Les mêmes 20 lignes de glue servent LangChain et LlamaIndex.

Dernière mise à jour: · 5 min de lecture

Réponse rapide

Comment charger des pages web dans LangChain ou LlamaIndex en Markdown propre ?

Appelez l'endpoint /read de WebReaderAPI et enveloppez chaque réponse dans un Document : le Markdown comme contenu, final_url et le titre comme metadata. Pour les agents, exposez le même appel comme outil read_url. Chaque page coûte 1 crédit, 2 avec un navigateur, à partir de 0,07 $US/1K pages en octobre 2026. Aucun package de notre part n'est nécessaire. Sans KYC ; payez en crypto.

Pourquoi des documents Markdown plutôt que du HTML brut ?

Parce que la recherche fonctionne sur du texte, et que le Markdown conserve la structure qui compte tout en supprimant le reste. Les titres survivent, donc un splitter peut découper selon les changements de sujet. Les listes et les tableaux survivent, donc les étapes et les spécifications restent ensemble. La navigation, les bannières de cookies et les publicités disparaissent, donc elles ne polluent pas vos embeddings et ne gaspillent pas de tokens.

Les metadata viennent du même appel : final_url comme source à citer, title, published_at et language pour filtrer.

reader.py : le helper partagé (enregistrez-le à côté de votre code)
import os

import requests

API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


class PageError(Exception):
    pass


def read_page(url, **options):
    resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
    body = resp.json()
    if resp.status_code != 200:
        raise PageError(body["error"]["code"])
    return body

Comment construire un document loader LangChain ?

Transformez chaque page en langchain_core.documents.Document, puis découpez-la par titres avec MarkdownHeaderTextSplitter du package langchain-text-splitters. Chaque chunk conserve le chemin de titres dans ses metadata, et vous ajoutez l'URL source pour que chaque réponse puisse renvoyer à sa page.

Les pages ignorées, comme celles refusées par robots.txt, lèvent PageError avec le code ; journalisez-les et continuez.

LangChain : charger et découper des pages
from langchain_core.documents import Document
from langchain_text_splitters import MarkdownHeaderTextSplitter

from reader import PageError, read_page

splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[("#", "h1"), ("##", "h2"), ("###", "h3")])


def load_pages(urls):
    chunks = []
    for url in urls:
        try:
            page = read_page(url)
        except PageError as exc:
            print("skipped", url, exc)
            continue
        for part in splitter.split_text(page["markdown"]):
            part.metadata.update({"source": page["final_url"], "title": page.get("title", "")})
            chunks.append(part)
    return chunks


docs = load_pages(["https://example.com/docs/install", "https://example.com/docs/configure"])
print(len(docs), "chunks", isinstance(docs[0], Document) if docs else "")

Comment donner à un agent LangChain un outil read_url ?

Décorez une fonction avec @tool de langchain_core.tools. La docstring devient la description de l'outil que lit le modèle, alors indiquez ce que l'outil renvoie et quand l'utiliser. Renvoyez du texte brut : une ligne de titre, la source et le Markdown, coupé avec max_chars selon votre modèle.

Liez l'outil à n'importe quel chat model qui prend en charge le tool calling, ou passez-le au constructeur de votre agent. Le modèle appelle ensuite read_url avec une URL issue de la conversation ou d'un résultat de recherche.

LangChain : l'outil read_url
import os

from langchain.chat_models import init_chat_model
from langchain_core.tools import tool

from reader import PageError, read_page


@tool
def read_url(url: str, max_chars: int = 20000) -> str:
    """Read a public web page and return its main content as Markdown, with its title and source URL."""
    try:
        page = read_page(url, max_chars=max_chars)
    except PageError as exc:
        return "Could not read the page: " + str(exc)
    return "# " + (page.get("title") or url) + "\nSource: " + page["final_url"] + "\n\n" + page["markdown"]


model = init_chat_model(os.environ.get("CHAT_MODEL", "openai:gpt-4.1-mini")).bind_tools([read_url])
reply = model.invoke("Summarise https://example.com/blog/first-post in three bullet points.")
print(reply.tool_calls)

Comment faire la même chose dans LlamaIndex ?

LlamaIndex utilise sa propre classe Document avec text et metadata, et FunctionTool.from_defaults pour transformer une fonction en outil. Le helper reste le même. Construisez un index à partir des documents avec VectorStoreIndex.from_documents, qui utilise le modèle d'embedding nommé dans vos réglages LlamaIndex.

LlamaIndex : indexer un crawl, et un outil read_url
import os
import time

import requests
from llama_index.core import Document, VectorStoreIndex
from llama_index.core.tools import FunctionTool

from reader import HEADERS, PageError, read_page


def crawl_pages(start_url, limit=200):
    job = requests.post("https://webreaderapi.com/api/v1/crawl", headers=HEADERS, json={"url": start_url, "limit": limit}, timeout=30).json()
    pages, cursor = [], None
    while True:
        params = {"cursor": cursor} if cursor else {}
        res = requests.get("https://webreaderapi.com/api/v1/jobs/" + job["job_id"], headers=HEADERS, params=params, timeout=30).json()
        pages += [r for r in res["results"] if "error" not in r]
        cursor = res.get("next_cursor")
        if not cursor and res["status"] == "done":
            return pages
        if not cursor:
            time.sleep(5)


pages = crawl_pages("https://docs.example.com/")
docs = [Document(text=p["markdown"], metadata={"source": p["final_url"], "title": p.get("title", "")}) for p in pages]
index = VectorStoreIndex.from_documents(docs)
print(index.as_query_engine().query("How do I install the product?"))


def read_url(url: str, max_chars: int = 20000) -> str:
    """Read a public web page and return its main content as Markdown."""
    try:
        return read_page(url, max_chars=max_chars)["markdown"]
    except PageError as exc:
        return "Could not read the page: " + str(exc)


read_tool = FunctionTool.from_defaults(fn=read_url, name="read_url")
print(os.environ.get("CHAT_MODEL", "set CHAT_MODEL for your agent"), read_tool.metadata.name)

Quelle taille de crawl faut-il pour un petit index RAG ?

Moins que vous ne le pensez. La documentation d'un produit fait souvent quelques centaines de pages, et un crawl avec include_paths réglé sur la section docs ne lit que celles-là. À 1 crédit la page, un site de docs de 300 pages coûte environ 300 crédits ; 10 000 pages (3,75 $US) le rafraîchit de nombreuses fois.

Relancez le crawl selon un rythme qui correspond à la fréquence de modification de la source, et comparez final_url et word_count avec l'exécution précédente pour ne ré-embedder que les pages qui ont changé. Les crédits prépayés n'expirent jamais : les pages non utilisées restent sur votre compte jusqu'à ce que vous les utilisiez.

LangChain et LlamaIndex : questions fréquentes

Existe-t-il un package WebReaderAPI pour LangChain ou LlamaIndex ?

Non. Le helper et l'outil de cette page constituent toute l'intégration, vous pouvez donc les adapter librement. WebReaderAPI est une API HTTPS simple que n'importe quel client HTTP Python peut appeler.

Quelle version de LangChain faut-il ?

N'importe quelle version récente avec langchain_core, langchain-text-splitters et init_chat_model. Le décorateur d'outil et la classe Document sont stables depuis plusieurs versions récentes.

Comment éviter de relire les pages qui n'ont pas changé ?

Stockez final_url, word_count et la date de chaque lecture. Relisez selon un rythme planifié, et ne ré-embeddez une page que lorsque son nombre de mots ou son hash de contenu change. Chaque relecture coûte un crédit, choisissez donc un rythme adapté à la source.

Et si certaines pages sont refusées ?

Elles lèvent PageError avec un code tel que blocked_by_robots, et elles ne coûtent rien. Si un site refuse notre requête, nous renvoyons sa réponse : nous ne résolvons jamais de CAPTCHA, ne nous connectons pas à des comptes et ne cherchons pas à contourner un blocage ou un paywall.

À partir de 0,07 $US/1K pages · sans KYC

Obtenir une clé API