# LangChain 및 LlamaIndex: 웹페이지 로더와 read_url 도구

> LangChain 및 LlamaIndex 웹페이지 로더: read_url 도구, 문서 로더, 크롤링으로 만든 소규모 RAG 인덱스를 모두 Python으로 구현합니다. KYC 없이 암호화폐로 결제하세요.

- URL: https://webreaderapi.com/ko/docs/langchain/
- 마지막 업데이트: 2026-10-22
- 발행자: WebReaderAPI (https://webreaderapi.com)

Python AI 스택에서 API를 사용하는 방법은 두 가지입니다. 페이지를 검색용 문서로 로드하거나, 에이전트에 `read_url` 도구를 제공하는 것입니다. 20줄짜리 동일한 연결 코드가 LangChain과 LlamaIndex 모두에서 작동합니다.

**빠른 답변 — 웹페이지를 깔끔한 Markdown으로 LangChain이나 LlamaIndex에 로드하려면 어떻게 하나요?**

**WebReaderAPI**의 `/read` endpoint를 호출하고 각 응답을 `Document`로 감싸세요. Markdown은 content로, `final_url`와 title은 metadata로 넣습니다. 에이전트에는 같은 호출을 `read_url` 도구로 노출하세요. 페이지당 1 크레딧, 브라우저 사용 시 2 크레딧이며, 2026년 10월 기준 US$0.07/1K 페이지부터입니다. 저희 패키지는 필요 없습니다. KYC 없이 암호화폐로 결제하세요.

## 원본 HTML 대신 Markdown 문서를 쓰는 이유는 무엇인가요?

검색은 텍스트에서 작동하고, Markdown은 중요한 구조를 유지하면서 나머지는 버리기 때문입니다. 제목(heading)이 유지되므로 스플리터가 주제가 바뀌는 지점에서 나눌 수 있습니다. 목록과 표가 유지되므로 단계와 사양이 함께 남습니다. 내비게이션, 쿠키 배너, 광고는 제거되므로 임베딩을 오염시키거나 토큰을 낭비하지 않습니다.

메타데이터는 같은 호출에서 나옵니다. 인용할 출처로 `final_url`, 필터링용으로 `title`, `published_at`, `language`을 넣습니다.

*reader.py: 공용 헬퍼 (코드 옆에 저장하세요)*

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

```

## LangChain 문서 로더는 어떻게 만드나요?

각 페이지를 `langchain_core.documents.Document`로 바꾼 다음, `langchain-text-splitters` 패키지의 `MarkdownHeaderTextSplitter`로 제목 기준으로 나누세요. 각 청크는 메타데이터에 제목 경로를 유지하고, 출처 URL을 추가해 모든 답변이 해당 페이지를 다시 가리킬 수 있게 하세요.

robots.txt에서 거부된 페이지처럼 건너뛴 페이지는 코드와 함께 `PageError`를 발생시키므로, 로그에 남기고 계속 진행하세요.

*LangChain: 페이지 로드 및 분할*

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

```

## LangChain 에이전트에 read_url 도구를 제공하려면 어떻게 하나요?

`langchain_core.tools`의 `@tool`로 함수를 데코레이션하세요. docstring이 모델이 읽는 도구 설명이 되므로, 도구가 무엇을 반환하는지와 언제 사용해야 하는지를 적으세요. 제목 줄, 출처, Markdown이 담긴 일반 텍스트를 반환하고, 모델에 맞게 `max_chars`로 잘라내세요.

도구 호출을 지원하는 모든 채팅 모델에 도구를 바인딩하거나, 에이전트 생성자에 전달하세요. 그러면 모델이 대화나 검색 결과에서 가져온 URL로 `read_url`를 호출합니다.

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

```

## LlamaIndex에서는 같은 작업을 어떻게 하나요?

LlamaIndex는 `text`과 `metadata`를 사용하는 자체 `Document` 클래스를 쓰고, 함수를 도구로 바꿀 때는 `FunctionTool.from_defaults`를 사용합니다. 헬퍼는 그대로 유지됩니다. `VectorStoreIndex.from_documents`로 문서에서 인덱스를 만들며, 이는 LlamaIndex 설정에 지정된 임베딩 모델을 사용합니다.

*LlamaIndex: 크롤링 인덱싱 및 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)

```

## 소규모 RAG 인덱스에는 얼마나 큰 크롤링이 필요한가요?

생각보다 적습니다. 제품 문서는 대개 수백 페이지 정도이고, `include_paths`를 문서 섹션으로 설정한 크롤링은 해당 섹션만 읽습니다. 페이지당 1 크레딧이면 300페이지 문서 사이트의 비용은 약 300 크레딧이며, 10,000페이지(US$3.75)으로 여러 번 새로고침할 수 있습니다.

출처가 바뀌는 빈도에 맞춰 정기적으로 재크롤링하고, `final_url`와 `word_count`를 이전 실행과 비교해 변경된 페이지만 다시 임베딩하세요. 선불 크레딧은 만료되지 않습니다. 사용하지 않은 페이지는 사용할 때까지 계정에 남아 있습니다.

## LangChain 및 LlamaIndex: 자주 묻는 질문

### LangChain이나 LlamaIndex용 WebReaderAPI 패키지가 있나요?

아니요. 이 페이지의 헬퍼와 도구가 통합의 전부이므로 자유롭게 응용할 수 있습니다. WebReaderAPI는 어떤 Python HTTP 클라이언트든 호출할 수 있는 일반 HTTPS API입니다.

### 어떤 LangChain 버전이 필요한가요?

`langchain_core`, `langchain-text-splitters`, `init_chat_model`가 있는 최신 버전이면 됩니다. 도구 데코레이터와 `Document` 클래스는 최근 릴리스에서 안정적으로 유지되어 왔습니다.

### 변경되지 않은 페이지를 다시 읽지 않으려면 어떻게 하나요?

각 읽기의 `final_url`, `word_count`, 날짜를 저장하세요. 정해진 일정에 따라 다시 읽되, 단어 수나 콘텐츠 해시가 바뀐 페이지만 다시 임베딩하세요. 다시 읽을 때마다 1 크레딧이 들므로 출처에 맞는 주기를 선택하세요.

### 일부 페이지가 거부되면 어떻게 되나요?

`blocked_by_robots` 같은 코드와 함께 `PageError`가 발생하며, 크레딧은 차감되지 않습니다. 사이트가 우리 요청을 거부하면 그 응답을 그대로 반환합니다. 우리는 CAPTCHA를 풀거나, 계정에 로그인하거나, 차단이나 페이월을 우회하려 시도하지 않습니다.

---

WebReaderAPI: WebReaderAPI는 AI 에이전트를 위한 웹 리더 API로, 모든 공개 웹페이지를 깔끔한 Markdown으로 변환하며 신분증 업로드 없이 암호화폐로 결제합니다. US$0.07/1K 페이지부터 시작하는 선불 크레딧은 만료되지 않으며, robots.txt를 준수합니다. KYC 없음 — 신분증도, 셀피도, 서류도 없습니다. 암호화폐로 결제하세요.
