PTENES
Saltar al contenido
MÓDULO 4.4

🔭 Paso a paso #2: vigilante web en busca de oportunidades

Un modelo local es ilimitado, así que puede vigilar la web todo el día sin que corra el medidor. Aquí montas un worker que lee fuentes permitidas, hace que el LLM puntúe cada elemento como posible oportunidad de negocio y te entrega una lista clasificada, con la fuente de cada una — todo dentro de los ToS, nada de «scraping a cualquier costo».

6
Temas
~40
Minutos
Avanzado
Nivel
Práctico
Tipo

Contenido detallado

1

🎯 El objetivo

El objetivo es un vigilante que nunca duerme: un worker que lee foros, comunidades y feeds, identifica quejas y deseos recurrentes — los "problemas" — y te entrega una lista de lo que podría convertirse en producto, clasificada por potencial y con enlace a la fuente. En vez de pasar horas recorriendo Reddit, el modelo local hace el trabajo pesado de lectura y solo te avisa cuando encuentra algo de verdad.

🆕 ¿Eres nuevo por aquí?

Una "dolor" es un problema del que la gente se queja repetidamente («pierdo tiempo haciendo X a mano», «ninguna herramienta resuelve Y»). Cuando el mismo problema aparece muchas veces y nadie lo resuelve bien, eso es un señal de oportunidad — la materia prima de un producto. El vigilante existe para captar estas señales a escala.

🔑 La idea central

El vigilante tiene cuatro responsabilidades, en este orden:

  • •Recolectar de fuentes permitidas (RSS, JSON oficial, API), sin infringir las reglas de nadie.
  • •Puntuar con cada elemento, pregúntale al LLM: ¿es un problema real? ¿Qué producto lo resolvería? Califícalo del 0 al 10.
  • •Clasificar y deduplicar, guardando la fuente de cada señal para que puedas verificarla.
  • •Ejecutarse solo cada N minutos y que solo te avise de lo que supere el límite.
Recolección
fuente permitida
Score
LLM 0–10
Ranking
con fuente
24/7
sin medidor
2

⚖️ Ética y reglas (lee antes de recopilar)

Antes de escribir cualquier línea de código: vigilar la web no es extraerlo todo a cualquier costo. Cada sitio tiene términos de uso (ToS) y un archivo robots.txt que indican a qué puedes acceder. Respetarlas no es solo «portarse bien»: es lo que mantiene tu worker sostenible, sin bloqueos y dentro de la ley.

🚨 Recuadro de alerta: las reglas innegociables

  • ✗Usa la API/feed oficial cuando exista. Reddit, YouTube, Stack Overflow, Hacker News y muchos foros exponen JSON/RSS oficial; prefiere siempre eso a extraer datos del HTML.
  • ✗Respeta los ToS y robots.txt. Si los términos prohíben la recopilación automatizada, no recopiles. Plataformas como X/Twitter imponen fuertes restricciones: no las fuerces.
  • ✗Aplica rate limit. Espacia las solicitudes (p. ej.: 1 cada 2–5 s), nunca satures el servidor. Identifícate con un User-Agent real y contacto.
  • ✗Nada de datos personales ni inicios de sesión de terceros. Recolecta solo contenido público y agrega tendencias; no persigas a individuos.

✓ Recopilación responsable

  • ✓RSS/JSON oficial o API con clave
  • ✓User-Agent identificable + límite de solicitudes
  • ✓Caché local para no volver a traer lo mismo
  • ✓Solo contenido público, conservando la fuente

✗ Recopilación problemática

  • ✗Eludir el inicio de sesión, el paywall o el captcha
  • ✗Ignorar robots.txt / ToS
  • ✗Enviar miles de solicitudes/min
  • ✗Recolectar datos personales de individuos

💡 Consejo práctico

Empieza por lo que es ofrecido explícitamente: Reddit sirve JSON en cualquier página (solo tienes que agregar .json) y RSS (.rss); Hacker News tiene la Firebase API; muchos blogs y foros tienen /feed. Con fuentes oficiales nunca tendrás dudas sobre si estás haciendo algo indebido.

3

📥 Recolección de la fuente permitida

Primer paso concreto: obtener elementos de una fuente oficial y normalizar todo a un formato único; así, el resto del pipeline no necesita saber de dónde viene. Usaremos como ejemplo el JSON oficial de Reddit, guardando una línea por elemento (formato JSONL).

⌨️ Copy-run: recopilar y normalizar

Objetivo: descargar las publicaciones nuevas de un subreddit desde la fuente oficial y guardar un elemento limpio por línea. Necesitas curl e jq.

# Identifícate y respeta el límite de solicitudes (fuente oficial: .json de Reddit)
UA="vigia-local/1.0 (contato: voce@exemplo.com)"
# Obtiene las 25 publicaciones más recientes y las normaliza a 1 JSON por línea
curl -s -A "$UA" \
  "https://www.reddit.com/r/SaaS/new.json?limit=25" \
  | jq -c '.data.children[].data
      | {fonte:"reddit/r/SaaS", id, titulo:.title,
         texto:.selftext, url:("https://reddit.com"+.permalink)}' \
  > coletado.jsonl
# Sé cortés: espera antes de consultar la siguiente fuente
sleep 3

Cómo verificar: ejecuta wc -l coletado.jsonl (debe mostrar ~25) y head -1 coletado.jsonl | jq — cada línea tiene título, texto e url de origen.

💡 Consejo práctico

Normalizar es el secreto para poder sumar fuentes sin reescribir nada. Mañana puedes cambiar la recopilación por un feed RSS (/feed) o mediante la API de Hacker News, manteniendo las las mismas claves (fuente, id, título, texto, url). El LLM y el ranking ni notan la diferencia.

4

🧠 Análisis con el modelo local

Ahora, lo esencial: para cada elemento recopilado, pídele al modelo local que clasifica y devuelve JSON estructurado. La clave es forzar la salida en JSON (Ollama acepta format:"json") y usar una temperatura baja para obtener una respuesta consistente. Esto transforma texto suelto en datos que el ranking entiende.

⌨️ Copy-run: puntuar un elemento mediante API local

Objetivo: enviar un elemento a la API local (compatible con OpenAI, de la Trilha 3) y recibir {dor_real, dor, produto, score}.

# $TXT = título + texto de UN elemento recopilado
TXT="Me cansé de versionar prompts en Notion, pierdo los buenos"
# Pide una clasificación ESTRUCTURADA (JSON) al modelo local
curl -s http://localhost:11434/v1/chat/completions \
  -d "$(jq -n --arg t "$TXT" '{
    model:"qwen2.5:7b-instruct", temperature:0.2, format:"json",
    messages:[
      {role:"system",
       content:"Eres analista de oportunidades. Responde SOLO JSON."},
      {role:"user", content:("Texto: "+$t+
       "\nFormato: {\"dolor_real\":bool,\"dolor\":\"\",
       \"produto\":\"\",\"score\":0-10}")}
    ]}')" \
  | jq -r '.choices[0].message.content'
# → {"dor_real":true,"dor":"versionar prompts","produto":"Git p/ prompts","score":8}

Cómo verificar: la salida debe ser un JSON válido con score entre 0 y 10. Si devuelve texto suelto, refuerza el prompt con "responde SOLO JSON" y confirma que pasó format:"json".

dolor_real
true/false
dolor
resumen del problema
producto
qué resolvería
score
0–10 potencial

💡 Consejo práctico

Pide también un campo justificación corta: además de mejorar la puntuación (el modelo «piensa» antes), te da una línea para leer en el informe sin volver a abrir la fuente. Y da ejemplos en el system prompt de lo que es una nota 2 frente a una nota 9 — calibra el score mucho más que ajustar la temperatura.

5

📊 Clasificando y guardando

Con cada elemento puntuado, el worker se convierte en un curador: ordena por score, descarta lo repetido, elimina lo que quedó por debajo del umbral y guarda solo lo que vale, siempre con el enlace a la fuente. La disciplina aquí es lo que separa un informe útil de una avalancha de ruido.

1

Ordenar por score

Mayor potencial en la cima. sort_by(-.score) resuélvelo en una línea de jq.

2

Deduplicar

El mismo problema aparece en varias publicaciones. unique_by(.id) y, idealmente, agrupa elementos con problemas similares.

3

Descartar por debajo del umbral

Mantén solo dolor_real == true e score >= 7. El recorte es tu botón de señal/ruido.

4

Guardar con cita

Cada oportunidad lleva la url de origen: sin fuente, es una suposición, no una señal.

📄 El archivo de salida: oportunidades.md

Objetivo: un markdown legible y clasificado que abres por la mañana y revisas de un vistazo. Cada elemento incluye el problema, el producto y la fuente.

# Oportunidades — 2026-06-29
 
## (score 9) Versionar prompts es un caos
- **Problema:** los equipos pierden buenos prompts en chats y documentos dispersos.
- **Producto:** «Git para prompts» con diff y rollback.
- **Fuente:** https://reddit.com/r/SaaS/comments/abc123
 
## (score 8) La incorporación de clientes es demasiado manual
- **Problema:** los founders repiten manualmente los mismos pasos.
- **Producto:** checklist de onboarding automatizado.
- **Fuente:** https://reddit.com/r/Entrepreneur/comments/def456

Cómo verificar: abre el oportunidades.md y haz clic en una fuente: debe llevar exactamente a la publicación que generó la señal. Sin eso, no puedes validar ni citar.

✓ Notificar

  • ✓Superó el umbral y es nuevo (no estaba en el informe de ayer)
  • ✓Dolor recurrente en más de una fuente
  • ✓Hay una fuente enlazada para que puedas validar

✗ Silenciar (ruido)

  • ✗Score por debajo del umbral o dolor_real:false
  • ✗Duplicado de algo ya reportado
  • ✗Desahogo sin un producto posible detrás
6

🔁 Ejecución continua

Junta las tres etapas —recopilación, puntuación, ranking— en un solo script y prográmalo con cron (Ruta 4.2). El pipeline completo fluye de izquierda a derecha: las fuentes alimentan la recopilación, el LLM local puntúa, el ranking filtra y la salida es tu informe del día.

Fuentes RSS · API oficial Recolección normaliza LLM local score 0–10 Ranking deduplicación + recorte oportunidades .md clasificado ⟳ cron: se repite cada N minutos, sin costo por llamada

Lee de izquierda a derecha: el flujo (en ámbar) sale de las fuentes permitidas, pasa por tres etapas de procesamiento (en cian) y termina en el informe del día. O cron abajo cierra el ciclo — al ser local, repetirlo todo el día solo cuesta electricidad.

⌨️ Copy-run principal: vigia.sh

Objetivo: todo el pipeline en un script: recopila, puntúa con el modelo local, clasifica y guarda el markdown fechado.

#!/usr/bin/env bash
set -euo pipefail
UA="vigia-local/1.0 (contato: voce@exemplo.com)"
HOJE=$(date +%F); SAIDA="oportunidades-$HOJE.md"
# 1) RECOPILACIÓN (fuente permitida)
curl -s -A "$UA" "https://www.reddit.com/r/SaaS/new.json?limit=25" \
  | jq -c '.data.children[].data | {id, titulo:.title,
    texto:.selftext, url:("https://reddit.com"+.permalink)}' > coletado.jsonl
# 2) ASIGNA PUNTAJE a cada elemento con el modelo local (salida JSON)
> pontuado.jsonl
while IFS= read -r linha; do
  txt=$(jq -r '.titulo + " — " + (.texto // "")' <<< "$linha")
  resp=$(curl -s http://localhost:11434/v1/chat/completions \
    -d "$(jq -n --arg t "$txt" '{model:"qwen2.5:7b-instruct",
      temperature:0.2, format:"json", messages:[{role:"user",
      content:("SOLO JSON {\"dor_real\":bool,\"dor\":\"\",
      \"produto\":\"\",\"score\":0-10}: "+$t)}]}')" \
    | jq -r '.choices[0].message.content')
  jq -c --argjson c "$resp" '. + $c' <<< "$linha" >> pontuado.jsonl
  sleep 1
done < coletado.jsonl
# 3) CLASIFICA, elimina duplicados y descarta puntajes < 7
jq -s 'unique_by(.id) | map(select(.dor_real and .score >= 7))
  | sort_by(-.score)' pontuado.jsonl > ranqueado.json
# 4) GUARDA markdown con cita de la fuente
{ echo "# Oportunidades — $HOJE"; echo;
  jq -r '.[] | "## (score \(.score)) \(.dor)\n- **Producto:** \(.produto)\n- **Fuente:** \(.url)\n"' ranqueado.json;
} > "$SAIDA"
echo "Gravado: $SAIDA ($(jq length ranqueado.json) itens)"
# 5) PROGRAMA: cada 30 min (crontab -e)
# */30 * * * * cd ~/vigia && /bin/bash vigia.sh >> vigia.log 2>&1

Cómo verificar: ejecuta bash vigia.sh una vez a mano — debe imprimir "Grabado:" y crear el oportunidades-AAAA-MM-DD.md. Solo después agrega la línea de cron. Sigue el vigia.log en las primeras rondas.

Autoevaluación (opcional): ¿por qué empezar la recopilación con una fuente oficial (RSS/JSON/API), en vez de extraer el HTML a toda costa?

Recolección
curl + jq
Puntúa
API local JSON
Clasifica
deduplicación + recorte
Agenda
cron cada N min

📌 Resumen del módulo

✓
El vigilante recopila, puntúa, clasifica y funciona por sí solo — el texto sin estructura se convierte en una lista de oportunidades con fuentes.
✓
La ética primero — API/RSS oficiales, ToS, robots.txt y límite de solicitudes. Nada de hacer scraping a cualquier costo.
✓
El JSON estructurado es el pegamento — pedir una puntuación de 0–10 en JSON hace que el ranking sea trivial.
✓
Local desbloquea el 24/7 — repetir cada N min solo consume energía; cron cierra el ciclo.

Siguiente módulo:

4.5 — Tu buddy local con nombre, cara y voz