# Web search trong LangChain và LlamaIndex: một tool và một retriever

> Tìm kiếm web cho LangChain và LlamaIndex: tool search_web, retriever trả Document mỗi trang, và FunctionTool LlamaIndex. Không cần KYC, trả bằng crypto.

- URL: https://llmwebsearch.com/vi/docs/langchain/
- Cập nhật lần cuối: 2026-10-22
- Đơn vị phát hành: LLMWebSearch (https://llmwebsearch.com)

Hai dạng bao quát hầu hết framework. Một tool cho phép agent tự quyết định khi nào cần search. Một retriever gắn web trực tiếp vào chuỗi retrieval, trong đó mỗi trang được đọc trở thành một document với URL trong metadata.

**Trả lời nhanh — Làm thế nào để thêm web search trực tiếp vào LangChain hoặc LlamaIndex?**

Bọc lời gọi `/search` của **LLMWebSearch** trong một `@tool` của LangChain, một retriever của LangChain hoặc một `FunctionTool` của LlamaIndex. Mỗi cái trả về các nguồn đã xếp hạng với 3 trang đầu dưới dạng Markdown kèm URL, sẵn sàng để trích dẫn. Không cần cài package nào của chúng tôi: đoạn code bên dưới chính là phần tích hợp. Mỗi lần search tốn 1 credit, từ 1,20 US$/1K lượt tìm kiếm tính đến tháng 10 năm 2026. Không cần KYC; thanh toán bằng crypto.

## Mỗi wrapper dùng để làm gì

| Thuộc tính | Giá trị |
| --- | --- |
| Tool LangChain | Agent gọi `search_web` khi nó quyết định cần web |
| Retriever LangChain | Chuỗi lấy document mới cho mỗi câu hỏi, không cần hỏi |
| FunctionTool LlamaIndex | Cùng hàm `search_web` đó, cho agent LlamaIndex |
| Package | `langchain-core` hoặc `llama-index-core`, cộng với `requests` |
| Key | `LLMWEBSEARCH_API_KEY` trong biến môi trường |
| Thanh toán | Một credit trả cho một lượt tìm kiếm: tối đa 20 kết quả được xếp hạng, với nội dung của 3 trang đầu được đọc và trả về dưới dạng Markdown. Đọc từ 4 đến 10 trang thì tính 2 credit. Lượt gọi kết thúc bằng lỗi không tốn gì |

## Làm thế nào để xây dựng một tool search cho agent LangChain?

Trang trí một hàm bằng `@tool` từ `langchain_core.tools`. LangChain đọc tên, các tham số có kiểu và docstring để mô tả tool cho model, nên hãy giữ docstring ngắn gọn và chính xác. Hàm trả về một chuỗi với các nguồn được đánh số, phù hợp với mọi chat model và giữ URL ngay cạnh văn bản.

Gọi trực tiếp với `.invoke()` để kiểm thử, sau đó truyền nó vào danh sách tools của agent.

*Tool LangChain: search_web*

```python
import os

import requests
from langchain_core.tools import tool

API = "https://llmwebsearch.com/api/v1/search"
HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}


@tool
def search_web(query: str, read: int = 3) -> str:
    """Search the live web. Returns numbered sources with URLs; the first `read` include page text."""
    body = {"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000}
    resp = requests.post(API, headers=HEADERS, json=body, timeout=60)
    resp.raise_for_status()
    parts = []
    for i, r in enumerate(resp.json()["results"], start=1):
        text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
        parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
    return "\n\n".join(parts)


print(search_web.invoke({"query": "postgres connection pool size"})[:500])

```

## Làm thế nào để dùng web trực tiếp làm retriever?

Kế thừa `BaseRetriever` và trả về một `Document` cho mỗi trang được đọc. Văn bản của trang nằm trong `page_content`; URL, tiêu đề, thứ hạng và ngày nằm trong `metadata`, nơi các chuỗi có trích dẫn nguồn tìm đến. Kết quả không có nội dung bị bỏ qua, vì retriever nên đưa ra văn bản mà model có thể trích dẫn.

Retriever chạy ở mọi câu hỏi. Điều đó đúng cho chuỗi hỏi-đáp về chủ đề thời sự, và lãng phí cho một cuộc trò chuyện mà phần lớn lượt không cần search; trong trường hợp đó hãy dùng tool.

*Retriever LangChain: LLMWebSearchRetriever*

```python
import os
from typing import List

import requests
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_core.retrievers import BaseRetriever


class LLMWebSearchRetriever(BaseRetriever):
    """Live web search results as Documents, one per page that was read."""

    max_results: int = 5
    read: int = 3
    max_chars: int = 6000

    def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun) -> List[Document]:
        resp = requests.post(
            "https://llmwebsearch.com/api/v1/search",
            headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
            json={"q": query, "max_results": self.max_results, "read": self.read, "max_chars": self.max_chars},
            timeout=60,
        )
        resp.raise_for_status()
        docs = []
        for r in resp.json()["results"]:
            if r["content_status"] != "ok":
                continue
            meta = {"source": r["url"], "title": r["title"], "position": r["position"], "published_at": r.get("published_at")}
            docs.append(Document(page_content=r["content"], metadata=meta))
        return docs


retriever = LLMWebSearchRetriever(read=3)
for doc in retriever.invoke("pgbouncer transaction mode limitations"):
    print(doc.metadata["position"], doc.metadata["source"], len(doc.page_content))

```

## Làm thế nào để cung cấp cùng chức năng search cho agent LlamaIndex?

Viết một hàm thường và bọc nó bằng `FunctionTool.from_defaults` từ `llama_index.core.tools`. LlamaIndex xây dựng tên và mô tả của tool từ tên hàm, signature và docstring. Gọi tool trả về một `ToolOutput` mà `content` của nó là văn bản agent sẽ đọc.

Hàm này giống với hàm dùng cho LangChain, nên một đội dùng cả hai framework có thể chia sẻ nó.

*LlamaIndex: FunctionTool*

```python
import os

import requests
from llama_index.core.tools import FunctionTool


def search_web(query: str, read: int = 3) -> str:
    """Search the live web and return numbered sources; the first `read` include their page text."""
    resp = requests.post(
        "https://llmwebsearch.com/api/v1/search",
        headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
        json={"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000},
        timeout=60,
    )
    resp.raise_for_status()
    parts = []
    for i, r in enumerate(resp.json()["results"], start=1):
        text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
        parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
    return "\n\n".join(parts)


search_tool = FunctionTool.from_defaults(fn=search_web)
print(search_tool.metadata.name)
print(search_tool.call(query="postgres connection pool size").content[:300])

```

## Làm sao để giữ cửa sổ ngữ cảnh trong tầm kiểm soát?

Đặt `max_chars` ngay trong lệnh gọi API thay vì cắt bớt sau đó: ít dữ liệu truyền đi hơn và framework không bao giờ phải giữ phiên bản dài. 5.000 ký tự mỗi trang và 3 trang là khoảng 4.000 token, thoải mái cho hầu hết model.

Với các retrieval chain kết hợp web trực tiếp với tài liệu của riêng bạn, hãy gán trọng số thấp hơn cho tài liệu web hoặc tách thành một mục prompt riêng. Trang web thay đổi và có thể sai; tài liệu của bạn thường là nguồn tham chiếu.

## Tôi có thể lưu những gì retriever trả về vào vector database không?

Có, theo quan điểm của chúng tôi. Chúng tôi không huấn luyện bất kỳ model nào trên truy vấn của bạn hay trên những gì API trả về, và chúng tôi không thêm quy tắc riêng nào chống lại việc lưu trữ kết quả; quyền của các trang có nội dung bạn đọc vẫn áp dụng. Lưu URL và `published_at` cùng mỗi chunk, để agent có thể trích dẫn trang và bạn có thể loại bỏ các chunk cũ dần.

Hầu hết các nhóm lưu cache kết quả web của những câu hỏi thường gặp trong một ngày hoặc một tuần. Việc đó giảm các tìm kiếm lặp lại, và mỗi tìm kiếm tránh được là một credit tiết kiệm.

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

### Có package langchain-llmwebsearch không?

Không. Chúng tôi không phát hành package nào. Đoạn code trên trang này đủ ngắn để tự sở hữu: sao chép, thay đổi các giá trị mặc định và giữ nó trong repository của bạn.

### Tôi có thể làm cho tool chạy async không?

Có. Thay `requests` bằng `httpx.AsyncClient` và định nghĩa hàm với `async def`. `@tool` của LangChain trang trí trực tiếp các hàm async; trong LlamaIndex, truyền phiên bản async vào `FunctionTool.from_defaults` dưới dạng `async_fn`.

### Làm sao để đọc một trang mà agent tìm thấy ở nơi khác?

Thêm một tool thứ hai để gửi URL tới `https://llmwebsearch.com/api/v1/extract`. Đọc các trang mà bạn đã có URL tốn 1 credit cho mỗi nhóm 3 trang bắt đầu được trả về kèm nội dung, tối đa 20 URL mỗi lượt gọi; các URL bị từ chối hoặc thất bại không được tính. [Tài liệu tham chiếu extract](https://llmwebsearch.com/vi/docs/extract/) cho thấy request.

---

LLMWebSearch: LLMWebSearch là một web search API cho AI agent, trả về kết quả đã xếp hạng kèm nội dung của các trang hàng đầu ở định dạng Markdown, thanh toán bằng crypto mà không cần tải lên giấy tờ tùy thân. Credit trả trước từ 1,20 US$/1K lượt tìm kiếm không bao giờ hết hạn. Không cần KYC — không ID, không selfie, không giấy tờ. Thanh toán bằng crypto.
