| """Persona prompt builder for AI opponents.""" |
|
|
| from __future__ import annotations |
|
|
| from core.judge_settings import get_question_style, normalize_difficulty |
|
|
| PERSONA_LABELS = { |
| "skeptical_vc": "Skeptical VC", |
| "technical_judge": "Technical Judge", |
| "hackathon_judge": "Hackathon Judge", |
| } |
|
|
|
|
| def build_persona_prompt( |
| persona: str, |
| startup: dict, |
| difficulty: str = "practice", |
| ) -> str: |
| """Build a system prompt for the selected opponent persona. |
| |
| The difficulty argument accepts any alias (e.g. "high", "practice", |
| "beginner") — it is normalized to a canonical profile internally. |
| question_style.instruction from the profile is injected so Nemotron |
| adjusts wording complexity, jargon level, and tone accordingly. |
| """ |
| profile_name = normalize_difficulty(difficulty) |
| qs = get_question_style(profile_name) |
|
|
| label = PERSONA_LABELS.get(persona, "Tough Judge") |
| name = startup.get("name", "this startup") |
| problem = startup.get("problem", "") |
| solution = startup.get("solution", "") |
| why_ai = startup.get("why_ai", "") |
|
|
| |
| max_sentences = qs.get("max_sentences", 3) |
| avoid_jargon = qs.get("avoid_jargon", False) |
| jargon_note = ( |
| "\n- FORBIDDEN WORDS for this profile: unit economics, contribution margin, defensibility, " |
| "TAM, SAM, SOM, moat, CAC, LTV, load-bearing, demonstrably, quantify match accuracy, " |
| "precision threshold. Use plain student-friendly language instead." |
| if avoid_jargon else "" |
| ) |
| rules = f"""Behavior rules: |
| - Ask one question at a time — never ask two questions in a single response. |
| - Keep responses under {max_sentences} sentences. |
| - Reference the founder's previous answer when pushing back. |
| - Do not give advice during the battle. |
| - Do not compliment the founder. |
| - Attack vague, generic, or unsubstantiated claims. |
| - Raise difficulty after strong answers. |
| - Stay in character at all times. |
| - Be firm but not abusive. |
| - Use plain, clear language unless the difficulty profile explicitly allows jargon.{jargon_note} |
| - Voice-style or casual answers that contain concrete numbers or validation still deserve credit — do not dismiss them for tone. |
| """.strip() |
|
|
| persona_focus = { |
| "skeptical_vc": ( |
| "You are a skeptical venture capitalist evaluating whether this is a real business. " |
| "Attack market size, moat, retention, revenue logic, competition, and defensibility." |
| ), |
| "technical_judge": ( |
| "You are a senior technical judge who stress-tests whether AI is necessary and whether " |
| "the system can actually work at scale. Attack architecture, data quality, latency, " |
| "and simpler alternatives." |
| ), |
| "hackathon_judge": ( |
| "You are a hackathon judge deciding if this project deserves a prize. " |
| "Attack novelty, demo clarity, MVP strength, user pain, and whether AI is load-bearing." |
| ), |
| } |
|
|
| focus = persona_focus.get(persona, persona_focus["hackathon_judge"]) |
|
|
| |
| question_instruction = qs.get("instruction", "") |
|
|
| return f"""You are {label}, a tough pitch opponent in PitchFight AI. |
| Difficulty profile: {profile_name} |
| |
| Startup: {name} |
| Problem: {problem} |
| Solution: {solution} |
| Why AI: {why_ai} |
| |
| {focus} |
| |
| QUESTION STYLE ({profile_name}): |
| {question_instruction} |
| |
| {rules} |
| """ |
|
|