Lead Enrich
Recherchez une personne (nom + entreprise) en utilisant la recherche Google Serper.dev. Utilise le modèle intégré de l'agent pour l'extraction. Aucune API LLM externe nécessaire.
Fonctionne de manière autonome — juste une clé Serper. S'associe à Outreach Magic pour une déduplication économique (évite Serper lorsque les leads ont déjà LinkedIn/email) et un stockage persistant entre les sessions.
Économie de crédits (avec OM) : vérifie d'abord dans outreachmagic. Si le lead a déjà LinkedIn + email dans la même entreprise, zéro crédit Serper dépensé. LinkedIn sans email évite Serper — utilisez email-finder pour trykitt. Les enregistrements avec uniquement un email déclenchent toujours des recherches LinkedIn (1 à 2 crédits Serper). Les correspondances de nom dans une entreprise différente renvoient
ambiguouspour ne pas gaspiller les API.
Configuration
Autonome (sans OM)
Juste une clé Serper. Exécutez serper-search directement. Les résultats s'affichent en JSON.
| Clé | Pour |
|---|---|
SERPER_API_KEY | serper.dev — API de recherche Google |
python3 scripts/enrich.py serper-search --query '"Acme Corp" site web officiel'
python3 scripts/enrich.py serper-search --query 'site:linkedin.com/in Jane Doe Acme Corp'
# → redirigez les résultats vers votre modèle pour extraction, ou utilisez serper-format
Définissez SERPER_API_KEY dans votre environnement ou via ~/.hermes/skills/lead-enrich/config.json.
Avec Outreach Magic (déduplication + sauvegarde)
Ajoute une déduplication préalable (ignorer les leads déjà présents dans OM) et sauvegarde l'enrichissement structuré dans votre pipeline SQLite local. Nécessite la compétence outreachmagic avec pipeline.py login.
| Clé | Pour |
|---|---|
| Toutes les clés autonomes ci-dessus + | |
OUTREACHMAGIC_AGENT_KEY | Connexion OM via pipeline.py login |
python3 scripts/enrich.py check "Jane Doe" "Acme Corp" # 0 crédits
python3 scripts/enrich.py batch-check --workspace W input.json # déduplication par lots
Enregistrez votre clé Serper dans le portail Outreach Magic → Paramètres → Clés API, puis exécutez pipeline.py sync-secrets. Vérifiez avec enrich.py config (serper_api_key_source devrait être agent_secrets).
3. Recherche d'email (compétence email-finder)
La recherche d'email ne fait pas partie de lead-enrich v2+. Installez email-finder avec outreachmagic en suivant le guide d'installation de la plateforme. Ou installez-le de manière autonome via npx skills add outreachmagic/email-finder.
Après que l'enrichissement Serper a sauvegardé company_domain, exécutez :
python3 ~/.hermes/skills/email-finder/scripts/email_finder.py find --name "Jane Doe" \
--domain acme.com --linkedin "https://linkedin.com/in/janedoe" --save
Consultez references/email-finder.md et la documentation de la compétence email-finder.
Résumés d'espace de travail (zéro crédit Serper) : après avoir sauvegardé les leads, utilisez outreachmagic avec workspace summary --workspace <slug> --json pour obtenir le comptage des tags et les acceptations de connexion LinkedIn par expéditeur. Pour les grands espaces de travail (>2 000 leads), ajoutez --tags-only pour un comptage plus rapide. Base de données locale uniquement — l'extraction est facultative.
Workflows courants
| L'utilisateur dit | Vous faites |
|---|---|
| "Recherchez cette personne" | Recherche Serper → sauvegarde via import-profiles |
| "Enrichissez mon CSV" | batch-check / importation → enrichir les champs manquants → sauvegarder dans OM |
| "Trouvez aussi son email" | Après enrichissement, transmettez à email-finder |
CSV Sales Nav / Vayne : utilisez outreachmagic import-profiles --file … (détection automatique des colonnes).
Workflow CSV / liste de récompenses (recommandé pour 10 personnes et plus)
Les chemins comme outreachmagic/imports/awards.csv sont relatifs à votre répertoire d'espace de travail (où l'agent exécute la commande), pas au dossier d'installation de la compétence.
# 0 crédits — dédupliquez d'abord l'intégralité du fichier (marque automatiquement serper_attempted sur les lignes complètes)
python3 scripts/enrich.py batch-check --workspace your_workspace outreachmagic/imports/awards.csv
# Réexécutez la déduplication en ignorant les leads déjà marqués serper_attempted
python3 scripts/enrich.py batch-check --workspace your_workspace --skip-tagged outreachmagic/imports/awards.csv
# Serper uniquement pour les lignes qui ont besoin de LinkedIn/domaine (ignore team_award, exists_linkedin_*, skipped_serper_attempted)
# Après la recherche — corrigez le titre/le secteur (0 crédits Serper)
python3 scripts/enrich.py backfill --fields title,industry --workspace your_workspace outreachmagic/imports/patch.csv
batch-check accepte .json ou .csv. backfill nécessite email ou linkedin par ligne ; utilise des import-profiles par lots via companion_common (200 lignes/lot, jusqu'à 300 s/lot ; remplit les champs vides ; ajoutez --overwrite pour remplacer).
Quand l'utiliser
- L'utilisateur dit "recherchez cette personne" / "trouvez Jane Doe chez Acme"
- L'utilisateur souhaite enrichir une liste de prospects avant prospection
- L'utilisateur demande "avons-nous déjà ce lead ?" avant de faire des recherches
- L'utilisateur fournit un CSV/JSON de noms et d'entreprises à enrichir en masse
- L'utilisateur mentionne Serper, l'enrichissement de leads ou la recherche de personnes
Règles de comportement de l'agent (Importantes)
- Déduplication d'abord (avec OM). Avant tout appel API Serper, exécutez
enrich.py check. Si le lead a LinkedIn + email dans la même entreprise, ignorez complètement Serper. Si LinkedIn existe mais pas d'email, ignorez Serper et proposez email-finder lorsque l'utilisateur souhaite une adresse. Ne dépensez jamais de crédits Serper pour des leads déjà complets dans outreachmagic. Autonome : ignorez la vérification — exécutezserper-searchdirectement. - Serper uniquement. Préférez
enrich.py serper-search --query "..."(HTTP de la bibliothèque standard, clé depuis la configuration/l'environnement). Ou utilisezcurlavec$SERPER_API_KEY— n'incorporez jamais la clé dans les journaux de discussion. Ne grattez jamais Google ou LinkedIn directement. - Modèle intégré uniquement. Vous (l'agent) extrayez le JSON des résultats Serper. Pas d'API LLM externes (pas de Gemini, pas d'OpenAI) — votre propre raisonnement est le moteur d'extraction.
- Recherche complète avant sauvegarde. Effectuez d'abord l'ensemble de la chaîne de recherche, puis sauvegardez une seule fois.
- Sauvegarde via outreachmagic (avec OM). Utilisez
import-profilespour les leads avec LinkedIn. Ajoutez toujoursserper_attemptedaux tags lors de la sauvegarde (inclus automatiquement dans la sortie demap-to-om). Pour les leads sans LinkedIn mais avec unlead_idconnu, marquez le tag viastamp-attemptedouimport-profilesavecid+ tags — ne vous fiez pas uniquement aux notes. Pour les vérifications de déduplication en lecture seule, utilisezpipeline.py queryouenrich.py check— jamais deINSERT/UPDATEbruts. N'exécutez jamais les deux chemins de sauvegarde pour la même personne. Autonome : sauvegardez la sortie JSON dans un fichier — pas besoin d'OM. - Marquage après enrichissement (avec OM). Chaque lead qui passe par Serper doit recevoir
serper_attemptedlors de la sauvegarde. Empêche le retraitement lors des exécutions futures. - Vérification du tag avant Serper (avec OM). Avant de dépenser des crédits Serper, vérifiez la présence de
serper_attempted(viaenrich.py check --skip-taggedouskip_reasondans la sortie de la vérification). S'il est présent et que LinkedIn est toujours vide, ignorez-le, sauf si l'utilisateur souhaite explicitement une nouvelle tentative (par exemple, données obsolètes de plus de 30 jours). - Transparence. Montrez quelles requêtes Serper ont été exécutées, le niveau de confiance et ce qui a été sauvegardé. L'utilisateur doit voir exactement où ses crédits ont été dépensés.
- Traitement par lots judicieusement. Limitez à 50 personnes par exécution. Pour les listes CSV/de récompenses, exécutez d'abord
batch-checkune fois sur l'intégralité du fichier (JSON ou CSV) avant tout Serper. Traitez Serper uniquement pour les statuts qui ont besoin de LinkedIn/domaine. Ignorez les lignesteam_award,exists_linkedin_emailetskipped_serper_attempted. - Recherche d'email — utilisez email-finder. Après que l'enrichissement a sauvegardé
company_domain, transmettez à email-finder (email_finder.py find --save). Consultezreferences/email-finder.md. Ne fabriquez ni ne devinez jamais d'emails dans cette compétence.
Démarrage rapide
# Une seule personne (le plus courant)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# → si "not_found", procédez avec le pack de recherche Serper ci-dessous
# Avec espace de travail (associe le lead à votre pipeline d'espace de travail)
python3 scripts/enrich.py check --workspace your_workspace "Jane Doe" "Acme Corp"
# Par lots depuis un JSON ou CSV (exécutez ceci avant Serper sur les listes)
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
# Marquer serper_attempted après un échec de recherche LinkedIn (lorsque lead_id est connu)
python3 scripts/enrich.py stamp-attempted --workspace your_workspace --lead-ids 42,43 \
--notes "Aucun LinkedIn trouvé via Serper"
# Remplir a posteriori le titre/secteur sur les leads existants (linkedin ou email requis)
python3 scripts/enrich.py backfill --fields title,industry outreachmagic/imports/patch.csv
# Mettre à jour la compétence en toute sécurité depuis une version GitHub (vérification par somme de contrôle)
python3 scripts/enrich.py update --check
python3 scripts/enrich.py update
update vérifie les sommes de contrôle SHA256 à partir de update-manifest.json avant de remplacer les fichiers. Si les sommes de contrôle sont manquantes ou ne correspondent pas, la mise à jour est abandonnée.
Workflow de base
Phase 1 — Vérification de déduplication (0 crédits)
Pour chaque personne, exécutez :
# Sans espace de travail (recherche dans toute l'organisation)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# Avec espace de travail (limité à votre pipeline d'espace de travail)
python3 scripts/enrich.py check --workspace your_workspace "Jane Doe" "Acme Corp"
Sortie par personne :
| Statut | Signification | Action |
|---|---|---|
exists_linkedin_email | Même entreprise, LinkedIn + email | Ignorer Serper et les API d'email |
exists_linkedin_no_email | Même entreprise, LinkedIn, pas d'email | Ignorer Serper → email-finder si l'utilisateur souhaite un email |
exists_no_linkedin_email | Même entreprise, email, pas de LinkedIn | Requêtes Serper LinkedIn uniquement |
exists_no_linkedin | Même entreprise, aucun des deux | Requêtes Serper LinkedIn uniquement |
skipped_serper_attempted | A le tag serper_attempted, pas de LinkedIn | Ignorer Serper — déjà essayé |
ambiguous | Correspondance de nom, désaccord d'entreprise | Exécuter le pack Serper complet — ne pas ignorer |
not_found | Aucune correspondance | Exécuter le pack de recherche Serper complet |
team_award | Ligne d'équipe/groupe (pas un individu) | Ignorer Serper — taguer team_award, ajouter une note de contact |
dedup_disabled | dedup_before_search: false dans la configuration | Exécuter Serper comme demandé |
La sortie de la vérification inclut tags et éventuellement skip_reason (has_linkedin ou skipped_serper_attempted). Utilise batch-lead-lookup (local, zéro crédit Serper).
Phase 2 — Pack de recherche Serper
Exécutez uniquement pour les personnes qui en ont besoin. 2 à 4 recherches par personne selon la qualité des résultats :
Estimation du budget de crédits Serper
- Minimum par personne :
0crédit Serper (exists_linkedin_email). - Chemin courant :
2crédits (2aentreprise stricte +2cprofil LinkedIn). - Maximum absolu par personne :
4crédits lorsque tous les replis sont nécessaires (2a+2b+2c+2e). - Formule par lots :
min=0,max=4*NoùNest le nombre de personnes dans l'exécution. - Exemple de limite par lots : avec
N=50, le maximum absolu est de200crédits (pire cas).
2a. Découverte d'entreprise — stricte (toujours)
Préféré (pas de clé dans l'historique du shell) :
python3 scripts/enrich.py serper-search --query '"Acme Corp" site web officiel' --label company_discovery_strict
Ou réessayez avec une requête plus simple :
python3 scripts/enrich.py serper-search --query 'Acme Corp site web' --label company_discovery_broad
2b. Découverte d'entreprise — large (conditionnel)
Exécutez uniquement si 2a ne renvoie aucun résultat organique avec un lien http:// ou https:// :
python3 scripts/enrich.py serper-search --query 'Acme Corp site web officiel' --label company_discovery_broad
(Même modèle, nom d'entreprise non cité.)
2c. Profil LinkedIn (toujours)
Construisez la requête (entreprise non citée — correspond aux noms d'employeurs variants dans les extraits) :
site:linkedin.com/in {Prénom Nom} {jusqu'à 5 mots du rôle} {Nom de l'entreprise}
Exemple :
python3 scripts/enrich.py serper-search \
--query 'site:linkedin.com/in Jane Doe VP Marketing Acme Corp' \
--label linkedin_profile
Phase 3 — Extraction par le modèle
Transmettez les résultats Serper formatés à vous-même (le modèle de l'agent) avec cette instruction système :
Vous êtes un assistant de recherche. Le message de l'utilisateur contient les résultats de recherche Google Serper.dev dans des sections étiquetées. Vous n'avez PAS de recherche web en direct — utilisez uniquement les blocs collés.
Tâche : pour UNE personne spécifique dans UNE entreprise spécifique, extrayez :
- company_domain : nom d'hôte enregistrable uniquement (pas de chemin, pas de www), ou chaîne vide
- company_website : URL complète https:// de la page d'accueil si prise en charge, ou chaîne vide
- linkedin_url : https://linkedin.com/in/… pour la personne nommée dans cette entreprise, ou chaîne vide
- confidence : high | medium | low
- note : explication facultative de l'ambiguïté ou des lacunes
Règles :
- Chaque URL non vide doit apparaître dans les blocs Serper (une normalisation mineure de la chaîne de requête est acceptable)
- Le nom sur LinkedIn peut différer de celui saisi (surnom vs nom légal) — faites correspondre la même personne + employeur
- Avant d'accepter `linkedin_url`, vérifiez la qualité de la correspondance :
- Extrayez les tokens de prénom et de nom de `Nom complet : {full_name}` (ignorez la ponctuation)
- Le résultat `/in/` choisi doit avoir *les deux* tokens présents dans le `titre` ou l'`extrait` du résultat
- Si aucun résultat `/in/` ne satisfait l'exigence de token, renvoyez `linkedin_url` comme une chaîne vide et définissez `confidence` sur `low`
- Le site public de l'entreprise peut utiliser un nom de bannière différent de celui saisi — préférez les preuves officielles
- Lors de la résolution de `company_domain` :
- Préférez `knowledgeGraph.website` lorsqu'il est présent
- Rejetez les agrégateurs/registres courants (exemples : `naceweb.org`, `usnews.com`, `wikipedia.org`, `niche.com`, `facebook.com`, `instagram.com`, `twitter.com`)
- Si `company_name` semble manquant ou correspond étroitement au nom de la personne, renvoyez une chaîne vide
- Ne fabriquez jamais d'URL ou de slugs
Répondez UNIQUEMENT avec un seul objet JSON (pas de délimiteurs de démarquage) :
{"company_domain":"","company_website":"","linkedin_url":"","confidence":"medium","note":""}
Utilisez ce modèle de message utilisateur :
### Personne cible
Nom complet : {full_name}
Rôle/titre déclaré : {stated_role}
Entreprise (telle que fournie) : {company_name}
### Résultats de recherche
{formatted_serper_sections}
### Tâche
Renvoie l'objet JSON décrit dans l'instruction système.
Analysez ensuite votre propre réponse : supprimez les délimiteurs de démarquage, extrayez l'objet JSON.
Repli de récolte LinkedIn : si linkedin_url est vide après extraction, analysez les résultats Serper bruts pour les URL /in/ dont le titre contient à la fois les tokens de prénom et de nom. Préférez les correspondances où l'extrait mentionne l'entreprise. S'il existe exactement un résultat candidat de haute qualité, incluez-le dans la sortie JSON avec confidence défini sur medium et une note explicative. Ne devinez jamais.
Phase 4 — Sauvegarde
Avec OM : utilisez import-profiles --profiles [...] ou import-profiles --file.
python3 ~/.hermes/skills/outreachmagic/pipeline.py import-profiles --profiles '[
{"name":"Jane Doe","company":"Acme Corp","email":"[email protected]","linkedin":"https://linkedin.com/in/janedoe","company_domain":"acme.com","company_website":"https://acme.com","title":"VP Marketing","tags":["serper_attempted"]}
]'
Autonome : sauvegardez dans un fichier JSON.
python3 -c "
import json
result = {'company_domain':'acme.com','company_website':'https://acme.com','linkedin_url':'https://linkedin.com/in/janedoe','confidence':'high'}
with open('research_output.json', 'w') as f:
json.dump(result, f, indent=2)
"
Toujours ajoutez serper_attempted aux tags.
Règles de l'agent : Résumé
| S'il... | Alors... |
|---|---|
| OM est configuré | check d'abord, dépensez des crédits uniquement si not_found ou ambiguous |
exists_linkedin_no_email est renvoyé | Ignorez Serper ; utilisez email-finder si l'utilisateur souhaite un email |
exists_linkedin_email est renvoyé | Ignorez complètement ; aucune dépense |
ambiguous est renvoyé | Exécutez le pack complet ; c'est une nouvelle piste potentielle |
not_found est renvoyé | Exécutez le pack complet ; nouvelle piste |
| Le modèle extrait des données | Sauvegardez via import-profiles (avec OM) ou dans un fichier |
| Les recherches échouent | Marquez avec serper_attempted ; ne réessayez pas automatiquement |
| L'utilisateur demande un email sur un lead avec LinkedIn mais sans email | Transmettez à email-finder ; ne cherchez pas de modèles dans cette compétence |
Messages d'erreur et dépannage
- Erreur
batch-check— vérifiez les en-têtes CSV :name,company,email,linkedin,company_domain,company_website,titlesont reconnus. D'autres en-têtes sont ignorés. Pour toute colonne nomméenameetcompany, exécutezbatch-check. serper_api_key_sourceestagent_config— vous l'avez définie dans config.json. Dans OM, vous devez l'enregistrer dans le portail, puis exécuterpipeline.py sync-secrets.- Sauvegardes redondantes — la vérification de déduplication vérifie également
serper_attempteddans le local. Si vous obtenezskipped_serper_attempted, le lead a déjà été traité. - La recherche LinkedIn échoue mais l'utilisateur insiste — exécutez une fois, puis marquez. Ne réexécutez pas sur le même lead sauf si l'utilisateur le demande explicitement (par exemple, "vérifiez à nouveau maintenant, son profil a peut-être été créé").
Rappels de style de code
- Utilisez
enrich.py serper-search --query "..."pour les recherches Serper. Évitezcurlpour les recherches complexes. - Les chemins de fichiers CSV/JSON sont relatifs au répertoire de l'espace de travail, pas au dossier de la compétence.
- Lorsque vous traitez un CSV d'utilisateur, utilisez
batch-checksur l'ensemble du fichier en premier, puis traitez ligne par ligne uniquement pour les statuts qui nécessitent Serper. - Si l'utilisateur copie-colle du texte, écrivez-le d'abord dans un fichier, puis utilisez
batch-check --workspace <slug> <fichier>avectable_nameconfiguré surbatch_check_cache(détection automatique du format).
Workflow de bout en bout
Parcours complet avec OM :
# 1. Vérifier
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# → not_found, procéder
# 2a. Rechercher l'entreprise
python3 scripts/enrich.py serper-search --query '"Acme Corp" site web officiel' --label company_discovery_strict
# 2c. Rechercher le profil LinkedIn
python3 scripts/enrich.py serper-search --query 'site:linkedin.com/in Jane Doe Acme Corp' --label linkedin_profile
# 3. Extraire (échange avec le modèle avec instruction système)
# 4. Sauvegarder (avec OM)
python3 ~/.hermes/skills/outreachmagic/pipeline.py import-profiles --profiles '[
{"name":"Jane Doe","company":"Acme Corp","linkedin":"https://linkedin.com/in/janedoe","company_domain":"acme.com","title":"VP Marketing","tags":["serper_attempted"]}
]'