Wo Ihr Kunde lebt
Basierend auf dem Produktnutzen und dem ICP werden reale ICP-Schmerzposts zu ihren Ursprungs-Communities zurückverfolgt. Wettbewerber-Diskussionssignale werden hinzugefügt. Slack/Discord/Newsletter/Podcast/Konferenz-Kanäle werden über DuckDuckGo entdeckt. Jeder Kanal wird nach ICP-Signalanzahl, Größe, Aktivität und Wettbewerbspräsenz bewertet. Ein geranktes Playbook wird ausgegeben: Nachweis, Einstiegstaktik, Content-Angle, Anti-Patterns – pro Kanal. Kein Raten. Nur signalgestützte Kanäle.
Kritische Regel: Jeder Kanalname in der Ausgabe muss entweder in der Reddit-API-Antwort oder in den DuckDuckGo-Suchergebnissen dieses Durchlaufs existieren. Jede Mitgliederanzahl muss von der about.json-API oder einem Snippet stammen – niemals geschätzt. Jede ICP-Signalanzahl muss mit den Rohdaten übereinstimmen. Wenn ein Kanaltyp 0 Ergebnisse liefert, melden Sie 0 – erfinden Sie keine Kanäle.
Häufige Fehler
| Der Agent wird versuchen... | Warum das falsch ist |
|---|---|
| Generische Kanäle zu empfehlen („LinkedIn“, „Twitter“) | Jeder Kanal muss spezifisch mit Name, Mitgliederzahl und URL sein. „LinkedIn-Gruppe: DevOps für Enterprise-Teams (45K Mitglieder)“ – nicht nur „LinkedIn“. |
| Für jeden ICP dieselben Kanäle zu verwenden | Signal-Trace ist ICP-spezifisch. Ein DevOps-ICP und ein Finance-ICP ergeben völlig unterschiedliche Kanallisten. Führen Sie das Skript pro ICP frisch aus. |
| Mitgliederzahlen oder Community-Namen zu erfinden | Jeder Kanalname muss von DuckDuckGo-Ergebnissen oder der Reddit-API stammen. Jede Mitgliederzahl muss von der API oder einem Snippet stammen. Wenn nicht verfügbar, schreiben Sie „Mitgliederzahl nicht gefunden“. |
| Die Wettbewerbsebene zu überspringen | Wo Wettbewerber diskutiert werden = Ihr ICP bewertet Alternativen = heißester Outreach-Kontext. Führen Sie immer eine Wettbewerbersuche durch, auch wenn der Benutzer nicht danach gefragt hat. |
| Einstiegstaktiken zu schreiben, die Produkt-Pitches sind | „Poste über dein Produkt in r/devops“ ist keine Einstiegstaktik. Einstiegstaktiken nennen den spezifischen Thread-Typ, das Beitragsformat und die Community-Norm. |
| Reddit als einzigen Kanaltyp zu behandeln | Die Ausgabe muss mindestens 3 Kanaltypen enthalten. Wenn nur Reddit gefunden wird, durchsuchen Sie DuckDuckGo explizit nach Slack/Discord/Newsletter/Konferenz, bevor Sie aufhören. |
Schritt 1: Setup-Prüfung
echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-nicht gesetzt – Wettbewerbsebene läuft mit 60 req/h unauthentifiziert}"
echo ""
echo "Datenquellen in diesem Durchlauf:"
echo " Reddit public JSON (kein Auth, Signal-Trace)"
echo " Reddit about.json (kein Auth, Subreddit-Metadaten)"
echo " HN Algolia API (kein Auth, Signal-Trace)"
echo " DuckDuckGo HTML (kein Auth, Kanalentdeckung)"
echo " GitHub API (${GITHUB_TOKEN:+authentifiziert, }optional für Wettbewerber-Anreicherung)"
Wenn GITHUB_TOKEN nicht gesetzt ist: trotzdem fortfahren. Die gesamte Kern-Kanalentdeckung funktioniert ohne ihn.
Schritt 2: ICP parsen
Sammeln aus dem Gespräch:
product– was das Produkt tut (ein Satz)icp_role– wer der ICP ist (z. B. „technische Mitgründer“, „DevOps-Ingenieure bei Series-A-Firmen“)icp_pain– ihr primäres Problem (z. B. „Kundenakquise“, „Alert Fatigue“)category– Marktkategorie-Schlagwörter (z. B. „Startup GTM Sales“, „DevOps Monitoring“)competitors– optionale Wettbewerbernamen (z. B. „Clay, Apollo, HubSpot“)
ICP-Kaskade:
- Wenn die Benutzeraufforderung product + icp_role + icp_pain enthält: direkt extrahieren und fortfahren.
- Wenn die Aufforderung dünn ist (nur category oder nur product):
docs/icp.mdauf ein gespeichertes ICP-Profil prüfen. Mit den Details aus der Aufforderung zusammenführen. - Wenn immer noch unzureichend (icp_role oder icp_pain fehlen): diese 3 Fragen nacheinander stellen:
- „Was tut Ihr Produkt in einem Satz?“
- „Wer ist Ihr idealer Kunde? (Rolle, Unternehmenstyp, Teamgröße)“
- „Was ist ihr primäres Problem, bevor sie Ihr Produkt finden?“
- Das finale ICP in
docs/icp.mdspeichern, damit andere Skills es wiederverwenden können.
ICP-Datei speichern, wenn docs/icp.md dieses Produkt noch nicht enthält:
python3 << 'PYEOF'
import json, os
icp = {
"product": "PRODUKT_HIER",
"icp_role": "ICP_ROLLE_HIER",
"icp_pain": "ICP_SCHMERZ_HIER",
"competitors": ["WETTBEWERBER_1", "WETTBEWERBER_2"],
"category": "KATEGORIE_HIER"
}
os.makedirs("docs", exist_ok=True)
with open("/tmp/wcl-input.json", "w") as f:
json.dump(icp, f, indent=2)
# Aktualisiere docs/icp.md
icp_md_path = "docs/icp.md"
new_block = f"""## {icp['product']}
- **ICP-Rolle:** {icp['icp_role']}
- **ICP-Schmerz:** {icp['icp_pain']}
- **Wettbewerber:** {', '.join(icp['competitors']) if icp['competitors'] else 'keine'}
- **Kategorie:** {icp['category']}
"""
existing = open(icp_md_path).read() if os.path.exists(icp_md_path) else ""
if icp['product'] not in existing:
with open(icp_md_path, "a") as f:
f.write(new_block)
print(f"ICP gespeichert in {icp_md_path}")
else:
print(f"ICP bereits in {icp_md_path}")
print(f"Produkt: {icp['product']}")
print(f"ICP-Rolle: {icp['icp_role']}")
print(f"ICP-Schmerz: {icp['icp_pain']}")
print(f"Wettbewerber: {', '.join(icp['competitors']) if icp['competitors'] else 'keine'}")
PYEOF
Schritt 3: Das eigenständige Datensammlungsskript ausführen
Prüfen, ob das Skript existiert:
ls scripts/fetch.py 2>/dev/null && echo "Skript verfügbar" || echo "nicht gefunden"
Kanalentdeckung ausführen:
GITHUB_TOKEN="${GITHUB_TOKEN:-}" python3 scripts/fetch.py \
"$(python3 -c "import json; d=json.load(open('/tmp/wcl-input.json')); print(d['category'])")" \
--icp-role "$(python3 -c "import json; d=json.load(open('/tmp/wcl-input.json')); print(d['icp_role'])")" \
--icp-pain "$(python3 -c "import json; d=json.load(open('/tmp/wcl-input.json')); print(d['icp_pain'])")" \
--product "$(python3 -c "import json; d=json.load(open('/tmp/wcl-input.json')); print(d['product'])")" \
--competitors "$(python3 -c "import json; d=json.load(open('/tmp/wcl-input.json')); print(','.join(d['competitors']))") " \
--output /tmp/wcl-raw.json
Auf Abschluss warten (bis zu 5 Minuten erlauben – Reddit + DuckDuckGo-Suchen dauern insgesamt ~120 Sekunden).
Ausgabe überprüfen:
python3 -c "
import json
with open('/tmp/wcl-raw.json') as f:
d = json.load(f)
print(f'Reddit-Beiträge gefunden: {d[\"reddit_posts_found\"]}')
print(f'HN-Signale gefunden: {d[\"hn_signals_found\"]}')
print(f'Kanäle entdeckt: {d[\"summary\"][\"total_channels\"]}')
print(f'Top-Priorität: {len(d[\"summary\"][\"top_priority\"])}')
print(f'Nach Typ: {d[\"summary\"][\"by_type\"]}')
print(f'Wettbewerbsebene ausgeführt: {d[\"summary\"][\"competitor_layer_ran\"]}')
"
Wenn total_channels < 3: dem Benutzer sagen: „Weniger als 3 Kanäle gefunden. Die ICP-Beschreibung ist möglicherweise zu eng für die Reddit/DDG-Abdeckung. Versuchen Sie breitere Kategorie-Schlüsselwörter oder fügen Sie Wettbewerbernamen hinzu, um die Wettbewerbsebene zu aktivieren.“ Dann einen erneuten Versuch mit breiteren Kategorie-Schlüsselwörtern unternehmen, bevor Sie aufhören.
Schritt 4: Kanalzusammenfassung ausgeben
Die Rohdaten laden und eine gerankte Zusammenfassungstabelle ausgeben:
python3 -c "
import json
with open('/tmp/wcl-raw.json') as f:
d = json.load(f)
channels = d['channels_discovered']
print(f'Kanäle gefunden: {len(channels)}')
print()
print(f'{'#':<4} {'Kanal':<35} {'Typ':<14} {'Mitglieder':<12} {'ICP-Signale':<13} {'Punktzahl':<8} Stufe')
print('-' * 100)
for i, ch in enumerate(channels[:15], 1):
members = ch.get('members', 0)
m_str = f'{members//1000}K' if members >= 1000 else str(members) if members else '?'
print(f'{i:<4} {ch[\"name\"]:<35} {ch[\"type\"]:<14} {m_str:<12} {ch.get(\"icp_signal_count\",0):<13} {ch.get(\"channel_score\",0):<8} {ch.get(\"tier\",\"\")}')
"
Die Top-3-Nachweisbeiträge aus dem am höchsten bewerteten Kanal ausgeben:
python3 -c "
import json
with open('/tmp/wcl-raw.json') as f:
d = json.load(f)
channels = d['channels_discovered']
if channels:
top = channels[0]
print(f'Top-Kanal: {top[\"name\"]}')
print(f'Nachweisbeiträge:')
for ep in top.get('evidence_posts', [])[:3]:
print(f' [{ep.get(\"score\",0):.0f}] {ep.get(\"title\",\"\")}')
print(f' {ep.get(\"url\",\"\")}')
"
Schritt 5: KI-Kanalanreicherung
Sie haben nun die rohen Kanaldaten. Generieren Sie für jeden Kanal in den Stufen „Top-Priorität“ und „Hoch“ einen Playbook-Eintrag.
Alle Kanäle laden:
python3 -c "
import json
with open('/tmp/wcl-raw.json') as f:
d = json.load(f)
top_channels = [ch for ch in d['channels_discovered'] if ch.get('tier') in ('top-priority', 'high')]
print(json.dumps(top_channels, indent=2))
"
Für jeden Kanal oben generieren:
who_is_here: 2 Sätze, die den spezifischen Typ des ICP beschreiben, der in diesem Kanal präsent ist. Ableiten aus den Nachweisbeiträgen, der Subreddit-Beschreibung und dem ICP-Profil. NICHT „Ihre Zielgruppe“ schreiben – seien Sie spezifisch. Beispiel: „DevOps-Ingenieure bei Unternehmen mit 50-500 Mitarbeitern, die den Infrastruktur-Stack ohne ein dediziertes SRE-Team verwalten. Sie posten über On-Call-Burnout, Kubernetes-Sprawl und die Wahl zwischen Cloud-nativen und selbstgehosteten Observability-Lösungen.“
entry_tactic: Eine spezifische, umsetzbare Einstiegsaktion. Nennen Sie den Thread-Typ, das Beitragsformat und die Community-Norm. NICHT „engagiere dich in der Community“. Beispiel: „Finde den wöchentlichen Thread 'Woran arbeitest du gerade?' (wird jeden Montag von automoderator gepostet). Antworte mit einer 3-Satz-technischen Herausforderung, die du gelöst hast – was kaputt war, was du versucht hast, was funktioniert hat. Keine Produkterwähnung. Baue Karma auf, bevor du eigenständige Inhalte postest.“
content_angle: Das Inhaltsformat, das in diesem spezifischen Kanal die höchste Interaktion erzielt, abgeleitet aus Beweistiteln und Punktzahlen. Beispiel: „Technische Post-Mortems erzielen hier 5:1 mehr Engagement als Produktankündigungen. Format: 'Wir haben 200K Benutzer von X nach Y migriert – hier ist, was kaputt ging und warum.' Konkrete Zahlen + was fehlschlug = die meisten Upvotes.“
anti_patterns: 2-3 spezifische Verhaltensweisen, die dazu führen, dass Beiträge entfernt werden oder der Ruf in dieser Community ruiniert wird. Ableiten aus Subreddit-Regeln (falls in der Beschreibung verfügbar) und Nachweisbeitragsmustern. Beispiel: [„Produktlinks in nicht-werblichen Threads posten – Moderatoren entfernen innerhalb von Stunden“, „Nach 'welche Tools verwendet ihr?' ohne spezifischen Kontext fragen – wird als Marktforschung abgestempelt“]
Das angereicherte Playbook nach /tmp/wcl-channels.json schreiben:
{
"playbook": [
{
"channel": "r/devops",
"evidence": "34 ICP-Signale hier verfolgt, durchschnittlicher Schmerz-Score 180",
"who_is_here": "...",
"entry_tactic": "...",
"content_angle": "...",
"anti_patterns": ["...", "..."]
}
]
}
python3 -c "
import json
with open('/tmp/wcl-channels.json') as f:
d = json.load(f)
print(f'Playbook-Einträge: {len(d[\"playbook\"])}')
for p in d['playbook']:
print(f' {p[\"channel\"]}')
"
Schritt 6: Vollständige gerankte Ausgabe generieren
Das vollständige gerankte Playbook nach /tmp/wcl-output.json schreiben:
python3 << 'PYEOF'
import json
from datetime import datetime
with open('/tmp/wcl-input.json') as f:
inp = json.load(f)
with open('/tmp/wcl-raw.json') as f:
raw = json.load(f)
with open('/tmp/wcl-channels.json') as f:
enriched = json.load(f)
playbook_by_channel = {p['channel']: p for p in enriched['playbook']}
channels = raw['channels_discovered']
output = {
"date": raw['date'],
"product": inp['product'],
"icp_role": inp['icp_role'],
"icp_pain": inp['icp_pain'],
"competitors": inp.get('competitors', []),
"total_channels": raw['summary']['total_channels'],
"channels": []
}
for ch in channels:
name = ch['name']
playbook = playbook_by_channel.get(name, {})
output['channels'].append({
"rank": channels.index(ch) + 1,
"name": name,
"type": ch['type'],
"url": ch['url'],
"members": ch.get('members', 0),
"active_users": ch.get('active_users', 0),
"icp_signal_count": ch.get('icp_signal_count', 0),
"competitor_mentions": ch.get('competitor_mentions', 0),
"channel_score": ch.get('channel_score', 0),
"tier": ch.get('tier', ''),
"entry_type": ch.get('entry_type', 'open'),
"evidence_posts": ch.get('evidence_posts', []),
"who_is_here": playbook.get('who_is_here', ''),
"entry_tactic": playbook.get('entry_tactic', ''),
"content_angle": playbook.get('content_angle', ''),
"anti_patterns": playbook.get('anti_patterns', []),
})
with open('/tmp/wcl-output.json', 'w') as f:
json.dump(output, f, indent=2)
print(f"Ausgabe geschrieben: /tmp/wcl-output.json")
print(f"Gesamtkanäle: {len(output['channels'])}")
PYEOF
Schritt 7: Selbst-QA
python3 -c "
import json
with open('/tmp/wcl-raw.json') as f:
raw = json.load(f)
with open('/tmp/wcl-output.json') as f:
output = json.load(f)
full_text = json.dumps(output)
raw_channel_names = {ch['name'].lower() for ch in raw['channels_discovered']}
passes = 0
fails = 0
# Prüfung 1: Keine em-Dashes
if chr(8212) in full_text:
print('FEHLGESCHLAGEN: em-Dash in der Ausgabe gefunden – durch Bindestrich ersetzen')
fails += 1
else:
print('BESTANDEN: keine em-Dashes')
passes += 1
# Prüfung 2: Keine verbotenen Wörter
banned = ['powerful', 'robust', 'seamless', 'innovative', 'game-changing',
'streamline', 'leverage', 'transform', 'revolutionize']
found = [w for w in banned if w.lower() in full_text.lower()]
if found:
print(f'FEHLGESCHLAGEN: verbotene Wörter: {found}')
fails += 1
else:
print('BESTANDEN: keine verbotenen Wörter')
passes += 1
# Prüfung 3: Mindestens 3 Kanaltypen
types = {ch['type'] for ch in output['channels']}
if len(types) < 3:
print(f'FEHLGESCHLAGEN: nur {len(types)} Kanaltyp(en) in der Ausgabe: {types}')
fails += 1
else:
print(f'BESTANDEN: {len(types)} Kanaltypen: {types}')
passes += 1
# Prüfung 4: Alle Kanalnamen existieren in Rohdaten
for ch in output['channels']:
if ch['name'].lower() not in raw_channel_names:
print(f'FEHLGESCHLAGEN: Kanal nicht in Rohdaten: {ch[\"name\"]}')
fails += 1
if fails == 0:
print('BESTANDEN: alle Kanalnamen in Rohdaten verifiziert')
passes += 1
# Prüfung 5: Keine generischen Einstiegstaktiken
generic_phrases = ['engage with the community', 'post about your product', 'share your content']
for ch in output['channels']:
tactic = ch.get('entry_tactic', '').lower()
for phrase in generic_phrases:
if phrase in tactic:
print(f'FEHLGESCHLAGEN: generische Phrase \"{phrase}\" in entry_tactic von Kanal {ch[\"name\"]}')
fails += 1
if fails == 0:
print('BESTANDEN: keine generischen Einstiegstaktiken')
passes += 1
# Prüfung 6: Keine geschätzten Mitgliederzahlen
for ch in output['channels']:
for ep in ch.get('evidence_posts', []):
if ep.get('member_count_estimated', False):
print(f'FEHLGESCHLAGEN: geschätzte Mitgliederzahl in Nachweisbeitrag von Kanal {ch[\"name\"]}')
fails += 1
break
if fails == 0:
print('BESTANDEN: keine geschätzten Mitgliederzahlen')
passes += 1
# Prüfung 7: Wettbewerbsebene ausgeführt, falls Wettbewerber angegeben
competitors = output.get('competitors', [])
if competitors:
if raw['summary']['competitor_layer_ran']:
print('BESTANDEN: Wettbewerbsebene ausgeführt')
passes += 1
else:
print('FEHLGESCHLAGEN: Wettbewerber angegeben, aber Wettbewerbsebene nicht ausgeführt')
fails += 1
print(f'\nErgebnis: {passes}/{passes+fails} bestanden')
"
Wenn alle Prüfungen bestanden sind: dem Benutzer das vollständige Playbook präsentieren. Wenn Prüfungen fehlschlagen: den Fehler beheben und erneut ausführen.
Schritt 8: Abschließende Zusammenfassung
python3 -c "
import json
with open('/tmp/wcl-output.json') as f:
output = json.load(f)
print('## Finales Playbook')
print()
print(f'**Produkt:** {output[\"product\"]}')
print(f'**ICP-Rolle:** {output[\"icp_role\"]}')
print(f'**ICP-Schmerz:** {output[\"icp_pain\"]}')
print(f'**Wettbewerber:** {\", \".join(output[\"competitors\"]) if output[\"competitors\"] else \"keine\"}')
print(f'**Kanäle gesamt:** {output[\"total_channels\"]}')
print()
for ch in sorted(output['channels'], key=lambda x: x['channel_score'], reverse=True):
print(f'### {ch[\"rank\"]}. {ch[\"name\"]} ({ch[\"type\"]})')
print(f'**URL:** {ch[\"url\"]}')
print(f'**Mitglieder:** {ch[\"members\"]}')
print(f'**ICP-Signale:** {ch[\"icp_signal_count\"]}')
print(f'**Wettbewerber-Erwähnungen:** {ch[\"competitor_mentions\"]}')
print(f'**Kanal-Punktzahl:** {ch[\"channel_score\"]}')
print(f'**Stufe:** {ch[\"tier\"]}')
if ch.get('who_is_here'):
print(f'**Wer ist hier:** {ch[\"who_is_here\"]}')
if ch.get('entry_tactic'):
print(f'**Einstiegstaktik:** {ch[\"entry_tactic\"]}')
if ch.get('content_angle'):
print(f'**Content-Angle:** {ch[\"content_angle\"]}')
if ch.get('anti_patterns'):
print(f'**Anti-Patterns:**')
for ap in ch['anti_patterns']:
print(f' - {ap}')
print()
"
Erfolgskriterien
- Jeder Kanalname ist in den DuckDuckGo-Ergebnissen oder der Reddit-API dieses Durchlaufs vorhanden.
- Jede Mitgliederzahl stammt aus der
about.json-API oder einem Snippet – niemals geschätzt. - ICP-Signale werden aus tatsächlichen Beiträgen gezählt, die
icp_roleundicp_painim Titel oder Text enthalten. - Die Ausgabe enthält mindestens 3 Kanaltypen.
- Einstiegstaktiken nennen ein spezifisches Thread-Format, ein Beitragsformat und eine Community-Norm – kein generisches Engagement.
- Jeder Kanal hat 2-3 Anti-Patterns, die aus den Regeln oder Mustern der Community abgeleitet sind.
- Der Wettbewerber-Layer wurde ausgeführt, wenn Wettbewerber im Prompt genannt wurden.
- Keine der verbotenen Wörter in der Ausgabe (powerful, robust, seamless, etc.).
- Keine em-Dashes – alle durch einfache Bindestriche ersetzen.
- Die Einstiegstaktiken enthalten keine Produkt-Pitches – sie beschreiben, wie man sich natürlich in die Community integriert.