无需 KYC — 无需身份证件、自拍或文件。用加密货币支付:BTC, USDT, USDC, ETH, SOL, LTC
语言: 简体中文
获取 API 密钥

文档 · LangChain · LlamaIndex

LangChain 和 LlamaIndex 中的网页搜索:一个工具和一个 retriever

两种形式可覆盖大多数框架。工具让 agent 自行决定何时搜索。retriever 把实时网页接入检索链,其中每个被读取的页面都会变成一个 Document,并在 metadata 中带上其 URL。

最后更新:

快速回答

如何为 LangChain 或 LlamaIndex 添加实时网页搜索?

将 LLMWebSearch 的 /search 调用包装进 LangChain @tool、LangChain retriever 或 LlamaIndex FunctionTool。它们都会返回带排名的来源,其中前 3 个页面为 Markdown 及其 URL,可直接用于引用。无需安装我们的包:下面的代码就是集成方式。一次搜索花费 1 积分,低至 US$1.20/千次搜索(截至 2026 年 10 月)。无需 KYC;用加密货币支付。

各个包装器的用途

LangChain 工具agent 在判断需要网页时调用 search_web
LangChain retriever链会为每个问题获取最新文档,无需询问
LlamaIndex FunctionTool同一个 search_web 函数,供 LlamaIndex agents 使用
软件包langchain-core 或 llama-index-core,外加 requests
密钥环境变量中的 LLMWEBSEARCH_API_KEY
计费1 积分支付一次查询:最多 20 条排名结果,其中前 3 条的页面会被读取并以 Markdown 返回。读取 4 到 10 个页面为 2 积分。以错误结束的调用不收费

如何为 LangChain agent 构建搜索工具?

用 langchain_core.tools 中的 @tool 装饰一个函数。LangChain 会读取名称、带类型的参数和 docstring 来向模型描述该工具,因此请保持 docstring 简短精确。函数返回一个带编号来源的字符串,适用于所有聊天模型,并让 URL 紧邻文本。

用 .invoke() 直接调用它来测试,然后把它传入你 agent 的 tools 列表。

LangChain 工具:search_web
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])

如何把实时网页用作 retriever?

继承 BaseRetriever,并为每个读取的页面返回一个 Document。页面文本放在 page_content 中;URL、标题、排名和日期放在 metadata 中,需要引用来源的链会在这里查找它们。无内容的结果会被跳过,因为 retriever 应当交给模型可以引用的文本。

retriever 会对每个问题运行。对于关于时事话题的问答链来说这没问题,但对于大多数轮次都无需搜索的聊天来说则很浪费;这种情况下请使用工具。

LangChain retriever:LLMWebSearchRetriever
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))

如何把同样的搜索提供给 LlamaIndex agent?

编写一个普通函数,并用 llama_index.core.tools 中的 FunctionTool.from_defaults 包装它。LlamaIndex 会根据函数名、签名和 docstring 构建工具的名称和描述。调用该工具会返回一个 ToolOutput,其中 content 是 agent 将读取的文本。

该函数与 LangChain 版本相同,因此同时使用这两个框架的团队可以共享它。

LlamaIndex:FunctionTool
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])

如何控制上下文窗口?

在调用 API 时设置 max_chars,而不是事后裁剪:传输的数据更少,框架也不会保留长版本。每页 5,000 个字符、3 页约为 4,000 个 token,对大多数模型来说很充裕。

对于将实时网络与你自己的文档结合的检索链,给网页文档较低的权重或单独的提示词部分。网页会变化,也可能出错;你自己的文档通常是参考依据。

我可以把检索器返回的内容存入向量数据库吗?

可以,对我们来说没问题。我们不会用你的查询或 API 返回的内容训练任何模型,也不会自行添加任何禁止存储结果的规则;你读取的页面所属网站的权利仍然适用。将 URL 和 published_at 与每个分块一起存储,这样 agent 可以引用该页面,你也可以删除过期的分块。

大多数团队会将高频问题的网页结果缓存一天或一周。这能减少重复搜索,每避免一次搜索就节省一个积分。

LangChain 和 LlamaIndex:常见问题

有 langchain-llmwebsearch 包吗?

没有。我们未发布任何包。本页的代码足够短,可以直接自己维护:复制它、修改默认值,并保存在你的代码库中。

我可以把工具改成异步的吗?

可以。将 requests 替换为 httpx.AsyncClient,并用 async def 定义函数。LangChain 的 @tool 直接装饰异步函数;在 LlamaIndex 中,将异步版本作为 async_fn 传给 FunctionTool.from_defaults。

如何读取 agent 在其他地方找到的页面?

添加第二个工具,将 URL 提交到 https://llmwebsearch.com/api/v1/extract。读取你已持有 URL 的页面:每开始一组 3 个返回内容的页面计 1 积分,每次调用最多 20 个 URL;被拒绝或失败的 URL 不计入。extract 参考 展示了该请求。

低至 US$1.20/千次搜索 · 无需 KYC

获取 API 密钥