Sin KYC — sin ID, sin selfie, sin documentos. Paga con criptomonedas: BTC, USDT, USDC, ETH, SOL, LTC
Idioma: Español
Obtén una clave API

Guía · Python · unas 60 líneas, cualquier LLM

Crea un agente de investigación profunda en Python con una API de búsqueda web

La investigación profunda es un bucle, no un modelo. Esta guía construye uno que puedes leer en unos minutos: planifica consultas, busca en la web con el texto de la página incluido, guarda cada fuente una sola vez, busca huecos y escribe una respuesta con citas numeradas.

Última actualización: · 6 min de lectura

Respuesta rápida

¿Cómo creo un agente de investigación profunda en Python?

Escribe un bucle que pida a un LLM consultas de búsqueda, pase cada una por una API de búsqueda web que devuelva el texto de la página, guarde cada fuente una sola vez, pida consultas de seguimiento y luego escriba una respuesta citada. Con LLMWebSearch, cada búsqueda devuelve las 3 primeras páginas en Markdown por un crédito; el script de abajo hace seis búsquedas por pregunta. Desde 1,20 US$/1K búsquedas, sin KYC, con criptomonedas.

¿Qué hará el agente?

  1. Plan

    Pide al modelo cuatro consultas cortas que en conjunto cubran la pregunta.

  2. Buscar

    Ejecuta cada consulta con read=3, para que las tres primeras páginas vuelvan en Markdown.

  3. Guarda cada fuente una sola vez

    Almacena los resultados por URL y omite las páginas ya recogidas o sin texto.

  4. Busca huecos

    Muestra al modelo los títulos encontrados hasta ahora y pide dos consultas sobre lo que falta.

  5. Escribe

    Numera hasta 12 fuentes y pide una respuesta que las cite como [1], [2].

¿Qué necesito antes de empezar?

Tres cosas:

  • Una clave API de LLMWebSearch en la variable de entorno LLMWEBSEARCH_API_KEY. Las claves aparecen en el panel tras tu primer pack.
  • Cualquier endpoint de chat compatible con OpenAI, configurado con LLM_BASE_URL, LLM_API_KEY y LLM_MODEL. Funcionan tanto modelos alojados como servidores locales.
  • Python 3.9 o posterior con el paquete requests.

El script usa HTTP simple para ambos servicios, así que puedes cambiar el modelo sin tocar el código de búsqueda.

Python: deep_research.py
import os

import requests

SEARCH = "https://llmwebsearch.com/api/v1/search"
SEARCH_HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
# Any OpenAI-compatible chat endpoint works here: set your own base URL, key and model name.
LLM_URL = os.environ.get("LLM_BASE_URL", "https://llm.example.com/v1") + "/chat/completions"
LLM_HEADERS = {"Authorization": "Bearer " + os.environ.get("LLM_API_KEY", "")}
MODEL = os.environ.get("LLM_MODEL", "your-model-name")


def ask_llm(prompt):
    body = {"model": MODEL, "messages": [{"role": "user", "content": prompt}]}
    resp = requests.post(LLM_URL, headers=LLM_HEADERS, json=body, timeout=120)
    resp.raise_for_status()
    return resp.json()["choices"][0]["message"]["content"]


def plan_queries(question, n=4):
    text = ask_llm(f"Write {n} short web search queries, one per line, to answer: {question}")
    queries = [line.strip("-*0123456789. ").strip() for line in text.splitlines()]
    return [q for q in queries if q][:n]


def search(query):
    body = {"q": query, "max_results": 5, "read": 3, "max_chars": 5000}
    resp = requests.post(SEARCH, headers=SEARCH_HEADERS, json=body, timeout=60)
    resp.raise_for_status()
    return resp.json()["results"]


def research(question, rounds=2):
    sources = {}  # url -> result, so the same page is never cited twice
    queries = plan_queries(question)
    for _ in range(rounds):
        for query in queries:
            for r in search(query):
                if r.get("content_status") == "ok" and r["url"] not in sources:
                    sources[r["url"]] = r
        notes = "\n".join(f"- {r['title']}" for r in sources.values())
        queries = plan_queries(f"{question}\nAlready found:\n{notes}\nWhat is still missing?", n=2)
    numbered = list(sources.values())[:12]
    context = "\n\n".join(
        f"[{i}] {r['title']} ({r['url']})\n{r['content']}" for i, r in enumerate(numbered, 1)
    )
    prompt = (
        "Answer the question using only the sources below. Cite them as [1], [2]. "
        "Say so when the sources disagree or do not cover a point.\n\n"
        f"Question: {question}\n\nSources:\n{context}"
    )
    return ask_llm(prompt), [r["url"] for r in numbered]


if __name__ == "__main__":
    answer, urls = research("What changed in the EU rules for general-purpose AI models in 2025?")
    print(answer)
    for i, url in enumerate(urls, 1):
        print(f"[{i}] {url}")

¿Cómo funciona cada parte?

plan_queries pide al modelo consultas cortas, una por línea, y elimina los marcadores de lista. Las consultas cortas encajan mejor con cómo los motores de búsqueda clasifican las páginas que las preguntas completas.

search llama a POST https://llmwebsearch.com/api/v1/search con max_results=5, read=3 y max_chars=5000. La respuesta lista cinco resultados; los tres primeros llevan el texto de su página en content.

research ejecuta las rondas. Guarda las fuentes en un diccionario indexado por URL, así una página encontrada por dos consultas cuenta una vez. Solo se conservan los resultados con content_status igual a ok, porque la respuesta debe apoyarse en texto que el modelo pueda leer.

Tras cada ronda, el modelo ve los títulos recopilados hasta el momento y propone dos consultas nuevas para lo que falta. El prompt final numera hasta doce fuentes y fija las reglas de citación.

¿Cuánto cuesta un informe?

Seis búsquedas por pregunta con la configuración por defecto: cuatro en la primera ronda y dos en la segunda. Con read en 3, son seis créditos. Así, 25.000 búsquedas a 41,25 US$ cubre más de 4.000 informes.

Las llamadas al modelo cuestan aparte en tu factura de LLM: tres llamadas de planificación y una llamada larga de respuesta por informe. La llamada de respuesta lleva hasta doce páginas de 5.000 caracteres, así que baja max_chars si tu modelo es caro.

¿Cómo hago que vaya más a fondo?

Cambia los números, no la estructura:

  • Más rondas. rounds=4 permite al modelo perseguir dos conjuntos de vacíos más.
  • Más consultas por ronda. Sube n en la llamada de búsqueda de vacíos de 2 a 4.
  • Fuentes más concretas. Añade include_domains al cuerpo de la búsqueda para sitios oficiales o académicos.
  • Solo material reciente. Añade "time_range": "month" o "topic": "news".
  • Más páginas por consulta. read=5 lee cinco páginas, a 2 créditos por búsqueda.

Añade una regla de parada antes de subir los límites: termina el bucle cuando una ronda traiga menos de dos fuentes nuevas.

¿Cómo mantengo al agente seguro y educado?

Trata cada página como texto no confiable. El prompt final le dice al modelo que use las fuentes solo como evidencia; añade una línea explícita de que las instrucciones dentro de las fuentes deben ignorarse. Nunca dejes que una fuente active una llamada a herramienta o una escritura de archivo.

En el lado web, Nuestro lector respeta robots.txt: se identifica como LLMWebSearch, sigue las reglas que un sitio escribe para él o para todos los robots, y deja fuera el contenido de cualquier página que no tenga permitido leer. Las páginas que rechazan el acceso vuelven sin contenido y el script simplemente las omite. Cuando un sitio rechaza a nuestro lector, el resultado vuelve sin contenido y con el estado del sitio: nunca resolvemos CAPTCHAs, iniciamos sesión en cuentas ni sorteamos un bloqueo o un paywall.

¿Qué puede salir mal?

En las pruebas aparecen tres cosas:

  • Consultas vagas. Si el plan es débil, cada ronda encuentra las mismas páginas. Pídele al modelo consultas que usen nombres y fechas concretos.
  • Muy pocas fuentes con texto. Algunos temas están detrás de muros de pago. Amplía las consultas o permite más resultados.
  • Prompts demasiado largos. Doce páginas largas pueden superar el contexto de un modelo pequeño. Baja max_chars o quédate con ocho fuentes.

Registra cada consulta y las URL que devolvió. Leer ese registro es la forma más rápida de mejorar el prompt del plan.

Agente de investigación profunda: preguntas frecuentes

¿Puedo ejecutarlo con un modelo local?

Sí, si el servidor local habla el formato chat-completions de OpenAI. Pon LLM_BASE_URL con su dirección y LLM_MODEL con el nombre del modelo.

¿Puedo convertirlo en una herramienta MCP?

Sí. Envuelve research() en una herramienta MCP, o dale a tu agente la herramienta de búsqueda simple de la página MCP y deja que ejecute el bucle por sí mismo.

¿Por qué no pedirle una respuesta directamente a la API de búsqueda?

Porque LLMWebSearch no escribe respuestas. La API devuelve fuentes, no una respuesta redactada: tu propio modelo lee las páginas, escribe la respuesta y cita las URL.

¿Cómo veo cuánto costó cada búsqueda?

Cada respuesta incluye credits_used, y GET https://llmwebsearch.com/api/v1/credits muestra tu saldo restante sin coste.

Desde 1,20 US$/1K búsquedas · sin KYC

Obtén una clave API