Lead Enrich
使用 Serper.dev Google 搜索研究一个人(姓名 + 公司)。使用 Agent 内置模型 进行提取。无需外部 LLM API。
可独立使用 – 只需一个 Serper 密钥。与 Outreach Magic 结合使用可实现积分节省的去重(如果线索已有 LinkedIn/Email 则跳过 Serper)和跨会话持久存储。
积分节省(与 OM 配合): 首先检查 outreachmagic。如果线索在同一公司已有 LinkedIn + 邮箱,则不消耗 Serper 积分。若仅有 LinkedIn 而无邮箱,则跳过 Serper – 使用 email-finder 进行 trykitt。仅邮箱的记录仍会进行 LinkedIn 搜索(消耗 1-2 个 Serper 积分)。若在不同公司找到姓名匹配,则返回
ambiguous,以免浪费 API 调用。
设置
独立使用(无 OM)
只需一个 Serper 密钥。直接运行 serper-search。结果以 JSON 格式输出。
| 密钥 | 用途 |
|---|---|
SERPER_API_KEY | serper.dev – Google 搜索 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
在你的环境中或通过 ~/.hermes/skills/lead-enrich/config.json 设置 SERPER_API_KEY。
与 Outreach Magic 搭配(去重 + 保存)
添加预检去重(跳过已在 OM 中的线索)并将结构化丰富结果保存到本地 SQLite 管道。需要 outreachmagic 技能 并配合 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 # 批量去重
在 Outreach Magic 门户 → 设置 → API 密钥 中保存你的 Serper 密钥,然后运行 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 连接。对于大型工作区(超过 2000 条线索),添加 --tags-only 以加速标签计数。仅限本地数据库 – 拉取操作是可选的。
常见工作流
| 用户说 | 你执行 |
|---|---|
| "研究这个人" | Serper 搜索 → 通过 import-profiles 保存 |
| "丰富我的 CSV" | batch-check / 导入 → 丰富缺失字段 → 保存到 OM |
| "也找找他们的邮箱" | 丰富后,交给 email-finder |
Sales Nav / Vayne CSV:使用 outreachmagic 的 import-profiles --file …(自动检测列)。
CSV / 获奖列表工作流(推荐用于 10 人以上)
像 outreachmagic/imports/awards.csv 这样的路径是相对于你的工作区目录(Agent 运行命令的地方),而不是技能安装文件夹。
# 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)
# 研究后 – 仅回填 title/industry(0 Serper 积分)
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”
- 用户想要在 outreach 之前丰富潜在客户列表
- 用户在研究前问“我们是否已经有这个线索?”
- 用户提供了 CSV/JSON 格式的姓名和公司列表,需要进行批量丰富
- 用户提到 Serper、线索丰富或人员研究
Agent 行为规则(重要)
- 先去重(与 OM 配合)。 在任何 Serper API 调用之前,运行
enrich.py check。如果线索在同一公司已有 LinkedIn + 邮箱,则完全跳过 Serper。如果 LinkedIn 存在但没有邮箱,则跳过 Serper 并当用户想要邮箱时提供 email-finder。永远不要在 outreachmagic 中已完整的线索上浪费 Serper 积分。独立使用: 跳过检查 – 直接运行serper-search。 - 仅使用 Serper。 首选
enrich.py serper-search --query "..."(标准库 HTTP,密钥来自配置/环境)。或者使用$SERPER_API_KEY配合curl– 绝不在聊天日志中嵌入密钥。绝不直接抓取 Google 或 LinkedIn。 - 仅使用内置模型。 你(Agent)从 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 查询、置信度以及保存了什么。用户应该清楚看到他们的积分用在了哪里。
- 批量处理要合理。 每次运行最多 50 人。对于 CSV/获奖列表,先对整个文件(JSON 或 CSV)运行一次
batch-check,然后再进行任何 Serper 操作。仅对需要 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"
# 在现有线索上回填 title/industry(需要 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 积分)。
第 2 阶段 – Serper 搜索包
仅对需要的人运行。根据结果质量,每人进行 2–4 次搜索:
Serper 积分预算估算器
- 每人最低:
0Serper 积分(exists_linkedin_email)。 - 常见路径:
2积分(2a严格公司 +2cLinkedIn 档案)。 - 每人硬上限:
4积分(当所有后备都需要时:2a+2b+2c+2e)。 - 批次公式:
min=0,max=4*N,其中N是本次运行的人数。 - 批次上限示例: 当
N=50时,硬上限为200积分(最坏情况)。
2a. 公司发现 – 严格(始终执行)
首选(shell 历史中不包含密钥):
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 结果连同以下系统指令传递给你自己(Agent 模型):
你是一个研究助手。用户消息包含以标签分节的 Serper.dev Google 搜索结果。你**没有**实时网络搜索能力 – 仅使用粘贴的板块。
任务:针对**一家公司**的**一个人**,提取:
- company_domain:仅可注册的主机名(无路径,无 www),或空字符串
- company_website:如果支持,完整的 https:// 首页 URL,或空字符串
- linkedin_url:此人在该公司对应的 https://linkedin.com/in/…,或空字符串
- confidence:高 | 中 | 低
- note:关于歧义或差距的可选说明
规则:
- 每个非空 URL 必须出现在 Serper 板块中(轻微的查询字符串规范化可接受)
- LinkedIn 上的姓名可能与输入不同(昵称 vs. 法定姓名) – 根据同一个人和雇主进行匹配
- 在接受 `linkedin_url` 之前,验证匹配质量:
- 从 `Full name: {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 对象响应(无 markdown 围栏):
{"company_domain":"","company_website":"","linkedin_url":"","confidence":"medium","note":""}
使用此用户消息模板:
### 目标人员
全名: {full_name}
所称职位/头衔: {stated_role}
公司(如提供): {company_name}
### 搜索结果
{formatted_serper_sections}
### 任务
返回系统指令中描述的 JSON 对象。
然后解析你自己的响应:移除 markdown 围栏,提取 JSON 对象。
LinkedIn 收获后备方案: 如果提取后 linkedin_url 为空,扫描原始 Serper 有机结果,查找标题中包含名字和姓氏两个令牌的 /in/ URL。优先选择……
[截断以进行翻译]