use crate::agent::Agent;
use crate::improve::RunCritique;
pub struct PromptGenerator {
pub max_chars: usize,
}
impl Default for PromptGenerator {
fn default() -> Self {
Self { max_chars: 4096 }
}
}
impl PromptGenerator {
pub fn new() -> Self {
Self::default()
}
pub async fn generate_improved_prompt(
&self,
agent: &dyn Agent,
current_prompt: &str,
critiques: &[RunCritique],
task_domain: &str,
) -> String {
let failure_summary: String = critiques
.iter()
.map(|c| c.format_report())
.collect::<Vec<_>>()
.join("\n---\n");
let previous_suggestions: String = critiques
.iter()
.flat_map(|c| &c.suggestions)
.map(|s| format!("- {}", Self::format_suggestion(s)))
.collect::<Vec<_>>()
.join("\n");
let prompt = format!(
"You are improving a system prompt for an AI coding agent.\n\n\
CURRENT PROMPT:\n{current_prompt}\n\n\
DOMAIN: {task_domain}\n\n\
FAILURE ANALYSIS (from previous runs):\n{failure_summary}\n\n\
PREVIOUS SUGGESTIONS:\n{previous_suggestions}\n\n\
TASK: Write an improved system prompt that addresses these failures.\n\
Rules:\n\
- Keep it under {max_chars} characters\n\
- Be specific about tools and behaviors\n\
- Include guardrails that prevent the observed failures\n\
- Do NOT repeat previous attempts — each iteration must make DIFFERENT changes\n\
- Output ONLY the new prompt text, no explanations\n\
- Wrap in <new_prompt>...</new_prompt> tags",
max_chars = self.max_chars,
current_prompt = current_prompt,
task_domain = task_domain,
failure_summary = failure_summary,
previous_suggestions = previous_suggestions,
);
let response = agent.execute(&prompt).await;
let raw = response.unwrap_or_else(|e| format!("Generation failed: {e}"));
Self::extract_tagged_content(&raw, "new_prompt")
.unwrap_or(raw)
.chars()
.take(self.max_chars)
.collect()
}
fn format_suggestion(suggestion: &crate::improve::ImprovementSuggestion) -> String {
use crate::improve::ImprovementSuggestion;
match suggestion {
ImprovementSuggestion::PromptChange {
section,
suggestion,
} => {
format!("Modify '{section}': {suggestion}")
}
ImprovementSuggestion::PolicyChange { rule, reason } => {
format!("Policy: {rule} (reason: {reason})")
}
ImprovementSuggestion::EvalGeneration { case_id, .. } => {
format!("Generate eval case: {case_id}")
}
}
}
fn extract_tagged_content(raw: &str, tag: &str) -> Option<String> {
let open = format!("<{tag}>");
let close = format!("</{tag}>");
let start = raw.find(&open)? + open.len();
let end = raw[start..].find(&close)?;
Some(raw[start..start + end].trim().to_string())
}
}