Skip to main content

The path a prompt takes

1

A batch is dispatched

weekly:prompt-batch collects every active prompt across active campaigns and dispatches WeeklyPromptBatchJob. Despite the name, the schedule runs daily at 03:00 UTC — the command name was kept for compatibility when the cadence changed.
2

Each provider is fetched

One Fetch* job per provider issues the prompt: OpenAI, Gemini, Claude, Perplexity and Grok through their APIs, and Google AI Overview / AI Mode through SerpApi (ProcessSerpApiOverviewJob, ProcessSerpApiAiModeJob). Every response is written as a PromptRun tagged with a shared batch_id.
3

Answers are parsed

The matching Process*AnswersJob runs VisibilityDetector over the answer text to decide whether the brand appeared and in what form, setting the run’s visibility fields. Brand variants generated during onboarding are what make this robust to the brand being referred to by a shortened or alternative name.
4

Competitors are extracted

Domains mentioned alongside the brand become DiscoveredCompetitor and CompetitorAppearance rows. ClassifyBatchCompetitorsJob (or competitors:classify) then asks an LLM which of them are genuine direct competitors.
5

Scores are calculated

competitors:score runs IndustryVisibilityScorer, applying the weighted formula in config/scoring.php to produce the industry visibility score shown on /visibility.
6

Screenshots are captured

TakeSearchScreenshotJob stores a SERP screenshot per run through ScreenshotOne, exposed on the model as screenshot_url.

Running it by hand

Waiting for 03:00 UTC is rarely what you want while developing. Each stage has a manual entry point:
These commands spend real money on provider APIs. --force bypasses the check that prevents re-running prompts that were processed recently, so it will re-bill every prompt in scope. Scope to a single campaign or prompt while developing.
A batch can also be triggered from the UI per campaign via POST /campaigns/{campaign}/generate-prompts, and GET /campaigns/{campaign}/generation-prompt-preview shows the exact generation prompt that would be sent — useful when tuning prompt generation without paying for a run.

Sentiment pipeline

Sentiment is a separate daily flow (sentiment:fetch-daily, 01:00 UTC) that dispatches FetchCampaignSentiment and per-provider jobs (FetchOpenAiSentimentJob, FetchGeminiSentimentJob, FetchGrokSentimentJob, FetchPerplexitySentimentJob). It runs through Services/BrandSentiment:
  1. TopicSelector picks which topics to evaluate this cycle
  2. TopicDataRetriever gathers answers using RetrievalPromptWriter
  3. TopicSentimentScorer scores mentions using EvaluationPromptWriter
  4. BrandSentimentFinalizer writes SentimentRecord and SentimentMention rows
The UI polls GET /api/sentiment/status while this is in flight.

Site intelligence

Independent of prompts, several jobs profile the tracked domain itself:

Cost tracking

Every provider call goes through TokenUsageTracker, which writes an LlmUsage row. config/ai.php carries cost_per_1k_tokens per model, so spend is derived from recorded token counts rather than fetched from the providers.