Không cần KYC — không ID, không selfie, không giấy tờ. Thanh toán bằng crypto: BTC, USDT, USDC, ETH, SOL, LTC
Ngôn ngữ: Tiếng Việt
Lấy khóa API

Docs · LangChain · LlamaIndex

LangChain và LlamaIndex: web page loader và tool read_url

Hai cách dùng API trong một stack AI Python: tải trang thành document để truy xuất, hoặc cấp cho agent một tool read_url. Cùng 20 dòng keo dính phục vụ cả LangChain và LlamaIndex.

Cập nhật lần cuối: · 5 phút đọc

Câu trả lời nhanh

Làm thế nào để tải web page vào LangChain hoặc LlamaIndex dưới dạng Markdown sạch?

Gọi endpoint /read của WebReaderAPI và bọc mỗi câu trả lời trong một Document: Markdown làm content, final_url và tiêu đề làm metadata. Với agent, hãy expose cùng lời gọi đó dưới dạng tool read_url. Mỗi trang tốn 1 credit, 2 credit nếu dùng trình duyệt, từ 0,07 US$/1K trang tính đến tháng 10 năm 2026. Không cần package nào của chúng tôi. Không cần KYC; thanh toán bằng crypto.

Tại sao dùng document Markdown thay vì HTML thô?

Vì truy xuất hoạt động trên văn bản, và Markdown giữ lại cấu trúc quan trọng trong khi bỏ phần còn lại. Tiêu đề được giữ, nên splitter có thể cắt theo chỗ đổi chủ đề. Danh sách và bảng được giữ, nên các bước và thông số kỹ thuật nằm cùng nhau. Thanh điều hướng, banner cookie và quảng cáo biến mất, nên chúng không làm bẩn embedding của bạn hay lãng phí token.

Metadata đến từ cùng lời gọi: final_url làm nguồn để trích dẫn, title, published_at và language để lọc.

reader.py: helper dùng chung (lưu cạnh code của bạn)
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

Làm thế nào để tạo một LangChain document loader?

Chuyển mỗi trang thành một langchain_core.documents.Document, rồi tách theo tiêu đề bằng MarkdownHeaderTextSplitter từ package langchain-text-splitters. Mỗi chunk giữ đường dẫn tiêu đề trong metadata, và bạn thêm URL nguồn để mỗi câu trả lời có thể dẫn ngược về trang của nó.

Các trang bị bỏ qua, như những trang bị robots.txt từ chối, sẽ raise PageError kèm mã; hãy log lại và tiếp tục.

LangChain: tải và tách trang
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 "")

Làm thế nào để cấp cho LangChain agent một tool read_url?

Trang trí một hàm bằng @tool từ langchain_core.tools. Docstring trở thành mô tả tool mà model đọc, nên hãy nói rõ tool trả về gì và khi nào dùng. Trả về văn bản thuần: một dòng tiêu đề, nguồn và Markdown, cắt bằng max_chars cho phù hợp với model của bạn.

Bind tool với bất kỳ chat model nào hỗ trợ tool calling, hoặc truyền nó vào constructor của agent. Model sau đó gọi read_url với một URL từ cuộc hội thoại hoặc từ kết quả tìm kiếm.

LangChain: tool 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)

Làm thế nào để làm điều tương tự trong LlamaIndex?

LlamaIndex dùng class Document riêng với text và metadata, và FunctionTool.from_defaults để biến một hàm thành tool. Helper vẫn giữ nguyên. Dựng index từ các document bằng VectorStoreIndex.from_documents, dùng bất kỳ embedding model nào mà cài đặt LlamaIndex của bạn chỉ định.

LlamaIndex: index một lần crawl, và một tool 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)

Một RAG index nhỏ cần crawl lớn cỡ nào?

Ít hơn bạn tưởng. Tài liệu của một sản phẩm thường chỉ vài trăm trang, và một lần crawl với include_paths đặt vào phần docs chỉ đọc đúng phần đó. Với 1 credit mỗi trang, một site docs 300 trang tốn khoảng 300 credit; 10.000 trang (3,75 US$) làm mới nó nhiều lần.

Re-crawl theo lịch khớp với tần suất nguồn thay đổi, và so sánh final_url và word_count với lần chạy trước để chỉ re-embed những trang đã thay đổi. Credit trả trước không bao giờ hết hạn: số trang chưa dùng vẫn ở trong tài khoản của bạn cho đến khi bạn dùng.

LangChain và LlamaIndex: câu hỏi thường gặp

Có package WebReaderAPI cho LangChain hay LlamaIndex không?

Không. Helper và tool trên trang này chính là toàn bộ tích hợp, nên bạn có thể tùy chỉnh thoải mái. WebReaderAPI là một HTTPS API đơn thuần mà bất kỳ HTTP client Python nào cũng gọi được.

Cái này cần phiên bản LangChain nào?

Bất kỳ phiên bản gần đây nào có langchain_core, langchain-text-splitters và init_chat_model. Tool decorator và class Document đã ổn định qua các bản phát hành gần đây.

Làm thế nào để tránh đọc lại những trang không thay đổi?

Lưu final_url, word_count và ngày đọc của mỗi lần. Đọc lại theo lịch, và chỉ re-embed một trang khi số từ hoặc content hash của nó thay đổi. Mỗi lần đọc lại tốn một credit, nên hãy chọn nhịp khớp với nguồn.

Nếu một số trang bị từ chối thì sao?

Chúng raise PageError với một mã như blocked_by_robots, và không tốn credit. Nếu một website từ chối yêu cầu của chúng tôi, chúng tôi trả lại câu trả lời của trang đó: chúng tôi không bao giờ giải CAPTCHA, đăng nhập vào tài khoản, hay tìm cách vượt qua chặn hoặc paywall.

Từ 0,07 US$/1K trang · không cần KYC

Lấy khóa API