Saltar al contenido principal
Los agentes de investigación resultan útiles cuando se necesita más que un único resultado de búsqueda o una respuesta rápida de un modelo. Un buen agente de investigación puede convertir un tema amplio en consultas de búsqueda, recopilar fuentes, extraer la evidencia importante, hacer un seguimiento de las lagunas y escribir un informe con citas que puedas revisar después. En este tutorial, construiremos un agente de investigación privado utilizando Python y la API de Venice. Al final tendrás una CLI capaz de investigar un tema, extraer páginas públicas a Markdown, resumir fragmentos de las fuentes, ejecutar pases de investigación adicionales conscientes de las lagunas y generar un informe con citas y, opcionalmente, artefactos JSONL locales. ¿Te interesa la implementación completa del código? Échale un vistazo al repositorio de GitHub. Antes de continuar, necesitarás una clave API de Venice:

Qué vamos a construir

La implementación de referencia es un pequeño proyecto en Python con varias partes claras: El flujo es así: Tubería del agente de investigación privado
  1. Pide a Venice que genere consultas de búsqueda diversas para el tema.
  2. Busca en la web con uno o más proveedores.
  3. Elimina URL duplicadas antes de leerlas.
  4. Usa el endpoint de scrape de Venice para convertir cada página fuente pública en Markdown.
  5. Divide las páginas largas en fragmentos.
  6. Pide a Venice que extraiga evidencias de cada fragmento.
  7. Pide a Venice que convierta las evidencias de los fragmentos en notas de la fuente.
  8. Identifica lagunas de investigación y problemas de equilibrio entre fuentes antes de generar consultas de seguimiento.
  9. Pide a Venice que sintetice el informe final con citas estilo notas al pie.
Esto es “privado” en el sentido práctico de que el agente mantiene la orquestación, las notas de fuente, los artefactos y los informes finales en tu máquina. Venice se encarga de las llamadas al modelo y del scraping a través de su API. La implementación de referencia por defecto sigue enviando las consultas de búsqueda a DuckDuckGo o arXiv, así que considera la elección del proveedor como parte de tu diseño de privacidad.

Configurando el proyecto

El proyecto de referencia usa Python 3.13 y uv, pero el mismo código funciona con un entorno virtual normal. Crea un nuevo proyecto:
Instala las dependencias:
Si prefieres pip, crea un entorno virtual e instala los mismos paquetes:
Crea un archivo .env para el desarrollo local:
Usamos VENICE_MODEL para que puedas cambiar el modelo sin editar el código. La implementación de referencia actualmente usa openai-gpt-55 por defecto, pero puedes cambiarlo por otro modelo de chat disponible en tu cuenta de Venice.

Creando los modelos de datos

Antes de escribir la lógica del agente, definiremos los objetos que viajan por la tubería. Estos modelos hacen que el resto del código sea más fácil de razonar porque cada fuente lleva su procedencia: de dónde proviene, qué consulta la encontró, cuándo se recuperó y cómo se fragmentó. Crea research_agent/models.py:
Los campos importantes aquí son canonical_url, content_hash y chunks. canonical_url permite al agente evitar leer la misma fuente repetidamente cuando los resultados de búsqueda solo difieren en parámetros de seguimiento o fragmentos. content_hash ayuda a detectar páginas duplicadas aunque vivan en URL distintas. chunks permite resumir páginas largas en piezas más pequeñas en lugar de perder evidencias útiles por los límites de contexto. Añade las funciones auxiliares debajo de los dataclasses:
El fragmentado aquí es deliberadamente simple: fragmentos de tamaño fijo en caracteres con superposición. Es suficiente para un agente de investigación de demostración porque el endpoint de scrape de Venice devuelve Markdown, que suele estar mucho más limpio que el HTML en bruto. Para investigación en producción sobre documentos técnicos largos, puedes mejorarlo dividiendo por encabezados, párrafos o cantidad de tokens.

Construyendo el cliente de Venice

A continuación, crearemos un pequeño cliente de Venice. Podrías utilizar el SDK de Python de OpenAI para las chat completions porque Venice es compatible con OpenAI, pero la implementación de referencia usa httpx directamente para que el mismo cliente pueda llamar al endpoint POST /augment/scrape de Venice. Crea research_agent/venice.py:
El helper from_env() mantiene los secretos fuera del código fuente. También resulta cómodo para el desarrollo local porque python-dotenv puede cargar VENICE_API_KEY y VENICE_MODEL desde .env. Ahora añade las chat completions:
Para el informe final, queremos usar streaming porque los informes profundos pueden tardar bastante más (al producir mucho más texto). Esto puede provocar problemas de timeout en solicitudes donde puede tardar muchísimo tiempo en producirse la salida final. Al usar streaming, podemos eliminar este problema y hacer que la solicitud sea más resistente a los fallos por timeout:
Luego añade el scraping:
El endpoint de scrape de Venice acepta una URL accesible públicamente y devuelve la página como Markdown. Eso significa que el modelo no necesita parsear HTML en bruto, y tus prompts de extracción pueden trabajar con texto más limpio. El helper restante gestiona los reintentos y el parseo de respuestas:
El repositorio completo también incluye un helper robusto _post_chat_stream() que lee server-sent events de las chat completions en streaming. Puedes empezar sin streaming y añadirlo una vez que funcione el resto del flujo de investigación.

Añadiendo proveedores de búsqueda

La capa de búsqueda tiene dos tareas: encontrar URL de fuentes y obtener esas URL a través del scraper de Venice. La implementación de referencia usa el endpoint HTML de DuckDuckGo para búsquedas web generales y la API Atom de arXiv para artículos. Crea research_agent/web.py:
Ahora añade DuckDuckGo:
Y arXiv:
La clase WebSearch coordina los proveedores y obtiene las páginas:
La implementación de referencia completa añade reintentos, retrasos de solicitud por host y errores más amigables. Vale la pena conservarlos porque los agentes de investigación pasan mucho tiempo lidiando con páginas que bloquean la automatización, redirigen de forma inesperada o devuelven errores transitorios. Añade los pequeños helpers de proveedor al final:

Escribiendo artefactos locales

Para los flujos de investigación, la auditabilidad importa. Si el informe final dice algo sorprendente, deberías poder inspeccionar qué fuente llevó a ello. Crea research_agent/artifacts.py:
Esto escribe un objeto JSON por línea, lo que facilita añadir, inspeccionar y procesar los artefactos con herramientas de línea de comandos más adelante.

Construyendo el agente de investigación

Ahora que tenemos Venice, búsqueda, modelos y artefactos, podemos construir el agente propiamente dicho. Crea research_agent/agent.py:
El system prompt es la barrera de comportamiento principal. No queremos que el modelo produzca un informe que suene impresionante a partir de su memoria. Queremos que utilice el material fuente y que señale la incertidumbre cuando la evidencia es escasa. También necesitamos dos dataclasses finales en models.py si todavía no las has añadido:
A continuación, define ResearchAgent:
El método run() coordina los pases de investigación:
Los dos conjuntos seen_* son los que impiden que el agente pierda tiempo con fuentes duplicadas. La deduplicación por URL detecta enlaces repetidos. La deduplicación por hash de contenido detecta mirrors, publicaciones sindicadas y páginas que redirigen al mismo contenido final.

Planificando búsquedas iniciales y de seguimiento

La primera llamada al modelo convierte el tema en consultas de búsqueda:
Después de cada pase de investigación, el agente actualizado realiza un paso de análisis de lagunas más deliberado. Examina las notas actuales, cuenta los clusters de fuentes por dominio, pregunta a Venice qué cobertura falta, escribe esas lagunas en los artefactos y luego utiliza las consultas resultantes para el siguiente pase. Bucle de análisis de lagunas Empieza haciendo un seguimiento del equilibrio entre fuentes:
Esto le da al agente una forma sencilla de detectar la captura por un cluster de fuentes. Si cada fuente proviene de una sola empresa, un solo framework o un solo dominio, las consultas de seguimiento deberían ampliar deliberadamente el conjunto de fuentes en lugar de recopilar más de lo mismo. Ahora usa esa información de equilibrio al crear las búsquedas de seguimiento:
La implementación de referencia más reciente envuelve esto en _gap_follow_up_queries(), que pide a Venice que devuelva tanto los registros de lagunas como las consultas:
Cuando --artifacts está habilitado, estos registros se escriben en research_gaps.jsonl. Esto te da una pista de auditoría útil sobre por qué el agente buscó una consulta concreta en el segundo pase. El parser debería ser tolerante. Si el modelo devuelve JSON mal formado, el agente recurre al tema original como respaldo:
Vale la pena usar este patrón en todo el código del agente: pedir salida estructurada, parsearla y proporcionar un fallback simple cuando la salida no sea utilizable.

Lectura y resumen de fuentes

Ahora recopilamos notas de fuente. El agente busca cada consulta, obtiene cada resultado a través de Venice scrape, fragmenta el Markdown y resume las evidencias útiles.
Los fallos individuales en búsqueda y obtención no deberían detener toda la ejecución. La web pública es caótica. Algunas páginas bloquean el scraping, otras devuelven PDFs, otras están caídas y otras redirigen a lugares inesperados. Un agente de investigación debe seguir adelante y registrar lo que falló. Aquí está el método de lectura de fuentes:
Para cada fragmento de la fuente, pide a Venice un resumen corto de la evidencia y citas exactas:
Luego colapsa los resúmenes de los fragmentos en una nota de fuente:
Este resumen en dos pasos es la parte que hace que el agente parezca más fiable que un script básico de “resumir estas URL”. El modelo lee primero los fragmentos de la fuente y después escribe una nota a nivel de fuente a partir de esas piezas de evidencia extraídas.

Escribiendo el informe final

Una vez que el agente tiene las notas de las fuentes, puede escribir el informe. Empieza con un escritor de informes de una sola pasada:
La implementación de referencia va más allá para los informes profundos: pide a Venice un esquema, redacta cada sección del informe por separado, y luego pide una pasada final de editor para ensamblar el informe terminado y convertir los IDs internos de fuentes en citas estilo notas al pie. Ese enfoque por etapas resulta útil cuando se desea una salida de investigación de formato largo, porque un solo prompt gigante a menudo comprime demasiado. Los prompts actualizados también empujan el informe hacia un panorama amplio y respaldado por fuentes en lugar de una guía de decisión superficial. Si la base de fuentes está sesgada hacia un cluster, el prompt del editor le indica a Venice que reconozca ese sesgo y evite presentarlo como representativo de todo el campo. Añade los helpers de resumen:
Por último, añade el registro de errores:
En este punto, el bucle principal de investigación está listo.

Añadiendo la CLI

Ahora necesitamos un punto de entrada por línea de comandos. Crea main.py:
La CLI expone los parámetros que realmente vas a ajustar durante la investigación: Ahora conecta todo:
Esto nos da una CLI de investigación local funcional.

Ejecutando el agente

Ejecuta un pase de investigación rápido:
Escribe el informe en un archivo Markdown:
Usa más fuentes y varios proveedores:
Elige el estilo del informe final:
Usa brief para un informe conciso respaldado por fuentes, standard para un panorama más completo y deep para el flujo por etapas de esquema/sección/editor. Guarda artefactos auditables:
Cuando los artefactos están habilitados, verás archivos como:
Estos archivos resultan útiles cuando quieres entender cómo el agente llegó a una conclusión. Por ejemplo, source_notes.jsonl muestra la evidencia resumida de la fuente, research_gaps.jsonl muestra por qué se generaron las búsquedas de seguimiento, y errors.jsonl muestra las páginas que fallaron durante la búsqueda, el scraping o el resumen.

Notas sobre privacidad y fiabilidad

Un agente de investigación toca varios sistemas, por lo que conviene ser preciso sobre qué información va a dónde: Límites de datos del agente de investigación privado Si deseas mantener más del recorrido de búsqueda dentro de Venice, puedes adaptar la capa de proveedores para que llame al endpoint POST /augment/search de Venice en lugar de consultar directamente a DuckDuckGo. La implementación de referencia utiliza proveedores públicos ligeros para que la demo siga siendo fácil de ejecutar y comprender. Por motivos de fiabilidad, mantén estos valores predeterminados conservadores:
  • Usa reintentos para las llamadas a Venice y las solicitudes web.
  • Añade un pequeño --request-delay si vas a leer muchas páginas del mismo host.
  • Limita --max-sources para que los temas amplios no se ejecuten indefinidamente.
  • Guarda --artifacts para los informes importantes y poder auditar la salida final.
  • Considera el informe como una nota informativa, no como verdad absoluta. Sigue las citas hasta la fuente original cuando la precisión importe.

Probando las piezas

No necesitas solicitudes web en vivo ni llamadas a Venice para probar la mayor parte del sistema. El repositorio de referencia usa clases falsas de Venice y de web para probar el bucle de investigación, el comportamiento de deduplicación, los artefactos y los prompts del informe. Una primera prueba útil es la canonicalización de URL:
Luego prueba que el contenido duplicado se omita:
Los fakes hacen que las pruebas del agente sean mucho más rápidas y menos inestables. Puedes verificar la lógica de orquestación sin depender de resultados de búsqueda en vivo, condiciones de red o salida del modelo.

Benchmarking

Muchos proveedores de IA ahora tienen sus propios flujos de deep research, así que el repositorio de referencia incluye un sencillo benchmark frente a la herramienta Deep Research de Perplexity. Se pidió a ambos agentes que escribieran un informe sobre la arquitectura de frameworks de agentes de IA, y los informes generados se han subido al repositorio de GitHub. No pretende ser un benchmark formal. Es una forma práctica de inspeccionar la estructura del informe, la cobertura de fuentes, la calidad de las citas y si el agente se centra demasiado en un cluster de fuentes. Esa es también la razón por la que la implementación actualizada hace seguimiento de research_gaps.jsonl y del equilibrio entre fuentes antes de las búsquedas de seguimiento.

Extendiendo este ejemplo

Una vez que el agente base funciona, aquí tienes formas prácticas de mejorarlo:
  • Añadir un proveedor de búsqueda de Venice utilizando POST /augment/search.
  • Almacenar los informes y artefactos en una pequeña base de datos SQLite en lugar de archivos JSONL.
  • Añadir allowlists o blocklists de fuentes para dominios de investigación de confianza.
  • Añadir soporte de PDF combinando Venice scrape con el parseo de documentos para fuentes que no expongan HTML limpio.
  • Añadir un conjunto de evaluación de temas y tipos de fuente esperados para poder comparar la calidad de la investigación tras cambios en los prompts.
  • Añadir un paso de revisión que pida a Venice encontrar afirmaciones sin respaldo en el informe final antes de guardarlo.
La mayor mejora suele ser una mejor selección de fuentes. La generación de consultas ayuda, pero también puedes mejorar la calidad prefiriendo fuentes primarias, documentos de estándares, documentación oficial, papers, changelogs y páginas de datasets frente a resúmenes de baja señal.

Para terminar

¡Gracias por leer! Esperamos que esto te haya ayudado a construir un agente de investigación privado y práctico con Python y la API de Venice. El patrón útil aquí no es solo “pedir a un modelo que investigue algo”. Es desglosar la investigación en pasos auditables: planificar búsquedas, recopilar fuentes, extraer evidencias, escribir notas de fuente, hacer seguimiento de las lagunas y sintetizar con citas. Al mantener esos pasos explícitos, obtenemos un flujo de investigación que es más fácil de inspeccionar, probar y mejorar con el tiempo.