演示
生成一组包含约 10 个丰富联系人及个性化消息示例的演示线索列表,由 webhook 提示触发。
执行前请阅读 ~/.claude/skills/gtm-pipeline/_shared/conventions.md。
何时使用
- Webhook 触发:用户提交一个描述其目标受众的免费演示表单
- 目标:证明 AI 代理能够使用真实线索编写真实、非千篇一律的外联消息
- 范围:约 10 个联系人,包含 LinkedIn 和邮箱丰富数据,2–4 个消息示例
演示限制
- 不丰富电话号码 — 仅限邮箱
- 约 10 个联系人(请求 10–15 个,预计丰富后会有所减少)
- 消息生成是可选的,但推荐
- 信号搜索为自愿加入 — 默认关闭。通过
--with-signals标志或用户明确请求启用。请参见步骤 5.5。 - 成本门槛: 在首次调用付费提供商之前,说明预期支出(按人员来源节奏的查找搜索大多是免费的;约 10-15 个邮箱丰富积分;如果启用信号搜索,费用 < $0.50)。交互模式:先与用户确认。无头模式:在
run_log.md中记录估算并继续,切勿阻塞。
步骤 1 — 解析提示并建立 ICP
Webhook 提示描述了用户的目标受众。提取以下信息:
必须包含:
- 你销售/提供什么?
- 你的理想客户是谁?(行业、职位、公司规模、地点)
- 你的价值主张是什么?
- 语气是什么?(正式 vs. 随意,如有示例请提供)
- 这是用于招聘还是向客户销售?
如果提示中未包含,则进行推断(不要停滞不前):
- 目标职位、地点、公司规模/类型
在解释受众之前,先自动解析请求者(修复反复出现的“他们到底卖什么 / 为什么他们想要这个受众”的差距):
- 解析请求者自己的域名(例如
[email protected]→acme.de),抓取/丰富其信息,并确定他们销售什么。对于提供多种产品的公司:确认演示是针对哪个产品线 — 提示中所述的产品可能与实际产品不符。 - 确定与目标受众的关系 — 目标术语(例如“呼叫中心”)通常是指他们向其销售的细分市场,而不是他们自己所属的类别。分类:销售给 / 从...购买 / 收购 / 合作 / 招聘。人物关键词由此衍生,而非猜测。
交互模式 vs. 已部署(无头模式):
- 交互式: 如果在自动解析后,某个必须包含的信息确实存在歧义,则提出一个简洁的澄清问题。否则继续。
- 已部署/无头模式(通过 webhook 调用
claude -p调用 — 请参阅部署):切勿因问题而阻塞。 根据提示和请求者域研究推断每个字段,将假设记录在context/icp.md的“假设”标题下,并端到端地继续执行。
在 ICP 足够清晰以构建有意义的筛选条件之前,不要进行搜索。
将 ICP 保存到: {client-slug}-gtm/context/icp.md(包括产品、关系分类以及任何无头模式下的假设)。
步骤 2 — 创建工作目录
按照 conventions.md 中的定义创建 {client-slug}-gtm/ 目录结构。将 ICP 定义写入 context/icp.md。
步骤 3 — 人员搜索(10 个联系人)
使用 people-search 技能查找约 10–15 个联系人。
演示的提供商选择: 遵循 conventions.md 中的查找节奏 → 人员来源节奏 — FullEnrich Finder → BetterContact → Pipe0 → Amplemarket/Crustdata(最后手段),每个来源最多尝试 2 次,对于中小企业/所有者主导/非英语细分市场,首选 FullEnrich (FE)。FE 和 BC 都直接返回 LinkedIn URL(邮箱丰富所需)。如果没有公司列表(基于人物的提示),则使用 Parallel FindAll 或 BC Search。对于通过目录/抓取获取的公司列表,按公司名称 + 地点进行搜索,绝不能按精确域名搜索(约定 #11)。
要收集的关键字段:
full_name, first_name, last_name,
job_title, company_name, company_domain,
linkedin_profile_url, location
遵循人员搜索的执行协议:沙盒 → 测试 → 审查 → 运行。
步骤 4 — 联系人筛选(ICP 排名)
对找到的 10–15 个联系人运行 contact-filter。即使是小批量,ICP 排名也很有益——它确保丰富步骤集中在最合适的联系人上。
- 应用职位层级、行业层级、地点层级和公司规模分类
- 拒绝硬性非 ICP 联系人
- 按优先级对通过的联系人进行排名
- 输出:
csv/intermediate/contacts_filtered.csv
对于演示:使用宽松的硬性拒绝阈值(允许层级 1–5 通过),优先考虑排名而非筛选。
步骤 5 — 人员丰富(仅限邮箱)
对筛选后的联系人运行 people-enrichment。演示模式:仅限邮箱,无电话。
推荐流程:
- FullEnrich v2(邮箱)— 所有联系人
- Pipe0 waterfall — 仅针对 FE 遗漏的联系人
用于消息个性化的额外丰富(如可用):
- LinkedIn 标题和摘要(通过 PhantomBuster 抓取 LinkedIn)
- 最近的 LinkedIn 帖子(每个联系人 2–3 条)— 显著提高消息质量
用于消息生成的最小可行字段:
name, job_title, company_name, linkedin_profile_url,
headline (optional), summary (optional), recent_posts (optional)
低产出: 如果通过筛选和丰富后幸存下来的联系人少于约 8 个,则在继续生成消息之前,再执行一次查找(按人员来源节奏的下一个来源)。
步骤 5.5 — 信号搜索(可选)
默认:关闭。 在以下情况下启用:
- 用户明确要求基于信号锚定的消息
- Webhook 提示提到购买触发因素(融资、招聘、转型、最新新闻)
- 客户的产品依赖于时机信号才有意义(例如“我们帮助后 A 轮公司扩展运营”)
- 向演示调用传递了
--with-signals标志
成本说明: 每个独特公司增加约 $0.01–0.05(网络搜索 + 评分)。对于一个 10 个联系人的演示,通常有 5–10 个独特公司,因此费用 < $0.50。
信号搜索所需的输入
信号搜索需要三个上下文文件:
context/icp.md— 已在步骤 1 中收集context/offering.md— 步骤 1 中的价值主张块(如果尚未保存,请立即写入)context/signal_criteria.md— 与产品相关的信号类型列表(项目符号格式)
如果 signal_criteria.md 不存在,请询问用户:“一家公司现在正在做什么,表明他们需要你提供的东西?” — 捕获 5–10 个项目符号并保存。
运行
# 从丰富联系人中提取独特公司列表
python3 -c "
import csv, sys
seen = set()
with open('csv/output/contacts_enriched.csv') as f, open('csv/input/companies_raw.csv', 'w', newline='') as out:
reader = csv.DictReader(f)
writer = csv.DictWriter(out, fieldnames=['company_name', 'company_domain', 'company_website'])
writer.writeheader()
for row in reader:
c = row.get('company_name') or row.get('company') or ''
if c and c not in seen:
seen.add(c)
writer.writerow({
'company_name': c,
'company_domain': row.get('company_domain') or '',
'company_website': row.get('company_website') or row.get('website') or '',
})
"
# 对独特公司运行信号搜索
source \"$HOME/.claude/skills/gtm-pipeline/_shared/resolve_env.sh\" && \
export $(grep -E '^(PARALLEL_API_KEY|OPENROUTER_API_KEY|FIRECRAWL_API_KEY|GEMINI_API_KEY)=' \"$GTM_ENV_PATH\" | xargs) && \
python3 ~/.claude/skills/gtm-signal-search/signal_search.py \
--client-dir {client-slug}-gtm
resolve_env.sh 的 source 行确保即使在新的 shell 中也设置了 $GTM_ENV_PATH(请参阅 signal-search SKILL.md / 约定)。对于演示,请将 Firecrawl 和 Parallel 丰富关闭— 对于一个约 10 个联系人的线索列表,网络搜索 + 评分就足够了。仅当站点内容(职业页面、博客)是主要信号源时才启用 Firecrawl;如果此机器仅通过 MCP 拥有 Firecrawl(无 FIRECRAWL_API_KEY),请使用 signal-search 技能中记录的 --firecrawl-pages-dir 路径。
将信号合并回联系人
import csv, json
signals = {}
with open('csv/intermediate/signals.csv') as f:
for row in csv.DictReader(f):
signals[row['company_name']] = {
'overall_score': row.get('overallScore', ''),
'scored_signals': row.get('scoredSignals', ''),
'overall_summary': row.get('overallSummary', ''),
}
rows_out = []
with open('csv/output/contacts_enriched.csv') as f:
for row in csv.DictReader(f):
s = signals.get(row.get('company_name') or row.get('company') or '', {})
row['company_overall_score'] = s.get('overall_score', '')
row['company_scored_signals'] = s.get('scored_signals', '')
row['company_overall_summary'] = s.get('overall_summary', '')
rows_out.append(row)
with open('csv/output/contacts_enriched.csv', 'w', newline='') as f:
w = csv.DictWriter(f, fieldnames=list(rows_out[0].keys()))
w.writeheader()
w.writerows(rows_out)
然后,步骤 6 中的消息生成步骤将为每个联系人提供 company_overall_summary 和 company_scored_signals — 使用评分最高的信号作为消息钩子。
步骤 6 — 生成消息示例
在承诺批量生成之前,先生成 2–4 个样本消息。
消息结构
每条消息必须遵循:钩子 → 桥梁 → 产品 → 软性行动号召
| 部分 | 目的 | 长度 |
|---|---|---|
| 钩子 | 引用此人的特定内容(帖子、职业变动、公司信号) | 1 句话 |
| 桥梁 | 将他们的处境与你的产品联系起来 | 1 句话 |
| 产品 | 你提供什么,清晰陈述 | 1 句话 |
| 行动号召 | 软性请求 — 不是“让我们安排一次通话” | 1 句话 |
总计:320–450 个字符。 问候语后不留空行。段落之间用单个换行符分隔。
质量规则
必须包含:
- 具体的钩子(帖子引用 OR 职业洞察 OR 有评分的购买信号 — 不能是通用的)
- 清晰的价值主张
- 自然、对话式的语气
- 软性行动号召
必须避免:
- 重复他们已经知道的个人资料信息(“你在 Y 公司担任 X 职位”)
- 通用观察(“令人印象深刻的背景”、“我注意到你从事 [行业]”)
- 企业行话或流行词
- 咄咄逼人的行动号召(“让我们本周安排一次通话”)
如果步骤 5.5 已运行: 对于每个联系人,优先使用 company_scored_signals 中评分最高的信号作为钩子,而不是通用的 LinkedIn 帖子引用。一个评分 >= 70 的、基于真实近期新闻的信号是演示能产生的最强钩子。
生成过程
- 编写特定于客户的系统提示(保存到
prompts/message_prompt.md) - 生成 2–4 个样本 — 包括有和没有 LinkedIn 帖子的联系人
- 对照上述质量检查清单进行审查
- 如果发现问题,优化系统提示并重新生成
- 仅在质量通过后才进行批量生成
系统提示模板(关键部分)
- 客户上下文:他们销售什么,针对谁,他们的价值主张,语气
- 禁止规则:不重复个人资料,不进行通用奉承
- 消息结构:钩子 → 桥梁 → 产品 → 行动号召
- 钩子示例:有帖子/无帖子
- 字符限制:320–450
步骤 7 — 清理与输出
步骤 7a — 清理(强制、确定性的、不使用 LLM)。 csv/intermediate/ 保留所有字段。
在构建任何面向线索的内容之前,运行共享的清理器,以便重复的手动清理(提供商标签、空列、错误的邮箱、过时的信号)得以一致地应用。