Documentation Index
Fetch the complete documentation index at: https://mintlify.com/scr83/reportr/llms.txt
Use this file to discover all available pages before exploring further.
AI Insights System
The AI Insights system uses Anthropic’s Claude API to generate strategic SEO recommendations based on report data. It provides intelligent, data-driven insights with fallback logic for reliability.
Architecture Overview
Core Component
Class: AIInsightsGenerator
Location: src/lib/ai/insights-generator.ts:15
export class AIInsightsGenerator {
async generateInsights(data: Partial<ReportData>): Promise<AIInsight[]>
}
Data Flow
1. Report Data → Format for Claude API
2. Build Structured Prompt → Include data context
3. Call Claude API → Get JSON response
4. Parse Response → Extract insights array
5. Return AIInsight[] → 5 strategic recommendations
↓ (on error)
6. Fallback Logic → Generate rule-based insights
Claude API Integration
Initialization
Location: src/lib/ai/insights-generator.ts:11
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
Environment Variable: ANTHROPIC_API_KEY
Model Configuration
Current Model: claude-sonnet-4-20250514
Location: src/lib/ai/insights-generator.ts:40
const requestPayload = {
model: 'claude-sonnet-4-20250514',
max_tokens: 2000,
messages: [{
role: 'user' as const,
content: prompt
}]
};
Token Budget:
- Input: ~1500 tokens (report data + prompt)
- Output: ~500 tokens (5 insights in JSON)
- Total: ~2000 tokens per request
Cost Estimate: ~0.006perreport(at0.003 per 1K tokens)
Prompt Engineering
Prompt Structure
Location: src/lib/ai/insights-generator.ts:87
Prompt Components
- Instruction Header - Defines output requirements
- Quality Criteria - Ensures actionable, specific insights
- Output Schema - JSON structure specification
- Example Insight - Shows ideal format
- Data Context - Client’s actual metrics
- Format Enforcement - Requests valid JSON
Full Prompt Template
return `
Generate exactly 5 strategic recommendations based on this SEO data.
Each recommendation MUST be:
1. SPECIFIC to this client's actual data (reference specific keywords, pages, or metrics)
2. ACTIONABLE with clear next steps
3. PRIORITIZED based on potential impact
For each recommendation provide:
- title: Action-oriented headline (5-8 words)
- description: 2-3 sentences explaining the opportunity and approach. Reference specific data points.
- priority: "high", "medium", or "low" based on potential ROI
- category: "keyword", "technical", "content", or "performance"
- expectedImpact: Quantified expected outcome (e.g., "Potential 20% increase in organic traffic")
- actionItems: Array of 2-3 specific action steps
Example of GOOD recommendation:
{
"title": "Target Page 2 Keywords for Quick Wins",
"description": "You have 12 keywords ranking positions 11-20 that are close to page 1. Keywords like 'digital marketing services' at position 14 with 450 monthly impressions represent immediate opportunities. Optimizing these pages could move them to page 1 within 30-60 days.",
"priority": "high",
"category": "keyword",
"expectedImpact": "Moving 5 keywords to page 1 could generate 200+ additional monthly clicks",
"actionItems": ["Identify top 5 keywords in positions 11-15", "Add target keyword to H1 and first paragraph", "Build 2-3 internal links to each target page"]
}
DATA TO ANALYZE:
- Domain: ${domain}
- Total Clicks: ${clicks} (${clicksChange}% change)
- Total Impressions: ${impressions}
- Average Position: ${position}
- Organic Sessions: ${organicSessions} (${sessionsChange}% change)
- Mobile Score: ${mobileScore}/100
- Desktop Score: ${desktopScore}/100
- Top Keywords: ${topKeywords.slice(0, 10).join(', ') || 'No data'}
- Top Pages: ${topPages.slice(0, 5).join(', ') || 'No data'}
Return ONLY valid JSON array with exactly 5 recommendations in this format:
{
"insights": [
{
"title": "Action-oriented title",
"description": "Detailed explanation referencing specific data",
"priority": "high|medium|low",
"category": "keyword|technical|content|performance",
"expectedImpact": "Quantified expected result",
"actionItems": ["Step 1", "Step 2", "Step 3"],
"dataSource": ["Google Search Console", "PageSpeed Insights", "etc."]
}
]
}
Make insights specific, actionable, and data-driven. Reference actual metrics from the data provided.`;
Prompt Best Practices
- Specificity Enforcement - Requires referencing actual data
- Format Constraints - Demands JSON output for reliable parsing
- Quality Examples - Shows ideal insight structure
- Context Loading - Includes all relevant metrics
- Action Orientation - Forces actionable recommendations
Response Processing
Parsing Claude’s Response
Location: src/lib/ai/insights-generator.ts:156
private parseInsights(aiResponse: string): AIInsight[] {
try {
// Strip markdown formatting if present
const cleanResponse = aiResponse
.replace(/```json\s*/, '')
.replace(/```\s*$/, '')
.trim();
const parsed = JSON.parse(cleanResponse);
return parsed.insights.map((insight: any, index: number) => ({
id: `ai-insight-${Date.now()}-${index}`,
title: insight.title,
description: insight.description,
priority: insight.priority,
category: insight.category,
expectedImpact: insight.expectedImpact,
actionItems: insight.actionItems,
dataSource: insight.dataSource
}));
} catch (error) {
console.error('Failed to parse AI insights:', error);
return this.getFallbackInsights();
}
}
Response Validation
Checks:
- Valid JSON format
- Contains
insights array
- Each insight has required fields
- Priority values are valid (
high|medium|low)
- Category values are valid (
keyword|technical|content|performance)
On Failure: Falls back to rule-based insights
Fallback System
Rule-Based Insights
Location: src/lib/ai/insights-generator.ts:182
Provides reliable insights when AI fails or API is unavailable.
Fallback Logic
private getFallbackInsights(data?: Partial<ReportData>): AIInsight[] {
const insights: AIInsight[] = [];
const timestamp = Date.now();
// 1. Mobile Performance Check
const mobileScore = data?.summary?.mobileScore || 50;
if (mobileScore < 75) {
insights.push({
id: `fallback-mobile-${timestamp}-1`,
title: 'Improve Mobile Page Speed',
description: `Your mobile page speed score is ${mobileScore}/100...`,
priority: mobileScore < 50 ? 'high' : 'medium',
category: 'technical',
expectedImpact: `Improving to 85+ could increase mobile traffic by 15-25%`,
actionItems: [
'Compress and optimize all images to WebP format',
'Minimize JavaScript and CSS file sizes',
'Implement lazy loading for non-critical content'
],
dataSource: ['PageSpeed Insights']
});
}
// 2. Keyword Position Optimization
const avgPosition = data?.summary?.averagePosition || 15;
if (avgPosition > 8) {
insights.push({
id: `fallback-keywords-${timestamp}-2`,
title: 'Target Page 2 Keywords for Quick Wins',
description: `Your average keyword position is ${avgPosition.toFixed(1)}...`,
priority: 'high',
category: 'keyword',
// ... more insight data
});
}
// 3-5: Additional rule-based insights
// ...
return insights.slice(0, 5);
}
Fallback Triggers
Conditions that trigger fallback:
- API Error - Claude API unreachable or returns error
- Invalid JSON - Response cannot be parsed
- Missing Fields - Required insight properties missing
- Timeout - API request exceeds timeout limit
- Rate Limit - Anthropic API quota exceeded
Fallback Insight Categories
Trigger: mobileScore < 75
Priority: High if score < 50, Medium otherwise
{
title: 'Improve Mobile Page Speed',
category: 'technical',
actionItems: [
'Compress and optimize all images to WebP format',
'Minimize JavaScript and CSS file sizes',
'Implement lazy loading for non-critical content'
]
}
2. Keyword Position
Trigger: averagePosition > 8
Priority: High
{
title: 'Target Page 2 Keywords for Quick Wins',
category: 'keyword',
actionItems: [
'Identify top 5 keywords in positions 11-15',
'Add target keyword to H1 and first paragraph',
'Build 2-3 internal links to each target page'
]
}
3. Content Expansion
Trigger: Always included
Priority: Medium
{
title: 'Expand Content for Target Topics',
category: 'content',
actionItems: [
'Identify top 5 topics relevant to your business',
'Create pillar pages for each main topic',
'Develop 8-10 supporting articles per pillar page'
]
}
4. Technical SEO
Trigger: Always included
Priority: Medium
{
title: 'Enhance Technical SEO Foundation',
category: 'technical',
actionItems: [
'Conduct comprehensive site audit for crawl errors',
'Optimize XML sitemaps and robots.txt',
'Implement structured data markup for key pages'
]
}
Trigger: Always included
Priority: Low
{
title: 'Implement Advanced Performance Tracking',
category: 'performance',
actionItems: [
'Set up conversion tracking for all business goals',
'Implement heat map tracking on key landing pages',
'Create custom dashboards for performance monitoring'
]
}
Insight Data Structure
AIInsight Type
Location: src/types/google-api.ts:123
export interface AIInsight {
id: string; // Unique identifier
title: string; // Action-oriented headline
description: string; // Detailed explanation
priority: 'high' | 'medium' | 'low'; // Impact priority
category: 'keyword' | 'technical' | 'content' | 'performance';
expectedImpact: string; // Quantified outcome
actionItems: string[]; // Specific steps
dataSource: string[]; // Data attribution
}
Example Insight Object
{
id: "ai-insight-1709584320123-0",
title: "Optimize Top Landing Pages for Conversions",
description: "Your top 3 landing pages receive 65% of organic traffic but have a 72% bounce rate. Pages like /services and /pricing show high engagement time (3:24 avg) but low conversion. Improving CTAs and form design could significantly boost conversions.",
priority: "high",
category: "content",
expectedImpact: "Reducing bounce rate to 55% could increase conversions by 30-40%",
actionItems: [
"A/B test primary CTA placement and copy on /services page",
"Simplify contact form from 8 fields to 3-4 essential fields",
"Add trust signals (testimonials, case studies) above the fold"
],
dataSource: [
"Google Analytics 4",
"Google Search Console"
]
}
Integration Points
Report Generation Pipeline
Used in: src/lib/reports/generate-report.ts (planned)
import { AIInsightsGenerator } from '@/lib/ai/insights-generator';
// After fetching all API data
const generator = new AIInsightsGenerator();
const reportData = {
client: clientInfo,
summary: aggregatedMetrics,
searchConsole: gscData,
analytics: ga4Data,
pageSpeed: pageSpeedData
};
const insights = await generator.generateInsights(reportData);
// insights array included in final report
PDF Report Display
Component: src/lib/pdf/components/StrategicRecommendationsPage.tsx
Insights are rendered as a dedicated page in PDF reports with:
- Priority badges (High/Medium/Low)
- Category icons
- Expandable action items
- Expected impact callouts
Database Storage
Insights can be stored in the database for historical tracking:
model Report {
id String
insights Json? // Store AIInsight[] as JSON
// ... other fields
}
Error Handling
Comprehensive Logging
Location: Throughout src/lib/ai/insights-generator.ts
console.log('🚀 [AI-INSIGHTS] Starting AI insights generation...');
console.log('🚀 [AI-INSIGHTS] Input data structure:', {
hasClient: !!data.client,
hasSummary: !!data.summary,
totalClicks: data.summary?.totalClicks
});
console.log('🚀 [AI-INSIGHTS] Making API call to Claude...');
// On success
console.log('🚀 [AI-INSIGHTS] ✅ Successfully generated AI insights:', {
count: insights.length,
categories: insights.map(i => i.category)
});
// On error
console.error('❌ [AI-INSIGHTS] AI insights generation failed:', {
error: error instanceof Error ? error.message : error,
stack: error instanceof Error ? error.stack : undefined
});
console.log('🔄 [AI-INSIGHTS] Falling back to rule-based insights...');
Error Recovery Flow
- Catch API Error - Log full error details
- Log Fallback - Indicate switch to rule-based system
- Generate Fallback - Create 5 rule-based insights
- Return Successfully - Never throw error to report generation
Graceful Degradation
Philosophy: Reports should always succeed, even if AI fails.
try {
return await anthropic.messages.create(requestPayload);
} catch (error) {
console.error('AI Error:', error);
// Return rule-based insights instead
return this.getFallbackInsights(data);
}
Timing
Typical Generation Time:
- Claude API call: 2-5 seconds
- Parsing: < 100ms
- Total: ~3-5 seconds per report
Cost Optimization
Token Usage
estimateInsightCost(dataSize: number): number {
// Rough estimate: ~1500 tokens input + 500 tokens output
// Claude pricing: ~$0.003 per 1K tokens
const estimatedTokens = 2000;
return (estimatedTokens / 1000) * 0.003;
}
Monthly Cost Example:
- 100 reports/month = 200K tokens
- Cost: $0.60/month
- Per report: $0.006
Caching Strategy
Recommended: Cache insights for duplicate report requests
// Pseudo-code
const cacheKey = `insights:${clientId}:${startDate}:${endDate}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const insights = await generator.generateInsights(data);
await redis.set(cacheKey, JSON.stringify(insights), 'EX', 3600);
Testing
Test Cases
1. Successful AI Generation
const data = {
client: { domain: 'example.com' },
summary: { totalClicks: 1234, averagePosition: 8.5 },
searchConsole: { topKeywords: [...] }
};
const insights = await generator.generateInsights(data);
expect(insights).toHaveLength(5);
expect(insights[0]).toHaveProperty('title');
expect(insights[0]).toHaveProperty('actionItems');
2. Fallback on API Error
// Mock API failure
anthropicMock.mockRejectedValue(new Error('API Error'));
const insights = await generator.generateInsights(data);
// Should still return 5 insights
expect(insights).toHaveLength(5);
expect(insights[0].id).toContain('fallback');
3. Invalid JSON Response
// Mock malformed response
anthropicMock.mockResolvedValue({
content: [{ type: 'text', text: 'Invalid JSON{' }]
});
const insights = await generator.generateInsights(data);
// Should fallback gracefully
expect(insights).toHaveLength(5);
Best Practices
Prompt Optimization
- Include Examples - Show Claude ideal output format
- Reference Data - Require specific metric references
- Constrain Output - Request exact JSON structure
- Action-Oriented - Demand actionable recommendations
- Quantify Impact - Ask for measurable expected outcomes
Error Handling
- Never Fail Reports - Always return insights (AI or fallback)
- Log Extensively - Track AI performance and failures
- Monitor Costs - Track token usage and API costs
- Fallback Quality - Ensure rule-based insights are valuable
Data Preparation
- Clean Input - Sanitize data before sending to Claude
- Format Numbers - Present metrics clearly
- Prioritize Context - Include most relevant data first
- Limit Length - Keep prompts under 2000 tokens
Monitoring
Metrics to Track
- AI Success Rate - % of reports using AI vs fallback
- Generation Time - Average time to generate insights
- Token Usage - Total tokens consumed per report
- Cost per Report - Track actual API costs
- Error Types - Categorize failures (API, parsing, timeout)
Example Logging
// Track AI performance
await prisma.apiUsage.create({
data: {
service: 'anthropic',
endpoint: 'messages',
tokensUsed: response.usage.total_tokens,
cost: estimateInsightCost(response.usage.total_tokens),
success: true
}
});