feat(prompts): versioned prompt registry, v1 seed, pinning + stamping
Pipeline v2 Phase 1, commit 2/4. Prompt_Registry (read) + Prompt_Registry_Writer (immutable create/activate/seed) over the new prompts table; canonical v1 bodies extracted verbatim into Prompt_Defaults(+Editorial) — single source for BOTH the seed migration (Migrate_Prompt_Seed_V0180, flag-gated, self-healing) and the in-code fallback, so a missing table renders byte-identical prompts and never fatals. Call sites swapped: Content_Prompts, Analysis_Prompts, Chief_Editor, LLM_Research_Provider. Includes the image-composer seam keys image.rewriter_system + image.style_template (composer PR consumes, must not add its own prompt storage). Pinning: Cost_Tracker::set_run_id() registers the process run context, ensures the runs ledger row, and pins active versions once per run; log_api_call/log_image_generation stamp prompt_version (run pins) + agent_role (stage map) on every generation_log row. Single-pass keeps stage 'draft' and stamps content.single_pass explicitly. Under-300 refactors: Content_Generator's four duplicated span/log blocks consolidated into execute_stage() (301 -> 224 lines, behavior identical); get_avg_tokens_for_stages body moved to Cost_Reporter with a delegating wrapper kept for API compatibility. Tests: byte-identical fallback renders (single_pass/outline/polish/ editor.system), token filling, key coverage incl. stage-map integrity. Agent-Session: pipeline-v2-phase1-engineer-20260612
This commit is contained in:
@@ -9,7 +9,8 @@ declare(strict_types=1);
|
||||
* What: Builds system and user prompts for source-data analysis, including
|
||||
* past-performance context for self-improvement feedback loops.
|
||||
* Who calls it: PRAutoBlogger_Content_Analyzer::analyze_recent_data().
|
||||
* Dependencies: WordPress $wpdb (for performance lookup), no external APIs.
|
||||
* Dependencies: PRAutoBlogger_Prompt_Registry (versioned prompt bodies),
|
||||
* WordPress $wpdb (for performance lookup), no external APIs.
|
||||
*
|
||||
* @see core/class-content-analyzer.php — Orchestrates analysis and calls these builders.
|
||||
* @see ARCHITECTURE.md — Data flow step 2.
|
||||
@@ -25,54 +26,26 @@ class PRAutoBlogger_Analysis_Prompts {
|
||||
* @return string System prompt text.
|
||||
*/
|
||||
public static 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";
|
||||
}
|
||||
// Blocks carry their own separators when non-empty so the rendered
|
||||
// output is byte-identical to the historical concatenation.
|
||||
$performance_block = '' !== $performance_context ? $performance_context . "\n\n" : '';
|
||||
|
||||
$recent_context = self::get_recent_articles_context();
|
||||
if ( '' !== $recent_context ) {
|
||||
$prompt .= $recent_context . "\n\n";
|
||||
}
|
||||
$recent_block = '' !== $recent_context ? $recent_context . "\n\n" : '';
|
||||
|
||||
// Append user-defined analysis instructions if configured.
|
||||
$instructions = trim( (string) get_option( 'prautoblogger_analysis_instructions', '' ) );
|
||||
if ( '' !== $instructions ) {
|
||||
$prompt .= "Additional instructions:\n" . $instructions . "\n\n";
|
||||
}
|
||||
$instructions = trim( (string) get_option( 'prautoblogger_analysis_instructions', '' ) );
|
||||
$instructions_block = '' !== $instructions ? "Additional instructions:\n" . $instructions . "\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;
|
||||
return PRAutoBlogger_Prompt_Registry::render(
|
||||
'analysis.system',
|
||||
array(
|
||||
'niche_clause' => '' !== $niche ? " in the {$niche} niche" : '',
|
||||
'target_count' => (string) $target_count,
|
||||
'performance_block' => $performance_block,
|
||||
'recent_articles_block' => $recent_block,
|
||||
'instructions_block' => $instructions_block,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,9 +56,13 @@ class PRAutoBlogger_Analysis_Prompts {
|
||||
* @return string User prompt text.
|
||||
*/
|
||||
public static 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;
|
||||
return PRAutoBlogger_Prompt_Registry::render(
|
||||
'analysis.user',
|
||||
array(
|
||||
'target_count' => (string) $target_count,
|
||||
'summary' => $summary,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user