Prompt Optimizer Skill
This repo generates company marketing campaigns via a Pydantic AI agent. The prompt pipeline is:
SYSTEM_PROMPT (src/campaign_ai/prompt.py)
+ generate_user_prompt(keyword) (src/campaign_ai/prompt.py)
→ Agent with output_type=TrendingTopicsReport (src/campaign_ai/agent.py)
→ TrendingTopicsReport { keyword, campaigns: list[MarketingCampaign x3] }
Key files:
src/campaign_ai/prompt.py — System prompt and user prompt template
src/campaign_ai/models.py — TrendingTopicsReport, MarketingCampaign Pydantic models with field constraints
src/campaign_ai/agent.py — Agent construction (build_agent, output_retries=3)
Step 1 — Read the current prompts
cat src/campaign_ai/prompt.py
Then read the output schema to understand field constraints:
cat src/campaign_ai/models.py
Key constraints to keep in mind when optimizing:
topic: max 100 chars
trend_summary: max 400 chars
company_relevance: max 400 chars
campaign_concept: max 600 chars
tagline: max 80 chars
channels: list, max 6 items
execution_steps: list, max 6 items, min 4 implied by prompt
campaigns: exactly 3 items (enforced by min_length=3, max_length=3)
Step 2 — Diagnose prompt quality issues
Common issues in campaign generation prompts:
A. Output count enforcement
Check that the system prompt and user prompt both explicitly demand exactly 3 campaigns.
Verify output_retries=3 in build_agent() provides fallback.
grep -n "exact\|3 campaign\|3 items\|exactly" src/campaign_ai/prompt.py
B. Field completeness
Ensure every field in MarketingCampaign is mentioned in the system prompt with clear length guidance.
Missing field instructions cause the model to invent its own format or truncate aggressively.
# Check each MarketingCampaign field is mentioned in SYSTEM_PROMPT
uv run python -c "
from campaign_ai.config import Settings
from campaign_ai.models import MarketingCampaign
from campaign_ai.prompt import build_writer_system_prompt
s = Settings()
prompt = build_writer_system_prompt(s.company_name)
fields = MarketingCampaign.model_fields.keys()
for f in fields:
mentioned = f in prompt
print(f' {\"✅\" if mentioned else \"❌\"} {f}')
"
C. Brand voice consistency
The system prompt should anchor the configured company's identity (via COMPANY_NAME and
COMPANY_DESCRIPTION settings) with concrete brand attributes. Ensure these are injected
via build_selector_system_prompt and build_writer_system_prompt in prompt.py.
D. Channel specificity
If the model returns vague channels ("Social Media" instead of "Instagram, TikTok"),
add examples to the system prompt: e.g. "Instagram", "OOH", "Podcast Sponsorship".
E. Tagline quality
Taglines should be punchy (<10 words). Check if the system prompt enforces this clearly.
Step 3 — Test the current prompt with a dry run
Run a prompt dry-run to inspect the user prompt for a given keyword:
# uv run python -c "..."
from campaign_ai.config import Settings
from campaign_ai.prompt import build_selector_system_prompt, build_writer_system_prompt, generate_user_prompt
settings = Settings()
selector_prompt = build_selector_system_prompt(settings.company_name, settings.company_description)
writer_prompt = build_writer_system_prompt(settings.company_name)
keyword = "music"
user_prompt = generate_user_prompt(keyword, settings.company_name)
print("=== SELECTOR SYSTEM PROMPT ===")
print(selector_prompt)
print()
print("=== WRITER SYSTEM PROMPT ===")
print(writer_prompt)
print()
print("=== USER PROMPT ===")
print(user_prompt)
Step 4 — Evaluate common prompt improvement patterns
Apply these patterns as appropriate:
Pattern A: Role + Goal + Constraints (RGC)
# Before (vague role)
"You are a marketing strategist."
# After (specific role with brand context via build_selector_system_prompt)
"You are a senior brand strategist at {company_name} — {company_description}.
Your campaigns must feel premium, globally relevant, and grounded in the company's core identity."
# Before (implicit format)
"Return exactly 3 campaigns"
# After (explicit JSON-aware constraint)
"Return EXACTLY 3 campaign objects in the campaigns array.
Each campaign MUST populate every field — omitting any field is not acceptable.
Keep text fields short and professional. Do not use markdown, bullet points,
or headers within field values."
Pattern C: Negative constraints
# Add explicit prohibitions
"Do NOT:
- Return fewer or more than 3 campaigns
- Use markdown formatting inside field values
- Repeat the same trend across multiple campaigns
- Include generic platitudes — every field must be specific to this trend"
Pattern D: Chain-of-thought for trend selection
# Add reasoning instruction before output
"First, identify 5-10 trending topics related to the keyword.
Then evaluate each against the company's brand pillars.
Finally, select the top 3 with the strongest brand fit and most distinct campaign angles."
Pattern E: Tagline enhancement
# Before
"tagline: one punchy phrase under 10 words"
# After
"tagline: one punchy phrase under 10 words — must contain a verb and evoke emotion.
Example style: 'Lead the Future' or 'Connect Every Moment'."
Step 5 — Apply and verify changes
After editing src/campaign_ai/prompt.py:
# 1. Verify syntax and imports
uv run python -c "from campaign_ai.prompt import build_selector_system_prompt, build_writer_system_prompt; print('OK')"
# 2. Run tests to ensure nothing is broken
uv run pytest tests/ -v --tb=short
# 3. Lint and format
uv run ruff check src/campaign_ai/prompt.py
uv run ruff format src/campaign_ai/prompt.py
Step 6 — Compare prompt variants (A/B style)
To compare prompt variants without real LLM calls, check the token count and readability:
# uv run python -c "..."
VARIANT_A = """...""" # original
VARIANT_B = """...""" # improved
for name, prompt in [("A (original)", VARIANT_A), ("B (improved)", VARIANT_B)]:
words = len(prompt.split())
chars = len(prompt)
lines = prompt.count("\n") + 1
print(f"Variant {name}: {words} words, {chars} chars, {lines} lines")
Step 7 — Validate Pydantic model field alignment
After any prompt change, re-verify that every MarketingCampaign field has:
- A mention in
SYSTEM_PROMPT with example or guidance
- An appropriate
max_length in models.py that matches your intent
- A
Field(description=...) that is descriptive for the LLM schema
# uv run python -c "..."
from campaign_ai.models import MarketingCampaign
import json
schema = MarketingCampaign.model_json_schema()
print(json.dumps(schema, indent=2))
The model sees this schema as part of the tool/output definition — descriptions matter.
Common prompt anti-patterns to avoid
| Anti-pattern | Problem | Fix |
|---|
| "Be creative" without constraints | Too vague, wildly varying outputs | Specify tone, length, examples |
| Implicit field count ("some steps") | Model skips fields or adds extras | Use "exactly N" or "4-6 items" |
| Markdown in prompts for output fields | Model mirrors markdown in values | Say "no markdown in field values" |
| Single instruction for all 3 campaigns | Campaigns end up similar | Add "ensure each campaign targets a distinct audience segment or channel" |
| No brand anchor | Generic marketing copy | Include company name + description via builder functions |