From dd10b43aabd25e834aa727cd6831e97254a5edec Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Fri, 17 Apr 2026 02:54:33 +0700 Subject: [PATCH] refactor: extract analysis prompts from content-analyzer (224/115 lines) Move system prompt, user prompt, and performance context builders into PRAutoBlogger_Analysis_Prompts. Analyzer retains orchestration, data fetch, parsing, and storage. Backward-compatible private proxies kept. Both files well under 300-line limit. Co-Authored-By: Claude Opus 4.6 --- includes/core/class-analysis-prompts.php | 115 ++++++++++++++++ includes/core/class-content-analyzer.php | 163 ++++++----------------- 2 files changed, 155 insertions(+), 123 deletions(-) create mode 100644 includes/core/class-analysis-prompts.php diff --git a/includes/core/class-analysis-prompts.php b/includes/core/class-analysis-prompts.php new file mode 100644 index 0000000..b7f5cba --- /dev/null +++ b/includes/core/class-analysis-prompts.php @@ -0,0 +1,115 @@ +prefix . 'prautoblogger_content_scores'; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $top_posts = $wpdb->get_results( + "SELECT cs.post_id, cs.composite_score, p.post_title + FROM {$scores_table} cs + JOIN {$wpdb->posts} p ON p.ID = cs.post_id + WHERE cs.composite_score > 0 + ORDER BY cs.composite_score DESC + LIMIT 5", + ARRAY_A + ); + + if ( empty( $top_posts ) ) { + return ''; + } + + $lines = [ 'Top performing past articles (learn from these):' ]; + foreach ( $top_posts as $post ) { + $lines[] = sprintf( + '- "%s" (score: %.1f)', + $post['post_title'], + (float) $post['composite_score'] + ); + } + + return implode( "\n", $lines ); + } +} diff --git a/includes/core/class-content-analyzer.php b/includes/core/class-content-analyzer.php index a615c06..52fc11d 100755 --- a/includes/core/class-content-analyzer.php +++ b/includes/core/class-content-analyzer.php @@ -7,14 +7,16 @@ declare(strict_types=1); * * Uses a cheap model (configurable) to keep analysis costs low. * Batches source data into manageable chunks for the LLM context window. + * Prompt construction is delegated to PRAutoBlogger_Analysis_Prompts. * * Triggered by: PRAutoBlogger::run_generation_pipeline() (step 2). - * Dependencies: LLM_Provider_Interface, Cost_Tracker, WordPress $wpdb. + * Dependencies: LLM_Provider_Interface, Cost_Tracker, PRAutoBlogger_Analysis_Prompts, WordPress $wpdb. * - * @see providers/interface-llm-provider.php — LLM used for analysis. - * @see core/class-cost-tracker.php — Logs API costs. - * @see core/class-idea-scorer.php — Consumes analysis results. - * @see ARCHITECTURE.md — Data flow step 2. + * @see core/class-analysis-prompts.php — System/user prompt builders + performance context. + * @see providers/interface-llm-provider.php — LLM used for analysis. + * @see core/class-cost-tracker.php — Logs API costs. + * @see core/class-idea-scorer.php — Consumes analysis results. + * @see ARCHITECTURE.md — Data flow step 2. */ class PRAutoBlogger_Content_Analyzer { @@ -37,11 +39,9 @@ class PRAutoBlogger_Content_Analyzer { * * Side effects: LLM API call, database reads/writes. * - * @return PRAutoBlogger_Analysis_Result[] Detected patterns. - */ - /** * @param int $target_idea_count How many distinct topic ideas the LLM should produce. * Defaults to 6 so the scorer has enough to pick from. + * @return PRAutoBlogger_Analysis_Result[] Detected patterns. */ public function analyze_recent_data( int $target_idea_count = 6 ): array { $source_items = $this->fetch_recent_source_data(); @@ -54,14 +54,10 @@ class PRAutoBlogger_Content_Analyzer { $niche = get_option( 'prautoblogger_niche_description', '' ); $model = get_option( 'prautoblogger_analysis_model', PRAUTOBLOGGER_DEFAULT_ANALYSIS_MODEL ); - // Build the source data summary for the LLM (trim to avoid huge contexts). - $summary = $this->build_source_summary( $source_items ); - - // Include past performance data for self-improvement. - $performance_context = $this->get_performance_context(); - - $system_prompt = $this->build_system_prompt( $niche, $performance_context, $target_idea_count ); - $user_prompt = $this->build_user_prompt( $summary, $target_idea_count ); + $summary = $this->build_source_summary( $source_items ); + $performance_context = PRAutoBlogger_Analysis_Prompts::get_performance_context(); + $system_prompt = PRAutoBlogger_Analysis_Prompts::build_system_prompt( $niche, $performance_context, $target_idea_count ); + $user_prompt = PRAutoBlogger_Analysis_Prompts::build_user_prompt( $summary, $target_idea_count ); $response = $this->llm->send_chat_completion( [ @@ -76,7 +72,6 @@ class PRAutoBlogger_Content_Analyzer { ] ); - // Log the cost. $this->cost_tracker->log_api_call( null, 'analysis', @@ -86,15 +81,38 @@ class PRAutoBlogger_Content_Analyzer { $response['completion_tokens'] ); - // Parse LLM response into Analysis_Result objects. $results = $this->parse_analysis_response( $response['content'], $source_items ); - - // Store in database. $this->store_results( $results ); return $results; } + // ── Backward-compatible proxies (prompt building moved to Analysis_Prompts) ── + + /** + * @deprecated Use PRAutoBlogger_Analysis_Prompts::build_system_prompt() directly. + * @see core/class-analysis-prompts.php + */ + private function build_system_prompt( string $niche, string $performance_context, int $target_count = 6 ): string { + return PRAutoBlogger_Analysis_Prompts::build_system_prompt( $niche, $performance_context, $target_count ); + } + + /** + * @deprecated Use PRAutoBlogger_Analysis_Prompts::build_user_prompt() directly. + * @see core/class-analysis-prompts.php + */ + private function build_user_prompt( string $summary, int $target_count = 6 ): string { + return PRAutoBlogger_Analysis_Prompts::build_user_prompt( $summary, $target_count ); + } + + /** + * @deprecated Use PRAutoBlogger_Analysis_Prompts::get_performance_context() directly. + * @see core/class-analysis-prompts.php + */ + private function get_performance_context(): string { + return PRAutoBlogger_Analysis_Prompts::get_performance_context(); + } + /** * Fetch source data collected in the last 24 hours. * @@ -120,7 +138,6 @@ class PRAutoBlogger_Content_Analyzer { * Prioritizes high-engagement content and keeps total text under ~8000 tokens. * * @param array> $items Source data rows. - * * @return string Formatted text summary. */ private function build_source_summary( array $items ): string { @@ -150,109 +167,11 @@ class PRAutoBlogger_Content_Analyzer { return implode( "\n", $parts ); } - /** - * Get past content performance data to feed into analysis for self-improvement. - * - * @return string Summary of what topics performed well/poorly. - */ - private function get_performance_context(): string { - global $wpdb; - $scores_table = $wpdb->prefix . 'prautoblogger_content_scores'; - - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery - $top_posts = $wpdb->get_results( - "SELECT cs.post_id, cs.composite_score, p.post_title - FROM {$scores_table} cs - JOIN {$wpdb->posts} p ON p.ID = cs.post_id - WHERE cs.composite_score > 0 - ORDER BY cs.composite_score DESC - LIMIT 5", - ARRAY_A - ); - - if ( empty( $top_posts ) ) { - return ''; - } - - $lines = [ 'Top performing past articles (learn from these):' ]; - foreach ( $top_posts as $post ) { - $lines[] = sprintf( - '- "%s" (score: %.1f)', - $post['post_title'], - (float) $post['composite_score'] - ); - } - - return implode( "\n", $lines ); - } - - /** - * Build the system prompt for content analysis. - * - * @param string $niche Niche description from settings. - * @param string $performance_context Past performance data. - * - * @return string - */ - private function build_system_prompt( string $niche, string $performance_context, int $target_count = 6 ): string { - $prompt = "You are a content strategist analyzing social media discussions to find article ideas for a blog"; - if ( '' !== $niche ) { - $prompt .= " in the {$niche} niche"; - } - $prompt .= ".\n\n"; - - $prompt .= "IMPORTANT: You must identify exactly {$target_count} DISTINCT article ideas. "; - $prompt .= "Each idea must cover a substantially different topic — no two ideas should overlap "; - $prompt .= "in their main subject. Aim for diversity across these categories:\n"; - $prompt .= "1. QUESTIONS: Recurring questions people ask (\"How do I...\", \"What is...\", \"Is it safe to...\")\n"; - $prompt .= "2. COMPLAINTS: Pain points, frustrations, or problems people report\n"; - $prompt .= "3. COMPARISONS: Product/method comparisons people discuss (\"X vs Y\", \"Which is better\")\n"; - $prompt .= "4. NEWS: Recent developments, rule changes, or trends people are discussing\n"; - $prompt .= "5. GUIDES: How-to topics, best practices, or educational content people need\n\n"; - - $prompt .= "Spread your {$target_count} ideas across multiple categories. "; - $prompt .= "Be specific — \"peptide dosing for BPC-157\" is better than \"peptide information\". "; - $prompt .= "Each idea should be narrow enough to be a single focused blog post.\n\n"; - - $prompt .= "For each idea, provide:\n"; - $prompt .= "- type: 'question', 'complaint', 'comparison', 'news', or 'guide'\n"; - $prompt .= "- topic: A clear, specific, narrow topic description\n"; - $prompt .= "- summary: 1-2 sentence summary of why this topic matters\n"; - $prompt .= "- frequency: How many of the provided posts relate to this topic\n"; - $prompt .= "- relevance_score: 0.0 to 1.0 indicating how relevant and article-worthy this is\n"; - $prompt .= "- suggested_title: A compelling, unique blog post title\n"; - $prompt .= "- key_points: Array of 3-5 key points the article should cover\n"; - $prompt .= "- target_keywords: Array of SEO keywords for this topic\n\n"; - - if ( '' !== $performance_context ) { - $prompt .= $performance_context . "\n\n"; - } - - $prompt .= "Respond with valid JSON containing exactly {$target_count} patterns:\n"; - $prompt .= '{"patterns": [{"type": "...", "topic": "...", "summary": "...", "frequency": N, "relevance_score": 0.X, "suggested_title": "...", "key_points": [...], "target_keywords": [...]}]}'; - - return $prompt; - } - - /** - * Build the user prompt containing the actual source data. - * - * @param string $summary Formatted source data summary. - * - * @return string - */ - private function build_user_prompt( string $summary, int $target_count = 6 ): string { - return "Here are the recent social media posts and comments to analyze. " - . "Find {$target_count} distinct, diverse article ideas from this data:\n\n" - . $summary; - } - /** * Parse the LLM's JSON response into Analysis_Result objects. * - * @param string $content LLM response content (JSON). - * @param array> $source_items Original source data for ID mapping. - * + * @param string $content LLM response content (JSON). + * @param array> $source_items Original source data for ID mapping. * @return PRAutoBlogger_Analysis_Result[] */ private function parse_analysis_response( string $content, array $source_items ): array { @@ -291,8 +210,6 @@ class PRAutoBlogger_Content_Analyzer { * Side effects: database inserts. * * @param PRAutoBlogger_Analysis_Result[] $results - * - * @return void */ private function store_results( array $results ): void { global $wpdb;