定价侦探 是做什么的?
告诉它你的产品 URL 或描述。它会找到 5 个竞争对手,抓取他们的实际定价页面,并返回一份完整的定价情报报告:你所在领域的主流模型、基准价格表、功能门控分析、定位图以及针对你的产品的具体定价建议。
无需任何 API 密钥。 完全依赖免费的 pip 依赖项运行。可选的 API 密钥可提升质量。
零幻觉政策: 输出中的每个价格点、层级名称和功能门控都必须追溯到抓取的定价页面内容或 DuckDuckGo 搜索摘要。这适用于:
- 竞争对手价格:仅从抓取的页面内容中逐字提取
- "联系销售":保持原样记录,绝不估计或替换为数字
- 层级名称:严格从页面复制,不得转述
- 功能列表:从页面内容提取,不得根据产品知识推断
- 定位观察:仅从基准表数据推导得出
常见错误
| AI 代理可能会... | 为什么这是错误的 |
|---|---|
| 用估计价格填充“联系销售” | 绝不要估计企业定价。原样记录为“联系销售”。 |
| 使用训练知识获取竞争对手价格 | 每个价格必须追溯到抓取的页面内容或搜索摘要。 |
| 跳过竞争对手确认步骤 | 总是展示发现的竞争对手并等待确认。错误的竞争对手 = 错误的基准。 |
| 未引用基准数据就推荐价格 | 每个价格推荐必须引用基准表中的具体数字。 |
| 当页面内容少于 500 个字符时标记为高质量 | < 500 字符意味着页面未被抓取 -- 将 data_quality 标记为 'low' 并使用搜索摘要回退。 |
| 在输出中使用破折线 | 将所有破折线替换为连字符。 |
每次运行前阅读参考文件
cat references/pricing-models.md
cat references/extraction-guide.md
cat references/positioning-guide.md
步骤 1:设置检查
echo "TAVILY_API_KEY: ${TAVILY_API_KEY:+已设置 (搜索质量提升)}${TAVILY_API_KEY:-未设置,将使用 DuckDuckGo (免费)}"
echo "FIRECRAWL_API_KEY: ${FIRECRAWL_API_KEY:+已设置 (JS 渲染增强)}${FIRECRAWL_API_KEY:-未设置,将使用 requests+BS4 (免费)}"
echo ""
python3 -c "from ddgs import DDGS; import requests, bs4, html2text; print('依赖项就绪')" 2>/dev/null \
|| echo "错误:缺少依赖项。请运行:pip install ddgs requests beautifulsoup4 html2text"
如果缺少依赖项: 立即停止。告知用户:“缺少 Python 依赖项。请运行以下命令安装:pip install ddgs requests beautifulsoup4 html2text -- 全部免费,无需账户。然后重试。”
如果仅缺少 API 密钥: 继续。DuckDuckGo 和 requests+BS4 是免费的默认选项。
生成产品标识符:
PRODUCT_SLUG=$(python3 -c "
from urllib.parse import urlparse
import sys, re
url = 'URL_HERE'
if url.startswith('http'):
host = urlparse(url).netloc.replace('www.', '')
print(host.split('.')[0])
else:
print(re.sub(r'[^a-z0-9]', '-', url[:30].lower()).strip('-'))
")
echo "产品标识符:$PRODUCT_SLUG"
步骤 2:解析输入
从对话中收集:
product_url:需要获取的 URL(必需,除非用户直接粘贴描述)geography:可选 -- 美国 / 欧洲 / 印度 / 全球。默认为美国
如果用户只提供粘贴的描述(无 URL): 跳过步骤 3 和 4。直接进入步骤 4(产品分析),将粘贴的文本用作 product_content。将 page_source 设置为 user_description,并在 data_quality_flags 中注明。
如果既没有 URL 也没有描述: 询问:“你的产品或创业公司的 URL 是什么?或者粘贴一段简短描述:它的功能、目标用户以及独特之处。”
步骤 3:获取产品页面
首选:Firecrawl(如果设置了 FIRECRAWL_API_KEY)
curl -s -X POST https://api.firecrawl.dev/v1/scrape \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "URL_HERE", "formats": ["markdown"], "onlyMainContent": true}' \
| python3 -c "
import sys, json
d = json.load(sys.stdin)
content = d.get('data', {}).get('markdown', '') or d.get('markdown', '')
print(f'通过 Firecrawl 获取: {len(content)} 个字符')
open('/tmp/pf-product-raw.md', 'w').write(content)
"
回退:requests + BS4(免费,始终可用)
python3 << 'PYEOF'
import requests, html2text, random
USER_AGENTS = [
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
]
headers = {"User-Agent": random.choice(USER_AGENTS), "Accept": "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8"}
resp = requests.get("URL_HERE", headers=headers, timeout=20, allow_redirects=True)
converter = html2text.HTML2Text()
converter.ignore_images = True
converter.body_width = 0
content = converter.handle(resp.text)[:8000]
print(f'通过 requests+BS4 获取: {len(content)} 个字符')
open('/tmp/pf-product-raw.md', 'w').write(content)
PYEOF
检查点:
python3 -c "
content = open('/tmp/pf-product-raw.md').read()
if len(content) < 200:
print('错误:获取的内容少于 200 个字符 -- 页面可能是 JS 渲染的')
else:
print(f'内容就绪:{len(content)} 个字符')
"
如果内容少于 200 个字符: 告知用户:“产品页面返回的内容太少 -- 该网站可能是 JavaScript 渲染的。请粘贴一段简短描述:你的产品功能、目标用户以及它与竞争对手的不同之处。”
步骤 4:产品分析(AI)
打印页面内容:
python3 -c "
content = open('/tmp/pf-product-raw.md').read()[:5000]
print('=== 产品页面(前 5000 个字符)===')
print(content)
"
AI 指令: 分析上面的产品页面并提取:
product_name:产品或公司名称one_line_description:功能、目标用户、核心价值主张。少于 20 词。无营销语言。industry_taxonomy:l1(顶级:开发者工具 / 金融科技 / 健康科技 / 消费类等),l2(领域:DevOps / 支付 / 人力资源等),l3(具体利基:CI/CD 自动化 / 嵌入式支付 / 异步视频等)differentiators:恰好 2-3 个区分此产品的具体点。它们将用于推荐 -- 必须具体。笼统的答案如“易于使用”不可接受。icp:buyer_persona(职位),company_type,company_sizegeography_bias:美国 / 欧洲 / 印度 / 全球page_source:"live_page" 或 "user_description"
写入 /tmp/pf-product-analysis.json:
python3 << 'PYEOF'
import json
analysis = {
# 从你的分析中填写
"product_name": "",
"one_line_description": "",
"industry_taxonomy": {"l1": "", "l2": "", "l3": ""},
"differentiators": [],
"icp": {"buyer_persona": "", "company_type": "", "company_size": ""},
"geography_bias": "US",
"page_source": "live_page"
}
json.dump(analysis, open('/tmp/pf-product-analysis.json', 'w'), indent=2)
print('产品分析已写入。')
PYEOF
验证:
python3 -c "
import json
a = json.load(open('/tmp/pf-product-analysis.json'))
print('产品:', a['product_name'])
print('行业:', a['industry_taxonomy']['l1'], '>', a['industry_taxonomy']['l2'], '>', a['industry_taxonomy']['l3'])
print('差异化因素:')
for d in a['differentiators']:
print(f' - {d}')
"
步骤 4b:阶段 1 -- 竞争对手发现
ls scripts/research.py 2>/dev/null && echo "脚本已找到" || echo "错误:scripts/research.py 未找到 -- 无法继续"
python3 scripts/research.py \
--phase discover \
--product-analysis /tmp/pf-product-analysis.json \
--output /tmp/pf-competitors-raw.json
打印结果供 AI 审查:
python3 -c "
import json
data = json.load(open('/tmp/pf-competitors-raw.json'))
print(f'搜索次数:{len(data[\"competitor_searches\"])}')
for s in data['competitor_searches']:
print(f'\n查询:{s[\"query\"]}')
for r in s.get('results', [])[:6]:
print(f' - {r[\"title\"]} | {r[\"url\"]}')
print(f' {r.get(\"snippet\",\"\")[:150]}')
"
AI 指令: 阅读上面的搜索结果。选择恰好 5 家竞争对手公司,需满足:
- 出现在搜索结果标题或摘要中
- 属于与被分析产品相同的 L3 利基
- 是实际的软件产品(非代理商、列表文章或评论网站)
- 彼此不同
为每个竞争对手写入:name、url、pricing_url(其定价页面 -- 如果摘要中未找到,推断为 [url]/pricing)、description(从摘要中提取一句话)、source_url。
步骤 5:竞争对手确认
python3 << 'PYEOF'
import json
analysis = json.load(open('/tmp/pf-product-analysis.json'))
# 填写:来自搜索结果的 5 个竞争对手
candidates = [
# {"name": str, "url": str, "pricing_url": str, "description": str, "source_url": str}
]
print(f"\n在 {analysis['industry_taxonomy']['l3']} 中找到 {analysis['product_name']} 的 5 个竞争对手:\n")
for i, c in enumerate(candidates, 1):
print(f" {i}. {c['name']} -- {c['description']}")
print(f" 产品: {c['url']}")
print(f" 定价: {c['pricing_url']}")
data = json.load(open('/tmp/pf-competitors-raw.json'))
data['competitor_candidates'] = candidates
json.dump(data, open('/tmp/pf-competitors-raw.json', 'w'), indent=2)
PYEOF
告知用户:“这是我将从中获取定价数据的 5 个竞争对手。您可以添加、删除或替换任何内容 -- 或者说“看起来不错”以继续。”
等待确认。 如果用户编辑了列表,请相应更新候选项。然后写入确认列表:
python3 << 'PYEOF'
import json
# 填写:已确认的竞争对手列表(用户审核后)
confirmed = [
# {"name": str, "url": str, "pricing_url": str}
]
json.dump({"confirmed_competitors": confirmed}, open('/tmp/pf-competitors-confirmed.json', 'w'), indent=2)
print(f"已确认 {len(confirmed)} 个竞争对手进行定价研究。")
for c in confirmed:
print(f" - {c['name']} | 定价: {c['pricing_url']}")
PYEOF
步骤 6:阶段 2 -- 获取定价页面
python3 scripts/research.py \
--phase fetch-pricing \
--competitors /tmp/pf-competitors-confirmed.json \
--output /tmp/pf-pricing-raw.json
这使用三级回退获取每个竞争对手的定价页面:
- 直接获取:
requests+beautifulsoup4+html2text - Google 缓存:
webcache.googleusercontent.com/search?q=cache:[url] - DuckDuckGo 搜索:
"[竞争对手]" pricing plans cost per month(摘要回退)
打印获取摘要:
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-raw.json'))
print(f'已获取竞争对手数:{data[\"competitors_fetched\"]}')
print()
for r in data['results']:
quality_label = {'high': '良好', 'medium': '尚可', 'low': '仅摘要'}.get(r['data_quality'], r['data_quality'])
print(f' {r[\"name\"]:20} {r[\"source\"]:15} {r[\"content_length\"]:5} 个字符 [{quality_label}]')
"
如果某个竞争对手的 data_quality: low: 这意味着定价页面被屏蔽或由 JS 渲染。分析将继续使用搜索摘要,但该竞争对手的置信度将标记为低。
步骤 7:定价提取(AI)
打印所有原始定价内容:
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-raw.json'))
for r in data['results']:
print(f'\n=== {r[\"name\"]} (来源:{r[\"source\"]}, 质量:{r[\"data_quality\"]}) ===')
print(f'定价 URL:{r[\"pricing_url\"]}')
print(r['content'][:4000])
print('---')
"
AI 指令: 对每个竞争对手,根据上面的内容提取结构化的定价数据。按照 references/extraction-guide.md 来识别层级、价格、限制和 CTA。
零幻觉规则:
- 仅从内容中逐字提取价格。如果内容中没有价格,写入
null。 - 原样记录“联系销售”。绝不能替换为估计数字。
data_quality: low表示数据来自搜索摘要 -- 提取已有的内容,但不要用训练知识填补空白。- 对于内容中不存在的任何字段:写入
"not found in page data"。 - 年度价格:始终记录每月的等价价格以及年度总额。
写入 /tmp/pf-pricing-extracted.json:
python3 << 'PYEOF'
import json
# 填写:每个竞争对手一个对象,遵循以下模式
extracted = [
# {
# "competitor": str,
# "pricing_url": str,
# "data_quality": "high" | "medium" | "low",
# "pricing_model": "per-seat" | "flat-rate" | "usage-based" | "freemium" | "tiered-flat" | "hybrid",
# "billing_cadence": ["monthly"] | ["annual"] | ["monthly", "annual"],
# "annual_discount": str, # 例如 "20%" 或 "not found in page data"
# "free_tier": true | false,
# "free_trial": true | false,
# "free_trial_days": int | null,
# "tiers": [
# {
# "name": str,
# "price_monthly": float | null, # 如果联系销售则为 null
# "price_annual_monthly": float | null, # 按年计费时每月的等价价格
# "price_note": str, # "联系销售", "免费", 或空
# "seats": str, # "每个席位", "无限制", "最多 5 个" 等
# "key_limits": [str], # 存储、API 调用、项目等
# "key_features": [str] # 此层级的 3-5 个主要功能
# }
# ],
# "enterprise_tier": true | false,
# "enterprise_pricing": str, # "联系销售" 或实际价格
# "regional_pricing": str | null # 例如 "印度:₹999/月" 或 null
# }
]
…
(由于原始文件被截断,其余步骤已省略。完整工作流程包括:基准定价分析、功能门控分析、竞争定位图以及定价策略建议。)