- Posicionamentos: novas campanhas saem só com Feed+Stories+Reels (FB+IG), sem Audience Network, Messenger, coluna direita ou Marketplace - Botão "Corrigir Posicionamentos" (ícone Target) em cada ad set existente — busca targeting atual, preserva geo/idade/interesses e aplica as restrições - Stats: compras, alcance, CPA e reach nos cards do dashboard e na tabela (campanha, ad set e anúncio); bug de leads/ROAS sempre 0 no nível campanha corrigido - Auditoria IA: prompt ciente do objetivo (Leads/Vendas/Tráfego), passa todas as métricas relevantes (CTR, CPC, CPL, CPA, alcance, impressões); hero da tela mostra cards dinâmicos conforme o tipo de campanha - strategist.ts: corrigido acesso indevido a actions/purchase_roas nos insights mapeados Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
// ============================================
|
|
// AI Layer — Performance Strategist
|
|
// ============================================
|
|
|
|
import { getAIClient } from './client';
|
|
import type { AccountOverview, CampaignWithInsights } from '@/lib/meta/types';
|
|
|
|
export interface StrategicAnalysis {
|
|
summary: string;
|
|
top_performers: string[];
|
|
issues: string[];
|
|
recommendations: string[];
|
|
suggested_actions: SuggestedAction[];
|
|
}
|
|
|
|
export interface SuggestedAction {
|
|
campaign_id: string;
|
|
campaign_name: string;
|
|
action: 'pause' | 'increase_budget' | 'reduce_budget' | 'create_variation';
|
|
reason: string;
|
|
priority: 'high' | 'medium' | 'low';
|
|
}
|
|
|
|
export async function analyzePerformance(
|
|
overview: AccountOverview,
|
|
campaigns: CampaignWithInsights[]
|
|
): Promise<StrategicAnalysis> {
|
|
const client = getAIClient();
|
|
|
|
const campaignSummary = campaigns.map((c) => {
|
|
const ins = c.insights as any;
|
|
return {
|
|
id: c.campaign.id,
|
|
name: c.campaign.name,
|
|
status: c.campaign.status,
|
|
objective: c.campaign.objective,
|
|
daily_budget: `R$${(parseInt(c.campaign.daily_budget || '0', 10) / 100).toFixed(2)}`,
|
|
spend: `R$${parseFloat(ins?.spend || '0').toFixed(2)}`,
|
|
ctr: `${parseFloat(ins?.ctr || '0').toFixed(2)}%`,
|
|
cpc: `R$${parseFloat(ins?.cpc || '0').toFixed(2)}`,
|
|
cpm: `R$${parseFloat(ins?.cpm || '0').toFixed(2)}`,
|
|
leads: ins?.leads || 0,
|
|
purchases: ins?.purchases || 0,
|
|
roas: ins?.roas ? ins.roas.toFixed(2) : '0',
|
|
};
|
|
});
|
|
|
|
const response = await client.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
temperature: 0.4,
|
|
response_format: { type: 'json_object' },
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: `Você é um estrategista de mídia paga expert em Facebook Ads.
|
|
Analise os dados de performance e forneça insights acionáveis.
|
|
|
|
Retorne um JSON com:
|
|
- "summary": resumo geral da performance em 2-3 frases (pt-BR)
|
|
- "top_performers": array de strings com os nomes das melhores campanhas e por quê
|
|
- "issues": array de strings com problemas identificados
|
|
- "recommendations": array de strings com recomendações estratégicas
|
|
- "suggested_actions": array de objetos com:
|
|
- "campaign_id": ID da campanha
|
|
- "campaign_name": nome
|
|
- "action": "pause" | "increase_budget" | "reduce_budget" | "create_variation"
|
|
- "reason": motivo da sugestão
|
|
- "priority": "high" | "medium" | "low"
|
|
|
|
Seja direto, use dados específicos, e foque em ROI.`,
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Analise a performance das campanhas:
|
|
|
|
OVERVIEW DA CONTA:
|
|
- Gasto Total: R$${overview.total_spend.toFixed(2)}
|
|
- Total Leads: ${overview.total_leads}
|
|
- ROAS Médio: ${overview.total_roas.toFixed(2)}x
|
|
- CTR Médio: ${overview.avg_ctr.toFixed(2)}%
|
|
- CPC Médio: R$${overview.avg_cpc.toFixed(2)}
|
|
- CPM Médio: R$${overview.avg_cpm.toFixed(2)}
|
|
- Campanhas Ativas: ${overview.campaigns_active}
|
|
- Campanhas Pausadas: ${overview.campaigns_paused}
|
|
|
|
CAMPANHAS DETALHADAS:
|
|
${JSON.stringify(campaignSummary, null, 2)}`,
|
|
},
|
|
],
|
|
});
|
|
|
|
const content = response.choices[0]?.message?.content;
|
|
if (!content) throw new Error('Empty response from AI');
|
|
|
|
return JSON.parse(content) as StrategicAnalysis;
|
|
}
|