O que Pricing Finder (Localizador de Preços) faz?
Informe a URL ou descrição do seu produto. Ele encontra 5 concorrentes, acessa suas páginas de preços reais e retorna um relatório completo de inteligência de preços: modelo dominante no seu segmento, tabela de referência de preços, análise de funcionalidades gated, mapa de posicionamento e uma recomendação concreta de preços para o seu produto.
Nenhuma chave de API necessária. Funciona inteiramente com dependências pip gratuitas. Chaves de API opcionais melhoram a qualidade.
Política de zero alucinação: Cada valor, nome de tier e funcionalidade na saída deve ser originado do conteúdo da página de preços ou de um snippet de busca DuckDuckGo. Isso se aplica a:
- Preços dos concorrentes: extraídos textualmente do conteúdo da página acessada
- "Contact Sales": registrado como está, nunca estimado ou substituído por um número
- Nomes dos tiers: copiados exatamente da página, sem parafrasear
- Listas de funcionalidades: extraídas do conteúdo da página, não inferidas do conhecimento do produto
- Observações de posicionamento: derivadas apenas dos dados da tabela de referência
Erros Comuns
| O agente vai querer... | Por que está errado |
|---|---|
| Preencher "Contact Sales" com um preço estimado | Nunca estime o preço enterprise. Registre como "Contact Sales" exatamente. |
| Usar conhecimento de treinamento para preços de concorrentes | Cada preço deve ser originado do conteúdo acessado ou de um snippet de busca. |
| Pular a etapa de confirmação dos concorrentes | Sempre mostre os concorrentes descobertos e aguarde confirmação. Concorrentes errados = benchmarks errados. |
| Recomendar um preço sem referenciar os dados de benchmark | Toda recomendação de preço deve citar um número específico da tabela de referência. |
| Marcar uma página como de alta qualidade quando o conteúdo < 500 caracteres | < 500 caracteres significa que a página não foi acessada -- marque data_quality como 'low' e use fallback do snippet de busca. |
| Usar travessões na saída | Substitua todos os travessões por hífens. |
Leia os Arquivos de Referência Antes de Cada Execução
cat references/pricing-models.md
cat references/extraction-guide.md
cat references/positioning-guide.md
Passo 1: Verificação de Configuração
echo "TAVILY_API_KEY: ${TAVILY_API_KEY:+set (qualidade de busca aprimorada)}${TAVILY_API_KEY:-não configurada, DuckDuckGo será usado (gratuito)}"
echo "FIRECRAWL_API_KEY: ${FIRECRAWL_API_KEY:+set (renderização JS aprimorada)}${FIRECRAWL_API_KEY:-não configurada, requests+BS4 será usado (gratuito)}"
echo ""
python3 -c "from ddgs import DDGS; import requests, bs4, html2text; print('Dependências OK')" 2>/dev/null \
|| echo "ERRO: Dependências ausentes. Execute: pip install ddgs requests beautifulsoup4 html2text"
Se as dependências estiverem ausentes: Pare imediatamente. Informe ao usuário: "Dependências Python ausentes. Execute para instalá-las: pip install ddgs requests beautifulsoup4 html2text -- tudo gratuito, sem necessidade de contas. Depois tente novamente."
Se apenas as chaves de API estiverem ausentes: Continue. DuckDuckGo e requests+BS4 são os padrões gratuitos.
Derive o slug do produto:
PRODUCT_SLUG=$(python3 -c "
from urllib.parse import urlparse
import sys, re
url = 'URL_AQUI'
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 "Slug do produto: $PRODUCT_SLUG"
Passo 2: Analisar a Entrada
Colete da conversa:
product_url: a URL para acessar (obrigatório, a menos que o usuário cole uma descrição diretamente)geography: opcional -- EUA / Europa / Índia / global. Padrão: EUA
Se o usuário fornecer apenas uma descrição colada (sem URL): Pule os Passos 3 e 4. Vá diretamente para o Passo 4 (análise do produto) usando o texto colado como product_content. Defina page_source como user_description e anote em data_quality_flags.
Se nem URL nem descrição: Pergunte: "Qual é a URL do seu produto ou startup? Ou cole uma breve descrição: o que faz, para quem é e o que o torna diferente."
Passo 3: Acessar a Página do Produto
Primário: Firecrawl (se FIRECRAWL_API_KEY estiver configurada)
curl -s -X POST https://api.firecrawl.dev/v1/scrape \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "URL_AQUI", "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'Accessado via Firecrawl: {len(content)} caracteres')
open('/tmp/pf-product-raw.md', 'w').write(content)
"
Fallback: requests + BS4 (gratuito, sempre disponível)
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_AQUI", 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'Accessado via requests+BS4: {len(content)} caracteres')
open('/tmp/pf-product-raw.md', 'w').write(content)
PYEOF
Ponto de verificação:
python3 -c "
content = open('/tmp/pf-product-raw.md').read()
if len(content) < 200:
print('ERRO: menos de 200 caracteres obtidos -- a página pode ser renderizada por JS')
else:
print(f'Conteúdo OK: {len(content)} caracteres')
"
Se o conteúdo < 200 caracteres: Informe ao usuário: "A página do produto retornou conteúdo insuficiente -- o site pode ser renderizado por JavaScript. Cole uma breve descrição: o que seu produto faz, para quem é e o que o torna diferente dos concorrentes."
Passo 4: Análise do Produto (IA)
Imprima o conteúdo da página:
python3 -c "
content = open('/tmp/pf-product-raw.md').read()[:5000]
print('=== PÁGINA DO PRODUTO (primeiros 5000 caracteres) ===')
print(content)
"
Instruções para a IA: Analise a página do produto acima e extraia:
product_name: o nome do produto ou empresaone_line_description: o que faz, para quem, proposta de valor principal. Menos de 20 palavras. Sem linguagem de marketing.industry_taxonomy:l1(nível superior: developer tools / fintech / healthtech / consumer / etc.),l2(setor: devops / payments / hr / etc.),l3(nicho específico: automação CI/CD / pagamentos embarcados / vídeo assíncrono / etc.)differentiators: exatamente 2-3 coisas específicas que distinguem este produto. Eles alimentam a recomendação -- seja específico. Respostas genéricas como "fácil de usar" não são aceitáveis.icp:buyer_persona(cargo),company_type,company_sizegeography_bias: EUA / Europa / Índia / globalpage_source: "live_page" ou "user_description"
Escreva em /tmp/pf-product-analysis.json:
python3 << 'PYEOF'
import json
analysis = {
# PREENCHA com sua análise acima
"product_name": "",
"one_line_description": "",
"industry_taxonomy": {"l1": "", "l2": "", "l3": ""},
"differentiators": [],
"icp": {"buyer_persona": "", "company_type": "", "company_size": ""},
"geography_bias": "EUA",
"page_source": "live_page"
}
json.dump(analysis, open('/tmp/pf-product-analysis.json', 'w'), indent=2)
print('Análise do produto salva.')
PYEOF
Verifique:
python3 -c "
import json
a = json.load(open('/tmp/pf-product-analysis.json'))
print('Produto:', a['product_name'])
print('Indústria:', a['industry_taxonomy']['l1'], '>', a['industry_taxonomy']['l2'], '>', a['industry_taxonomy']['l3'])
print('Diferenciadores:')
for d in a['differentiators']:
print(f' - {d}')
"
Passo 4b: Fase 1 -- Descoberta de Concorrentes
ls scripts/research.py 2>/dev/null && echo "script encontrado" || echo "ERRO: scripts/research.py não encontrado -- não é possível continuar"
python3 scripts/research.py \
--phase discover \
--product-analysis /tmp/pf-product-analysis.json \
--output /tmp/pf-competitors-raw.json
Imprima os resultados para revisão da IA:
python3 -c "
import json
data = json.load(open('/tmp/pf-competitors-raw.json'))
print(f'Buscas executadas: {len(data[\"competitor_searches\"])}')
for s in data['competitor_searches']:
print(f'\nConsulta: {s[\"query\"]}')
for r in s.get('results', [])[:6]:
print(f' - {r[\"title\"]} | {r[\"url\"]}')
print(f' {r.get(\"snippet\",\"\")[:150]}')
"
Instruções para a IA: Leia os resultados da busca acima. Escolha exatamente 5 empresas concorrentes que:
- Estejam nomeadas nos títulos ou snippets dos resultados
- Estejam no mesmo nicho L3 do produto sendo analisado
- Sejam produtos de software reais (não agências, listas de artigos ou sites de avaliação)
- Sejam distintas entre si
Para cada concorrente, escreva: name, url, pricing_url (sua página de preços -- infira como [url]/pricing se não encontrado nos snippets), description (uma frase do snippet), source_url.
Passo 5: Confirmação dos Concorrentes
python3 << 'PYEOF'
import json
analysis = json.load(open('/tmp/pf-product-analysis.json'))
# PREENCHA: 5 concorrentes dos resultados da busca acima
candidates = [
# {"name": str, "url": str, "pricing_url": str, "description": str, "source_url": str}
]
print(f"\nEncontrados 5 concorrentes para {analysis['product_name']} em {analysis['industry_taxonomy']['l3']}:\n")
for i, c in enumerate(candidates, 1):
print(f" {i}. {c['name']} -- {c['description']}")
print(f" Produto: {c['url']}")
print(f" Preços: {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
Informe ao usuário: "Estes são os 5 concorrentes dos quais vou extrair dados de preços. Adicione, remova ou troque qualquer um -- ou diga 'está bom' para continuar."
Aguarde a confirmação. Se o usuário editar a lista, atualize os candidatos conforme necessário. Depois, escreva a lista confirmada:
python3 << 'PYEOF'
import json
# PREENCHA: lista de concorrentes confirmada (após revisão do usuário)
confirmed = [
# {"name": str, "url": str, "pricing_url": str}
]
json.dump({"confirmed_competitors": confirmed}, open('/tmp/pf-competitors-confirmed.json', 'w'), indent=2)
print(f"Confirmados {len(confirmed)} concorrentes para pesquisa de preços.")
for c in confirmed:
print(f" - {c['name']} | preços: {c['pricing_url']}")
PYEOF
Passo 6: Fase 2 -- Acessar Páginas de Preços
python3 scripts/research.py \
--phase fetch-pricing \
--competitors /tmp/pf-competitors-confirmed.json \
--output /tmp/pf-pricing-raw.json
Isso acessa a página de preços de cada concorrente usando um fallback de 3 níveis:
- Acesso direto:
requests+beautifulsoup4+html2text - Cache do Google:
webcache.googleusercontent.com/search?q=cache:[url] - Busca DuckDuckGo:
"[concorrente]" planos de preços custo por mês(fallback de snippet)
Imprima o resumo do acesso:
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-raw.json'))
print(f'Concorrentes acessados: {data[\"competitors_fetched\"]}')
print()
for r in data['results']:
quality_label = {'high': 'BOM', 'medium': 'OK', 'low': 'APENAS SNIPPET'}.get(r['data_quality'], r['data_quality'])
print(f' {r[\"name\"]:20} {r[\"source\"]:15} {r[\"content_length\"]:5} caracteres [{quality_label}]')
"
Se um concorrente tiver data_quality: low: Isso significa que a página de preços foi bloqueada ou é renderizada por JS. A análise prosseguirá usando snippets de busca, mas a confiança para esse concorrente será anotada como baixa.
Passo 7: Extração de Preços (IA)
Imprima todo o conteúdo bruto de preços:
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-raw.json'))
for r in data['results']:
print(f'\n=== {r[\"name\"]} (fonte: {r[\"source\"]}, qualidade: {r[\"data_quality\"]}) ===')
print(f'URL de Preços: {r[\"pricing_url\"]}')
print(r['content'][:4000])
print('---')
"
Instruções para a IA: Para cada concorrente, extraia dados estruturados de preços do conteúdo acima. Siga references/extraction-guide.md para saber como identificar tiers, preços, limites e CTAs.
Regras de zero alucinação:
- Extraia preços textualmente do conteúdo apenas. Se um preço não estiver no conteúdo, escreva
null. - Registre "Contact Sales" exatamente como está. Nunca substitua por um número estimado.
data_quality: lowsignifica que os dados vieram de snippets de busca -- extraia o que está lá, mas não preencha lacunas com conhecimento de treinamento.- Para qualquer campo não presente no conteúdo: escreva
"não encontrado nos dados da página". - Preços anuais: sempre registre o equivalente mensal junto com o total anual.
Escreva em /tmp/pf-pricing-extracted.json:
python3 << 'PYEOF'
import json
# PREENCHA: um objeto por concorrente, seguindo o esquema abaixo
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, # ex.: "20%" ou "não encontrado nos dados da página"
# "free_tier": true | false,
# "free_trial": true | false,
# "free_trial_days": int | null,
# "tiers": [
# {
# "name": str,
# "price_monthly": float | null, # null se Contact Sales
# "price_annual_monthly": float | null, # equivalente mensal quando faturado anualmente
# "price_note": str, # "Contact Sales", "Free" ou vazio
# "seats": str, # "por assento", "ilimitado", "até 5", etc.
# "key_limits": [str], # armazenamento, chamadas de API, projetos, etc.
# "key_features": [str] # top 3-5 funcionalidades neste tier
# }
# ],
# "enterprise_tier": true | false,
# "enterprise_pricing": str, # "Contact Sales" ou preço real
# "regional_pricing": str | null # ex.: "Índia: ₹999/mês" ou null
# }
]
…
[truncado para tradução]
Passo 8: Gerar Relatório de Inteligência de Preços
python3 scripts/research.py \
--phase build-report \
--extracted /tmp/pf-pricing-extracted.json \
--product-analysis /tmp/pf-product-analysis.json \
--output /tmp/pf-report.md
Imprima o relatório:
cat /tmp/pf-report.md
Passo 9: Apresentar ao Usuário
Forneça ao usuário:
- O relatório completo de
/tmp/pf-report.md - As principais conclusões em formato conciso, respondendo: "Qual deve ser o preço do meu produto?" e "Por quê?"
- Pergunte se o usuário deseja refinar a análise para uma geografia diferente ou ajustar a lista de concorrentes