คำตอบสั้น
ฉันจะโหลดหน้าเว็บเข้า LangChain หรือ LlamaIndex เป็น Markdown ที่สะอาดได้อย่างไร?
เรียก endpoint /read ของ WebReaderAPI แล้วห่อแต่ละคำตอบใน Document: Markdown เป็น content, final_url และ title เป็น metadata สำหรับเอเจนต์ ให้เปิดเผยการเรียกเดียวกันเป็น tool read_url หน้าเว็บละ 1 เครดิต, 2 เครดิตเมื่อใช้เบราว์เซอร์ เริ่มต้นที่ US$0.07/1K หน้า ณ ตุลาคม 2026 ไม่ต้องใช้แพ็กเกจใด ๆ ของเรา ไม่ต้อง KYC จ่ายด้วยคริปโต
ทำไมใช้เอกสาร Markdown แทน HTML ดิบ?
เพราะ retrieval ทำงานบนข้อความ และ Markdown เก็บโครงสร้างที่สำคัญไว้ขณะที่ตัดส่วนที่เหลือทิ้ง หัวข้อยังอยู่ ดังนั้น splitter สามารถตัดตามการเปลี่ยนหัวข้อได้ ลิสต์และตารางยังอยู่ ดังนั้นขั้นตอนและสเปกจึงอยู่ด้วยกัน ส่วนนำทาง แบนเนอร์คุกกี้ และโฆษณาหายไป จึงไม่ปนเปื้อน embeddings ของคุณหรือเปลือง token
metadata มาจากการเรียกเดียวกัน: final_url เป็นแหล่งที่มาสำหรับอ้างอิง, title, published_at และ language สำหรับการกรอง
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 document loader ได้อย่างไร?
แปลงแต่ละหน้าเป็น langchain_core.documents.Document แล้วแบ่งตามหัวข้อด้วย MarkdownHeaderTextSplitter จากแพ็กเกจ langchain-text-splitters แต่ละ chunk เก็บเส้นทางหัวข้อไว้ใน metadata และคุณเพิ่ม URL ต้นทางเพื่อให้ทุกคำตอบชี้กลับไปยังหน้าได้
หน้าที่ถูกข้าม เช่น หน้าที่ถูก robots.txt ปฏิเสธ จะ raise PageError พร้อมรหัส ให้ log ไว้แล้วทำงานต่อไป
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 มี tool read_url ได้อย่างไร?
ตกแต่งฟังก์ชันด้วย @tool จาก langchain_core.tools docstring จะกลายเป็นคำอธิบาย tool ที่โมเดลอ่าน ดังนั้นระบุว่า tool คืนอะไรและใช้เมื่อใด คืนข้อความล้วน: บรรทัดชื่อเรื่อง แหล่งที่มา และ Markdown ตัดด้วย max_chars ให้เหมาะกับโมเดลของคุณ
bind tool เข้ากับ chat model ใดก็ได้ที่รองรับ tool calling หรือส่งให้ agent constructor ของคุณ จากนั้นโมเดลจะเรียก read_url ด้วย 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)
ฉันจะทำแบบเดียวกันใน LlamaIndex ได้อย่างไร?
LlamaIndex ใช้คลาส Document ของตัวเองกับ text และ metadata และ FunctionTool.from_defaults เพื่อแปลงฟังก์ชันเป็น tool helper ยังคงเหมือนเดิม สร้าง index จากเอกสารด้วย VectorStoreIndex.from_documents ซึ่งใช้ embedding model ที่การตั้งค่า LlamaIndex ของคุณระบุ
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 index ขนาดเล็กต้อง crawl มากแค่ไหน?
น้อยกว่าที่คุณคิด เอกสารของผลิตภัณฑ์มักมีไม่กี่ร้อยหน้า และการ crawl ที่ตั้ง include_paths เป็นส่วน docs จะอ่านเฉพาะส่วนนั้น ที่ 1 เครดิตต่อหน้า ไซต์ docs ขนาด 300 หน้าคิดประมาณ 300 เครดิต; 10,000 หน้า (US$3.75) รีเฟรชได้หลายครั้ง
crawl ใหม่ตามตารางที่ตรงกับความถี่ในการเปลี่ยนแปลงของแหล่งข้อมูล และเปรียบเทียบ final_url และ word_count กับการรันครั้งก่อนเพื่อ re-embed เฉพาะหน้าที่เปลี่ยน เครดิตแบบเติมเงินไม่มีวันหมดอายุ: หน้าที่ไม่ได้ใช้จะยังคงอยู่ในบัญชีของคุณจนกว่าคุณจะใช้
LangChain และ LlamaIndex: คำถามที่พบบ่อย
มีแพ็กเกจ WebReaderAPI สำหรับ LangChain หรือ LlamaIndex ไหม?
ไม่มี helper และ tool ในหน้านี้คือการผสานรวมทั้งหมด คุณจึงปรับแต่งได้อย่างอิสระ WebReaderAPI เป็น API HTTPS ธรรมดาที่ไคลเอนต์ HTTP ของ Python ใดก็เรียกได้
ต้องใช้ LangChain เวอร์ชันใด?
เวอร์ชันล่าสุดใดก็ได้ที่มี langchain_core, langchain-text-splitters และ init_chat_model tool decorator และคลาส Document มีความเสถียรตลอดหลายเวอร์ชันล่าสุด
ฉันจะหลีกเลี่ยงการอ่านหน้าที่ยังไม่เปลี่ยนแปลงซ้ำได้อย่างไร?
เก็บ final_url, word_count และวันที่อ่านแต่ละครั้ง อ่านซ้ำตามตาราง และ re-embed หน้าเฉพาะเมื่อจำนวนคำหรือ content hash เปลี่ยน การอ่านซ้ำแต่ละครั้งเสีย 1 เครดิต ดังนั้นเลือกจังหวะที่ตรงกับแหล่งข้อมูล
ถ้ามีบางหน้าถูกปฏิเสธล่ะ?
หน้าจะ raise PageError พร้อมรหัสเช่น blocked_by_robots และไม่เสียเครดิต หากเว็บไซต์ปฏิเสธคำขอของเรา เราจะส่งคำตอบของเว็บนั้นกลับไป: เราไม่แก้ CAPTCHA ไม่เข้าสู่ระบบบัญชี และไม่พยายามหลบเลี่ยงการบล็อกหรือ paywall