デモ
Webhookプロンプトをトリガーとして、約10件のリッチなコンタクトとパーソナライズされたメッセージ例を含むデモ用リードリストを生成します。
実行前に ~/.claude/skills/gtm-pipeline/_shared/conventions.md を読むこと。
いつ使用するか
- Webhookトリガー:ユーザーがターゲットオーディエンスを記述した無料デモフォームを送信したとき
- 目標:AIエージェントが実際のリードを使用して、本物で非ジェネリックなアウトリーチを作成できることを証明する
- 範囲:約10件のコンタクト、LinkedIn + メールでエンリッチ、2~4件のメッセージ例
デモの制限
- 電話番号のエンリッチメントはなし — メールのみ
- 約10件のコンタクト(10~15件をリクエストし、エンリッチメントによる減少を想定)
- メッセージ生成はオプションですが推奨
- シグナル検索はオプトイン — デフォルトではオフ。
--with-signalsフラグまたは明示的なユーザー要求で有効化。手順5.5を参照。 - コストゲート:最初の有料プロバイダー呼び出しの前に、予想される支出を明示する(People-Source Cadenceごとのファインダー検索はほとんど無料、約10~15件のメールエンリッチメントクレジット、シグナル(有効時)は0.50ドル未満)。インタラクティブモード:最初にユーザーに確認する。ヘッドレスモード:
run_log.mdに見積もりを記録し、ブロックせずに進める。
ステップ1 — プロンプトの解析とICPの確立
Webhookプロンプトはユーザーのターゲットオーディエンスを記述しています。以下を抽出します。
必須事項:
- 何を販売/提供しているか?
- 理想的な顧客は誰か?(業界、役職、企業規模、場所)
- バリュープロポジションは何か?
- トーンは?(フォーマル vs カジュアル、可能であれば例)
- これは採用目的か、それとも顧客への販売目的か?
プロンプトにない場合、推測する(停止しない):
- ターゲットとなる役職、場所、企業規模/タイプ
オーディエンスを解釈する前に、リクエスタを自動解決する(「彼らは何を売っているのか/なぜこのオーディエンスが必要なのか」という繰り返しのギャップを修正):
- リクエスタのメールから自身のドメインを解決する(例:
[email protected]→acme.de)、スクレイピング/エンリッチして、彼らが何を販売しているかを確立する。複数の製品を提供する企業の場合:デモがどの製品ライン向けかを確認する — プロンプトに記載された製品は実際のものと一致しない可能性がある。 - ターゲットオーディエンスとの関係を決定する — ターゲット用語(例:「コールセンター」)は通常、彼らが販売するセグメントであり、彼ら自身のことではない。分類:販売先 / 購入元 / 買収先 / パートナー / 採用先。ペルソナキーワードはこれから導出され、推測からではない。
インタラクティブ vs デプロイ(ヘッドレス):
- インタラクティブ: 自動解決後も必須事項が本当に曖昧な場合、1つの簡潔な明確化質問をする。それ以外は進める。
- デプロイ/ヘッドレス(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のファインダーケイデンスに従う → People-Source Cadence — FullEnrich Finder → BetterContact → Pipe0 → Amplemarket/Crustdata(最終手段)、ソースあたり最大2回の試行、SME/オーナー主導/非英語セグメントにはFEを優先。FEとBCの両方がLinkedIn URLを直接返します(メールエンリッチメントに必要)。企業リストがない場合(ペルソナベースのプロンプト)、Parallel FindAll または BC Search を使用します。ディレクトリ/スクレイピングソースの企業リストの場合、企業の名前 + 場所で検索し、正確なドメインでは決して検索しない(conventions #11)。
収集する主要フィールド:
full_name, first_name, last_name,
job_title, company_name, company_domain,
linkedin_profile_url, location
people-searchの実行プロトコルに従う:サンドボックス → テスト → レビュー → 実行。
ステップ4 — コンタクトフィルタ(ICPランキング)
見つかった10~15件のコンタクトに対して contact-filter を実行します。小規模なバッチでもICPランキングは有益です — エンリッチメントステップが最適なコンタクトに焦点を当てることを保証します。
- ジョブティア、業界ティア、ロケーションティア、企業規模分類を適用
- ハードな非ICPコンタクトを拒否
- 合格したコンタクトを優先度でランク付け
- 出力:
csv/intermediate/contacts_filtered.csv
デモの場合:リラックスしたハード拒否しきい値を使用(ティア1~5を許可)、フィルタリングよりもランキングを優先。
ステップ5 — 人材エンリッチメント(メールのみ)
フィルタリングされたコンタクトに対して people-enrichment を実行します。デモモード:メールのみ、電話番号なし。
推奨フロー:
- FullEnrich v2(メール) — すべてのコンタクト
- Pipe0ウォーターフォール — FEのミスがあった場合のみ
メッセージパーソナライゼーションのための追加エンリッチメント(利用可能な場合):
- LinkedInのヘッドラインとサマリー(PhantomBuster経由のLinkedInスクレイピングから)
- 最近のLinkedIn投稿(コンタクトごとに2~3件) — メッセージ品質を大幅に向上
メッセージ生成のための最小限のフィールド:
name, job_title, company_name, linkedin_profile_url,
headline (optional), summary (optional), recent_posts (optional)
低収量: フィルタリングとエンリッチメント後に約8件未満のコンタクトしか残らなかった場合、メッセージに進む前に、People-Source Cadenceの次のソースでファインダーパスをもう1回実行します。
ステップ5.5 — シグナル検索(オプション)
デフォルト:オフ。 以下の場合に有効化:
- ユーザーがシグナルに基づいたメッセージを明示的に要求した場合
- Webhookプロンプトが購入トリガー(資金調達、採用、変革、最近のニュース)に言及している場合
- クライアントの提供がタイミングシグナルに依存して意味をなす場合(例:「シリーズA後の企業がオペレーションをスケールするのを支援します」)
--with-signalsフラグがデモ呼び出しに渡された場合
コストに関する注意: ユニーク企業あたり約0.01~0.05ドル追加(ウェブ検索とスコアリング)。10件のコンタクトのデモでは、通常5~10のユニーク企業となるため、0.50ドル未満。
シグナル検索に必要な入力
シグナル検索には3つのコンテキストファイルが必要です:
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 のソース行は、新しいシェルでも $GTM_ENV_PATH が設定されることを保証します(signal-search SKILL.md / conventionsを参照)。デモでは、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件のサンプルメッセージを生成します。
メッセージ構造
すべてのメッセージは次の順序に従う必要があります:フック → ブリッジ → オファー → ソフトCTA
| パート | 目的 | 長さ |
|---|---|---|
| フック | この人物に固有の何かを参照(投稿、キャリアの動き、企業シグナル) | 1文 |
| ブリッジ | 彼らの状況をあなたの提供に結びつける | 1文 |
| オファー | あなたが提供するものを明確に述べる | 1文 |
| CTA | ソフトな依頼 — 「スケジュールを設定しましょう」ではない | 1文 |
合計:320~450文字。 挨拶の後に空白行はなし。段落は単一の改行で区切ります。
品質ルール
必須事項:
- 具体的なフック(投稿の参照、またはキャリアの洞察、またはスコア化された購買シグナル — ジェネリックではない)
- 明確なバリュープロポジション
- 自然で会話的なトーン
- ソフトCTA
避けるべきこと:
- 相手がすでに知っているプロフィール情報の繰り返し(「あなたはX社でYとして働いていますね」)
- 一般的な観察(「印象的な経歴ですね」「あなたが[業界]にいらっしゃることに気づきました」)
- 企業の専門用語やバズワード
- 押し付けがましいCTA(「今週中にミーティングを設定しましょう」)
ステップ5.5を実行した場合: 各コンタクトについて、company_scored_signals から最もスコアの高いシグナルを、一般的なLinkedIn投稿の参照よりもフックとして優先します。スコアが70以上のシグナルは、実際の最近のニュースに基づいており、デモが生成できる最強のフックです。
生成プロセス
- クライアント固有のシステムプロンプトを書く(
prompts/message_prompt.mdに保存) - 2~4件のサンプルを生成 — LinkedIn投稿があるコンタクトとないコンタクトを含める
- 上記の品質チェックリストに対してレビュー
- 問題が見つかった場合、システムプロンプトを洗練して再生成
- 品質が承認された場合のみバッチ生成する
システムプロンプトテンプレート(主要セクション)
- クライアントコンテキスト:何を販売しているか、誰をターゲットにしているか、価値提案、トーン
- 禁止ルール:プロフィールの繰り返し禁止、一般的なお世辞禁止
- メッセージ構造:フック → ブリッジ → オファー → CTA
- フックの例:投稿あり / 投稿なし
- 文字数制限:320~450
ステップ7 — サニタイズと出力
ステップ7a — サニタイズ(必須、決定論的、LLM不使用)。 csv/intermediate/ はすべてのフィールドを保持します。リードに表示するものを作成する前に、共有サニタイザを実行して、繰り返し発生する手動スクラビング(プロバイダラベル、空の列、不正なメール、古いシグナル)を自動処理します。