¿Qué hace Lead Enrich?
Investiga una persona (nombre + empresa) usando Serper.dev Google Search. Utiliza el modelo incorporado del agente para la extracción. No necesita API LLM externa.
Funciona de forma independiente — solo una clave de Serper. Se combina con Outreach Magic para deduplicación que ahorra créditos (evita Serper cuando los leads ya tienen LinkedIn/email) y almacenamiento persistente entre sesiones.
Ahorro de créditos (con OM): verifica outreachmagic primero. Si el lead ya tiene LinkedIn + email en la misma empresa, no gasta créditos de Serper. LinkedIn sin email evita Serper — use email-finder para trykitt. Los registros solo con email aún obtienen búsquedas de LinkedIn (1–2 créditos de Serper). Las coincidencias de nombre en una empresa diferente devuelven
ambiguouspara no desperdiciar APIs.
Configuración
Independiente (sin OM)
Solo una clave de Serper. Ejecute serper-search directamente. Los resultados se imprimen en JSON.
| Clave | Para |
|---|---|
SERPER_API_KEY | serper.dev — API de Búsqueda de Google |
python3 scripts/enrich.py serper-search --query '"Acme Corp" official website'
python3 scripts/enrich.py serper-search --query 'site:linkedin.com/in Jane Doe Acme Corp'
# → canalice los resultados a su modelo para extracción, o use serper-format
Establezca SERPER_API_KEY en su entorno o mediante ~/.hermes/skills/lead-enrich/config.json.
Con Outreach Magic (deduplicación + guardado)
Agrega deduplicación previa (salta leads ya en OM) y guarda el enriquecimiento estructurado
en su pipeline SQLite local. Requiere la habilidad outreachmagic
con pipeline.py login.
| Clave | Para |
|---|---|
| Todas las claves independientes anteriores + | |
OUTREACHMAGIC_AGENT_KEY | Inicio de sesión OM mediante pipeline.py login |
python3 scripts/enrich.py check "Jane Doe" "Acme Corp" # 0 créditos
python3 scripts/enrich.py batch-check --workspace W input.json # deduplicación por lotes
Guarde su clave de Serper en Portal de Outreach Magic → Configuración → API Keys, luego ejecute
pipeline.py sync-secrets. Verifique con enrich.py config (serper_api_key_source
debe ser agent_secrets).
3. Búsqueda de correo electrónico (habilidad email-finder)
La búsqueda de correo no está en lead-enrich v2+. Instale email-finder con outreachmagic
usando la guía de instalación de la plataforma. O instálelo de forma independiente mediante npx skills add outreachmagic/email-finder.
Después de que el enriquecimiento de Serper guarde company_domain, ejecute:
python3 ~/.hermes/skills/email-finder/scripts/email_finder.py find --name "Jane Doe" \
--domain acme.com --linkedin "https://linkedin.com/in/janedoe" --save
Consulte references/email-finder.md y la documentación de la habilidad email-finder.
Resúmenes de espacio de trabajo (sin créditos de Serper): después de guardar leads, use outreachmagic
workspace summary --workspace <slug> --json para conteos de etiquetas y
conexiones de LinkedIn aceptadas por remitente. En espacios de trabajo grandes (>2,000 leads), agregue
--tags-only para conteos de etiquetas más rápidos. Solo base de datos local: opcional.
Flujos de trabajo comunes
| El usuario dice | Usted hace |
|---|---|
| "Investiga a esta persona" | Búsqueda en Serper → guardar mediante import-profiles |
| "Enriquece mi CSV" | batch-check / importar → enriquecer campos faltantes → guardar en OM |
| "Encuentra también su correo" | Después del enriquecimiento, pasar a email-finder |
CSV de Sales Nav / Vayne: use outreachmagic import-profiles --file … (detecta columnas automáticamente).
Flujo de trabajo CSV / lista de premios (preferido para 10+ personas)
Rutas como outreachmagic/imports/awards.csv son relativas a su directorio de espacio de trabajo (donde el agente ejecuta el comando), no a la carpeta de instalación de la habilidad.
# 0 créditos — deduplicar todo el archivo primero (marca automáticamente serper_attempted en filas completas)
python3 scripts/enrich.py batch-check --workspace your_workspace outreachmagic/imports/awards.csv
# Re-ejecutar deduplicación saltando leads ya etiquetados como serper_attempted
python3 scripts/enrich.py batch-check --workspace your_workspace --skip-tagged outreachmagic/imports/awards.csv
# Serper solo para filas que necesitan LinkedIn/dominio (salta team_award, exists_linkedin_*, skipped_serper_attempted)
# Después de la investigación — parchear título/industria solamente (0 créditos de Serper)
python3 scripts/enrich.py backfill --fields title,industry --workspace your_workspace outreachmagic/imports/patch.csv
batch-check acepta .json o .csv. backfill requiere email o linkedin por fila; usa import-profiles fragmentado mediante companion_common (200 filas/fragmento, hasta 300s/fragmento; completa campos vacíos; agregue --overwrite para reemplazar).
Cuándo usar
- El usuario dice "investiga a esta persona" / "busca a Jane Doe en Acme"
- El usuario quiere enriquecer una lista de prospectos antes del contacto
- El usuario pregunta "¿ya tenemos este lead?" antes de investigar
- El usuario proporciona un CSV/JSON de nombres y empresas para enriquecer en lote
- El usuario menciona Serper, enriquecimiento de leads o investigación de personas
Reglas de comportamiento del agente (Importante)
- Deduplicar primero (con OM). Antes de cualquier llamada a la API de Serper, ejecute
enrich.py check. Si el lead tiene LinkedIn + email en la misma empresa, salte Serper por completo. Si LinkedIn existe pero sin email, salte Serper y ofrezca email-finder cuando el usuario quiera una dirección. Nunca gaste créditos de Serper en leads ya completos en outreachmagic. Independiente: salte la verificación — ejecuteserper-searchdirectamente. - Solo Serper. Prefiera
enrich.py serper-search --query "..."(HTTP estándar, clave desde configuración/entorno). O usecurlcon$SERPER_API_KEY— nunca incruste la clave en registros de chat. Nunca raspe Google o LinkedIn directamente. - Solo modelo incorporado. Usted (el agente) extrae JSON de los resultados de Serper. Sin APIs LLM externas (sin Gemini, sin OpenAI) — su propio razonamiento es el motor de extracción.
- Investigar completamente antes de guardar. Ejecute primero la escalera de búsqueda completa, luego guarde una vez.
- Guardar mediante outreachmagic (con OM). Use
import-profilespara leads con LinkedIn. Siempre agregueserper_attempteda las etiquetas al guardar (incluido automáticamente en la salidamap-to-om). Para leads sin LinkedIn pero con unlead_idconocido, marque la etiqueta mediantestamp-attemptedoimport-profilesconid+ etiquetas — no confíe solo en notas. Para verificaciones de deduplicación de solo lectura usepipeline.py queryoenrich.py check— nuncaINSERT/UPDATEsin procesar. Nunca ejecute ambas rutas de guardado para la misma persona. Independiente: guarde la salida JSON en un archivo — no se necesita OM. - Etiquetar después del enriquecimiento (con OM). Cada lead que pase por Serper debe obtener
serper_attemptedal guardar. Evita el reprocesamiento en ejecuciones futuras. - Verificar etiqueta antes de Serper (con OM). Antes de gastar créditos de Serper, verifique
serper_attempted(medianteenrich.py check --skip-taggedoskip_reasonen la salida de check). Si está presente y LinkedIn aún está vacío, salte a menos que el usuario quiera explícitamente un reintento (p. ej., desactualizado >30 días). - Transparencia. Muestre qué consultas de Serper se ejecutaron, la confianza y qué se guardó. El usuario debe ver exactamente dónde se gastaron sus créditos.
- Procesar por lotes con criterio. Limite a 50 personas por ejecución. Para CSV/listas de premios, ejecute
batch-checkuna vez en todo el archivo (JSON o CSV) antes de cualquier Serper. Procese Serper solo para estados que necesiten LinkedIn/dominio. Salte las filasteam_award,exists_linkedin_emailyskipped_serper_attempted. - Búsqueda de correo — use email-finder. Después de que el enriquecimiento guarde
company_domain, pase a email-finder (email_finder.py find --save). Consultereferences/email-finder.md. Nunca invente ni adivine patrones de correo electrónico en esta habilidad.
Inicio rápido
# Persona individual (más común)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# → si "not_found", proceda con el paquete de búsqueda de Serper a continuación
# Con espacio de trabajo (asocia el lead con su pipeline de espacio de trabajo)
python3 scripts/enrich.py check --workspace your_workspace "Jane Doe" "Acme Corp"
# Lote desde JSON o CSV (ejecute esto antes de Serper en listas)
python3 scripts/enrich.py batch-check --workspace your_workspace outreachmagic/imports/people.json
python3 scripts/enrich.py batch-check --workspace your_workspace --skip-tagged outreachmagic/imports/awards.csv
# Marcar serper_attempted después de una búsqueda fallida de LinkedIn (cuando se conoce lead_id)
python3 scripts/enrich.py stamp-attempted --workspace your_workspace --lead-ids 42,43 \
--notes "No se encontró LinkedIn mediante Serper"
# Rellenar título/industria en leads existentes (se requiere linkedin o email)
python3 scripts/enrich.py backfill --fields title,industry outreachmagic/imports/patch.csv
# Actualizar la habilidad de forma segura desde el lanzamiento de GitHub (verificado por suma de verificación)
python3 scripts/enrich.py update --check
python3 scripts/enrich.py update
update verifica sumas de verificación SHA256 de update-manifest.json antes de reemplazar
archivos. Si las sumas de verificación faltan o no coinciden, la actualización se cancela.
Flujo de trabajo principal
Fase 1 — Verificación de deduplicación (0 créditos)
Para cada persona, ejecute:
# Sin espacio de trabajo (búsqueda en toda la organización)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# Con espacio de trabajo (limitado a su pipeline de espacio de trabajo)
python3 scripts/enrich.py check --workspace your_workspace "Jane Doe" "Acme Corp"
Salida por persona:
| Estado | Significado | Acción |
|---|---|---|
exists_linkedin_email | Misma empresa, LinkedIn + email | Saltar Serper y APIs de correo |
exists_linkedin_no_email | Misma empresa, LinkedIn, sin email | Saltar Serper → email-finder si el usuario quiere correo |
exists_no_linkedin_email | Misma empresa, email, sin LinkedIn | Solo consultas de LinkedIn en Serper |
exists_no_linkedin | Misma empresa, ninguno | Solo consultas de LinkedIn en Serper |
skipped_serper_attempted | Tiene etiqueta serper_attempted, sin LinkedIn | Saltar Serper — ya se intentó |
ambiguous | Coincidencia de nombre, desajuste de empresa | Ejecutar paquete completo de Serper — no saltar |
not_found | Sin coincidencia | Ejecutar paquete de búsqueda completo de Serper |
team_award | Fila de equipo/grupo (sin individuo) | Saltar Serper — etiquetar team_award, agregar nota de contacto |
dedup_disabled | dedup_before_search: false en configuración | Ejecutar Serper según lo solicitado |
La salida de check incluye tags y skip_reason opcional (has_linkedin o
skipped_serper_attempted). Usa batch-lead-lookup (local, cero créditos de Serper).
Fase 2 — Paquete de búsqueda de Serper
Ejecutar solo para personas que lo necesiten. 2–4 búsquedas por persona dependiendo de la calidad del resultado:
Estimador de presupuesto de créditos de Serper
- Mínimo por persona:
0créditos de Serper (exists_linkedin_email). - Ruta común:
2créditos (2aempresa estricta +2cperfil de LinkedIn). - Máximo duro por persona:
4créditos cuando se necesitan todos los respaldos (2a+2b+2c+2e). - Fórmula de lote:
min=0,max=4*NdondeNson las personas en la ejecución. - Ejemplo de límite de lote: con
N=50, el máximo duro es200créditos (peor caso).
2a. Descubrimiento de empresa — estricto (siempre)
Preferido (sin clave en el historial del shell):
python3 scripts/enrich.py serper-search --query '"Acme Corp" official website' --label company_discovery_strict
O reintentar con una consulta más simple:
python3 scripts/enrich.py serper-search --query 'Acme Corp website' --label company_discovery_broad
2b. Descubrimiento de empresa — amplio (condicional)
Ejecutar solo si 2a no devuelve ningún resultado orgánico con un enlace http:// o https://:
python3 scripts/enrich.py serper-search --query 'Acme Corp official website' --label company_discovery_broad
(Misma plantilla, nombre de empresa sin comillas.)
2c. Perfil de LinkedIn (siempre)
Construir consulta (empresa sin comillas — coincide con nombres de empleador variantes en fragmentos):
site:linkedin.com/in {Primer Apellido} {hasta 5 palabras del rol} {Nombre de la Empresa}
Ejemplo:
python3 scripts/enrich.py serper-search \
--query 'site:linkedin.com/in Jane Doe VP Marketing Acme Corp' \
--label linkedin_profile
Fase 3 — Extracción del modelo
Pase los resultados formateados de Serper a usted mismo (el modelo del agente) con esta instrucción del sistema:
Eres un asistente de investigación. El mensaje del usuario contiene resultados de búsqueda de Google
Serper.dev en secciones etiquetadas. NO tienes búsqueda web en vivo — usa solo los bloques
pegados.
Tarea: para UNA persona específica en UNA empresa, extrae:
- company_domain: nombre de host registrable solamente (sin ruta, sin www), o cadena vacía
- company_website: URL completa de https:// página de inicio si es compatible, o cadena vacía
- linkedin_url: https://linkedin.com/in/… para la persona nombrada en esta empresa, o cadena vacía
- confidence: high | medium | low
- note: explicación opcional de ambigüedad o lagunas
Reglas:
- Cada URL no vacía debe aparecer en los bloques de Serper (normalización menor de cadena de consulta está bien)
- El nombre en LinkedIn puede diferir de la entrada (apodo vs nombre legal) — coincidir en la misma persona + empleador
- Antes de aceptar `linkedin_url`, verifique la calidad de coincidencia:
- Extraiga tokens de primer nombre + apellido de `Full name: {full_name}` (ignore la puntuación)
- El resultado `/in/` elegido debe tener *ambos* tokens presentes en el `title` o `snippet` del resultado
- Si ningún resultado `/in/` cumple con el requisito de token, devuelva `linkedin_url` como cadena vacía y establezca `confidence` en `low`
- El sitio público de la empresa puede usar un nombre de banner diferente al de la entrada — prefiera evidencia oficial
- Al resolver `company_domain`:
- Prefiera `knowledgeGraph.website` cuando esté presente
- Rechace agregadores/registros comunes (ejemplos: `naceweb.org`, `usnews.com`, `wikipedia.org`, `niche.com`, `facebook.com`, `instagram.com`, `twitter.com`)
- Si `company_name` parece faltante o coincide estrechamente con el nombre de la persona, devuelva cadena vacía
- Nunca invente URLs o slugs
Responda SOLO con un único objeto JSON (sin fences de markdown):
{"company_domain":"","company_website":"","linkedin_url":"","confidence":"medium","note":""}
Use esta plantilla de mensaje de usuario:
### Persona objetivo
Nombre completo: {full_name}
Rol/título declarado: {stated_role}
Empresa (según lo proporcionado): {company_name}
### Resultados de búsqueda
{formatted_serper_sections}
### Tarea
Devuelva el objeto JSON descrito en la instrucción del sistema.
Luego analice su propia respuesta: elimine los fences de markdown, extraiga el objeto JSON.
Respaldo de cosecha de LinkedIn: si linkedin_url está vacío después de la extracción, escanee
los resultados orgánicos sin procesar de Serper en busca de URLs /in/ donde el título contenga
tanto el primer nombre como el token del apellido. Prefiera coincidencias
...
[truncado para traducción]