# Búsqueda web en LangChain y LlamaIndex: una herramienta y un retriever

> Búsqueda web para LangChain y LlamaIndex: una herramienta search_web, un retriever con un Document por página y un FunctionTool. Sin KYC, con criptomonedas.

- URL: https://llmwebsearch.com/es/docs/langchain/
- Última actualización: 2026-10-22
- Editor: LLMWebSearch (https://llmwebsearch.com)

Dos formas cubren la mayoría de los frameworks. Una herramienta permite que un agente decida cuándo buscar. Un retriever conecta la web en vivo a una cadena de recuperación, donde cada página leída se convierte en un documento con su URL en los metadatos.

**Respuesta rápida — ¿Cómo añado búsqueda web en vivo a LangChain o LlamaIndex?**

Envuelve la llamada a `/search` de **LLMWebSearch** en un `@tool` de LangChain, un retriever de LangChain o un `FunctionTool` de LlamaIndex. Cada uno devuelve fuentes clasificadas con las 3 páginas principales en Markdown y sus URLs, listas para citar. No hay que instalar ningún paquete nuestro: el código de abajo es la integración. Una búsqueda cuesta 1 crédito, desde 1,20 US$/1K búsquedas a fecha de octubre de 2026. Sin KYC; paga con criptomonedas.

## Para qué sirve cada wrapper

| Atributo | Valor |
| --- | --- |
| Herramienta de LangChain | Un agente llama a `search_web` cuando decide que necesita la web |
| Retriever de LangChain | Una cadena obtiene documentos nuevos para cada pregunta, sin preguntar |
| FunctionTool de LlamaIndex | La misma función `search_web`, para agentes de LlamaIndex |
| Paquetes | `langchain-core` o `llama-index-core`, más `requests` |
| Clave | `LLMWEBSEARCH_API_KEY` en el entorno |
| Facturación | Un crédito paga una búsqueda: hasta 20 resultados clasificados, con las páginas de los 3 primeros leídas y devueltas como Markdown. Leer de 4 a 10 páginas pasa a 2 créditos. Una llamada que termina en error no cuesta nada |

## ¿Cómo creo una herramienta de búsqueda para un agente de LangChain?

Decora una función con `@tool` de `langchain_core.tools`. LangChain lee el nombre, los argumentos tipados y el docstring para describir la herramienta al modelo, así que mantén el docstring corto y preciso. La función devuelve una sola cadena con fuentes numeradas, que encaja con cualquier modelo de chat y mantiene las URLs junto al texto.

Llámala directamente con `.invoke()` para probarla y luego pásala en la lista tools de tu agente.

*Herramienta de 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])

```

## ¿Cómo uso la web en vivo como retriever?

Crea una subclase de `BaseRetriever` y devuelve un `Document` por cada página leída. El texto de la página va en `page_content`; la URL, el título, el ranking y la fecha van en `metadata`, donde las cadenas que citan sus fuentes los buscan. Los resultados sin contenido se omiten, porque un retriever debe entregar texto que el modelo pueda citar.

Un retriever se ejecuta en cada pregunta. Eso es correcto para una cadena de respuesta a preguntas sobre temas de actualidad, y un desperdicio para un chat en el que la mayoría de los turnos no necesitan búsqueda; en ese caso, usa la herramienta.

*Retriever de 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))

```

## ¿Cómo doy la misma búsqueda a un agente de LlamaIndex?

Escribe una función normal y envuélvela con `FunctionTool.from_defaults` de `llama_index.core.tools`. LlamaIndex construye el nombre y la descripción de la herramienta a partir del nombre, la firma y el docstring de la función. Al llamar a la herramienta se devuelve un `ToolOutput` cuyo `content` es el texto que leerá el agente.

La función es la misma que la de LangChain, así que un equipo que use ambos frameworks puede compartirla.

*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])

```

## ¿Cómo mantengo controlada la ventana de contexto?

Configura `max_chars` en la llamada a la API en lugar de recortar después: viajan menos datos y el framework nunca guarda la versión larga. 5.000 caracteres por página y 3 páginas son unos 4.000 tokens, cómodo para la mayoría de los modelos.

Para las cadenas de recuperación que combinan la web en vivo con tus propios documentos, asigna a los documentos web un peso menor o una sección de prompt aparte. Las páginas web cambian y pueden estar mal; tus propios documentos suelen ser la referencia.

## ¿Puedo almacenar lo que devuelve el retriever en una base de datos vectorial?

Sí, por lo que a nosotros respecta. No entrenamos ningún modelo con tus consultas ni con lo que devuelve la API, y no añadimos ninguna regla propia contra almacenar resultados; los derechos de los sitios cuyas páginas lees siguen aplicándose. Guarda la URL y `published_at` con cada fragmento, así el agente puede citar la página y tú puedes descartar los fragmentos que se quedan obsoletos.

La mayoría de los equipos guardan en caché los resultados web de las preguntas frecuentes durante un día o una semana. Eso reduce las búsquedas repetidas, y cada búsqueda evitada es un crédito ahorrado.

## LangChain y LlamaIndex: preguntas habituales

### ¿Existe un paquete langchain-llmwebsearch?

No. No publicamos ningún paquete. El código de esta página es lo bastante corto para que sea tuyo: cópialo, cambia los valores por defecto y guárdalo en tu repositorio.

### ¿Puedo hacer la herramienta asíncrona?

Sí. Sustituye `requests` por `httpx.AsyncClient` y define la función con `async def`. El decorador `@tool` de LangChain decora funciones asíncronas directamente; en LlamaIndex, pasa la versión asíncrona a `FunctionTool.from_defaults` como `async_fn`.

### ¿Cómo leo una página que el agente encontró en otro sitio?

Añade una segunda herramienta que envíe URLs a `https://llmwebsearch.com/api/v1/extract`. Leer páginas cuyas URL ya tienes cuesta 1 crédito por cada grupo iniciado de 3 páginas devueltas con contenido, hasta 20 URL por llamada; las URL rechazadas o fallidas no se cuentan. La [referencia de extract](https://llmwebsearch.com/es/docs/extract/) muestra la solicitud.

---

LLMWebSearch: LLMWebSearch es una API de búsqueda web para agentes de IA que devuelve resultados clasificados con el contenido de las mejores páginas en Markdown, se paga con criptomonedas y sin subir documentos de identidad. Los créditos prepago desde 1,20 US$/1K búsquedas nunca caducan. Sin KYC: sin ID, sin selfie, sin documentos. Paga con criptomonedas.
