From 22d9688bb1e00acf8818b2e55ce1bab9a7c2b239 Mon Sep 17 00:00:00 2001 From: danbusse <81775558+danbusse@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:29:08 -0600 Subject: [PATCH] fix: strengthen JSON structure requirements in system prompt --- backend/app/services/ai/prompts.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/backend/app/services/ai/prompts.py b/backend/app/services/ai/prompts.py index 5716a65..a4ec59a 100644 --- a/backend/app/services/ai/prompts.py +++ b/backend/app/services/ai/prompts.py @@ -27,16 +27,13 @@ SYSTEM_PROMPT = ( "preamble, no commentary outside the JSON. Your card names must exactly match " "official Magic card names (English). Every card must be legal in Commander " "and within the commander's colour identity.\n\n" - "CRITICAL: Your response must use EXACTLY this JSON structure:\n" - "{\n" - ' "deck_name": "string",\n' - ' "strategy_summary": "string",\n' - ' "cards": [\n' - ' {"name": "Card Name", "slot": "creature|instant|sorcery|enchantment|artifact|planeswalker|land|battle", "quantity": 1, "reasoning": "..."}\n' - " ],\n" - ' "cuts": []\n' - "}\n" - "Do NOT use any other key names. The cards array must contain exactly the non-commander cards." + "CRITICAL JSON REQUIREMENTS:\n" + "1. Use EXACTLY these top-level keys: \"deck_name\", \"strategy_summary\", \"cards\", \"cuts\"\n" + "2. Do NOT use \"commander\", \"decklist\", \"deck\", or any other key names\n" + "3. The \"cards\" array must contain ALL 99 non-commander cards\n" + "4. Do NOT include a separate commander entry — it is tracked separately\n" + "5. Card names with apostrophes must be properly escaped in JSON strings\n" + "6. Every card object must have: \"name\", \"slot\", \"quantity\", \"reasoning\"" ) @@ -59,7 +56,7 @@ def generate_prompt( f"CONSTRAINTS:\n{constraint_text}\n" f"{owned_list_text}\n" f"DECK REQUIREMENTS:\n" - f"- Exactly 99 cards (not counting the commander)\n" + f"- Exactly 99 cards in the \"cards\" array (not counting the commander)\n" f"- All cards must be legal in Commander and within {commander}'s colour identity\n" f"- Include a balanced mana base (35-40 lands for most strategies)\n" f"- Include ramp (8-12 pieces), card draw (8-10 pieces), removal (8-10 pieces), " @@ -67,7 +64,7 @@ def generate_prompt( f"- Slot values: creature, instant, sorcery, enchantment, artifact, planeswalker, land, battle\n" f"- Quantity for basic lands may be >1; all other cards quantity = 1\n" f"- The 'reasoning' field must explain why the card fits THIS specific deck\n\n" - f"Respond with the JSON object only." + f"Respond with the JSON object only. Use exactly these keys: deck_name, strategy_summary, cards, cuts." ) return SYSTEM_PROMPT, user_message @@ -109,7 +106,7 @@ def complete_prompt( f"- Analyse existing cards to infer strategy and fill gaps (ramp, draw, removal, win-cons)\n" f"- The 'cards' array contains ONLY the new cards you are recommending\n" f"- strategy_summary should describe how the completed deck plays\n\n" - f"Respond with the JSON object only." + f"Respond with the JSON object only. Use exactly these keys: deck_name, strategy_summary, cards, cuts." ) return SYSTEM_PROMPT, user_message @@ -155,6 +152,6 @@ def cull_prompt( f"- The 'cards' array contains all {target_count} REMAINING cards after cuts\n" f"- Provide specific reasoning for each cut explaining why it's weaker than what stays\n" f"- strategy_summary describes the refined deck after cuts\n\n" - f"Respond with the JSON object only." + f"Respond with the JSON object only. Use exactly these keys: deck_name, strategy_summary, cards, cuts." ) return SYSTEM_PROMPT, user_message