Volver al blog

Dale a un LLM local búsqueda web en vivo con Ollama + Scrapeless

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

14-Jul-2026

Un modelo que se ejecuta en su propia máquina no sabe nada más allá de su fecha límite de entrenamiento. Pregunte a un Llama, Qwen o Mistral local sobre un titular de esta semana y o bien se niega o, lo que es peor, afirma algo plausible pero incorrecto. El perfil de riesgo de IA generativa de NIST tiene un nombre para el segundo modo de fallo: confabulación, definido como un sistema que "genera y presenta de manera confiada contenido erróneo o falso en respuesta a indicaciones." Un modelo sin acceso a datos actuales no puede evitarlo cuando la pregunta es sobre hoy.

La solución no es un modelo más grande. Es una herramienta de búsqueda que el modelo puede invocar, conectada a una API de búsqueda que devuelve resultados reales, con los resultados alimentados de nuevo en la misma conversación antes de que el modelo escriba su respuesta final. Esta guía conecta ese patrón de extremo a extremo: Ollama ejecutando un modelo que llama a herramientas completamente en su propio hardware, y Scrapeless Deep SerpApi como el backend de búsqueda al que el modelo accede. Cada solicitud y respuesta mostradas a continuación es una ejecución real, no una transcripción simulada.

Lo que esta integración habilita

Un modelo local con una herramienta de búsqueda adjunta puede responder preguntas que sus propios pesos no pueden:

  • Eventos actuales y precios. Un modelo entrenado hace meses no puede conocer los números de este trimestre; una llamada de búsqueda en vivo puede.
  • Verificación de hechos de su propia memoria. El modelo da una respuesta, luego una llamada de búsqueda la confirma o corrige antes de que la respuesta final se envíe.
  • Agentes prioritarios en modo offline con una única dependencia de red. Todo — los pesos, el razonamiento, la lógica de selección de herramientas — se ejecuta en la máquina local. La única llamada externa es la solicitud de búsqueda, limitada exactamente a la consulta que el modelo eligió preguntar.
  • Modelos pequeños sobresaliendo por encima de sus datos de entrenamiento. El modelo en esta guía tiene 0.5 mil millones de parámetros. No necesita saber nada sobre misiones a Marte; necesita reconocer que la pregunta requiere una búsqueda y formular una consulta razonable.

Por qué Deep SerpApi para la herramienta de búsqueda

Ollama ofrece su propia característica de búsqueda alojada (ollama.com/api/web_search), y es un valor predeterminado razonable para un prototipo rápido. También requiere una cuenta de Ollama, una OLLAMA_API_KEY, y enruta cada consulta a través del propio servicio en la nube de Ollama — el modelo se mantiene local, pero el paso de búsqueda no permanece fuera de la infraestructura de Ollama más de lo que lo haría con cualquier otro proveedor de búsqueda alojada. Su límite predeterminado documentado es de 5 resultados por llamada, 10 como máximo.

Deep SerpApi es un endpoint de búsqueda estructurada dedicado: un POST autenticado devuelve los resultados orgánicos de Google, búsquedas relacionadas, paginación y (dependiendo de la consulta) datos de video y panel de conocimiento como JSON parseado — no un resumen reducido. La propia página de producto de Scrapeless lista la cobertura en "más de 20 escenarios SERP de Google y motores de búsqueda convencionales" (Búsqueda, Noticias, Mapas, Compras, Tendencias y más), tiempos de respuesta de "1-2 segundos," y un nivel gratuito de "2,000 llamadas API gratuitas" sin tarjeta requerida. El uso de pago comienza "desde $1.05/1K consultas." Si un proyecto ya depende de Scrapeless para otro trabajo de recopilación de datos, o necesita el esquema completo de resultados orgánicos en lugar de un resumen de respuesta corto, conectar la clave Deep SerpApi de la misma cuenta en el bucle de llamada a herramientas mantiene un proveedor y una factura en lugar de dos.

Obtenga una clave API gratuita registrándose — sin tarjeta requerida: app.scrapeless.com.

Requisitos previos

  • Una máquina Linux, macOS o WSL2 con al menos 2 GB de RAM libre (el modelo de esta guía necesita mucho menos de 1 GB una vez cargado; una GPU es opcional y solo acelera la inferencia).
  • curl y Python 3.9 o más reciente.
  • Una cuenta de Scrapeless y una clave API de la página de gestión de claves API del panel de control.
  • Sin cuenta de Ollama y sin OLLAMA_API_KEY — este camino nunca llama al servicio alojado de Ollama.

Instalar y ejecutar un modelo que llama a herramientas localmente

Instalar Ollama:

bash Copy
curl -fsSL https://raw.githubusercontent.com/ollama/ollama/main/scripts/install.sh | sh

En un host Linux gestionado por systemd (incluyendo WSL2 con systemd habilitado) el instalador registra y comienza un servicio ollama de forma automática, escuchando en 127.0.0.1:11434. Confirme el binario y el servicio:

bash Copy
ollama --version
# la versión de ollama es 0.31.2

Descargue un pequeño modelo capaz de llamar a herramientas. Los checkpoints ajustados por instrucciones de Qwen2.5 soportan la llamada a funciones hasta el tamaño de 0.5 mil millones de parámetros, lo que mantiene la descarga y la huella de memoria pequeñas:

bash Copy
ollama pull qwen2.5:0.5b

ollama list después confirma que el modelo es local: una entrada de 397 MB llamada qwen2.5:0.5b, lista para servir sin ningún acceso a la red adicional.
No todos los modelos que se pueden ejecutar localmente admiten la llamada de herramientas; verifica la página de un modelo en la biblioteca de Ollama para buscar una etiqueta de "Herramientas" antes de construir un bucle de agente en torno a él. Los puntos de control más grandes de Qwen2.5, Llama 3.1 y Mistral tienen la misma etiqueta si 0.5B resulta demasiado pequeño para una tarea determinada.

Obtén una clave API de Scrapeless gratuita mientras se descarga el modelo; no se requiere tarjeta: app.scrapeless.com.

Verifica el Endpoint de Deep SerpApi

Deep SerpApi toma una forma para cada escenario: un nombre de actor más un objeto input, documentado en docs.scrapeless.com. El escenario de búsqueda de Google usa el actor scraper.google.search:

bash Copy
curl -s -X POST "https://api.scrapeless.com/api/v1/scraper/request" \
  -H "x-api-token: $SCRAPELESS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actor": "scraper.google.search",
    "input": {"q": "últimos titulares sobre la misión de retorno de muestras de Marte", "gl": "us", "hl": "en"}
  }'

Una llamada en vivo para esa consulta devuelve HTTP 200 con un cuerpo JSON que contiene estos campos de nivel superior: organic_results, pagination, related_searches, search_information, inline_videos, video_results y metadata. Cada entrada en organic_results lleva position, title, link, redirect_link, favicon, snippet, snippet_highlighted_words y source. La respuesta es un JSON simple: sin streaming, sin sesión abierta, una solicitud y un documento de salida. Una solicitud que no ha terminado del lado del servidor devuelve HTTP 201 con un taskId en su lugar; el caso ordinario para una consulta de búsqueda de Google es el síncrono 200 mostrado arriba.

Define y Adjunta la Herramienta de Búsqueda

El endpoint /api/chat de Ollama acepta un arreglo tools estructurado como definiciones de función en JSON Schema. Define una herramienta, web_search, y pásala junto a la conversación:

python Copy
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": "Buscar en la web en vivo información actual y devolver los principales resultados orgánicos con títulos, enlaces y fragmentos.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "La consulta de búsqueda"}
                },
                "required": ["query"],
            },
        },
    }
]

Adjunta TOOLS a cada solicitud de /api/chat para que el modelo siempre sepa que la herramienta existe:

python Copy
import json
import urllib.request

OLLAMA_URL = "http://127.0.0.1:11434/api/chat"
MODEL = "qwen2.5:0.5b"

def ollama_chat(messages):
    body = json.dumps({"model": MODEL, "stream": False, "messages": messages, "tools": TOOLS}).encode()
    req = urllib.request.Request(OLLAMA_URL, data=body, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=180) as resp:
        return json.load(resp)

El modelo nunca llama a Deep SerpApi directamente; solo emite una entrada de tool_calls nombrando web_search con los argumentos que eligió. Una delgada función en Python realiza el trabajo HTTP real y devuelve el resultado estructurado:

python Copy
import os

def web_search(query: str) -> str:
    body = json.dumps({
        "actor": "scraper.google.search",
        "input": {"q": query, "gl": "us", "hl": "en"},
    }).encode()
    req = urllib.request.Request(
        "https://api.scrapeless.com/api/v1/scraper/request",
        data=body,
        headers={
            "x-api-token": os.environ["SCRAPELESS_API_KEY"],
            "Content-Type": "application/json",
        },
    )
    with urllib.request.urlopen(req, timeout=60) as resp:
        data = json.load(resp)
    results = data.get("organic_results", [])[:3]
    shaped = [
        {"title": r.get("title"), "link": r.get("link"), "snippet": r.get("snippet")}
        for r in results
    ]
    return json.dumps(shaped)

Recortar a los tres mejores resultados antes de devolver la salida de la herramienta mantiene pequeña la segunda llamada a /api/chat; un modelo de 0.5 mil millones de parámetros tiene una ventana de contexto limitada, y la respuesta completa lleva enlaces de paginación, favicons y bloques de búsquedas relacionadas que el modelo no necesita para responder la pregunta.

Uso Impulsado por Prompts: Observando al Modelo Decidir

Uniendo las piezas: envía un prompt que necesita información actual, deja que el modelo solicite la herramienta, ejecuta esa solicitud contra Deep SerpApi y envía el resultado de vuelta para una respuesta final fundamentada.

python Copy
import json
import os
import urllib.request

OLLAMA_URL = "http://127.0.0.1:11434/api/chat"
```python
SCRAPELESS_URL = "https://api.scrapeless.com/api/v1/scraper/request"
MODEL = "qwen2.5:0.5b"

HERRAMIENTAS = [
    {
        "tipo": "función",
        "función": {
            "nombre": "búsqueda_web",
            "descripción": "Buscar en la web en vivo información actual y devolver los principales resultados orgánicos con títulos, enlaces y fragmentos.",
            "parámetros": {
                "tipo": "objeto",
                "propiedades": {
                    "consulta": {"tipo": "cadena", "descripción": "La consulta de búsqueda"}
                },
                "requerido": ["consulta"],
            },
        },
    }
]

def charla_ollama(mensajes):
    cuerpo = json.dumps({"modelo": MODEL, "stream": False, "mensajes": mensajes, "herramientas": HERRAMIENTAS}).encode()
    req = urllib.request.Request(OLLAMA_URL, data=cuerpo, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=180) as resp:
        return json.load(resp)

def búsqueda_web(consulta: str) -> str:
    cuerpo = json.dumps({
        "actor": "scraper.google.search",
        "input": {"q": consulta, "gl": "us", "hl": "en"},
    }).encode()
    req = urllib.request.Request(
        SCRAPELESS_URL,
        data=cuerpo,
        headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=60) as resp:
        data = json.load(resp)
    resultados = data.get("organic_results", [])[:3]
    return json.dumps([
        {"título": r.get("title"), "enlace": r.get("link"), "fragmento": r.get("snippet")}
        for r in resultados
    ])

mensajes = [{"rol": "usuario", "contenido":
    "¿Cuáles son los últimos titulares sobre la misión de retorno de muestras de Marte hoy? "
    "Utiliza la herramienta de búsqueda si necesitas información actual."}]

primer_turno = charla_ollama(mensajes)
print(json.dumps(primer_turno["mensaje"], indent=2))

llamada_herramienta = primer_turno["mensaje"]["tool_calls"][0]
consulta = llamada_herramienta["función"]["argumentos"]["consulta"]
resultado = búsqueda_web(consulta)

mensajes.append(primer_turno["mensaje"])
mensajes.append({"rol": "herramienta", "contenido": resultado, "nombre_herramienta": "búsqueda_web"})

segundo_turno = charla_ollama(mensajes)
print(json.dumps(segundo_turno["mensaje"], indent=2))

Una ejecución en vivo contra qwen2.5:0.5b imprime esto para el primer turno — aún no hay respuesta final, solo una solicitud de herramienta:

json Copy
{
  "rol": "asistente",
  "contenido": "",
  "tool_calls": [
    {
      "id": "call_dwndere1",
      "función": {
        "índice": 0,
        "nombre": "búsqueda_web",
        "argumentos": {
          "consulta": "Últimos titulares de la misión de retorno de muestras de Marte"
        }
      }
    }
  ]
}

El modelo leyó el aviso, reconoció que necesitaba información actual y eligió su propia consulta de búsqueda — la formulación de esta ejecución provino del modelo, no de una copia de la frase del usuario. El código de llamada ejecuta búsqueda_web(consulta="Últimos titulares de la misión de retorno de muestras de Marte") contra Deep SerpApi, obtiene de vuelta tres resultados estructurados y los agrega como un mensaje de rol: "herramienta" antes de enviar la conversación a /api/chat una segunda vez.

Lo que obtienes de vuelta

La segunda declaración print es la respuesta real del modelo, fundamentada en lo que devolvió la llamada de búsqueda:

json Copy
{
  "rol": "asistente",
  "contenido": "Aquí están algunos de los últimos titulares sobre la misión de retorno de muestras de Marte:\n\n- [Retorno de muestras de Marte - Misiones de Marte](https://www.jpl.nasa.gov/missions/mars-sample-return-msr/)\n  - Este enlace te lleva al sitio web de NASA que contiene información sobre el programa de retorno de muestras de Marte.\n- [Retorno de muestras de Marte de NASA-ESA](https://en.wikipedia.org/wiki/NASA-ESA_Mars_Sample_Return)\n  - También está disponible una página de Wikipedia sobre el proyecto, que proporciona información más detallada sobre la misión y sus costos.\n\nAquí hay algunos enlaces a artículos relacionados con la misión de retorno de muestras de Marte:\n\n1. [El retorno de muestras de Marte de NASA está muerto, allanando el camino para China](https://www.universetoday.com/articles/nasas-mars-sample-return-is-dead-paving-the-way-for-china/)\n  - Este artículo proporciona más información sobre el estado actual de la misión de retorno de muestras de Marte.\n\n¡Espero que esto ayude! Déjame saber si necesitas otros artículos o actualizaciones."
}

Cada enlace en esa respuesta se remonta a uno de los tres resultados que realmente devolvió búsqueda_web — el modelo resumió y reorganizó en su propia lista en lugar de inventar algo nuevo esta vez. Eso no es una garantía: un modelo de 0.5 mil millones de parámetros aún puede producir un detalle que parece plausible y que nunca estuvo en el resultado de la herramienta, incluso con resultados de búsqueda reales en su contexto. Fundar reduce las probabilidades de que eso suceda; no las elimina, y un sistema de producción que extrae enlaces de la respuesta de un modelo debería verificar cada uno contra la salida de la herramienta antes de tratarlo como hecho. El mismo patrón de solicitud y respuesta se sostiene independientemente del tema de la consulta: el modelo decide cuándo buscar, la llamada a la herramienta es el único viaje de red que no poseen los propios pesos del modelo, y la segunda completación nunca se ejecuta hasta que el resultado de la herramienta esté en la conversación.

Conclusión

Conectar un modelo local a Deep SerpApi requirió una definición de herramienta, una función HTTP y dos llamadas a /api/chat: el modelo maneja el razonamiento sobre cuándo buscar y qué preguntar, de la misma manera que lo haría un marco de agente de modelo alojado, pero cada token de generación permanece en la máquina que lo ejecuta. El patrón se escala más allá de este único ejemplo: cambia el aviso, cambia el modelo, o extiende TOOLS con más funciones, y el mismo bucle solicitud-herramienta-llamada-respuesta lleva el resto.

Comienza gratis — no se requiere tarjeta: app.scrapeless.com. Referencia completa de parámetros en docs.scrapeless.com, y precios actuales por consulta en scrapeless.com/en/pricing. Para la diferencia entre un punto final de SERP como este y los actores de Scrapeless que capturan las propias respuestas de una plataforma de IA alojada, consulta la comparación entre SERP-API y LLM-scraper.

Preguntas Frecuentes

P: ¿Qué modelos locales soportan llamadas a herramientas?

Cualquier modelo etiquetado como "Tools" en la biblioteca de modelos de Ollama funciona con este patrón. Qwen2.5 (hasta 0.5B), Llama 3.1 y 3.2, Mistral e IBM Granite envían puntos de control capaces de llamar herramientas. Un modelo sin esa etiqueta aún puede emitir JSON en forma de tool_calls como texto plano, que el código llamador debe analizar manualmente en lugar de leer un campo estructurado: verifica la etiqueta antes de construir en torno a un modelo determinado.

P: ¿Requiere alguna de estas cosas conexión a internet para el modelo en sí?

No. El modelo, el aviso y el razonamiento se ejecutan en la máquina local. La única solicitud saliente es la llamada a la herramienta web_search a Deep SerpApi, limitada exactamente a la consulta que generó el modelo; nada más sobre la ejecución toca la red.

P: ¿Por qué no usar la búsqueda web integrada de Ollama en lugar de clave API separada?

La búsqueda alojada de Ollama (ollama.com/api/web_search) es una opción legítima para un prototipo rápido, y no necesita una cuenta de proveedor separada más allá de la propia de Ollama. Sin embargo, requiere una OLLAMA_API_KEY vinculada a una cuenta gratuita de Ollama, limita los resultados a 10 por llamada y devuelve una lista de resultados genérica en lugar del esquema completo de resultados orgánicos de Google (posiciones, búsquedas relacionadas, paginación, verticales). Deep SerpApi se adapta mejor cuando un proyecto necesita ese esquema más completo, escenarios no relacionados con Google Search o ya ejecuta otro trabajo a través de una cuenta de Scrapeless.

P: ¿Qué sucede si la llamada de búsqueda falla?

Una solicitud malformada devuelve HTTP 400; una clave API inválida o faltante devuelve un error de autenticación; una consulta que no ha finalizado del lado del servidor devuelve HTTP 201 con un taskId en lugar de un cuerpo de resultado. Verifica el código de estado antes de asumir que organic_results existe en la respuesta, de la misma manera que cualquier cliente HTTP verifica una respuesta antes de analizar su cuerpo.

P: ¿Puedo dirigir esto a un país o idioma diferente al inglés?

Sí: gl establece el código de país de Google y hl establece el idioma de la interfaz en cada solicitud de scraper.google.search; ambos son campos de cadena ordinarios en el objeto input, que se establecen por llamada.

P: ¿El modelo alguna vez llama a Deep SerpApi directamente?

No. El modelo solo emite una entrada tool_calls describiendo qué función ejecutar y con qué argumentos, no tiene acceso a la red por su cuenta. El código Python llamador es el que posee la solicitud HTTP real, lo que también evita que la clave API esté completamente fuera del contexto del modelo.

P: ¿Es seguro apuntar esto a raspar resultados de búsqueda en lugar de a una API?

Deep SerpApi devuelve datos de Google ya analizados a través de un punto final autenticado, por lo que no hay preocupación por robots.txt o limitación de tasa del lado del llamador; ese trabajo de infraestructura se realiza en el extremo de Scrapeless. Cualquiera que construya el equivalente raspando las páginas de resultados de Google directamente debe leer el Protocolo de Exclusión de Robots y los propios términos del objetivo primero; un punto final de búsqueda gestionado existe específicamente para evitar esa clase de problema.

P: ¿Qué está sucediendo realmente cuando el modelo "decide" buscar?
Este es el patrón de recuperar-then-generar descrito en la investigación sobre generación aumentada por recuperación: un modelo condiciona su salida final en documentos obtenidos en el momento de la inferencia en lugar de basarse únicamente en lo que está almacenado en sus pesos. La llamada a herramientas es el mecanismo que utilizan los modelos modernos ajustados para chat para activar esa búsqueda por sí mismos, en medio de la conversación, en lugar de un paso de recuperación fijo que se ejecuta antes de cada solicitud.

P: ¿La fundamentación impide que el modelo invente cosas?

No. Reduce la confabulación sobre los hechos específicos que realmente cubre el resultado de la búsqueda, pero un modelo pequeño aún puede atribuir erróneamente, sobre-resumir o agregar un detalle que no estaba en la salida de la herramienta. Trata la segunda respuesta como un borrador informado por datos reales, no como una cita autorizada; para cualquier cosa crucial, compara las afirmaciones del modelo con la carga útil de organic_results que realmente se le dio antes de confiar en ellas.

En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.

Artículos más populares

Catalogar