リードエンリッチ は何をしますか?
Serper.dev の Google Search を使用して人物(名前+会社)を調査します。抽出にはエージェント組み込みのモデルを使用します。外部LLM APIは不要です。
スタンドアロンで動作 — Serperキーのみ必要です。Outreach Magicと組み合わせることで、クレジット節約の重複排除(既にLinkedIn/メールがあるリードはSerperをスキップ)と、セッションを超えた永続的なストレージを実現します。
クレジット節約(OM使用時): outreachmagicを最初に確認します。リードが同じ会社でLinkedIn + メールを既に持っている場合、Serperクレジットは0です。メールなしでLinkedInがある場合はSerperをスキップ — email-finder for trykittを使用します。メールのみのレコードはLinkedIn検索を実行します(1~2Serperクレジット)。別の会社での名前一致は
ambiguousを返すため、APIが無駄になりません。
セットアップ
スタンドアロン(OMなし)
Serperキーのみ必要です。serper-searchを直接実行します。結果はJSONで出力されます。
| キー | 用途 |
|---|---|
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'
# → 結果をモデルにパイプして抽出するか、serper-formatを使用
SERPER_API_KEY を環境変数または ~/.hermes/skills/lead-enrich/config.json に設定します。
Outreach Magic との連携(重複排除+保存)
事前の重複排除(OMに既に存在するリードをスキップ)を追加し、構造化されたエンリッチメントをローカルのSQLiteパイプラインに保存します。outreachmagic skill と pipeline.py login が必要です。
| キー | 用途 |
|---|---|
| 上記のスタンドアロン用キーすべて + | |
OUTREACHMAGIC_AGENT_KEY | pipeline.py login によるOMログイン |
python3 scripts/enrich.py check "Jane Doe" "Acme Corp" # 0クレジット
python3 scripts/enrich.py batch-check --workspace W input.json # バッチ重複排除
SerperキーをOutreach Magicポータル → 設定 → API Keys に保存し、pipeline.py sync-secrets を実行します。enrich.py config で確認します(serper_api_key_source が agent_secrets になっている必要があります)。
3. メール検索(email-finderスキル)
メール検索は lead-enrich v2+ には含まれていません。プラットフォームのインストールガイドに従って、outreachmagic で email-finder をインストールしてください。または、npx skills add outreachmagic/email-finder でスタンドアロンインストールします。
Serperエンリッチメントで company_domain が保存された後、以下を実行します:
python3 ~/.hermes/skills/email-finder/scripts/email_finder.py find --name "Jane Doe" \
--domain acme.com --linkedin "https://linkedin.com/in/janedoe" --save
references/email-finder.md および email-finder スキルのドキュメントを参照してください。
ワークスペースロールアップ(Serperクレジット不要): リード保存後、outreachmagic の workspace summary --workspace <slug> --json を使用して、タグカウントと送信者ごとのLinkedIn接続承認状況を取得できます。大規模ワークスペース(2,000リード超)の場合は、--tags-only を追加してタグカウントを高速化します。ローカルDBのみ — プルは任意です。
一般的なワークフロー
| ユーザーの発言 | あなたの行動 |
|---|---|
| 「この人を調査して」 | Serper検索 → import-profiles で保存 |
| 「私のCSVをエンリッチして」 | batch-check / インポート → 不足フィールドをエンリッチ → OMに保存 |
| 「メールアドレスも見つけて」 | エンリッチ後、email-finder に引き継ぐ |
Sales Nav / Vayne CSV:outreachmagic の import-profiles --file … を使用します(カラムを自動検出)。
CSV / アワードリストのワークフロー(10人以上に推奨)
outreachmagic/imports/awards.csv のようなパスは、スキルのインストールフォルダではなく、ワークスペースディレクトリ(エージェントがコマンドを実行する場所)からの相対パスです。
# 0クレジット - 最初にファイル全体の重複排除(完了行には自動的に serper_attempted がスタンプされます)
python3 scripts/enrich.py batch-check --workspace your_workspace outreachmagic/imports/awards.csv
# 既に serper_attempted がタグ付けされたリードをスキップして重複排除を再実行
python3 scripts/enrich.py batch-check --workspace your_workspace --skip-tagged outreachmagic/imports/awards.csv
# LinkedIn/ドメインが必要な行のみSerperを実行(team_award、exists_linkedin_*、skipped_serper_attempted はスキップ)
# 調査後 - 役職/業種のみを修正(Serperクレジット0)
python3 scripts/enrich.py backfill --fields title,industry --workspace your_workspace outreachmagic/imports/patch.csv
batch-check は .json または .csv を受け付けます。backfill は各行に email または linkedin が必要です。companion_common 経由でチャンク化された import-profiles を使用します(200行/チャンク、最大300秒/チャンク。空のフィールドを埋めます。置き換えるには --overwrite を追加)。
使用するタイミング
- ユーザーが「この人を調査して」/「AcmeのJane Doeを調べて」と言ったとき
- ユーザーがアウトリーチ前に見込み客リストをエンリッチしたいとき
- ユーザーが調査前に「このリードはもう持っているか?」と尋ねたとき
- ユーザーがエンリッチする名前と会社のCSV/JSONを提供したとき
- ユーザーがSerper、リードエンリッチメント、人物調査に言及したとき
エージェントの行動ルール(重要)
- 最初に重複排除(OM使用時)。 Serper API呼び出しの前に、
enrich.py checkを実行します。リードが同じ会社で LinkedIn + メール を持っている場合、Serperを完全にスキップします。LinkedInはあるが メールがない 場合、Serperをスキップし、ユーザーがメールアドレスを希望する場合は email-finder を提案します。outreachmagic で既に完了しているリードにSerperクレジットを決して使用しないでください。スタンドアロン: チェックをスキップ —serper-searchを直接実行します。 - Serperのみ。
enrich.py serper-search --query "..."を優先します(stdlib HTTP、設定/環境からキーを取得)。または、$SERPER_API_KEYを指定してcurlを使用します — キーをチャットログに埋め込まないでください。GoogleやLinkedInを直接スクレイピングしないでください。 - 組み込みモデルのみ。 あなた(エージェント)がSerper結果からJSONを抽出します。外部LLM API(Gemini、OpenAIは不可) — あなた自身の推論が抽出エンジンです。
- 保存前に調査を完了する。 まず完全な検索ラダーを実行し、その後一度だけ保存します。
- outreachmagic 経由で保存(OM使用時)。 LinkedInがあるリードには
import-profilesを使用します。保存時には常にserper_attemptedをタグに追加します(map-to-om出力に自動的に含まれます)。LinkedInがなく、lead_idが既知のリードの場合は、stamp-attemptedまたはid+ tags を指定したimport-profilesを使用してタグをスタンプします — メモだけに依存しないでください。読み取り専用の重複排除チェックにはpipeline.py queryまたはenrich.py checkを使用します — 生のINSERT/UPDATEは絶対に行わないでください。同じ人物に対して両方の保存パスを実行しないでください。スタンドアロン: JSON出力をファイルに保存します — OMは不要です。 - エンリッチ後にタグ付け(OM使用時)。 Serperを通過したすべてのリードには、保存時に
serper_attemptedを付ける必要があります。将来の実行での再処理を防ぎます。 - Serperの前にタグを確認(OM使用時)。 Serperクレジットを使用する前に、
serper_attemptedを確認します(enrich.py check --skip-taggedまたはチェック出力のskip_reasonを使用)。存在し、LinkedInがまだ空の場合、ユーザーが明示的に再試行を希望しない限り(例:30日以上経過)、スキップします。 - 透明性。 どのSerperクエリが実行されたか、信頼度、何が保存されたかを表示します。ユーザーは自分のクレジットが正確にどこに使われたかを確認できる必要があります。
- 賢くバッチ処理。 1回の実行で最大50人に制限します。CSV/アワードリストの場合は、Serperの前にファイル全体(JSONまたはCSV)に対して
batch-checkを1回 実行します。LinkedIn/ドメインが必要なステータスの行に対してのみSerperを処理します。team_award、exists_linkedin_email、skipped_serper_attemptedの行はスキップします。 - メール検索 — email-finder を使用。 エンリッチメントで
company_domainが保存された後、email-finder(email_finder.py find --save)に引き継ぎます。references/email-finder.mdを参照してください。このスキルでメールアドレスを捏造したりパターン推測したりしないでください。
クイックスタート
# 単一人物(最も一般的)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# → 「not_found」の場合、以下のSerper検索パックに進む
# ワークスペースを指定(リードをパイプラインワークスペースに関連付ける)
python3 scripts/enrich.py check --workspace your_workspace "Jane Doe" "Acme Corp"
# JSONまたはCSVからのバッチ(リストに対してSerperの前にこれを実行)
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
# LinkedInルックアップ失敗後に serper_attempted をスタンプ(lead_id が既知の場合)
python3 scripts/enrich.py stamp-attempted --workspace your_workspace --lead-ids 42,43 \
--notes "SerperでLinkedInが見つかりませんでした"
# 既存リードの役職/業種をバックフィル(linkedinまたはemailが必要)
python3 scripts/enrich.py backfill --fields title,industry outreachmagic/imports/patch.csv
# GitHubリリースから安全にスキルを更新(チェックサム検証)
python3 scripts/enrich.py update --check
python3 scripts/enrich.py update
update はファイルを置き換える前に update-manifest.json のSHA256チェックサムを検証します。チェックサムがないか一致しない場合、更新は中止されます。
コアワークフロー
フェーズ1 — 重複排除チェック(0クレジット)
各人物に対して以下を実行します:
# ワークスペースなし(組織全体のルックアップ)
python3 scripts/enrich.py check "Jane Doe" "Acme Corp"
# ワークスペースあり(パイプラインワークスペースにスコープ)
python3 scripts/enrich.py check --workspace your_workspace "Jane Doe" "Acme Corp"
人物ごとの出力:
| ステータス | 意味 | アクション |
|---|---|---|
exists_linkedin_email | 同じ会社、LinkedIn + メール | SerperおよびメールAPIをスキップ |
exists_linkedin_no_email | 同じ会社、LinkedIn、メールなし | Serperをスキップ → ユーザーがメールを希望する場合は email-finder |
exists_no_linkedin_email | 同じ会社、メール、LinkedInなし | LinkedIn Serperクエリのみ |
exists_no_linkedin | 同じ会社、どちらもなし | LinkedIn Serperクエリのみ |
skipped_serper_attempted | serper_attempted タグあり、LinkedInなし | Serperをスキップ — 既に試行済み |
ambiguous | 名前一致、会社不一致 | 完全なSerperパックを実行 — スキップしない |
not_found | 一致なし | 完全なSerper検索パックを実行 |
team_award | チーム/グループ行(個人なし) | Serperをスキップ — team_award をタグ付け、連絡先メモを追加 |
dedup_disabled | 設定で dedup_before_search: false | 要求に応じてSerperを実行 |
チェック出力には tags とオプションの skip_reason(has_linkedin または skipped_serper_attempted)が含まれます。batch-lead-lookup を使用します(ローカル、Serperクレジット0)。
フェーズ2 — Serper検索パック
必要な人物に対してのみ実行します。結果の品質に応じて1人あたり2~4回の検索を行います:
Serperクレジット予算見積もり
- 1人あたり最小:
0Serperクレジット(exists_linkedin_email)。 - 一般的なパス:
2クレジット(2a厳密な会社 +2cLinkedInプロフィール)。 - 1人あたりの最大:
4クレジット(すべてのフォールバックが必要な場合:2a+2b+2c+2e)。 - バッチの計算式:
最小=0、最大=4*N、ただしNは実行する人数。 - バッチ上限の例:
N=50の場合、ハード最大は200クレジット(最悪の場合)。
2a. 会社の発見 — 厳密(常に)
推奨(シェル履歴にキーが保存されない):
python3 scripts/enrich.py serper-search --query '"Acme Corp" official website' --label company_discovery_strict
または、よりシンプルなクエリで再試行:
python3 scripts/enrich.py serper-search --query 'Acme Corp website' --label company_discovery_broad
2b. 会社の発見 — 広範(条件付き)
2aが http:// または https:// リンクを持つオーガニック結果を返さなかった場合のみ実行:
python3 scripts/enrich.py serper-search --query 'Acme Corp official website' --label company_discovery_broad
(同じテンプレート、引用符なしの会社名。)
2c. LinkedInプロフィール(常に)
クエリを構築(引用符なしの会社 — スニペット内の異なる雇用主名に一致):
site:linkedin.com/in {名 姓} {役職の最大5単語} {会社名}
例:
python3 scripts/enrich.py serper-search \
--query 'site:linkedin.com/in Jane Doe VP Marketing Acme Corp' \
--label linkedin_profile
フェーズ3 — モデル抽出
フォーマットされたSerper結果を、以下のシステム指示と共に自分自身(エージェントモデル)に渡します:
あなたはリサーチアシスタントです。ユーザーメッセージには、ラベル付きのセクションにSerper.devのGoogle検索結果が含まれています。ライブWeb検索はできません — 貼り付けられたブロックのみを使用してください。
タスク:特定の会社の特定の人物について以下を抽出します:
- company_domain:登録可能なホスト名のみ(パスなし、wwwなし)、または空文字列
- company_website:サポートされている場合は完全なhttps:// ホームページURL、または空文字列
- linkedin_url:この会社の指定された人物の https://linkedin.com/in/…、または空文字列
- confidence:high | medium | low
- note:曖昧さやギャップの説明(任意)
ルール:
- 空でないすべてのURLはSerperブロックに存在している必要があります(マイナーなクエリ文字列の正規化は許容されます)
- LinkedInの名前は入力と異なる場合があります(ニックネームと正式名称) — 同じ人物+雇用主で照合します
- `linkedin_url` を受け入れる前に、一致品質を確認します:
- `フルネーム: {full_name}` から名と姓のトークンを抽出します(句読点は無視)
- 選択した `/in/` 結果は、結果の `title` または `snippet` の *両方* のトークンを含んでいる必要があります
- トークン要件を満たす `/in/` 結果がない場合、`linkedin_url` を空文字列として返し、`confidence` を `low` に設定します
- 会社の公開サイトは、入力とは異なるバナー名を使用する場合があります — 公式の証拠を優先します
- `company_domain` を解決する場合:
- `knowledgeGraph.website` が存在する場合はそれを優先します
- 一般的なアグリゲーター/レジストリは拒否します(例:`naceweb.org`、`usnews.com`、`wikipedia.org`、`niche.com`、`facebook.com`、`instagram.com`、`twitter.com`)
- `company_name` がないように見える場合、または人物名と密接に一致する場合、空文字列を返します
- URLやスラッグを決して捏造しないでください
以下のみを含む単一のJSONオブジェクトで応答してください(マークダウンフェンスなし):
{"company_domain":"","company_website":"","linkedin_url":"","confidence":"medium","note":""}
以下のユーザーメッセージテンプレートを使用します:
### 対象人物
フルネーム: {full_name}
表明された役職: {stated_role}
会社(提供されたもの): {company_name}
### 検索結果
{formatted_serper_sections}
### タスク
システム指示で説明されたJSONオブジェクトを返します。
次に、自身の応答を解析します:マークダウンフェンスを削除し、JSONオブジェクトを抽出します。
LinkedInハーベストフォールバック: 抽出後に linkedin_url が空の場合、生のSerperオーガニック結果をスキャンして、タイトルに名と姓の両方のトークンを含む /in/ URLを探します。結果が複数ある場合、姓と会社名が最もよく一致するものを優先します。見つかった場合は、そのURLを linkedin_url として使用し、confidence を medium に設定し、note に「フォールバックスキャンから抽出」と記録します。」
(原文がここで切れているため、後続の完全なドキュメントに応じて続きます。)