Lead Enrich
Recherchiere eine Person (Name + Unternehmen) mit Serper.dev Google Search. Verwendet das eingebaute Modell des Agenten zur Extraktion. Keine externe LLM-API nötig.
Funktioniert standalone – nur ein Serper-Key. Kombinierbar mit Outreach Magic für Credits sparende Deduplizierung (Serper überspringen, wenn Leads bereits LinkedIn/E-Mail haben) und dauerhafte Speicherung über Sitzungen hinweg.
Credits sparen (mit OM): Prüft outreachmagic zuerst. Wenn der Lead bereits LinkedIn + E-Mail im selben Unternehmen hat, werden null Serper-Credits verbraucht. LinkedIn ohne E-Mail überspringt Serper – verwende email-finder für trykitt. Nur-E-Mail-Datensätze erhalten weiterhin LinkedIn-Suchen (1–2 Serper-Credits). Namensübereinstimmungen in einem anderen Unternehmen geben
ambiguouszurück, damit APIs nicht verschwendet werden.
Einrichtung
Standalone (ohne OM)
Nur ein Serper-Key. Führe serper-search direkt aus. Ergebnisse werden als JSON ausgegeben.
| Schlüssel | Zweck |
|---|---|
SERPER_API_KEY | serper.dev – Google Search API |
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'
# → Leite Ergebnisse an dein Modell zur Extraktion weiter oder verwende serper-format
Setze SERPER_API_KEY in deiner Umgebung oder über ~/.hermes/skills/lead-enrich/config.json.
Mit Outreach Magic (Deduplizierung + Speichern)
Fügt eine Vorab-Deduplizierung hinzu (Überspringen von Leads, die bereits in OM sind) und speichert die strukturierte Anreicherung in deiner lokalen SQLite-Pipeline. Erfordert den outreachmagic skill mit pipeline.py login.
| Schlüssel | Zweck |
|---|---|
| Alle Standalone-Schlüssel oben + | |
OUTREACHMAGIC_AGENT_KEY | OM-Login via pipeline.py login |
python3 scripts/enrich.py check "Jane Doe" "Acme Corp" # 0 Credits
python3 scripts/enrich.py batch-check --workspace W input.json # Batch-Deduplizierung
Speichere deinen Serper-Key im Outreach Magic Portal → Einstellungen → API-Schlüssel, führe dann pipeline.py sync-secrets aus. Überprüfe mit enrich.py config (serper_api_key_source sollte agent_secrets sein).
3. E-Mail-Suche (email-finder Skill)
Die E-Mail-Suche ist nicht in lead-enrich v2+ enthalten. Installiere email-finder mit outreachmagic gemäß der Plattform-Installationsanleitung. Oder installiere standalone via npx skills add outreachmagic/email-finder.
Nachdem die Serper-Anreicherung company_domain gespeichert hat, führe aus:
python3 ~/.hermes/skills/email-finder/scripts/email_finder.py find --name "Jane Doe" \
--domain acme.com --linkedin "https://linkedin.com/in/janedoe" --save
Siehe references/email-finder.md und die Dokumentation des email-finder Skills.
Workspace-Zusammenfassungen (keine Serper-Credits): Nach dem Speichern von Leads verwende outreachmagic workspace summary --workspace <slug> --json für Tag-Anzahl und akzeptierte LinkedIn-Verbindungen pro Absender. Bei großen Workspaces (>2.000 Leads) füge --tags-only für schnellere Tag-Anzahlen hinzu. Nur lokale DB – Pull ist optional.
Häufige Workflows
| Benutzer sagt | Du tust |
|---|---|
| „Recherchiere diese Person“ | Serper-Suche → speichern via import-profiles |
| „Reichere meine CSV an“ | batch-check / import → fehlende Felder anreichern → in OM speichern |
| „Finde auch ihre E-Mail“ | Nach der Anreicherung an email-finder übergeben |
Sales Nav / Vayne CSVs: Verwende outreachmagic import-profiles --file … (erkennt Spalten automatisch).
CSV / Award-Liste Workflow (bevorzugt für 10+ Personen)
Pfade wie outreachmagic/imports/awards.csv sind relativ zu deinem Workspace-Verzeichnis (wo der Agent den Befehl ausführt), nicht zum Skill-Installationsordner.
# 0 Credits – dedupliziere zuerst die gesamte Datei (markiert serper_attempted automatisch bei vollständigen Zeilen)
python3 scripts/enrich.py batch-check --workspace dein_workspace outreachmagic/imports/awards.csv
# Deduplizierung wiederholen, aber Leads mit Tag serper_attempted überspringen
python3 scripts/enrich.py batch-check --workspace dein_workspace --skip-tagged outreachmagic/imports/awards.csv
# Serper nur für Zeilen, die LinkedIn/Domain benötigen (überspringt team_award, exists_linkedin_*, skipped_serper_attempted)
# Nach der Recherche – Titel/Branche aktualisieren (0 Serper-Credits)
python3 scripts/enrich.py backfill --fields title,industry --workspace dein_workspace outreachmagic/imports/patch.csv
batch-check akzeptiert .json oder .csv. backfill erfordert email oder linkedin pro Zeile; verwendet chunked import-profiles via companion_common (200 Zeilen/Chunk, bis zu 300s/Chunk; füllt leere Felder; füge --overwrite hinzu, um zu ersetzen).
Wann verwenden
- Benutzer sagt „recherchiere diese Person“ / „suche Jane Doe bei Acme“
- Benutzer möchte eine Liste von Interessenten vor der Kontaktaufnahme anreichern
- Benutzer fragt „haben wir diesen Lead schon?“ vor der Recherche
- Benutzer stellt eine CSV/JSON mit Namen und Unternehmen zur Massenanreicherung bereit
- Benutzer erwähnt Serper, Lead-Anreicherung oder Personenrecherche
Agent-Verhaltensregeln (Wichtig)
- Zuerst deduplizieren (mit OM). Vor jedem Serper-API-Aufruf
enrich.py checkausführen. Wenn der Lead LinkedIn + E-Mail im selben Unternehmen hat, Serper komplett überspringen. Wenn LinkedIn existiert, aber keine E-Mail, Serper überspringen und email-finder anbieten, wenn der Benutzer eine Adresse möchte. Nie Serper-Credits für Leads ausgeben, die in outreachmagic bereits vollständig sind. Standalone: Prüfung überspringen –serper-searchdirekt ausführen. - Nur Serper. Bevorzuge
enrich.py serper-search --query "..."(stdlib HTTP, Key aus Konfiguration/Umgebung). Oder verwendecurlmit$SERPER_API_KEY– Key niemals in Chat-Logs einbetten. Niemals Google oder LinkedIn direkt scrapen. - Nur eingebautes Modell. Du (der Agent) extrahierst JSON aus Serper-Ergebnissen. Keine externen LLM-APIs (kein Gemini, kein OpenAI) – deine eigene Argumentation ist die Extraktions-Engine.
- Vollständige Recherche vor dem Speichern. Führe zuerst die vollständige Suchleiter aus, dann einmal speichern.
- Speichern via outreachmagic (mit OM). Verwende
import-profilesfür Leads mit LinkedIn. Hänge beim Speichern immerserper_attemptedan die Tags an (automatisch inmap-to-omAusgabe enthalten). Für Leads ohne LinkedIn aber bekannterlead_id, markiere den Tag viastamp-attemptedoderimport-profilesmitid+ Tags – verlasse dich nicht nur auf Notizen. Für schreibgeschützte Deduplizierungsprüfungen verwendepipeline.py queryoderenrich.py check– niemals rohesINSERT/UPDATE. Führe niemals beide Speicherpfade für dieselbe Person aus. Standalone: Speichere JSON-Ausgabe in einer Datei – kein OM nötig. - Taggen nach der Anreicherung (mit OM). Jeder Lead, der durch Serper geht, muss beim Speichern
serper_attemptederhalten. Verhindert erneute Verarbeitung bei zukünftigen Läufen. - Tag vor Serper prüfen (mit OM). Vor dem Ausgeben von Serper-Credits auf
serper_attemptedprüfen (viaenrich.py check --skip-taggedoderskip_reasonin der Prüfausgabe). Wenn vorhanden und LinkedIn immer noch leer, überspringen, es sei denn, der Benutzer möchte explizit einen erneuten Versuch (z.B. älter als 30 Tage). - Transparenz. Zeige an, welche Serper-Abfragen ausgeführt wurden, die Konfidenz und was gespeichert wurde. Der Benutzer soll genau sehen, wohin seine Credits geflossen sind.
- Klug bündeln. Maximal 50 Personen pro Durchlauf. Für CSV/Award-Listen führe zuerst
batch-checkeinmal auf der gesamten Datei (JSON oder CSV) vor Serper aus. Verarbeite Serper nur für Status, die LinkedIn/Domain benötigen. Überspringeteam_award,exists_linkedin_emailundskipped_serper_attemptedZeilen. - E-Mail-Suche – verwende email-finder. Nachdem die Anreicherung
company_domaingespeichert hat, übergib an email-finder (email_finder.py find --save). Siehereferences/email-finder.md. Erfinde oder rate niemals E-Mails in diesem Skill.
Schnellstart
# Einzelperson (häufigster Fall)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# → wenn "not_found", mit dem Serper-Suchpaket unten fortfahren
# Mit Workspace (verknüpft Lead mit deinem Pipeline-Workspace)
python3 scripts/enrich.py check --workspace dein_workspace "Jane Doe" "Acme Corp"
# Stapelverarbeitung aus JSON oder CSV (vor Serper bei Listen ausführen)
python3 scripts/enrich.py batch-check --workspace dein_workspace outreachmagic/imports/people.json
python3 scripts/enrich.py batch-check --workspace dein_workspace --skip-tagged outreachmagic/imports/awards.csv
# Markiere serper_attempted nach fehlgeschlagener LinkedIn-Suche (wenn lead_id bekannt ist)
python3 scripts/enrich.py stamp-attempted --workspace dein_workspace --lead-ids 42,43 \
--notes "Kein LinkedIn via Serper gefunden"
# Backfill von Titel/Branche bei bestehenden Leads (LinkedIn oder E-Mail erforderlich)
python3 scripts/enrich.py backfill --fields title,industry outreachmagic/imports/patch.csv
# Skill sicher von GitHub Release aktualisieren (Checksum-verifiziert)
python3 scripts/enrich.py update --check
python3 scripts/enrich.py update
update überprüft SHA256-Prüfsummen aus update-manifest.json, bevor Dateien ersetzt werden. Wenn Prüfsummen fehlen oder nicht übereinstimmen, wird das Update abgebrochen.
Kern-Workflow
Phase 1 – Deduplizierungsprüfung (0 Credits)
Für jede Person ausführen:
# Ohne Workspace (organisationsweite Suche)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# Mit Workspace (auf deinen Pipeline-Workspace beschränkt)
python3 scripts/enrich.py check --workspace dein_workspace "Jane Doe" "Acme Corp"
Ausgabe pro Person:
| Status | Bedeutung | Aktion |
|---|---|---|
exists_linkedin_email | Gleiches Unternehmen, LinkedIn + E-Mail | Serper und E-Mail-APIs überspringen |
exists_linkedin_no_email | Gleiches Unternehmen, LinkedIn, keine E-Mail | Serper überspringen → email-finder, wenn Benutzer E-Mail möchte |
exists_no_linkedin_email | Gleiches Unternehmen, E-Mail, kein LinkedIn | Nur LinkedIn Serper-Abfragen |
exists_no_linkedin | Gleiches Unternehmen, weder noch | Nur LinkedIn Serper-Abfragen |
skipped_serper_attempted | Hat Tag serper_attempted, kein LinkedIn | Serper überspringen – bereits versucht |
ambiguous | Namensübereinstimmung, Unternehmen weicht ab | Volles Serper-Paket ausführen – nicht überspringen |
not_found | Keine Übereinstimmung | Volles Serper-Suchpaket ausführen |
team_award | Team-/Gruppenzeile (keine Einzelperson) | Serper überspringen – tagge team_award, Kontaktnotiz hinzufügen |
dedup_disabled | dedup_before_search: false in Konfiguration | Serper wie gewünscht ausführen |
Prüfausgabe enthält tags und optionalen skip_reason (has_linkedin oder skipped_serper_attempted). Verwendet batch-lead-lookup (lokal, null Serper-Credits).
Phase 2 – Serper-Suchpaket
Nur für Personen ausführen, die es benötigen. 2–4 Suchen pro Person abhängig von der Ergebnisqualität:
Serper-Credit-Budget-Schätzer
- Pro Person Minimum:
0Serper-Credits (exists_linkedin_email). - Häufiger Pfad:
2Credits (2astriktes Unternehmen +2cLinkedIn-Profil). - Pro Person hartes Maximum:
4Credits, wenn alle Fallbacks nötig sind (2a+2b+2c+2e). - Batch-Formel:
min=0,max=4*NwobeiNdie Anzahl der Personen im Durchlauf ist. - Batch-Obergrenze Beispiel: mit
N=50ist das harte Maximum200Credits (schlimmster Fall).
2a. Unternehmensfindung – strikt (immer)
Bevorzugt (kein Schlüssel in der Shell-Historie):
python3 scripts/enrich.py serper-search --query '"Acme Corp" offizielle website' --label company_discovery_strict
Oder Wiederholung mit einfacherer Abfrage:
python3 scripts/enrich.py serper-search --query 'Acme Corp website' --label company_discovery_broad
2b. Unternehmensfindung – breit (bedingt)
Nur ausführen, wenn 2a keine organischen Ergebnisse mit einem http:// oder https:// Link zurückgibt:
python3 scripts/enrich.py serper-search --query 'Acme Corp offizielle website' --label company_discovery_broad
(Gleiche Vorlage, Firmenname in Anführungszeichen.)
2c. LinkedIn-Profil (immer)
Abfrage erstellen (Firmenname ohne Anführungszeichen – passt auf verschiedene Arbeitgebervarianten in Snippets):
site:linkedin.com/in {Vorname Nachname} {bis zu 5 Wörter der Rolle} {Firmenname}
Beispiel:
python3 scripts/enrich.py serper-search \
--query 'site:linkedin.com/in Jane Doe VP Marketing Acme Corp' \
--label linkedin_profile
Phase 3 – Modellextraktion
Übergib die formatierten Serper-Ergebnisse an dich selbst (das Agentenmodell) mit dieser Systemanweisung:
Du bist ein Forschungsassistent. Die Benutzernachricht enthält Serper.dev Google-Suchergebnisse
in beschrifteten Abschnitten. Du hast KEINE Live-Websuche – verwende nur die eingefügten Blöcke.
Aufgabe: Für EINE bestimmte Person bei EINEM Unternehmen extrahiere:
- company_domain: registrierbarer Hostname nur (kein Pfad, kein www), oder leerer String
- company_website: vollständige https:// Startseiten-URL falls unterstützt, oder leerer String
- linkedin_url: https://linkedin.com/in/… für die genannte Person bei diesem Unternehmen, oder leerer String
- confidence: high | medium | low
- note: optionale Erklärung von Mehrdeutigkeiten oder Lücken
Regeln:
- Jede nicht-leere URL muss in den Serper-Blöcken vorkommen (geringfügige Query-String-Normalisierung ok)
- Name auf LinkedIn kann vom Input abweichen (Spitzname vs. legaler Name) – gleiche Person + Arbeitgeber erkennen
- Bevor du `linkedin_url` akzeptierst, überprüfe die Übereinstimmungsqualität:
- Extrahiere Vor- und Nachnamen-Token aus "Full name: {full_name}" (ignoriere Satzzeichen)
- Das gewählte `/in/` Ergebnis muss *beide* Token entweder im `title` oder `snippet` des Ergebnisses enthalten
- Wenn kein `/in/` Ergebnis die Token-Anforderung erfüllt, gib `linkedin_url` als leeren String zurück und setze `confidence` auf `low`
- Die öffentliche Website des Unternehmens kann einen anderen Namen verwenden als der Input – bevorzuge offizielle Belege
- Bei der Auflösung von `company_domain`:
- Bevorzuge `knowledgeGraph.website`, wenn vorhanden
- Lehne allgemeine Aggregatoren/Register ab (Beispiele: `naceweb.org`, `usnews.com`, `wikipedia.org`, `niche.com`, `facebook.com`, `instagram.com`, `twitter.com`)
- Wenn `company_name` zu fehlen scheint oder dem Personennamen stark ähnelt, gib leeren String zurück
- Erfinde niemals URLs oder Slugs
Antworte NUR mit einem einzelnen JSON-Objekt (keine Markdown-Umrandungen):
{"company_domain":"","company_website":"","linkedin_url":"","confidence":"medium","note":""}
Verwende diese Benutzernachrichtenvorlage:
### Zielperson
Vollständiger Name: {full_name}
Angegebene Rolle/Position: {stated_role}
Unternehmen (wie angegeben): {company_name}
### Suchergebnisse
{formatted_serper_sections}
### Aufgabe
Gib das JSON-Objekt zurück, das in der Systemanweisung beschrieben wird.
Dann analysiere deine eigene Antwort: Entferne Markdown-Umrandungen, extrahiere das JSON-Objekt.
LinkedIn-Harvest-Fallback: Wenn linkedin_url nach der Extraktion leer ist, durchsuche die rohen Serper-Ergebnisse nach /in/ URLs, deren Titel sowohl Vor- als auch Nachnamen-Token enthält. Bevorzuge Übereinstimmungen mit höherer Token-Überlappung.
Phase 4 – Speichern (mit OM: import-profiles)
# Formatiere den extrahierten JSON-Lead für OM (wandelt Feldnamen um, hängt serper_attempted an Tags an)
python3 scripts/enrich.py map-to-om
# → liest von stdin und gibt formatiertes JSON aus
# Beispiel mit Pipe:
echo '{"company_domain":"acme.com","company_website":"https://acme.com","linkedin_url":"https://linkedin.com/in/janedoe","confidence":"high"}' | \
python3 scripts/enrich.py map-to-om
# Speichere in OM mit Workspace-Scope
python3 scripts/enrich.py map-to-om | \
python3 -m outreachmagic.pipeline import-profiles --workspace dein_workspace
map-to-om akzeptiert JSON von stdin. Ausgabe: formatiertes import-profiles JSON mit hinzugefügten Tags (serper_attempted, ggf. team_award).
Wichtig: Lies import-profiles Ausgabe auf Fehler. Bei Erfolg gilt der Lead als gespeichert.
Standalone (kein OM): Schreibe JSON in eine Datei:
python3 scripts/enrich.py map-to-om > output/lead-janedoe.json
Phase 4a – Nur Taggen (wenn Lead-ID bekannt ist)
Wenn die Suche fehlschlägt (kein LinkedIn, keine Domain) und der Lead bereits in OM existiert, tagge serper_attempted ohne Daten zu überschreiben:
python3 scripts/enrich.py stamp-attempted --workspace dein_workspace --lead-ids 42,43 \
--notes "Kein LinkedIn via Serper gefunden"
Standalone: Überspringe diesen Schritt.
Zusätzliche Konfiguration
# Zeige aktuelle Konfiguration an
python3 scripts/enrich.py config
# Setze eine Konfiguration (nach Bedarf)
python3 scripts/enrich.py config set dedup_before_search false # Erzwinge Suche, überspringe Deduplizierung
Serper-Preisübersicht
| Abfrage | Credits | Hinweise |
|---|---|---|
| Unternehmensdomäne (strikt) | 1 | 2a oben |
| Unternehmensdomäne (breit) | 1 | Nur wenn 2a keine Ergebnisse liefert |
| LinkedIn-Profil | 1 | 2c oben |
| Zusätzliche LinkedIn-Versuche | 1–2 | Für häufige Nachnamen oder Titelvariationen |
| Gesamttypischer Fall | 2–3 Credits / Person | LinkedIn + Unternehmenswebsite (wenn vorhanden) |
Sicherheits- und Plattformregeln
- Credits sind teuer. Überspringe Serper immer, wenn Daten bereits in OM existieren. Verwende
enrich.py checkals erste deduplizierende Prüfung. - Nur werkzeuggestützt. Führe Serper über die bereitgestellten Skripte aus – niemals direkte HTTP-Aufrufe in Chat-Logs.
- Kein Prompt-Injection für Ausgaben. Gib Befehlsausgaben direkt an den Benutzer weiter oder verarbeite sie über
map-to-om. Kein Erfinden von JSON-Feldern. - Keine Web-Scraping-Ausgaben. Gib keine Roh-HTML-Ausgaben von Suchergebnissen an den Benutzer weiter.
- Max 50 Personen / Batch. Für Dateien über 50 Zeilen: Vor Serper deduplizieren, dann separate Batches von ≤50 Personen aus den verbleibenden, die eine Anreicherung benötigen.
- Kein Anhängen von Tag-Arrays. OM import-profiles aktualisiert das gesamte Tags-Feld. Stelle sicher, dass
map-to-omserper_attemptedenthält, bevor du an import-profiles übergibst.
Referenzen
- Outreach Magic Skill
- email-finder Skill
references/email-finder.md– E-Mail-Finder-Integration (im Skill-Ordner)scripts/enrich.py --help– Vollständige Befehlsreferenz- Serper.dev Preisrechner für Budgetplanung