繪製你的市場
輸入產品描述、類別關鍵字或競爭對手名稱。搜尋 Reddit、HN、GitHub Issues、G2 和 Google Trends,找出真實的痛點訊號。對它們進行評分和聚類。建立完整的定位框架:ICP 定義、按排名排列的痛點主題(附逐字引述)、市場規模訊號,以及從人們實際使用的語言中提煉出的訊息角度。
關鍵規則: 輸出中的每個痛點引述都必須來自腳本收集的原始數據。市場地圖中的每個供應商名稱都必須來自 G2 爬取結果或 GitHub 搜尋結果。市場規模必須說「信號顯示」——切勿根據薄弱的間接指標估算金錢數字。如果某個來源回傳 0 個結果,就報告 0——不要用虛構的例子來補充。
常見錯誤
| 代理可能會傾向於... | 為什麼這是錯的 |
|---|---|
| 捏造痛點或市場規模數字 | 每個痛點引述都必須是原始數據中的逐字內容。市場規模必須引用找到的訊號。切勿估算「典型」市場規模。 |
| 根據文章數量而非 pain_score 評分 | 一篇有 2,000 個 upvote 關於定價的文章比 50 篇各有 10 個 upvote 的文章更有份量。使用 references/pain-scoring.md 中的 pain_score 公式。 |
| 對每個類別使用相同的 subreddit | r/politics 會為 DevOps 搜尋增加雜訊。在搜尋之前,先根據類別和競爭對手名稱自動偵測相關的 subreddit。 |
| 不經評分就將所有原始訊號傳送給 AI | 先在本地評分。只將前 60 個高痛點分數的訊號傳送給 AI 聚類。這能節省 token 並提高聚類品質。 |
| 跳過從文章元資料中提取 ICP | Subreddit、flair、作者簡介(HN)和 GitHub 組織類型是比文章內容更豐富的 ICP 訊號。務必擷取並報告它們。 |
| 將供應商數量與市場規模混為一談 | 「G2 上有 47 家供應商」表示競爭激烈,而非市場龐大。將所有訊號呈現為方向性指標,而非確切數字。 |
第一步:設定檢查
echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-未設定——未經認證的 GitHub Issues 搜尋每小時 60 次請求}"
echo "不需要其他 API 金鑰。"
echo ""
echo "本次執行將使用的資料來源:"
echo " Reddit 公開 JSON (無需認證,每分鐘 10 次請求)"
echo " HN Algolia API (無需認證,免費)"
echo " GitHub Issues API (${GITHUB_TOKEN:+已認證,}每小時 60-5000 次請求)"
echo " G2 類別爬取 (無需認證,HTML 解析)"
echo " Google Trends (無需認證,非官方端點)"
如果未設定 GITHUB_TOKEN:繼續執行。未經認證的 GitHub 搜尋每小時 60 次請求——足以應付一次標準執行。如需重複使用,請在 github.com/settings/tokens 新增一個 token(公開儲存庫不需要任何範圍)。
第二步:解析輸入
從對話中收集:
category——描述市場空間的關鍵字(例如「開發者可觀測性」、「B2B 分析」、「DevOps 工具」)competitors——可選的競爭對手產品名稱或域名清單(例如「Datadog, New Relic, Grafana」)product_context——可選:使用者的產品做什麼(有助於調整訊息角度)
如果使用者只提供產品描述而沒有類別關鍵字:自行從中提取 2-3 個類別關鍵字。
如果使用者只提供競爭對手名稱而沒有類別:透過查詢競爭對手來推斷類別。
寫入解析後的輸入:
python3 << 'PYEOF'
import json, os
data = {
"category": "CATEGORY_HERE",
"competitors": ["COMP_1", "COMP_2"],
"product_context": "PRODUCT_CONTEXT_HERE"
}
with open("/tmp/mym-input.json", "w") as f:
json.dump(data, f, indent=2)
print("輸入已寫入 /tmp/mym-input.json")
print(f"類別:{data['category']}")
print(f"競爭對手:{', '.join(data['competitors']) if data['competitors'] else '未提供'}")
PYEOF
第三步:執行獨立的資料收集腳本
該腳本負責所有資料收集。首先檢查它是否存在:
ls scripts/fetch.py 2>/dev/null && echo "腳本可用" || echo "未找到"
如果可用,執行它:
GITHUB_TOKEN="${GITHUB_TOKEN:-}" python3 scripts/fetch.py \
"$(python3 -c "import json; d=json.load(open('/tmp/mym-input.json')); print(d['category'])")" \
--competitors "$(python3 -c "import json; d=json.load(open('/tmp/mym-input.json')); print(','.join(d['competitors']))")" \
--context "$(python3 -c "import json; d=json.load(open('/tmp/mym-input.json')); print(d['product_context'])")" \
--output /tmp/mym-raw.json
等待完成(最多等待 4 分鐘——Reddit + HN 搜尋總共約需 90 秒)。
驗證輸出:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
print(f'Reddit 訊號數:{d[\"market_signals\"][\"reddit_signals_found\"]}')
print(f'HN 訊號數: {d[\"market_signals\"][\"hn_signals_found\"]}')
print(f'GitHub 訊號數:{d[\"market_signals\"][\"github_issue_signals\"]}')
print(f'G2 供應商數: {d[\"market_signals\"][\"vendor_count_g2\"]}')
print(f'趨勢方向: {d[\"market_signals\"][\"trends_direction\"]}')
print(f'總訊號數: {d[\"summary\"][\"total_pain_signals\"]}')
"
如果總訊號數 < 10:停止。告訴使用者:「為此類別找到的痛點訊號少於 10 個。市場可能過於小眾,Reddit/HN 的覆蓋率不足,或者類別關鍵字需要調整。請嘗試更廣泛的關鍵字或新增競爭對手名稱。」
第四步:AI 痛點聚類
列印前 60 個痛點訊號供 AI 分析:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
top60 = sorted(d['raw_pains'], key=lambda x: x['pain_score'], reverse=True)[:60]
print(json.dumps(top60, indent=2))
"
你現在有前 60 個痛點訊號。分析它們並產生痛點聚類。
AI 分析指示:
- 找出所有來源中反覆出現的 5-7 個痛點主題
- 對於每個主題:使用市場自己的語言(而非你的話語)來命名
- 匯總每個聚類中所有訊號的 pain_score
- 為每個主題選擇 3-5 個最佳逐字引述(分數最高、語言最具體)
- 注意每個主題集中在哪些來源和 subreddit
- 標記任何僅出現在一個來源的主題(信賴度較低)
將聚類寫入 /tmp/mym-clusters.json:
{
"clusters": [
{
"theme": "來自數據的準確語言",
"total_score": 847,
"signal_count": 34,
"sources": {"reddit": 18, "hn": 12, "github_issue": 4},
"top_subreddits": ["devops", "sysadmin"],
"verbatim_quotes": [
{"text": "確切引述", "source": "reddit", "score": 234, "url": "..."},
{"text": "確切引述", "source": "hn", "score": 87, "url": "..."}
],
"who_has_this_pain": "發文者的描述"
}
]
}
python3 -c "
import json, os
# 確認聚類檔案已寫入
with open('/tmp/mym-clusters.json') as f:
d = json.load(f)
print(f'聚類已寫入:{len(d[\"clusters\"])}')
for c in d['clusters']:
print(f' {c[\"theme\"]} -- 分數:{c[\"total_score\"]}, 訊號數:{c[\"signal_count\"]}')
"
第五步:ICP 分析
從原始資料中列印 ICP 訊號:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
print('ICP 訊號:')
print(json.dumps(d['icp_signals'], indent=2))
print()
print('Subreddit 分佈:')
sub_counts = {}
for p in d['raw_pains']:
s = p.get('subreddit', '')
if s:
sub_counts[s] = sub_counts.get(s, 0) + 1
for sub, count in sorted(sub_counts.items(), key=lambda x: -x[1])[:10]:
print(f' r/{sub}:{count} 個訊號')
"
使用上述的 ICP 訊號和 subreddit 分佈,綜合出 ICP 檔案。透過新增一個 icp 鍵將其寫入 /tmp/mym-clusters.json:
{
"icp": {
"who_they_are": "使用數據語言的 2-3 句描述",
"where_they_live": ["r/devops (89 篇文章)", "r/sysadmin (67 篇文章)", "HN ask-hn (34 個討論串)"],
"what_they_say": ["逐字引述 1", "逐字引述 2", "逐字引述 3"],
"what_they_have_tried": ["數據中提到的替代工具或方法"],
"confidence": "高|中|低 -- 基於訊號量和來源多樣性"
}
}
第六步:市場規模綜合
列印市場訊號:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
ms = d['market_signals']
print('市場訊號:')
print(f' G2 供應商數: {ms[\"vendor_count_g2\"]}')
print(f' 趨勢方向: {ms[\"trends_direction\"]}')
print(f' HN 訊號數(12 個月): {ms[\"hn_signals_found\"]}')
print(f' Reddit 訊號數: {ms[\"reddit_signals_found\"]}')
print(f' G2 頂級供應商: {json.dumps(ms.get(\"top_vendors\", []), indent=4)}')
"
僅使用這些訊號來綜合一個方向性的市場規模評估。不要估算金錢數字。使用像這樣的語言:
- 「訊號顯示這是一個競爭激烈、成長中的市場」(供應商多 + 趨勢上升)
- 「訊號顯示這是一個早期市場」(供應商少 + 訊號量低)
- 「訊號顯示這是一個飽和的市場」(供應商多 + 趨勢持平/下降)
將評估作為一個 market_size 鍵新增到 /tmp/mym-clusters.json。
第七步:定位框架
使用聚類(第四步)、ICP(第五步)和市場規模(第六步),生成定位框架。
指示:
- 選擇前 3 個痛點聚類作為主要定位角度
- 對於每個角度:使用數據中的逐字語言(而非改寫)撰寫一個定位陳述句
- 生成 3 個登陸頁面標題,使用人們在痛點數據中使用的確切詞語
- 基於痛點語言生成 3 個 cold email 主旨行
- 請勿使用禁用詞彙:強大、穩健、無縫、創新、顛覆性、簡化、利用、轉型、革命性
將完整的定位框架寫入 /tmp/mym-output.json:
{
"positioning_angles": [
{
"pain": "主題名稱",
"statement": "使用市場語言的一行定位",
"headline": "使用逐字痛點語言的登陸頁面標題",
"cold_email_subject": "主旨行"
}
],
"icp_card": {
"one_liner": "一句話:他們是誰 + 他們關心什麼",
"where_to_find_them": [...],
"how_to_talk_to_them": "來自數據的語氣 + 詞彙備註"
},
"market_map": [...來自 G2 的頂級供應商及定位備註...]
}
第八步:自我 QA 並儲存輸出
執行自我 QA 檢查:
python3 -c "
import json
# 載入所有輸出
with open('/tmp/mym-raw.json') as f:
raw = json.load(f)
with open('/tmp/mym-clusters.json') as f:
clusters = json.load(f)
with open('/tmp/mym-output.json') as f:
output = json.load(f)
raw_texts = set()
for p in raw['raw_pains']:
raw_texts.add(p.get('title', ''))
raw_texts.add(p.get('body_excerpt', ''))
# 檢查 1:無 em dash
import json as j
full_text = j.dumps(output)
if '—' in full_text:
print('失敗:輸出中找到 em dash')
else:
print('通過:無 em dash')
# 檢查 2:無禁用詞
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'失敗:找到禁用詞:{found}')
else:
print('通過:無禁用詞')
# 檢查 3:市場規模語言檢查
if 'billion' in full_text.lower() or 'trillion' in full_text.lower() or 'worth \$' in full_text.lower():
print('失敗:找到具體市場規模估算——僅使用方向性語言')
else:
print('通過:無具體市場規模估算')
# 檢查 4:訊號數匹配
total = raw['summary']['total_pain_signals']
print(f'通過:原始數據中共 {total} 個痛點訊號')
print('自我 QA 完成。')
"
在儲存前修正任何失敗。
儲存最終報告:
python3 << 'PYEOF'
import json, re
from datetime import datetime
with open('/tmp/mym-input.json') as f:
inp = json.load(f)
with open('/tmp/mym-raw.json') as f:
raw = json.load(f)
with open('/tmp/mym-clusters.json') as f:
clusters = json.load(f)
with open('/tmp/mym-output.json') as f:
output = json.load(f)
slug = re.sub(r'[^a-z0-9]+', '-', inp['category'].lower()).strip('-')
date = datetime.now().strftime('%Y-%m-%d')
outpath_md = f"docs/market-maps/{slug}-{date}.md"
outpath_json = f"docs/market-maps/{slug}-{date}.json"
# 建置 Markdown 報告
ms = raw['market_signals']
icp = clusters.get('icp', {})
market_assessment = clusters.get('market_size', {})
angles = output.get('positioning_angles', [])
icp_card = output.get('icp_card', {})
market_map = output.get('market_map', [])
lines = [
f"# Market Map:{inp['category'].title()}",
f"日期:{date} | 分析的訊號數:{raw['summary']['total_pain_signals']} | 來源:Reddit ({ms['reddit_signals_found']}) + HN ({ms['hn_signals_found']}) + GitHub Issues ({ms['github_issue_signals']})",
"",
"---",
"",
"## 市場規模訊號",
f"G2 上的供應商數:{ms['vendor_count_g2']} | Google Trends:{ms['trends_direction'].upper()} | 市場階段:{market_assessment.get('stage', '請見下方訊號')}",
"",
market_assessment.get('summary', ''),
"",
"---",
"",
"## 你的 ICP",
"",
f"**他們是誰:** {icp.get('who_they_are', '')}",
"",
f"**他們在哪裡:** {', '.join(icp.get('where_they_live', []))}",
"",
"**他們說什麼:**",
]
for q in icp.get('what_they_say', []):
lines.append(f'> "{q}"')
lines += ["", "---", "", "## 主要痛點(按訊號強度排序)", ""]
for i, c in enumerate(clusters.get('clusters', []), 1):
lines.append(f"### 痛點 {i}:{c['theme']} [分數:{c['total_score']}]")
sources = c.get('sources', {})
source_str = " + ".join(f"{src} ({cnt})" for src, cnt in sources.items())
lines.append(f"{c['signal_count']} 個訊號 | 來源:{source_str}")
lines.append(f"誰有此痛點:{c.get('who_has_this_pain', '')}")
lines.append("")
lines.append("逐字引述:")
for q in c.get('verbatim_quotes', [])[:4]:
lines.append(f"- [{q['source']}, score {q['score']}]({q['url']}): \"{q['text']}\"")
lines.append("")
lines += ["---", "", "## 定位框架", "", "### 定位角度", ""]
for a in angles:
lines.append(f"**痛點:** {a['pain']}")
lines.append(f"**陳述:** {a['statement']}")
lines.append(f"**標題:** {a['headline']}")
lines.append(f"**冷郵件主旨:** {a['cold_email_subject']}")
lines.append("")
lines += ["### ICP 卡片", "", f"**一句話:** {icp_card.get('one_liner', '')}", "", "**在哪裡找到他們:**"]
for loc in icp_card.get('where_to_find_them', []):
lines.append(f"- {loc}")
lines += ["", f"**如何與他們交談:** {icp_card.get('how_to_talk_to_them', '')}", "", "### 市場地圖", ""]
for v in market_map:
if isinstance(v, dict):
lines.append(f"- **{v.get('name', '')}** - {v.get('positioning_note', '')}")
else:
lines.append(f"- {v}")
lines += ["", "---", "", "*此報告由 Map Your Market 腳本生成。所有痛點引述均為逐字。市場規模數據為方向性。*"]
report_md = '\n'.join(lines)
# 寫入檔案
os.makedirs(os.path.dirname(outpath_md), exist_ok=True)
with open(outpath_md, 'w') as f:
f.write(report_md)
with open(outpath_json, 'w') as f:
json.dump(output, f, indent=2)
print(f"報告已寫入 {outpath_md}")
print(f"JSON 已寫入 {outpath_json}")
PYEOF
執行完成。所有痛點引述均來自原始數據。