# LangChain i LlamaIndex: loader stron internetowych i narzędzie read_url

> Loader stron LangChain i LlamaIndex: read_url, loader dokumentów i mały indeks RAG z crawlingu w Pythonie. Bez KYC, płatność kryptowalutą.

- URL: https://webreaderapi.com/pl/docs/langchain/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: WebReaderAPI (https://webreaderapi.com)

Dwa sposoby użycia API w stosie AI w Pythonie: ładowanie stron jako dokumentów do wyszukiwania lub danie agentowi narzędzia `read_url`. Te same 20 linii kleju obsługują LangChain i LlamaIndex.

**Szybka odpowiedź — Jak załadować strony internetowe do LangChain lub LlamaIndex jako czysty Markdown?**

Wywołaj endpoint `/read` **WebReaderAPI** i opakuj każdą odpowiedź w `Document`: Markdown jako zawartość, `final_url` i tytuł jako metadane. Dla agentów udostępnij to samo wywołanie jako narzędzie `read_url`. Strony kosztują 1 kredyt każda, 2 z przeglądarką, od 0,07 USD/1K stron na dzień października 2026. Nie potrzebujesz żadnego naszego pakietu. Bez KYC; płatność kryptowalutą.

## Dlaczego dokumenty Markdown, a nie surowy HTML?

Bo wyszukiwanie działa na tekście, a Markdown zachowuje strukturę, która ma znaczenie, i odrzuca resztę. Nagłówki zostają, więc splitter może ciąć na zmianach tematu. Listy i tabele zostają, więc kroki i specyfikacje trzymają się razem. Nawigacja, bannery cookies i reklamy znikają, więc nie zanieczyszczają Twoich embeddingów i nie marnują tokenów.

Metadane pochodzą z tego samego wywołania: `final_url` jako źródło do cytowania, `title`, `published_at` i `language` do filtrowania.

*reader.py: wspólny helper (zapisz go obok swojego kodu)*

```python
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

```

## Jak zbudować loader dokumentów LangChain?

Zamień każdą stronę w `langchain_core.documents.Document`, a następnie podziel ją według nagłówków za pomocą `MarkdownHeaderTextSplitter` z pakietu `langchain-text-splitters`. Każdy fragment zachowuje ścieżkę nagłówków w metadanych, a Ty dodajesz URL źródła, aby każda odpowiedź mogła wskazać swoją stronę.

Pominięte strony, takie jak te odrzucone przez robots.txt, zgłaszają `PageError` z kodem; zaloguj je i kontynuuj.

*LangChain: ładowanie i dzielenie stron*

```python
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 "")

```

## Jak dać agentowi LangChain narzędzie read_url?

Udekoruj funkcję za pomocą `@tool` z `langchain_core.tools`. Docstring staje się opisem narzędzia, który czyta model, więc napisz, co narzędzie zwraca i kiedy go używać. Zwracaj czysty tekst: linię tytułu, źródło i Markdown, przycięty za pomocą `max_chars` do rozmiaru Twojego modelu.

Powiąż narzędzie z dowolnym modelem czatu, który obsługuje tool calling, lub przekaż je do konstruktora agenta. Model wywołuje wtedy `read_url` z URL-em z rozmowy lub z wyniku wyszukiwania.

*LangChain: narzędzie read_url*

```python
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)

```

## Jak zrobić to samo w LlamaIndex?

LlamaIndex używa własnej klasy `Document` z `text` i `metadata` oraz `FunctionTool.from_defaults` do zamiany funkcji w narzędzie. Helper pozostaje ten sam. Zbuduj indeks z dokumentów za pomocą `VectorStoreIndex.from_documents`, który używa modelu embeddingów wskazanego w ustawieniach LlamaIndex.

*LlamaIndex: indeks crawlingu i narzędzie read_url*

```python
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)

```

## Jak duży crawling potrzebuje mały indeks RAG?

Mniejszy, niż myślisz. Dokumentacja produktu to często kilkaset stron, a crawling z `include_paths` ustawionym na sekcję dokumentacji czyta tylko je. Przy 1 kredycie za stronę witryna z dokumentacją licząca 300 stron kosztuje około 300 kredytów; 10 000 stron (3,75 USD) odświeża ją wiele razy.

Ponawiaj crawling zgodnie z harmonogramem dopasowanym do częstotliwości zmian źródła i porównuj `final_url` i `word_count` z poprzednim przebiegiem, aby ponownie osadzić tylko zmienione strony. Przedpłacone kredyty nigdy nie wygasają: niewykorzystane strony pozostają na Twoim koncie, dopóki ich nie wykorzystasz.

## LangChain i LlamaIndex: najczęstsze pytania

### Czy istnieje pakiet WebReaderAPI dla LangChain lub LlamaIndex?

Nie. Helper i narzędzie na tej stronie to cała integracja, więc możesz je dowolnie dostosować. WebReaderAPI to zwykłe API HTTPS, które może wywołać dowolny klient HTTP w Pythonie.

### Której wersji LangChain to wymaga?

Dowolnej niedawnej z `langchain_core`, `langchain-text-splitters` i `init_chat_model`. Dekorator narzędzia i klasa `Document` są stabilne w ostatnich wydaniach.

### Jak uniknąć ponownego odczytu stron, które się nie zmieniły?

Zapisuj `final_url`, `word_count` i datę każdego odczytu. Odczytuj ponownie zgodnie z harmonogramem i ponownie osadzaj stronę tylko wtedy, gdy zmieni się jej liczba słów lub hash treści. Każdy ponowny odczyt kosztuje kredyt, więc wybierz rytm dopasowany do źródła.

### Co jeśli niektóre strony są odrzucane?

Zgłaszają `PageError` z kodem takim jak `blocked_by_robots` i nic nie kosztują. Jeśli witryna odrzuci nasze zapytanie, zwracamy jej odpowiedź: nigdy nie rozwiązujemy CAPTCHA, nie logujemy się do kont ani nie próbujemy obejść blokady lub paywalla.

---

WebReaderAPI: WebReaderAPI to API czytnika stron internetowych dla agentów AI, które zamienia każdą publiczną stronę internetową w czysty Markdown, z płatnością kryptowalutą i bez przesyłania dokumentów tożsamości. Przedpłacone kredyty od 0,07 USD/1K stron nigdy nie wygasają, a robots.txt jest respektowany. Bez KYC — bez dowodu, bez selfie, bez dokumentów. Płatność kryptowalutą.
