candidate-sourcing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Candidate Sourcing Pipeline

候选人挖掘流程

An end-to-end skill that takes a hiring role and produces ready-to-send Gmail drafts for strong candidates — handling discovery, LinkedIn verification, email enrichment, personalized copy, and draft creation in one continuous workflow.
The pipeline has five phases. Each phase feeds into the next, and the skill is designed to run them in sequence with minimal human intervention. The user reviews the final Gmail drafts and clicks send.

这是一项端到端技能,输入招聘岗位信息后,为优质候选人生成可直接发送的Gmail草稿——在一个连续工作流中完成候选人发现、LinkedIn验证、邮箱信息补全、个性化文案撰写和草稿创建。
该流程分为五个阶段,每个阶段的输出作为下一个阶段的输入,技能设计为按顺序运行,只需最少的人工干预。用户最终审核Gmail草稿并点击发送即可。

Phase 1: Define the search and find candidates

阶段1:定义搜索条件并寻找候选人

Clarify the role and company

明确岗位与公司信息

Before searching, extract or confirm these details from the user. Do not assume any of these — always ask if not provided:
About the hiring company (needed for outreach in Phase 4):
  • Company name
  • What the company does (1-2 sentences)
  • Stage/traction (e.g., "Series A, $10M raised" or "500-person public company")
  • Location / remote policy
  • The sender's name and title (for the email signature)
About the role and ideal candidate:
  • The role title (e.g., "Founding ML Engineer")
  • 2-3 core technical problems the role involves
  • Target companies, research labs, or communities to search
  • Any school/alumni connections to prioritize (e.g., "IIIT Hyderabad alumni")
  • Location preferences or constraints
  • How many candidates the user wants in this batch
About the ideal candidate profile:
  • Seniority level (e.g., "3-7 years", "senior", "staff+")
  • Must-have technical skills or domain expertise
  • Nice-to-have signals (open source contributions, publications, specific frameworks)
  • Any deal-breakers or filters (e.g., "no FAANG lifers", "must have startup experience")
Use the company and role details to research what makes this company compelling to candidates — check their website, recent funding, product launches, or press mentions via
crustdata_web_search
. This research informs the company blurb in Phase 4.
开始搜索前,从用户处提取或确认以下细节。请勿假设任何信息——若未提供,务必询问:
关于招聘公司(阶段4开发信所需):
  • 公司名称
  • 公司业务介绍(1-2句话)
  • 发展阶段/业务进展(例如:“A轮融资,已筹集1000万美元”或“500人规模的上市公司”)
  • 办公地点/远程政策
  • 发件人姓名与职位(用于邮件签名)
关于岗位与理想候选人:
  • 岗位名称(例如:“创始ML工程师”)
  • 岗位涉及的2-3个核心技术问题
  • 目标搜索的公司、研究实验室或社区
  • 优先考虑的学校/校友关联(例如:“印度国际信息技术学院海德拉巴分校校友”)
  • 地点偏好或限制
  • 本次批次需要的候选人数量
关于理想候选人画像:
  • 资历水平(例如:“3-7年经验”、“资深”、“Staff+级别”)
  • 必备技术技能或领域专业知识
  • 加分项信号(开源贡献、学术出版物、特定框架经验)
  • 排除项或筛选条件(例如:“不考虑长期在FAANG任职的人员”、“必须有创业公司经验”)
利用公司和岗位信息,研究该公司对候选人的吸引力——通过
crustdata_web_search
查看其官网、近期融资信息、产品发布或媒体报道。这些研究内容将用于阶段4的公司介绍文案。

Search by technical output, not job title

按技术成果搜索,而非岗位名称

Three signals matter more than years of experience or company prestige:
Proof of work — papers they authored, tools/repos they built with real usage, blog posts with technical depth, open-source contributions, demos or benchmarks they released.
Hunger — technical opinions posted publicly, Medium/Substack articles, conference talks, being cited by others, active GitHub beyond just commits.
Relevance — their specific work maps to the actual problems in the role, not just adjacent domains.
有三个信号比工作年限或公司声望更重要:
工作成果证明——他们发表的论文、开发的有实际用户的工具/仓库、有技术深度的博客文章、开源贡献、发布的演示或基准测试。
积极性——公开发布的技术观点、Medium/Substack文章、会议演讲、被他人引用、除提交记录外的活跃GitHub行为。
相关性——他们的具体工作与岗位实际问题匹配,而非仅相关领域。

Search sources and patterns

搜索来源与模式

Crustdata
people_search_db
for structured search:
json
{
  "params": {
    "filters": {
      "op": "and",
      "conditions": [
        {"column": "current_employers.title", "type": "(.)","value": "ML Engineer"},
        {"column": "current_employers.name", "type": "[.]", "value": "Company Name"}
      ]
    },
    "limit": 20
  }
}
Research papers — search arXiv, Google Scholar, Semantic Scholar for the core technical problem. Look at first/second authors, especially those not at top-5 labs.
GitHub — search repos by topic/keyword, look at meaningful contributors (not just maintainers).
Crustdata web search
"[technical problem]" site:arxiv.org
,
"[name]" "[company]" github
,
"[community]" alumni engineer ML
.
Crustdata
people_search_db
用于结构化搜索:
json
{
  "params": {
    "filters": {
      "op": "and",
      "conditions": [
        {"column": "current_employers.title", "type": "(.)","value": "ML Engineer"},
        {"column": "current_employers.name", "type": "[.]", "value": "Company Name"}
      ]
    },
    "limit": 20
  }
}
学术论文——在arXiv、Google Scholar、Semantic Scholar上搜索核心技术问题。关注第一/第二作者,尤其是非顶尖5大实验室的作者。
GitHub——按主题/关键词搜索仓库,寻找有意义的贡献者(而非仅维护者)。
Crustdata网页搜索——
"[技术问题]" site:arxiv.org
"[姓名]" "[公司]" github
"[社区]" alumni engineer ML

Output from Phase 1

阶段1输出

A list of candidates, each with: full name, current role, current company, any school/community connection, and one specific piece of work that makes them relevant (paper, repo, tool, post). This "proof of work" note is critical — it becomes the basis for the outreach email opener in Phase 4.

候选人列表,每位候选人包含:全名、当前岗位、当前公司、学校/社区关联(如有),以及一项使其符合要求的具体工作成果(论文、仓库、工具、文章)。这份“工作成果证明”至关重要——它将成为阶段4开发信开头的基础。

Phase 2: Verify LinkedIn URLs

阶段2:验证LinkedIn网址

Never guess or construct LinkedIn URLs. This is the single most common source of errors in outreach. Guessed URLs like
firstname-lastname
or
firstname-lastname-school
frequently 404 or point to the wrong person. Real LinkedIn slugs are auto-generated and look like
david-park-086833264
or
hongjunchoi92
— they are not predictable from a person's name.
切勿猜测或构造LinkedIn网址。这是开发信中最常见的错误来源。像
firstname-lastname
firstname-lastname-school
这样的猜测网址经常出现404错误或指向错误的人。真实的LinkedIn别名是自动生成的,格式类似
david-park-086833264
hongjunchoi92
——无法通过姓名预测。

Waterfall approach

瀑布式验证方法

Use this exact priority order. Stop as soon as you get a confident match.
Step 1:
crustdata_people_search_db
(always try this first)
json
{
  "params": {
    "filters": {
      "op": "and",
      "conditions": [
        {"column": "name", "type": "[.]", "value": "Person Name"},
        {"column": "current_employers.name", "type": "[.]", "value": "Company Name"}
      ]
    },
    "limit": 3
  }
}
Extract
flagship_profile_url
— this is the verified canonical LinkedIn URL.
Tips:
  • Company names have variations ("Google DeepMind" vs "DeepMind" vs "Google") — try shorter names first, then variations
  • If 0 results, try past employers or just the person's name with a broader filter
  • If multiple results, match by headline, location, or education
  • Run these in batches of 5-6 parallel calls for efficiency
Step 2:
crustdata_web_search
(fallback when Step 1 returns 0)
json
{
  "params": {
    "query": "Person Name Company distinctive-keyword",
    "site": "linkedin.com",
    "limit": 5
  }
}
Verify the result matches by checking the snippet for employer/role alignment. Don't just grab the first LinkedIn URL.
Step 3: Mark as unverified (if both fail)
Keep whatever URL exists but add a note: "LinkedIn URL unverified." Never fabricate a URL.
严格按照以下优先级顺序操作,一旦获得可靠匹配立即停止。
步骤1:
crustdata_people_search_db
(始终优先尝试此方法)
json
{
  "params": {
    "filters": {
      "op": "and",
      "conditions": [
        {"column": "name", "type": "[.]", "value": "Person Name"},
        {"column": "current_employers.name", "type": "[.]", "value": "Company Name"}
      ]
    },
    "limit": 3
  }
}
提取
flagship_profile_url
——这是经过验证的标准LinkedIn网址。
提示:
  • 公司名称存在变体(例如“Google DeepMind” vs “DeepMind” vs “Google”)——先尝试较短的名称,再尝试变体
  • 如果返回0条结果,尝试过往雇主或仅使用人名搭配更宽泛的筛选条件
  • 如果返回多条结果,通过职位头衔、地点或教育背景匹配
  • 为提高效率,批量进行5-6次并行调用
步骤2:
crustdata_web_search
(步骤1返回0条结果时的备选方案)
json
{
  "params": {
    "query": "Person Name Company distinctive-keyword",
    "site": "linkedin.com",
    "limit": 5
  }
}
通过查看搜索结果片段中的雇主/岗位信息是否匹配来验证结果,不要直接抓取第一个LinkedIn网址。
步骤3:标记为未验证(若上述两种方法均失败)
保留现有网址(如有)并添加注释:“LinkedIn网址未验证”。切勿编造网址。

Slug comparison

别名对比

When verifying an existing URL: extract the slug (part after
linkedin.com/in/
), strip trailing slashes, compare case-insensitively. Different slugs = wrong URL, use the
flagship_profile_url
instead.
验证现有网址时:提取
linkedin.com/in/
之后的别名部分,去除末尾斜杠,不区分大小写进行对比。别名不同则表示网址错误,改用
flagship_profile_url

Real mistakes this prevents

此方法避免的真实错误

These are actual errors from production outreach campaigns:
Guessed slugActual slugWhat happened
david-park-princeton
david-park-086833264
404 — made-up slug
vincent-chen-mit
vincent-chen-662a031b5
404 — school suffix doesn't work
benoit-rostykus
benoitrostykus
Wrong person — different profile
hongjun-choi
hongjunchoi92
Wrong person — different profile
abhay-gupta-cmu
gupta-abhay
Wrong person — completely different slug format
benedict-arockiaraj
benedictflorance
Wrong person — person uses a different name on LinkedIn
initeshmethani
nitesh-methani-7554b3121
Wrong person — typo in guessed slug
以下是实际招聘开发信活动中出现的错误:
猜测的别名真实别名后果
david-park-princeton
david-park-086833264
404错误——别名是编造的
vincent-chen-mit
vincent-chen-662a031b5
404错误——添加学校后缀无效
benoit-rostykus
benoitrostykus
指向错误的人——不同的个人资料
hongjun-choi
hongjunchoi92
指向错误的人——不同的个人资料
abhay-gupta-cmu
gupta-abhay
指向错误的人——别名格式完全不同
benedict-arockiaraj
benedictflorance
指向错误的人——此人在LinkedIn上使用不同的名字
initeshmethani
nitesh-methani-7554b3121
指向错误的人——猜测的别名存在拼写错误

URL format

网址格式

Always use
https://www.linkedin.com/in/{slug}
— never bare
linkedin.com/in/
without the protocol. Characters like
ü
need URL encoding (
%C3%BC
).

始终使用
https://www.linkedin.com/in/{slug}
——切勿使用不带协议的
linkedin.com/in/
。像
ü
这样的字符需要进行URL编码(
%C3%BC
)。

Phase 3: Find email addresses

阶段3:查找邮箱地址

Every candidate needs an email address for the Gmail draft. Use this priority chain.
每位候选人都需要邮箱地址来创建Gmail草稿。按照以下优先级顺序操作。

3A: Business email via Crustdata enrichment

3A:通过Crustdata补全获取企业邮箱

Batch up to 25 LinkedIn URLs per call:
crustdata_people_enrich:
  linkedin_profile_url: "url1,url2,url3..."
  fields: "name,business_email"
The
fields: "name,business_email"
parameter is critical — the default response does NOT include business email.
Expected hit rates: ~70-80% for professionals at known companies, ~40-50% for independent operators, ~20-30% for people between roles.
每次调用最多批量处理25个LinkedIn网址:
crustdata_people_enrich:
  linkedin_profile_url: "url1,url2,url3..."
  fields: "name,business_email"
fields: "name,business_email"
参数至关重要——默认响应不包含企业邮箱。
**预期获取成功率:**知名公司专业人士约70-80%,独立从业者约40-50%,待业人员约20-30%。

3B: Personal email via Crustdata person enrichment

3B:通过Crustdata个人信息补全获取个人邮箱

Crustdata's person enrich API can return personal email addresses directly. This is faster and more reliable than GitHub commit extraction, so always try this before falling back to GitHub.
Request personal emails by including
fields=personal_contact_info.personal_emails
:
crustdata_people_enrich:
  linkedin_profile_url: "url1,url2,url3..."
  fields: "personal_contact_info.personal_emails"
The response includes a
personal_contact_info
object with a
personal_emails
array containing the person's personal email addresses (Gmail, ProtonMail, etc.).
You can combine this with business email and phone numbers in a single call:
crustdata_people_enrich:
  linkedin_profile_url: "url1,url2,url3..."
  fields: "name,business_email,personal_contact_info.personal_emails,personal_contact_info.phone_numbers"
Credit cost: 2 credits per profile for
personal_contact_info.personal_emails
, 2 credits per profile for
personal_contact_info.phone_numbers
(on top of the base enrichment cost).
Batch limit: Up to 25 LinkedIn URLs per call, same as business email enrichment.
Recommended approach: Combine steps 3A and 3B into a single enrichment call by requesting both
business_email
and
personal_contact_info.personal_emails
fields together. This saves an API round-trip and gets both email types at once.
If personal email is found, prefer it over business email for cold outreach (higher response rate, less likely to be filtered by corporate spam). If this returns no personal email for a candidate, fall through to GitHub commit extraction below.
Crustdata的个人信息补全API可直接返回个人邮箱地址。这比从GitHub提交记录提取更快捷可靠,因此在尝试GitHub方法前务必先尝试此方法
通过包含
fields=personal_contact_info.personal_emails
请求个人邮箱:
crustdata_people_enrich:
  linkedin_profile_url: "url1,url2,url3..."
  fields: "personal_contact_info.personal_emails"
响应包含
personal_contact_info
对象,其中
personal_emails
数组包含该人的个人邮箱地址(Gmail、ProtonMail等)。
你可以在单次调用中同时获取企业邮箱、个人邮箱和电话号码:
crustdata_people_enrich:
  linkedin_profile_url: "url1,url2,url3..."
  fields: "name,business_email,personal_contact_info.personal_emails,personal_contact_info.phone_numbers"
**信用成本:**获取
personal_contact_info.personal_emails
每用户2个信用点,获取
personal_contact_info.phone_numbers
每用户2个信用点(基础补全成本之外)。
**批量限制:**每次调用最多25个LinkedIn网址,与企业邮箱补全相同。
**推荐方法:**通过同时请求
business_email
personal_contact_info.personal_emails
字段,将步骤3A和3B合并为单次补全调用。这样可以节省一次API往返,同时获取两种类型的邮箱。
如果找到个人邮箱,在陌生开发信中优先使用个人邮箱(回复率更高,被企业垃圾邮件过滤器拦截的可能性更低)。如果此方法未找到候选人的个人邮箱,则使用下文的GitHub提交记录提取方法。

3C: Personal email via GitHub commit history (fallback if 3B returns nothing)

3C:通过GitHub提交历史获取个人邮箱(3B失败时的备选方案)

This is the most powerful technique for technical candidates. Git records the author's email in every commit, and this metadata is accessible even when profile email privacy is enabled.
Step 1: Find GitHub username
  • Check Crustdata enrichment response (may include GitHub URL, but verify it's the right person)
  • Web search:
    "[Name] [Company] GitHub site:github.com"
  • Check their personal website or Twitter bio
Step 2: Verify the GitHub profile belongs to this person Confirm at least 2 of: bio mentions known company/role, profile name matches, repo topics align with known expertise, web search confirms the connection.
Step 3: Find oldest non-fork repo
crustdata_web_fetch:
  urls: ["https://api.github.com/users/{username}/repos?sort=created&direction=asc&per_page=5"]
Pick first repo where
"fork": false
. Older repos (pre-2019) are more likely to have real emails.
Step 4: Extract email from commits
Method A — Commits API:
crustdata_web_fetch:
  urls: ["https://api.github.com/repos/{owner}/{repo}/commits?per_page=1"]
Look in
[0].commit.author.email
.
Method B —
.patch
endpoint (bypasses privacy settings):
crustdata_web_fetch:
  urls: ["https://github.com/{owner}/{repo}/commit/{sha}.patch"]
Extract from
From: Name <email>
header line.
Step 5: Validate — discard
*@users.noreply.github.com
,
noreply@github.com
, and any email containing
noreply
.
这是获取技术候选人邮箱最有效的方法。Git会在每次提交中记录作者邮箱,即使个人资料邮箱隐私已启用,此元数据仍可访问。
步骤1:查找GitHub用户名
  • 查看Crustdata补全响应(可能包含GitHub网址,但需验证是否属于正确的人)
  • 网页搜索:
    "[姓名] [公司] GitHub site:github.com"
  • 查看其个人网站或Twitter简介
步骤2:验证GitHub个人资料是否属于该候选人 确认以下至少2项:简介提及已知公司/岗位、个人资料名称匹配、仓库主题与已知专业领域一致、网页搜索确认关联。
步骤3:查找最早的非复刻仓库
crustdata_web_fetch:
  urls: ["https://api.github.com/users/{username}/repos?sort=created&direction=asc&per_page=5"]
选择第一个
"fork": false
的仓库。较旧的仓库(2019年前)更可能包含真实邮箱。
步骤4:从提交记录中提取邮箱
方法A——提交记录API:
crustdata_web_fetch:
  urls: ["https://api.github.com/repos/{owner}/{repo}/commits?per_page=1"]
查看
[0].commit.author.email
字段。
方法B——
.patch
端点(绕过隐私设置):
crustdata_web_fetch:
  urls: ["https://github.com/{owner}/{repo}/commit/{sha}.patch"]
From: Name <email>
标题行提取邮箱。
步骤5:验证——丢弃
*@users.noreply.github.com
noreply@github.com
以及任何包含
noreply
的邮箱。

3D: Web search fallbacks

3D:网页搜索备选方案

When both Crustdata personal email enrichment and GitHub don't work, try these in order:
  1. GitHub issues/READMEs:
    "[name]" "@gmail.com" site:github.com
  2. Competitive programming:
    "[name]" site:codeforces.com
  3. Personal websites:
    "[name]" "[company]" email contact
  4. Conference speaker pages:
    "[name]" "[company]" speaker email
  5. Academic profiles:
    "[name]" site:scholar.google.com
当Crustdata个人邮箱补全和GitHub方法均失败时,按以下顺序尝试:
  1. GitHub issues/READMEs:
    "[姓名]" "@gmail.com" site:github.com
  2. 编程竞赛平台:
    "[姓名]" site:codeforces.com
  3. 个人网站:
    "[姓名]" "[公司]" email contact
  4. 会议演讲者页面:
    "[姓名]" "[公司]" speaker email
  5. 学术资料:
    "[姓名]" site:scholar.google.com

3E: Handle GitHub API rate limits

3E:处理GitHub API速率限制

GitHub allows 60 unauthenticated requests/hour. Workarounds:
  • Use
    .patch
    endpoints (don't count against REST API limits)
  • Fetch HTML commit pages and extract SHAs with regex, then use
    .patch
  • Batch
    crustdata_web_fetch
    calls with multiple URLs (up to 10 per call)
  • Process in waves — API-dependent steps first, then non-API methods while rate limit resets
GitHub允许每小时60次未认证请求。解决方法:
  • 使用
    .patch
    端点(不计入REST API速率限制)
  • 获取HTML提交页面并通过正则表达式提取SHA,再使用
    .patch
    端点
  • 批量调用
    crustdata_web_fetch
    ,每次调用包含多个网址(最多10个)
  • 分阶段处理——先处理依赖API的步骤,在速率限制重置期间处理非API方法

Email priority

邮箱优先级

When you have multiple emails for a candidate, prefer in this order:
  1. Personal Gmail/ProtonMail — highest response rate for cold outreach
  2. University email — if they're still in academia
  3. Business email — last resort for cold outreach (often filtered by corporate spam)

当候选人有多个邮箱时,按以下优先级选择:
  1. 个人Gmail/ProtonMail——陌生开发信回复率最高
  2. 大学邮箱——若候选人仍在学术界
  3. 企业邮箱——陌生开发信的最后选择(常被企业垃圾邮件过滤器拦截)

Phase 4: Write personalized outreach emails

阶段4:撰写个性化开发信

The one rule that matters

最重要的规则

Praise specific work, not job titles or implied capabilities.
"Leading applied science at Mistral means you know how to take ML from research to production" reads as "I saw your LinkedIn title and ran it through a template." Instead, find one thing they actually made and say something honest about it.
赞扬具体的工作成果,而非岗位头衔或隐含能力。
“在Mistral领导应用科学工作意味着你知道如何将ML从研究推向生产”听起来像是“我看到你的LinkedIn头衔,然后套用了模板”。相反,找到他们实际完成的一项工作,并给出真诚的评价。

Email structure

邮件结构

Every email follows this exact template:
Hi {first_name},

[1-sentence opener: say what impressed you — casual, no analysis]

[1-sentence pitch: role + company + location]

[3-line company blurb]

{sender_name},
{sender_title}
Short. No headers, no bullet points, no multi-paragraph explanations. Ask the user for their name, title, company name, and a 2-3 line company blurb if not already provided.
每封邮件严格遵循以下模板:
Hi {first_name},

[1句话开头:说明你对什么印象深刻——语气随意,无需分析]

[1句话推介:岗位 + 公司 + 地点]

[3行公司介绍]

{sender_name},
{sender_title}
简短。无标题、无项目符号、无多段落解释。若用户未提供姓名、职位、公司名称和2-3行公司介绍,需向用户索要。

Writing the opener

撰写开头

Use the "proof of work" note from Phase 1 — the specific paper, repo, tool, or post you identified during candidate discovery.
Good opener patterns:
  • "Read your work on [specific paper/project] — [genuine reaction]."
  • "Your [specific tool/repo] is [honest assessment with concrete detail]."
  • "The path from [specific journey point A] to [B] is impressive."
Bad opener patterns (never use these):
  • [their work] + [which is relevant because we also do X] — turns compliment into pitch
  • [job title at company] + [therefore X skill] — title-based, not work-based
  • [their research] + [is exactly the kind of thinking we need] — evaluates them for them
  • Long parenthetical explanations — breaks casual flow
使用阶段1中的“工作成果证明”——候选人发现阶段确定的具体论文、仓库、工具或文章。
优秀开头示例:
  • “拜读了你关于[具体论文/项目]的研究——[真实感受]。”
  • “你开发的[具体工具/仓库]非常[带有具体细节的真诚评价]。”
  • “从[具体历程节点A]到[B]的路径令人印象深刻。”
糟糕开头示例(切勿使用):
  • [他们的工作] + [与我们的业务相关,因为我们也做X]——将赞美变成了推介
  • [公司岗位头衔] + [因此具备X技能]——基于头衔,而非工作成果
  • [他们的研究] + [正是我们需要的思维方式]——对他们进行评判
  • 冗长的括号解释——破坏随意的语气

The pitch line

推介语

"We're hiring a [role] at [Company] in [location]. Would you be interested?"
Keep it direct. The opener already did the work.
“我们正在[地点]的[公司]招聘[岗位]。你是否感兴趣?”
保持直接。开头部分已经完成了铺垫。

The company blurb

公司介绍

Ask the user for a 2-3 line company blurb if they haven't provided one. It should highlight traction, mission, and why now. Vary phrasing slightly across a batch to avoid identical emails.
若用户未提供,向用户索要2-3行公司介绍。内容应突出业务进展、使命和当前机遇。同一批次的邮件需略微调整措辞,避免完全相同。

Subject line

邮件主题

Default:
"[Role] @ [Company]"

默认:
"[岗位] @ [公司]"

Phase 5: Create Gmail drafts and log to tracker

阶段5:创建Gmail草稿并记录到跟踪表

Create Gmail drafts

创建Gmail草稿

For each candidate with a verified email, use
gmail_create_draft
:
  • to
    : candidate's email address
  • subject
    : the subject line
  • body
    : the full email (opener + pitch + blurb + signature)
This is the terminal step. Gmail's MCP creates drafts but cannot send — the user reviews and clicks send manually.
对于每个有验证邮箱的候选人,使用
gmail_create_draft
  • to
    : 候选人邮箱地址
  • subject
    : 邮件主题
  • body
    : 完整邮件内容(开头 + 推介语 + 公司介绍 + 签名)
这是最终步骤。Gmail MCP仅创建草稿,不发送邮件——用户审核后手动点击发送。

Log to the outreach tracker

记录到开发信跟踪表

After creating each draft, append a row to the role-specific CSV tracker:
/sessions/{session}/mnt/outputs/outreach_tracker_{role_slug}.csv
Where
role_slug
is lowercase-hyphenated (e.g., "Founding ML Engineer" →
founding_ml_engineer
).
CSV headers:
date,role_hiring_for,candidate_name,current_role,company,school_connection,linkedin_url,email,channel,subject,message_opener,status,notes,message_body
python
import csv, datetime, os

tracker_path = "outreach_tracker_{role_slug}.csv"
fields = ["date","role_hiring_for","candidate_name","current_role","company",
          "school_connection","linkedin_url","email","channel","subject",
          "message_opener","status","notes","message_body"]

row = {
    "date": datetime.date.today().isoformat(),
    "role_hiring_for": "<role name>",
    "candidate_name": "<full name>",
    "current_role": "<title>",
    "company": "<company>",
    "school_connection": "<school or empty>",
    "linkedin_url": "<verified linkedin url>",
    "email": "<email address>",
    "channel": "email",
    "subject": "<subject line>",
    "message_opener": "<first sentence of email body>",
    "status": "drafted",
    "notes": "<email type + source: e.g. Personal Gmail via GitHub commit>",
    "message_body": "<full email body>"
}

file_exists = os.path.isfile(tracker_path)
with open(tracker_path, "a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fields)
    if not file_exists:
        writer.writeheader()
    writer.writerow(row)
Status values:
drafted
(Gmail draft created),
sent
(user clicked send),
replied
,
no_contact
(no email found).
Channel values:
email
创建每个草稿后,将一行记录追加到岗位专属CSV跟踪表:
/sessions/{session}/mnt/outputs/outreach_tracker_{role_slug}.csv
其中
role_slug
为小写连字符格式(例如“Founding ML Engineer” →
founding_ml_engineer
)。
CSV表头:
date,role_hiring_for,candidate_name,current_role,company,school_connection,linkedin_url,email,channel,subject,message_opener,status,notes,message_body
python
import csv, datetime, os

tracker_path = "outreach_tracker_{role_slug}.csv"
fields = ["date","role_hiring_for","candidate_name","current_role","company",
          "school_connection","linkedin_url","email","channel","subject",
          "message_opener","status","notes","message_body"]

row = {
    "date": datetime.date.today().isoformat(),
    "role_hiring_for": "<role name>",
    "candidate_name": "<full name>",
    "current_role": "<title>",
    "company": "<company>",
    "school_connection": "<school or empty>",
    "linkedin_url": "<verified linkedin url>",
    "email": "<email address>",
    "channel": "email",
    "subject": "<subject line>",
    "message_opener": "<first sentence of email body>",
    "status": "drafted",
    "notes": "<email type + source: e.g. Personal Gmail via GitHub commit>",
    "message_body": "<full email body>"
}

file_exists = os.path.isfile(tracker_path)
with open(tracker_path, "a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fields)
    if not file_exists:
        writer.writeheader()
    writer.writerow(row)
状态值:
drafted
(已创建Gmail草稿)、
sent
(用户已点击发送)、
replied
(已回复)、
no_contact
(未找到邮箱)。
渠道值:
email

Candidates without email

无邮箱的候选人

If no email was found after the full enrichment chain, log them to the tracker with
status: "no_contact"
and a note explaining what was tried. The user can decide whether to pursue other channels manually.

若经过完整的补全流程仍未找到邮箱,将其记录到跟踪表中,
status: "no_contact"
并添加注释说明尝试过的方法。用户可自行决定是否通过其他渠道联系。

Batch workflow for efficiency

高效批量工作流

When processing many candidates (>5), use this sequence to minimize time:
  1. Search and collect candidates (Phase 1) — build the full list first
  2. Batch LinkedIn verification (Phase 2) — 5-6 parallel
    people_search_db
    calls; web search fallback for failures
  3. Batch email enrichment (Phase 3A + 3B) — up to 25 LinkedIn URLs per call with
    fields: "name,business_email,personal_contact_info.personal_emails"
    to get both business and personal emails in one round-trip
  4. Triage for GitHub (Phase 3C) — identify candidates still missing emails who are engineers likely to have GitHub profiles; prioritize them for commit email extraction
  5. Batch GitHub lookups (Phase 3C) —
    crustdata_web_fetch
    with multiple GitHub URLs per call (up to 10)
  6. Web search fallbacks (Phase 3D) — for candidates where Crustdata enrichment and GitHub both failed
  7. Write all email copy (Phase 4) — draft all openers in one pass, using the proof-of-work notes from Phase 1
  8. Create all Gmail drafts (Phase 5) — create drafts and log to tracker
处理大量候选人(>5人)时,使用以下顺序以节省时间:
  1. 搜索并收集候选人(阶段1)——先构建完整的候选人列表
  2. 批量验证LinkedIn网址(阶段2)——并行进行5-6次
    people_search_db
    调用;对失败的候选人使用网页搜索备选方案
  3. 批量邮箱补全(阶段3A + 3B)——每次调用最多25个LinkedIn网址,使用
    fields: "name,business_email,personal_contact_info.personal_emails"
    在一次往返中同时获取企业和个人邮箱
  4. 筛选需使用GitHub的候选人(阶段3C)——找出仍无邮箱且可能拥有GitHub个人资料的工程师候选人;优先为他们提取提交记录中的邮箱
  5. 批量GitHub查询(阶段3C)——
    crustdata_web_fetch
    每次调用包含多个GitHub网址(最多10个)
  6. 网页搜索备选方案(阶段3D)——针对Crustdata补全和GitHub方法均失败的候选人
  7. 撰写所有邮件文案(阶段4)——一次性撰写所有开头,使用阶段1中的工作成果证明记录
  8. 创建所有Gmail草稿(阶段5)——创建草稿并记录到跟踪表

Handling large API responses

处理大型API响应

Crustdata enrichment and people_search_db calls can return results that exceed token limits and get saved to files. When this happens:
python
import json
with open(filepath) as f:
    data = json.load(f)
inner = json.loads(data[0]['text'])
for p in inner.get('profiles', []):
    print(p.get('name'), '|', p.get('flagship_profile_url'))
Always parse saved results with Python rather than trying to process them inline.

Crustdata补全和
people_search_db
调用的结果可能超出令牌限制并保存到文件中。遇到这种情况时:
python
import json
with open(filepath) as f:
    data = json.load(f)
inner = json.loads(data[0]['text'])
for p in inner.get('profiles', []):
    print(p.get('name'), '|', p.get('flagship_profile_url'))
始终使用Python解析保存的结果,而非尝试在线处理。

Tool dependencies

工具依赖

This skill requires:
  • Crustdata MCP server (mcp.crustdata.com/mcp): provides
    crustdata_people_search_db
    ,
    crustdata_people_enrich
    (with
    fields
    supporting
    business_email
    ,
    personal_contact_info.personal_emails
    ,
    personal_contact_info.phone_numbers
    ),
    crustdata_company_enrich
    ,
    crustdata_web_search
    ,
    crustdata_web_fetch
  • Gmail MCP:
    gmail_create_draft
  • Python (with
    openpyxl
    for spreadsheet I/O,
    csv
    for tracker)
  • Web search (Crustdata) for fallback email discovery

此技能需要:
  • Crustdata MCP服务器 (mcp.crustdata.com/mcp):提供
    crustdata_people_search_db
    crustdata_people_enrich
    (支持
    fields
    参数获取
    business_email
    personal_contact_info.personal_emails
    personal_contact_info.phone_numbers
    )、
    crustdata_company_enrich
    crustdata_web_search
    crustdata_web_fetch
  • Gmail MCP
    gmail_create_draft
  • Python(使用
    openpyxl
    处理电子表格I/O,
    csv
    处理跟踪表)
  • 网页搜索(Crustdata)用于备选邮箱发现

Founder exclusion rule

创始人排除规则

Do not reach out to current founders, co-founders, CEOs, or CTOs — people who are actively running their own company. They are unlikely to leave, and contacting them wastes a slot and can feel tone-deaf.
请勿联系现任创始人、联合创始人、CEO或CTO——即正在运营自己公司的人员。他们不太可能离职,联系他们会浪费名额且可能显得不合时宜。

How to detect founders

如何识别创始人

Check the candidate's current title during Phase 1. Flag anyone whose title contains: Founder, Co-Founder, Co-founder, CEO, CTO, or "Chief" with "Officer" (e.g., Chief Technology Officer).
在阶段1中检查候选人的当前职位。标记任何职位头衔包含以下内容的人员:Founder、Co-Founder、Co-founder、CEO、CTO,或“Chief”搭配“Officer”(例如Chief Technology Officer)。

The shrinking-company exception

公司收缩例外情况

The only case where it's worth reaching out to a current founder is when their company is visibly failing. All three conditions must be true:
  1. Headcount is declining — use
    crustdata_company_enrich
    with the company's domain:
    crustdata_company_enrich:
      company_domain: "example.com"
      fields: "company_name,headcount,web_traffic,founders"
    Check
    headcount_latest.linkedin_headcount_total_growth_percent
    — look for negative month-over-month and quarter-over-quarter growth.
  2. Website traffic is declining — in the same enrichment response, check
    web_traffic
    for downward trends in monthly visitors.
  3. People are leaving faster than the company can sustain — the number of ex-employees who left in the last 3 months is greater than the current headcount. This signals a company that's actively losing people, not just flat.
If all three conditions are met, the founder may be open to a new opportunity. If any condition is NOT met (e.g., the company is small but growing), skip the candidate and mark them as
status: "skipped"
in the tracker with a note like
"Excluded — active founder (company not shrinking)"
.
唯一值得联系现任创始人的情况是其公司明显陷入困境。必须同时满足以下三个条件:
  1. 员工数量下降——使用
    crustdata_company_enrich
    查询公司域名:
    crustdata_company_enrich:
      company_domain: "example.com"
      fields: "company_name,headcount,web_traffic,founders"
    查看
    headcount_latest.linkedin_headcount_total_growth_percent
    ——寻找月度和季度同比增长为负的情况。
  2. 网站流量下降——在同一补全响应中,查看
    web_traffic
    的月度访问量下降趋势。
  3. 人员流失速度超过公司承受能力——过去3个月离职的员工数量超过当前员工总数。这表明公司正在主动流失人员,而非仅仅处于停滞状态。
如果三个条件均满足,创始人可能会对新机会感兴趣。如果任何一个条件不满足(例如公司规模小但正在增长),则跳过该候选人,并在跟踪表中标记
status: "skipped"
,注释如
"已排除——现任创始人(公司未收缩)"

Handling the enrichment response

处理补全响应

Company enrichment responses can exceed token limits. Parse with Python:
python
import json
with open(filepath) as f:
    data = json.load(f)
inner = json.loads(data[0]['text'])
companies = inner['companies']
for c in companies:
    hc = c.get('headcount_latest', c.get('headcount', {}))
    linkedin_hc = hc.get('linkedin_headcount', '?')
    growth = hc.get('linkedin_headcount_total_growth_percent', {})
    mom = growth.get('1_month', 0)
    qoq = growth.get('3_months', 0)
    print(f"{c['company_name']}: {linkedin_hc} employees, MoM: {mom}%, QoQ: {qoq}%")
公司补全响应可能超出令牌限制。使用Python解析:
python
import json
with open(filepath) as f:
    data = json.load(f)
inner = json.loads(data[0]['text'])
companies = inner['companies']
for c in companies:
    hc = c.get('headcount_latest', c.get('headcount', {}))
    linkedin_hc = hc.get('linkedin_headcount', '?')
    growth = hc.get('linkedin_headcount_total_growth_percent', {})
    mom = growth.get('1_month', 0)
    qoq = growth.get('3_months', 0)
    print(f"{c['company_name']}: {linkedin_hc} employees, MoM: {mom}%, QoQ: {qoq}%")

Real examples

真实案例

CandidateCompanyWhat happened
Di Jin (Co-Founder)Eigen AI (12 employees, -7.7% MoM)Slightly declining but not enough ex-employees leaving → skipped
Sai Surbehera (Co-Founder/CTO)Lapis Labs (5 employees, +25% MoM)Company growing → skipped
Aayush Anand (Co-Founder)Level.game (9 employees, stable)Company active → skipped

候选人公司处理结果
Di Jin(联合创始人)Eigen AI(12名员工,月度增长-7.7%)略有下降但人员流失未达阈值 → 已跳过
Sai Surbehera(联合创始人/CTO)Lapis Labs(5名员工,月度增长+25%)公司正在增长 → 已跳过
Aayush Anand(联合创始人)Level.game(9名员工,稳定)公司运营正常 → 已跳过

What NOT to do

禁止事项

  • Never guess LinkedIn URLs — always verify through Crustdata. Guessed URLs caused 12 errors in a single 91-person campaign.
  • Never send emails — Gmail MCP creates drafts only. The user reviews and sends manually.
  • Never use title-based openers — always reference specific work the candidate has done.
  • Never skip email validation — discard noreply addresses, verify GitHub profiles belong to the right person.
  • Never write the same opener twice — each candidate gets a unique opener based on their specific work.
  • Never reach out to active founders — skip current founders/co-founders/CEOs/CTOs unless their company is visibly failing (declining headcount, declining traffic, and people leaving faster than the company can sustain). See "Founder exclusion rule" section above.
  • 切勿猜测LinkedIn网址——始终通过Crustdata验证。猜测的网址在一次91人的招聘活动中导致了12个错误。
  • 切勿发送邮件——Gmail MCP仅创建草稿。用户审核后手动发送。
  • 切勿使用基于头衔的开头——始终引用候选人完成的具体工作成果。
  • 切勿跳过邮箱验证——丢弃noreply地址,验证GitHub个人资料是否属于正确的人。
  • 切勿重复使用相同的开头——每位候选人的开头均需基于其具体工作成果,保持独特性。
  • 切勿联系现任创始人——跳过现任创始人/联合创始人/CEO/CTO,除非其公司明显陷入困境(员工数量下降、网站流量下降、人员流失速度超过公司承受能力)。详见上文“创始人排除规则”部分。