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:
peptiderepo
2026-06-11 04:12:32 +00:00
parent 809e7176ad
commit 1b04809092
21 changed files with 1712 additions and 386 deletions

View File

@@ -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,
)
);
}
/**