mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
first attempt at two part orders, unformatted -> formatted
This commit is contained in:
parent
b5a84867a1
commit
6e4821735d
32 changed files with 2510 additions and 801 deletions
|
|
@ -14,6 +14,7 @@ from .utils import load_prompt, run_llm_and_log, log_llm_response
|
|||
from .prompt_constructor import build_context_prompt # Added import
|
||||
from .clients import GameHistory
|
||||
from diplomacy import Game
|
||||
from .formatter import format_with_gemini_flash, FORMAT_ORDER_DIARY, FORMAT_NEGOTIATION_DIARY, FORMAT_STATE_UPDATE
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -425,16 +426,19 @@ class DiplomacyAgent:
|
|||
success_status = "Failure: Initialized" # Default
|
||||
|
||||
try:
|
||||
# Load the template file but safely preprocess it first
|
||||
prompt_template_content = _load_prompt_file('negotiation_diary_prompt.txt', prompts_dir=self.prompts_dir)
|
||||
# Load the unformatted template file
|
||||
prompt_template_content = _load_prompt_file('unformatted/negotiation_diary_prompt.txt', prompts_dir=self.prompts_dir)
|
||||
if not prompt_template_content:
|
||||
logger.error(f"[{self.power_name}] Could not load negotiation_diary_prompt.txt. Skipping diary entry.")
|
||||
logger.error(f"[{self.power_name}] Could not load unformatted/negotiation_diary_prompt.txt. Skipping diary entry.")
|
||||
success_status = "Failure: Prompt file not loaded"
|
||||
return # Exit early if prompt can't be loaded
|
||||
|
||||
# Prepare context for the prompt
|
||||
board_state_dict = game.get_state()
|
||||
board_state_str = f"Units: {board_state_dict.get('units', {})}, Centers: {board_state_dict.get('centers', {})}"
|
||||
# Create readable board state string
|
||||
units_str = "\n".join([f" {p}: {', '.join(u)}" for p, u in board_state_dict.get('units', {}).items()])
|
||||
centers_str = "\n".join([f" {p}: {', '.join(c)}" for p, c in board_state_dict.get('centers', {}).items()])
|
||||
board_state_str = f"Units:\n{units_str}\n\nSupply Centers:\n{centers_str}"
|
||||
|
||||
messages_this_round = game_history.get_messages_this_round(
|
||||
power_name=self.power_name,
|
||||
|
|
@ -526,7 +530,15 @@ class DiplomacyAgent:
|
|||
|
||||
parsed_data = None
|
||||
try:
|
||||
parsed_data = self._extract_json_from_text(raw_response)
|
||||
# Format the natural language response into JSON
|
||||
formatted_response = await format_with_gemini_flash(
|
||||
raw_response,
|
||||
FORMAT_NEGOTIATION_DIARY,
|
||||
power_name=self.power_name,
|
||||
phase=game.current_short_phase,
|
||||
log_file_path=log_file_path
|
||||
)
|
||||
parsed_data = self._extract_json_from_text(formatted_response)
|
||||
logger.debug(f"[{self.power_name}] Parsed diary data: {parsed_data}")
|
||||
success_status = "Success: Parsed diary data"
|
||||
except json.JSONDecodeError as e:
|
||||
|
|
@ -560,7 +572,7 @@ class DiplomacyAgent:
|
|||
|
||||
# Fix 2: Be more robust about extracting relationship updates
|
||||
new_relationships = None
|
||||
for key in ['relationship_updates', 'updated_relationships', 'relationships']:
|
||||
for key in ['current_relationships', 'relationship_updates', 'updated_relationships', 'relationships']:
|
||||
if key in parsed_data and isinstance(parsed_data[key], dict):
|
||||
new_relationships = parsed_data[key]
|
||||
logger.info(f"[{self.power_name}] Successfully extracted '{key}' for relationship updates.")
|
||||
|
|
@ -590,7 +602,7 @@ class DiplomacyAgent:
|
|||
if success_status == "Success: Parsed diary data": # If only parsing was successful before
|
||||
success_status = "Success: Parsed, no valid relationship updates"
|
||||
elif new_relationships is not None: # It was provided but not a dict
|
||||
logger.warning(f"[{self.power_name}] 'updated_relationships' from diary LLM was not a dictionary: {type(new_relationships)}")
|
||||
logger.warning(f"[{self.power_name}] 'current_relationships' from diary LLM was not a dictionary: {type(new_relationships)}")
|
||||
|
||||
# Add the generated (or fallback) diary entry
|
||||
self.add_diary_entry(diary_entry_text, game.current_short_phase)
|
||||
|
|
@ -626,14 +638,17 @@ class DiplomacyAgent:
|
|||
"""
|
||||
logger.info(f"[{self.power_name}] Generating order diary entry for {game.current_short_phase}...")
|
||||
|
||||
# Load the template but we'll use it carefully with string interpolation
|
||||
prompt_template = _load_prompt_file('order_diary_prompt.txt', prompts_dir=self.prompts_dir)
|
||||
# Load the unformatted template for better content generation
|
||||
prompt_template = _load_prompt_file('unformatted/order_diary_prompt.txt', prompts_dir=self.prompts_dir)
|
||||
if not prompt_template:
|
||||
logger.error(f"[{self.power_name}] Could not load order_diary_prompt.txt. Skipping diary entry.")
|
||||
logger.error(f"[{self.power_name}] Could not load unformatted/order_diary_prompt.txt. Skipping diary entry.")
|
||||
return
|
||||
|
||||
board_state_dict = game.get_state()
|
||||
board_state_str = f"Units: {board_state_dict.get('units', {})}, Centers: {board_state_dict.get('centers', {})}"
|
||||
# Create readable board state string
|
||||
units_str = "\n".join([f" {p}: {', '.join(u)}" for p, u in board_state_dict.get('units', {}).items()])
|
||||
centers_str = "\n".join([f" {p}: {', '.join(c)}" for p, c in board_state_dict.get('centers', {}).items()])
|
||||
board_state_str = f"Units:\n{units_str}\n\nSupply Centers:\n{centers_str}"
|
||||
|
||||
orders_list_str = "\n".join([f"- {o}" for o in orders]) if orders else "No orders submitted."
|
||||
|
||||
|
|
@ -700,7 +715,15 @@ class DiplomacyAgent:
|
|||
|
||||
if raw_response:
|
||||
try:
|
||||
response_data = self._extract_json_from_text(raw_response)
|
||||
# Format the natural language response into JSON
|
||||
formatted_response = await format_with_gemini_flash(
|
||||
raw_response,
|
||||
FORMAT_ORDER_DIARY,
|
||||
power_name=self.power_name,
|
||||
phase=game.current_short_phase,
|
||||
log_file_path=log_file_path
|
||||
)
|
||||
response_data = self._extract_json_from_text(formatted_response)
|
||||
if response_data:
|
||||
# Directly attempt to get 'order_summary' as per the prompt
|
||||
diary_text_candidate = response_data.get("order_summary")
|
||||
|
|
@ -874,10 +897,10 @@ class DiplomacyAgent:
|
|||
self.log_state(f"Before State Update ({current_phase})")
|
||||
|
||||
try:
|
||||
# 1. Construct the prompt using the dedicated state update prompt file
|
||||
prompt_template = _load_prompt_file('state_update_prompt.txt', prompts_dir=self.prompts_dir)
|
||||
# 1. Construct the prompt using the unformatted state update prompt file
|
||||
prompt_template = _load_prompt_file('unformatted/state_update_prompt.txt', prompts_dir=self.prompts_dir)
|
||||
if not prompt_template:
|
||||
logger.error(f"[{power_name}] Could not load state_update_prompt.txt. Skipping state update.")
|
||||
logger.error(f"[{power_name}] Could not load unformatted/state_update_prompt.txt. Skipping state update.")
|
||||
return
|
||||
|
||||
# Get previous phase safely from history
|
||||
|
|
@ -916,12 +939,15 @@ class DiplomacyAgent:
|
|||
other_powers = [p for p in game.powers if p != power_name]
|
||||
|
||||
# Create a readable board state string from the board_state dict
|
||||
board_state_str = f"Board State:\n"
|
||||
for p_name, power_data in board_state.get('powers', {}).items():
|
||||
# Get units and centers from the board state
|
||||
units = power_data.get('units', [])
|
||||
centers = power_data.get('centers', [])
|
||||
board_state_str += f" {p_name}: Units={units}, Centers={centers}\n"
|
||||
board_state_str = "Units:\n"
|
||||
for p_name, units in board_state.get('units', {}).items():
|
||||
units_str = ", ".join(units) if units else "None"
|
||||
board_state_str += f" {p_name}: {units_str}\n"
|
||||
|
||||
board_state_str += "\nSupply Centers:\n"
|
||||
for p_name, centers in board_state.get('centers', {}).items():
|
||||
centers_str = ", ".join(centers) if centers else "None"
|
||||
board_state_str += f" {p_name}: {centers_str}\n"
|
||||
|
||||
# Extract year from the phase name (e.g., "S1901M" -> "1901")
|
||||
current_year = last_phase_name[1:5] if len(last_phase_name) >= 5 else "unknown"
|
||||
|
|
@ -956,7 +982,15 @@ class DiplomacyAgent:
|
|||
|
||||
if response is not None and response.strip(): # Check if response is not None and not just whitespace
|
||||
try:
|
||||
update_data = self._extract_json_from_text(response)
|
||||
# Format the natural language response into JSON
|
||||
formatted_response = await format_with_gemini_flash(
|
||||
response,
|
||||
FORMAT_STATE_UPDATE,
|
||||
power_name=power_name,
|
||||
phase=current_phase,
|
||||
log_file_path=log_file_path
|
||||
)
|
||||
update_data = self._extract_json_from_text(formatted_response)
|
||||
logger.debug(f"[{power_name}] Successfully parsed JSON: {update_data}")
|
||||
|
||||
# Ensure update_data is a dictionary
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from .game_history import GameHistory
|
|||
from .utils import load_prompt, run_llm_and_log, log_llm_response, generate_random_seed
|
||||
# Import DiplomacyAgent for type hinting if needed, but avoid circular import if possible
|
||||
from .prompt_constructor import construct_order_generation_prompt, build_context_prompt
|
||||
from .formatter import format_with_gemini_flash, FORMAT_ORDERS, FORMAT_CONVERSATION
|
||||
|
||||
# set logger back to just info
|
||||
logger = logging.getLogger("client")
|
||||
|
|
@ -121,8 +122,17 @@ class BaseModelClient:
|
|||
f"[{self.model_name}] Raw LLM response for {power_name} orders:\n{raw_response}"
|
||||
)
|
||||
|
||||
# Attempt to parse the final "orders" from the LLM
|
||||
move_list = self._extract_moves(raw_response, power_name)
|
||||
# Format the natural language response into structured format
|
||||
formatted_response = await format_with_gemini_flash(
|
||||
raw_response,
|
||||
FORMAT_ORDERS,
|
||||
power_name=power_name,
|
||||
phase=phase,
|
||||
log_file_path=log_file_path
|
||||
)
|
||||
|
||||
# Attempt to parse the final "orders" from the formatted response
|
||||
move_list = self._extract_moves(formatted_response, power_name)
|
||||
|
||||
if not move_list:
|
||||
logger.warning(
|
||||
|
|
@ -454,18 +464,37 @@ class BaseModelClient:
|
|||
agent_relationships: Optional[Dict[str, str]] = None,
|
||||
agent_private_diary_str: Optional[str] = None, # Added
|
||||
) -> str:
|
||||
instructions = load_prompt("conversation_instructions.txt", prompts_dir=self.prompts_dir)
|
||||
instructions = load_prompt("unformatted/conversation_instructions.txt", prompts_dir=self.prompts_dir)
|
||||
|
||||
context = build_context_prompt(
|
||||
game,
|
||||
board_state,
|
||||
power_name,
|
||||
possible_orders,
|
||||
game_history,
|
||||
agent_goals=agent_goals,
|
||||
agent_relationships=agent_relationships,
|
||||
agent_private_diary=agent_private_diary_str, # Pass diary string
|
||||
prompts_dir=self.prompts_dir,
|
||||
# Load conversation-specific context template
|
||||
context_template = load_prompt("unformatted/conversation_context.txt", prompts_dir=self.prompts_dir)
|
||||
|
||||
# Get phase info
|
||||
current_phase = game.get_current_phase()
|
||||
|
||||
# Get active powers
|
||||
active_powers = [p for p in game.powers.keys() if not game.powers[p].is_eliminated()]
|
||||
eliminated_powers = [p for p in game.powers.keys() if game.powers[p].is_eliminated()]
|
||||
eliminated_status = f"Eliminated: {', '.join(eliminated_powers)}" if eliminated_powers else "No powers eliminated yet"
|
||||
|
||||
# Get messages this round
|
||||
messages_this_round = game_history.get_messages_this_round(
|
||||
power_name=power_name,
|
||||
current_phase_name=game.current_short_phase
|
||||
)
|
||||
if not messages_this_round.strip():
|
||||
messages_this_round = "(No messages exchanged yet this round)"
|
||||
|
||||
# Format the context
|
||||
context = context_template.format(
|
||||
power_name=power_name,
|
||||
current_phase=current_phase,
|
||||
agent_goals="\n".join(f"- {g}" for g in agent_goals) if agent_goals else "- Survive and expand",
|
||||
agent_relationships="\n".join(f"- {p}: {r}" for p, r in agent_relationships.items()) if agent_relationships else "- All powers: Neutral",
|
||||
recent_private_diary=agent_private_diary_str[-500:] if agent_private_diary_str else "(No recent diary entries)", # Last 500 chars
|
||||
messages_this_round=messages_this_round,
|
||||
active_powers=", ".join(active_powers),
|
||||
eliminated_status=eliminated_status
|
||||
)
|
||||
|
||||
# Get recent messages targeting this power to prioritize responses
|
||||
|
|
@ -574,10 +603,34 @@ class BaseModelClient:
|
|||
)
|
||||
logger.debug(f"[{self.model_name}] Raw LLM response for {power_name}:\n{raw_response}")
|
||||
|
||||
# Format the natural language response into structured JSON
|
||||
formatted_response = await format_with_gemini_flash(
|
||||
raw_response,
|
||||
FORMAT_CONVERSATION,
|
||||
power_name=power_name,
|
||||
phase=game_phase,
|
||||
log_file_path=log_file_path
|
||||
)
|
||||
|
||||
parsed_messages = []
|
||||
json_blocks = []
|
||||
json_decode_error_occurred = False
|
||||
|
||||
# For formatted response, we expect a clean JSON array
|
||||
try:
|
||||
data = json.loads(formatted_response)
|
||||
if isinstance(data, list):
|
||||
parsed_messages = data
|
||||
json_blocks = [json.dumps(item) for item in data if isinstance(item, dict)]
|
||||
else:
|
||||
logger.warning(f"[{self.model_name}] Formatted response is not a list")
|
||||
except json.JSONDecodeError:
|
||||
logger.warning(f"[{self.model_name}] Failed to parse formatted response as JSON, falling back to regex")
|
||||
# Fall back to original parsing logic using formatted_response
|
||||
raw_response = formatted_response
|
||||
|
||||
# Original parsing logic as fallback
|
||||
if not parsed_messages:
|
||||
# Attempt to find blocks enclosed in {{...}}
|
||||
double_brace_blocks = re.findall(r'\{\{(.*?)\}\}', raw_response, re.DOTALL)
|
||||
if double_brace_blocks:
|
||||
|
|
@ -602,68 +655,42 @@ class BaseModelClient:
|
|||
# If no markdown block, fall back to regex for any JSON object in the response
|
||||
json_blocks = re.findall(r'\{.*?\}', raw_response, re.DOTALL)
|
||||
|
||||
if not json_blocks:
|
||||
logger.warning(f"[{self.model_name}] No JSON message blocks found in response for {power_name}. Raw response:\n{raw_response}")
|
||||
success_status = "Success: No JSON blocks found"
|
||||
# messages_to_return remains empty
|
||||
else:
|
||||
# Process json_blocks if we have them from fallback parsing
|
||||
if not parsed_messages and json_blocks:
|
||||
for block_index, block in enumerate(json_blocks):
|
||||
try:
|
||||
cleaned_block = block.strip()
|
||||
# Attempt to fix common JSON issues like trailing commas before parsing
|
||||
cleaned_block = re.sub(r',\s*([\}\]])', r'\1', cleaned_block)
|
||||
parsed_message = json.loads(cleaned_block)
|
||||
|
||||
if isinstance(parsed_message, dict) and "message_type" in parsed_message and "content" in parsed_message:
|
||||
# Further validation, e.g., recipient for private messages
|
||||
if parsed_message["message_type"] == "private" and "recipient" not in parsed_message:
|
||||
logger.warning(f"[{self.model_name}] Private message missing recipient for {power_name} in block {block_index}. Skipping: {cleaned_block}")
|
||||
continue # Skip this message
|
||||
parsed_messages.append(parsed_message)
|
||||
else:
|
||||
logger.warning(f"[{self.model_name}] Invalid message structure or missing keys in block {block_index} for {power_name}: {cleaned_block}")
|
||||
|
||||
except json.JSONDecodeError as jde:
|
||||
# Try to fix unescaped newlines and retry parsing
|
||||
try:
|
||||
# Fix unescaped newlines and other control characters in JSON strings
|
||||
def escape_json_string(match):
|
||||
# Get the string content (without quotes)
|
||||
string_content = match.group(1)
|
||||
# Escape newlines, tabs, and carriage returns
|
||||
string_content = string_content.replace('\n', '\\n')
|
||||
string_content = string_content.replace('\r', '\\r')
|
||||
string_content = string_content.replace('\t', '\\t')
|
||||
# Return with quotes
|
||||
return '"' + string_content + '"'
|
||||
|
||||
# Apply escaping to all string values in the JSON
|
||||
fixed_block = re.sub(r'"([^"]*)"', escape_json_string, cleaned_block)
|
||||
|
||||
# Try parsing again with fixed block
|
||||
parsed_message = json.loads(fixed_block)
|
||||
|
||||
if isinstance(parsed_message, dict) and "message_type" in parsed_message and "content" in parsed_message:
|
||||
# Further validation, e.g., recipient for private messages
|
||||
if parsed_message["message_type"] == "private" and "recipient" not in parsed_message:
|
||||
logger.warning(f"[{self.model_name}] Private message missing recipient for {power_name} in block {block_index}. Skipping: {fixed_block}")
|
||||
continue # Skip this message
|
||||
parsed_messages.append(parsed_message)
|
||||
logger.info(f"[{self.model_name}] Successfully parsed JSON block {block_index} for {power_name} after fixing escape sequences")
|
||||
else:
|
||||
logger.warning(f"[{self.model_name}] Invalid message structure or missing keys in block {block_index} for {power_name} after escape fix: {fixed_block}")
|
||||
except json.JSONDecodeError as jde2:
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(f"[{self.model_name}] Failed to parse JSON block {block_index} for {power_name}: {e}")
|
||||
json_decode_error_occurred = True
|
||||
logger.warning(f"[{self.model_name}] Failed to decode JSON block {block_index} for {power_name} even after escape fixes. Error: {jde}. Block content:\n{block}")
|
||||
|
||||
if not parsed_messages:
|
||||
logger.warning(f"[{self.model_name}] No valid messages found in response for {power_name}")
|
||||
success_status = "Success: No messages found"
|
||||
# messages_to_return remains empty
|
||||
else:
|
||||
# Validate parsed messages
|
||||
validated_messages = []
|
||||
for msg in parsed_messages:
|
||||
if isinstance(msg, dict) and "message_type" in msg and "content" in msg:
|
||||
if msg["message_type"] == "private" and "recipient" not in msg:
|
||||
logger.warning(f"[{self.model_name}] Private message missing recipient for {power_name}")
|
||||
continue
|
||||
validated_messages.append(msg)
|
||||
else:
|
||||
logger.warning(f"[{self.model_name}] Invalid message structure for {power_name}")
|
||||
parsed_messages = validated_messages
|
||||
|
||||
# Set final status and return value
|
||||
if parsed_messages:
|
||||
success_status = "Success: Messages extracted"
|
||||
messages_to_return = parsed_messages
|
||||
elif json_decode_error_occurred:
|
||||
success_status = "Failure: JSONDecodeError during block parsing"
|
||||
messages_to_return = []
|
||||
else: # JSON blocks found, but none were valid messages
|
||||
success_status = "Success: No valid messages extracted from JSON blocks"
|
||||
else:
|
||||
success_status = "Success: No valid messages"
|
||||
messages_to_return = []
|
||||
|
||||
logger.debug(f"[{self.model_name}] Validated conversation replies for {power_name}: {messages_to_return}")
|
||||
|
|
|
|||
489
ai_diplomacy/formatter.py
Normal file
489
ai_diplomacy/formatter.py
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
"""
|
||||
Formatter module for converting natural language LLM responses to structured JSON.
|
||||
Uses Gemini 2.5 Flash to extract and format information from reasoning-focused responses.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from typing import Dict, Any, Optional
|
||||
import google.generativeai as genai
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# Import logging function
|
||||
from .utils import log_llm_response
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configure Gemini
|
||||
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", os.getenv("GOOGLE_API_KEY"))
|
||||
if GEMINI_API_KEY:
|
||||
genai.configure(api_key=GEMINI_API_KEY)
|
||||
|
||||
# Format type constants
|
||||
FORMAT_STATE_UPDATE = "state_update"
|
||||
FORMAT_CONVERSATION = "conversation"
|
||||
FORMAT_NEGOTIATION_DIARY = "negotiation_diary"
|
||||
FORMAT_ORDERS = "orders"
|
||||
FORMAT_INITIAL_STATE = "initial_state"
|
||||
FORMAT_ORDER_DIARY = "order_diary"
|
||||
|
||||
|
||||
async def format_with_gemini_flash(
|
||||
raw_response: str,
|
||||
format_type: str,
|
||||
power_name: Optional[str] = None,
|
||||
phase: Optional[str] = None,
|
||||
log_file_path: Optional[str] = None
|
||||
) -> str:
|
||||
"""
|
||||
Convert a natural language response to the required JSON format using Gemini Flash.
|
||||
|
||||
Args:
|
||||
raw_response: The natural language response from the primary LLM
|
||||
format_type: One of the FORMAT_* constants indicating desired output format
|
||||
power_name: Optional power name for logging
|
||||
phase: Optional phase for logging
|
||||
log_file_path: Optional path to CSV log file
|
||||
|
||||
Returns:
|
||||
JSON string in the expected format for the given type
|
||||
|
||||
Raises:
|
||||
Exception: If formatting fails or format_type is invalid
|
||||
"""
|
||||
logger.info(f"[FORMATTER] Called format_with_gemini_flash for format_type: {format_type}")
|
||||
logger.debug(f"[FORMATTER] Raw response preview: {raw_response[:200]}...")
|
||||
|
||||
# Load the appropriate formatting prompt
|
||||
prompts_dir = Path(__file__).parent / "prompts" / "formatting"
|
||||
prompt_file = prompts_dir / f"format_{format_type}.txt"
|
||||
|
||||
if not prompt_file.exists():
|
||||
logger.error(f"[FORMATTER] Format prompt file not found: {prompt_file}")
|
||||
raise ValueError(f"Unknown format type: {format_type}")
|
||||
|
||||
with open(prompt_file, 'r') as f:
|
||||
format_prompt = f.read()
|
||||
|
||||
# Replace placeholder with actual response
|
||||
format_prompt = format_prompt.replace("[RAW_RESPONSE]", raw_response)
|
||||
|
||||
# Don't log the request separately - we'll log once after we get the response
|
||||
|
||||
# Use Gemini Flash for formatting
|
||||
model = genai.GenerativeModel('gemini-2.5-flash')
|
||||
|
||||
generation_config = {
|
||||
"temperature": 0, # Deterministic formatting
|
||||
"max_output_tokens": 4096,
|
||||
}
|
||||
|
||||
try:
|
||||
logger.info(f"[FORMATTER] Calling Gemini Flash for {format_type} formatting")
|
||||
response = model.generate_content(
|
||||
format_prompt,
|
||||
generation_config=generation_config
|
||||
)
|
||||
|
||||
formatted_response = response.text.strip()
|
||||
logger.debug(f"[FORMATTER] Gemini Flash response: {formatted_response[:200]}...")
|
||||
|
||||
# Remove markdown code blocks if present
|
||||
if formatted_response.startswith("```json"):
|
||||
formatted_response = formatted_response[7:] # Remove ```json
|
||||
elif formatted_response.startswith("```"):
|
||||
formatted_response = formatted_response[3:] # Remove ```
|
||||
|
||||
if formatted_response.endswith("```"):
|
||||
formatted_response = formatted_response[:-3] # Remove trailing ```
|
||||
|
||||
formatted_response = formatted_response.strip()
|
||||
|
||||
# For orders format, wrap in PARSABLE OUTPUT if needed
|
||||
if format_type == FORMAT_ORDERS and "PARSABLE OUTPUT:" not in formatted_response:
|
||||
# Extract just the JSON part and wrap it
|
||||
if formatted_response.startswith("{"):
|
||||
formatted_response = f"PARSABLE OUTPUT:\n{formatted_response}"
|
||||
|
||||
logger.info(f"[FORMATTER] Successfully formatted {format_type} response")
|
||||
|
||||
# Log the successful formatting response
|
||||
if log_file_path:
|
||||
log_llm_response(
|
||||
log_file_path=log_file_path,
|
||||
model_name="gemini-2.5-flash",
|
||||
power_name=power_name,
|
||||
phase=phase or "unknown",
|
||||
response_type=f"format_{format_type}",
|
||||
raw_input_prompt=format_prompt,
|
||||
raw_response=formatted_response,
|
||||
success="TRUE"
|
||||
)
|
||||
|
||||
return formatted_response
|
||||
|
||||
except Exception as e:
|
||||
# If Gemini fails, return the original response
|
||||
# The existing parsing logic will attempt to handle it
|
||||
logger.error(f"[FORMATTER] Gemini Flash formatting failed for {format_type}: {str(e)}")
|
||||
logger.error(f"[FORMATTER] Returning original response as fallback")
|
||||
|
||||
# Log the failed formatting attempt
|
||||
if log_file_path:
|
||||
log_llm_response(
|
||||
log_file_path=log_file_path,
|
||||
model_name="gemini-2.5-flash",
|
||||
power_name=power_name,
|
||||
phase=phase or "unknown",
|
||||
response_type=f"format_{format_type}",
|
||||
raw_input_prompt=format_prompt,
|
||||
raw_response=str(e), # Log the error
|
||||
success=f"FAILURE: {type(e).__name__}"
|
||||
)
|
||||
|
||||
return raw_response
|
||||
|
||||
|
||||
def create_formatting_prompts():
|
||||
"""Create the formatting prompt files if they don't exist."""
|
||||
prompts_dir = Path(__file__).parent / "prompts" / "formatting"
|
||||
prompts_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Format prompts with examples moved from original prompts
|
||||
format_prompts = {
|
||||
FORMAT_ORDER_DIARY: """Extract the order summary from the response below and format it as JSON.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"order_summary": "Brief summary of orders and strategic intent"
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- Extract the summary of what orders were given and why
|
||||
- Keep it concise but informative
|
||||
- Focus on the strategic intent behind the orders
|
||||
|
||||
Response to format:
|
||||
[RAW_RESPONSE]
|
||||
|
||||
Return ONLY the JSON object, no other text.""",
|
||||
|
||||
FORMAT_INITIAL_STATE: """Extract the initial goals and relationships from the response below and format as JSON.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"initial_goals": [
|
||||
"Specific goal 1",
|
||||
"Specific goal 2",
|
||||
"Specific goal 3"
|
||||
],
|
||||
"initial_relationships": {
|
||||
"AUSTRIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ENGLAND": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"FRANCE": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"GERMANY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ITALY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"RUSSIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"TURKEY": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
}
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- Extract 3-5 specific strategic goals from the reasoning
|
||||
- For relationships, use ONLY: Enemy, Unfriendly, Neutral, Friendly, or Ally
|
||||
- Include all 7 powers (remove yourself from the list)
|
||||
|
||||
Response to format:
|
||||
[RAW_RESPONSE]
|
||||
|
||||
Return ONLY the JSON object, no other text.""",
|
||||
|
||||
FORMAT_NEGOTIATION_DIARY: """Extract the negotiation analysis and format as JSON.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"negotiation_summary": "Key outcomes from negotiations - what was discussed and agreed",
|
||||
"intent": "Strategic intent for upcoming orders based on negotiations",
|
||||
"updated_relationships": {
|
||||
"POWER_NAME": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
}
|
||||
}
|
||||
|
||||
Example scenarios:
|
||||
|
||||
Scenario 1 - Alliance forming:
|
||||
{
|
||||
"negotiation_summary": "Reached agreement with Italy for DMZ in Piedmont and mutual support against Austria. England remains non-committal about channel.",
|
||||
"intent": "Will honor DMZ with Italy and support their move to Trieste while securing Belgium",
|
||||
"updated_relationships": {
|
||||
"ITALY": "Friendly",
|
||||
"ENGLAND": "Neutral",
|
||||
"AUSTRIA": "Unfriendly"
|
||||
}
|
||||
}
|
||||
|
||||
Scenario 2 - Detecting deception:
|
||||
{
|
||||
"negotiation_summary": "Germany claims they'll support me into Belgium but also told England they'd help them. Russia suspiciously quiet.",
|
||||
"intent": "Assume Germany is unreliable, prepare defensive positions",
|
||||
"updated_relationships": {
|
||||
"GERMANY": "Unfriendly",
|
||||
"RUSSIA": "Neutral"
|
||||
}
|
||||
}
|
||||
|
||||
Scenario 3 - Coordinated attack:
|
||||
{
|
||||
"negotiation_summary": "Coordinated joint attack on Turkey with Austria. Russia agrees to DMZ Black Sea.",
|
||||
"intent": "Execute agreed plan: Army Greece to Bulgaria, Fleet Aegean to Eastern Med",
|
||||
"updated_relationships": {
|
||||
"AUSTRIA": "Ally",
|
||||
"RUSSIA": "Friendly",
|
||||
"TURKEY": "Enemy"
|
||||
}
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- Summarize what was actually discussed and agreed (or disagreed) upon
|
||||
- State clear intent for upcoming moves based on negotiations
|
||||
- Only include powers whose relationships have changed
|
||||
- Use ONLY: Enemy, Unfriendly, Neutral, Friendly, or Ally
|
||||
|
||||
Response to format:
|
||||
[RAW_RESPONSE]
|
||||
|
||||
Return ONLY the JSON object, no other text.""",
|
||||
|
||||
FORMAT_STATE_UPDATE: """Extract the state update information and format as JSON.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"reasoning": "Brief explanation of your analysis",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ENGLAND": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"FRANCE": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"GERMANY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ITALY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"RUSSIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"TURKEY": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
},
|
||||
"goals": [
|
||||
"Specific goal 1",
|
||||
"Specific goal 2",
|
||||
"Specific goal 3"
|
||||
]
|
||||
}
|
||||
|
||||
Example scenarios:
|
||||
|
||||
Scenario 1 - Early game position:
|
||||
{
|
||||
"reasoning": "France moved to Channel despite promises. Germany supporting me as agreed. Focus shifting to defending homeland.",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Neutral",
|
||||
"ENGLAND": "Neutral",
|
||||
"FRANCE": "Enemy",
|
||||
"GERMANY": "Friendly",
|
||||
"ITALY": "Neutral",
|
||||
"RUSSIA": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
},
|
||||
"goals": [
|
||||
"Defend London from French fleet in Channel",
|
||||
"Secure Norway before Russia",
|
||||
"Coordinate with Germany against France"
|
||||
]
|
||||
}
|
||||
|
||||
Scenario 2 - Mid-game betrayal:
|
||||
{
|
||||
"reasoning": "Italy broke our alliance and took Marseilles. Need new allies urgently. Germany looking strong.",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Unfriendly",
|
||||
"ENGLAND": "Neutral",
|
||||
"FRANCE": "Neutral",
|
||||
"GERMANY": "Unfriendly",
|
||||
"ITALY": "Enemy",
|
||||
"RUSSIA": "Friendly",
|
||||
"TURKEY": "Ally"
|
||||
},
|
||||
"goals": [
|
||||
"Retake Marseilles from Italy",
|
||||
"Fortify Alpine positions",
|
||||
"Support Turkey against Austria"
|
||||
]
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- Extract the strategic reasoning from the analysis
|
||||
- Include ALL 7 powers in relationships (remove yourself)
|
||||
- Use ONLY: Enemy, Unfriendly, Neutral, Friendly, or Ally
|
||||
- Extract 2-4 specific, actionable goals
|
||||
|
||||
Response to format:
|
||||
[RAW_RESPONSE]
|
||||
|
||||
Return ONLY the JSON object, no other text.""",
|
||||
|
||||
FORMAT_ORDERS: """Extract the orders from the response and format them properly.
|
||||
|
||||
Required format:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": ["order1", "order2", "order3"]
|
||||
}}
|
||||
|
||||
Order format examples:
|
||||
- Hold: "A PAR H"
|
||||
- Move: "A PAR - MAR", "F BRE - MAO"
|
||||
- Support: "A MAR S A PAR - BUR", "F MAO S F BRE - ENG"
|
||||
- Convoy: "F MAO C A BRE - LON"
|
||||
- Build: "A PAR B", "F BRE B"
|
||||
- Disband: "A PAR D"
|
||||
- Retreat: "A PAR - BUR"
|
||||
- Dual-coast: "F STP/SC" (south coast), "F SPA/NC" (north coast)
|
||||
|
||||
Example 1 - France Spring 1901:
|
||||
If the response mentions:
|
||||
"I'll move army from Paris to Burgundy, fleet from Brest to Mid-Atlantic, and hold Marseilles"
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A PAR - BUR",
|
||||
"F BRE - MAO",
|
||||
"A MAR H"
|
||||
]
|
||||
}}
|
||||
|
||||
Example 2 - Italy with supports:
|
||||
If the response mentions:
|
||||
"Venice attacks Trieste with support from Apulia and Ionian Sea"
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A VEN - TRI",
|
||||
"A APU S A VEN - TRI",
|
||||
"F ION S A VEN - TRI"
|
||||
]
|
||||
}}
|
||||
|
||||
Example 3 - Build phase:
|
||||
If the response mentions:
|
||||
"Build army in Paris and fleet in Marseilles"
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A PAR B",
|
||||
"F MAR B"
|
||||
]
|
||||
}}
|
||||
|
||||
Instructions:
|
||||
- Extract all orders mentioned in the response
|
||||
- Use exact 3-letter province codes
|
||||
- Format each order exactly as shown in examples
|
||||
- Include ALL units that were given orders
|
||||
- Pay attention to support orders - they must reference exact moves
|
||||
|
||||
Response to format:
|
||||
[RAW_RESPONSE]
|
||||
|
||||
Return in this exact format with double braces:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [list of order strings]
|
||||
}}""",
|
||||
|
||||
FORMAT_CONVERSATION: """Extract the messages from the response and format as JSON array.
|
||||
|
||||
Required JSON format:
|
||||
[
|
||||
{
|
||||
"message_type": "global",
|
||||
"content": "Message text for all powers"
|
||||
},
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "POWER_NAME",
|
||||
"content": "Private message text"
|
||||
}
|
||||
]
|
||||
|
||||
Example 1 - Multiple messages:
|
||||
If the response mentions:
|
||||
"Send a global message: 'I propose we all work together against the leader'
|
||||
Tell Germany privately: 'I'll support you into Denmark if you help me with Belgium'
|
||||
Message Russia: 'Are you still interested in the Black Sea DMZ?'"
|
||||
|
||||
Extract as:
|
||||
[
|
||||
{
|
||||
"message_type": "global",
|
||||
"content": "I propose we all work together against the leader"
|
||||
},
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "GERMANY",
|
||||
"content": "I'll support you into Denmark if you help me with Belgium"
|
||||
},
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "RUSSIA",
|
||||
"content": "Are you still interested in the Black Sea DMZ?"
|
||||
}
|
||||
]
|
||||
|
||||
Example 2 - Single private message:
|
||||
If the response mentions:
|
||||
"Reply to Italy: 'I accept your proposal for Piedmont DMZ'"
|
||||
|
||||
Extract as:
|
||||
[
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "ITALY",
|
||||
"content": "I accept your proposal for Piedmont DMZ"
|
||||
}
|
||||
]
|
||||
|
||||
Example 3 - No messages:
|
||||
If the response indicates no messages to send:
|
||||
|
||||
Extract as:
|
||||
[]
|
||||
|
||||
Instructions:
|
||||
- Extract each message the player wants to send
|
||||
- Identify if it's meant for everyone (global) or specific power (private)
|
||||
- For private messages, identify the recipient power (use uppercase: AUSTRIA, ENGLAND, FRANCE, GERMANY, ITALY, RUSSIA, TURKEY)
|
||||
- Preserve the key diplomatic points but clean up formatting
|
||||
- Use proper JSON string escaping for quotes
|
||||
- Return empty array [] if no messages
|
||||
|
||||
Response to format:
|
||||
[RAW_RESPONSE]
|
||||
|
||||
Return ONLY the JSON array, no other text."""
|
||||
}
|
||||
|
||||
# Write all formatting prompts
|
||||
for format_type, prompt_content in format_prompts.items():
|
||||
prompt_file = prompts_dir / f"format_{format_type}.txt"
|
||||
with open(prompt_file, 'w') as f:
|
||||
f.write(prompt_content)
|
||||
|
||||
print(f"Created {len(format_prompts)} formatting prompts in {prompts_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create the formatting prompts when module is run directly
|
||||
create_formatting_prompts()
|
||||
|
|
@ -9,9 +9,10 @@ if False: # TYPE_CHECKING
|
|||
from diplomacy.models.game import GameHistory
|
||||
from .agent import DiplomacyAgent
|
||||
|
||||
from .agent import ALL_POWERS, ALLOWED_RELATIONSHIPS
|
||||
from .agent import ALL_POWERS, ALLOWED_RELATIONSHIPS, _load_prompt_file
|
||||
from .utils import run_llm_and_log, log_llm_response
|
||||
from .prompt_constructor import build_context_prompt
|
||||
from .formatter import format_with_gemini_flash, FORMAT_INITIAL_STATE
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -32,14 +33,18 @@ async def initialize_agent_state_ext(
|
|||
success_status = "Failure: Initialized" # Default status
|
||||
|
||||
try:
|
||||
# Use a simplified prompt for initial state generation
|
||||
# Load the unformatted prompt template
|
||||
allowed_labels_str = ", ".join(ALLOWED_RELATIONSHIPS)
|
||||
initial_prompt = f"You are the agent for {power_name} in a game of Diplomacy at the very start (Spring 1901). " \
|
||||
f"Analyze the initial board position and suggest 2-3 strategic high-level goals for the early game. " \
|
||||
f"Consider your power's strengths, weaknesses, and neighbors. " \
|
||||
f"Also, provide an initial assessment of relationships with other powers. " \
|
||||
f"IMPORTANT: For each relationship, you MUST use exactly one of the following labels: {allowed_labels_str}. " \
|
||||
f"Format your response as a JSON object with two keys: 'initial_goals' (a list of strings) and 'initial_relationships' (a dictionary mapping power names to one of the allowed relationship strings)."
|
||||
initial_prompt_template = _load_prompt_file('unformatted/initial_state_prompt.txt', prompts_dir=prompts_dir)
|
||||
if not initial_prompt_template:
|
||||
logger.error(f"[{power_name}] Could not load unformatted/initial_state_prompt.txt. Cannot initialize.")
|
||||
return
|
||||
|
||||
# Format the prompt with variables
|
||||
initial_prompt = initial_prompt_template.format(
|
||||
power_name=power_name,
|
||||
allowed_labels_str=allowed_labels_str
|
||||
)
|
||||
|
||||
board_state = game.get_state() if game else {}
|
||||
possible_orders = game.get_all_possible_orders() if game else {}
|
||||
|
|
@ -75,7 +80,15 @@ async def initialize_agent_state_ext(
|
|||
|
||||
parsed_successfully = False
|
||||
try:
|
||||
update_data = agent._extract_json_from_text(response)
|
||||
# Format the natural language response into JSON
|
||||
formatted_response = await format_with_gemini_flash(
|
||||
response,
|
||||
FORMAT_INITIAL_STATE,
|
||||
power_name=power_name,
|
||||
phase=current_phase,
|
||||
log_file_path=log_file_path
|
||||
)
|
||||
update_data = agent._extract_json_from_text(formatted_response)
|
||||
logger.debug(f"[{power_name}] Successfully parsed JSON: {update_data}")
|
||||
parsed_successfully = True
|
||||
except json.JSONDecodeError as e:
|
||||
|
|
|
|||
|
|
@ -184,16 +184,16 @@ def construct_order_generation_prompt(
|
|||
"""
|
||||
# Load prompts
|
||||
_ = load_prompt("few_shot_example.txt", prompts_dir=prompts_dir) # Loaded but not used, as per original logic
|
||||
# Pick the phase-specific instruction file
|
||||
# Pick the phase-specific instruction file (using unformatted versions)
|
||||
phase_code = board_state["phase"][-1] # 'M' (movement), 'R', or 'A' / 'B'
|
||||
if phase_code == "M":
|
||||
instructions_file = "order_instructions_movement_phase.txt"
|
||||
instructions_file = "unformatted/order_instructions_movement_phase.txt"
|
||||
elif phase_code in ("A", "B"): # builds / adjustments
|
||||
instructions_file = "order_instructions_adjustment_phase.txt"
|
||||
instructions_file = "unformatted/order_instructions_adjustment_phase.txt"
|
||||
elif phase_code == "R": # retreats
|
||||
instructions_file = "order_instructions_retreat_phase.txt"
|
||||
instructions_file = "unformatted/order_instructions_retreat_phase.txt"
|
||||
else: # unexpected – default to movement rules
|
||||
instructions_file = "order_instructions_movement_phase.txt"
|
||||
instructions_file = "unformatted/order_instructions_movement_phase.txt"
|
||||
|
||||
instructions = load_prompt(instructions_file, prompts_dir=prompts_dir)
|
||||
_use_simple = os.getenv("SIMPLE_PROMPTS", "0").lower() in {"1", "true", "yes"}
|
||||
|
|
@ -212,10 +212,15 @@ def construct_order_generation_prompt(
|
|||
include_messages=not _use_simple, # include only when *not* simple
|
||||
)
|
||||
|
||||
final_prompt = system_prompt + "\n\n" + context + "\n\n" + instructions
|
||||
# Append goals at the end for focus
|
||||
goals_section = ""
|
||||
if agent_goals:
|
||||
goals_section = "\n\nYOUR STRATEGIC GOALS:\n" + "\n".join(f"- {g}" for g in agent_goals) + "\n\nKeep these goals in mind when choosing your orders."
|
||||
|
||||
final_prompt = system_prompt + "\n\n" + context + "\n\n" + instructions + goals_section
|
||||
|
||||
# Make the power names more LLM friendly
|
||||
final_prompt = final_prompt.replace('AUSTRIA', 'Austria').replace('ENGLAND', "England").replace('FRANCE', 'France').replace('GERMANY', 'Germany').replace('ITALY', "Italy").replace('RUSSIA', 'Russia').replace('TURKEY', 'Turkey')
|
||||
print(final_prompt)
|
||||
logger.debug(f"Final order generation prompt preview for {power_name}: {final_prompt[:500]}...")
|
||||
|
||||
return final_prompt
|
||||
82
ai_diplomacy/prompts/formatting/format_conversation.txt
Normal file
82
ai_diplomacy/prompts/formatting/format_conversation.txt
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
IMPORTANT: You are a formatting assistant. Your ONLY job is to extract diplomatic messages from the response below and format them as JSON.
|
||||
|
||||
The response may contain strategic analysis, order suggestions, or other content - IGNORE all of that. ONLY extract actual messages intended to be sent to other powers.
|
||||
|
||||
If the response contains NO messages to other powers (only strategy discussion or orders), return an empty array: []
|
||||
|
||||
Required JSON format:
|
||||
[
|
||||
{
|
||||
"message_type": "global",
|
||||
"content": "Message text for all powers"
|
||||
},
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "POWER_NAME",
|
||||
"content": "Private message text"
|
||||
}
|
||||
]
|
||||
|
||||
Example 1 - Multiple messages:
|
||||
If the response mentions:
|
||||
"Send a global message: 'I propose we all work together against the leader'
|
||||
Tell Germany privately: 'I'll support you into Denmark if you help me with Belgium'
|
||||
Message Russia: 'Are you still interested in the Black Sea DMZ?'"
|
||||
|
||||
Extract as:
|
||||
[
|
||||
{
|
||||
"message_type": "global",
|
||||
"content": "I propose we all work together against the leader"
|
||||
},
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "GERMANY",
|
||||
"content": "I'll support you into Denmark if you help me with Belgium"
|
||||
},
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "RUSSIA",
|
||||
"content": "Are you still interested in the Black Sea DMZ?"
|
||||
}
|
||||
]
|
||||
|
||||
Example 2 - Single private message:
|
||||
If the response mentions:
|
||||
"Reply to Italy: 'I accept your proposal for Piedmont DMZ'"
|
||||
|
||||
Extract as:
|
||||
[
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "ITALY",
|
||||
"content": "I accept your proposal for Piedmont DMZ"
|
||||
}
|
||||
]
|
||||
|
||||
Example 3 - No messages:
|
||||
If the response indicates no messages to send:
|
||||
|
||||
Extract as:
|
||||
[]
|
||||
|
||||
Instructions:
|
||||
- ONLY extract actual diplomatic messages (communications to other powers)
|
||||
- Do NOT extract strategic thoughts, order discussions, or analysis
|
||||
- Look for phrases like "Tell X", "Message to Y", "Propose to Z", "I suggest we", etc.
|
||||
- If the response only contains strategy/orders with NO messages, return []
|
||||
- For each message found:
|
||||
- Identify if it's global (to all) or private (to specific power)
|
||||
- For private messages, identify the recipient (AUSTRIA, ENGLAND, FRANCE, GERMANY, ITALY, RUSSIA, TURKEY)
|
||||
- Extract the actual message content
|
||||
- Use proper JSON escaping for quotes
|
||||
|
||||
<content_to_format>
|
||||
[RAW_RESPONSE]
|
||||
</content_to_format>
|
||||
|
||||
REMEMBER: You are ONLY formatting messages, not creating them. If there are no messages in the response above, return an empty array [].
|
||||
|
||||
Return ONLY the JSON array, no other text.
|
||||
|
||||
Now format the content within the <content_to_format> tags above.
|
||||
85
ai_diplomacy/prompts/formatting/format_initial_state.txt
Normal file
85
ai_diplomacy/prompts/formatting/format_initial_state.txt
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
IMPORTANT: You are a formatting assistant. Your ONLY job is to extract initial strategic goals and relationship assessments from the response below and format them as JSON.
|
||||
|
||||
The response contains strategic analysis about a Diplomacy game starting position. Extract the goals and relationships.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"initial_goals": [
|
||||
"Specific goal 1",
|
||||
"Specific goal 2",
|
||||
"Specific goal 3"
|
||||
],
|
||||
"initial_relationships": {
|
||||
"AUSTRIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ENGLAND": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"FRANCE": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"GERMANY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ITALY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"RUSSIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"TURKEY": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
}
|
||||
}
|
||||
|
||||
Example 1 - Russia's opening:
|
||||
If the response mentions:
|
||||
"My goals are to secure the Western Front by preventing German expansion, control the Black Sea to limit Turkey, and neutralize Austria who is my traditional rival. Germany is unfriendly, Austria is an enemy, Turkey could go either way."
|
||||
|
||||
Extract as:
|
||||
{
|
||||
"initial_goals": [
|
||||
"Secure the Western Front by preventing German expansion",
|
||||
"Control the Black Sea to limit Turkey",
|
||||
"Neutralize Austria who is my traditional rival"
|
||||
],
|
||||
"initial_relationships": {
|
||||
"AUSTRIA": "Enemy",
|
||||
"ENGLAND": "Neutral",
|
||||
"FRANCE": "Neutral",
|
||||
"GERMANY": "Unfriendly",
|
||||
"ITALY": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
}
|
||||
}
|
||||
|
||||
Example 2 - England's opening:
|
||||
If the response mentions:
|
||||
"I need to secure control of the seas, prevent France from taking the Channel, and expand into Scandinavia. France is my main concern, while Germany could be a useful partner against them."
|
||||
|
||||
Extract as:
|
||||
{
|
||||
"initial_goals": [
|
||||
"Secure control of the North Sea and English Channel",
|
||||
"Prevent French expansion into the Channel",
|
||||
"Expand into Scandinavia"
|
||||
],
|
||||
"initial_relationships": {
|
||||
"AUSTRIA": "Neutral",
|
||||
"FRANCE": "Unfriendly",
|
||||
"GERMANY": "Friendly",
|
||||
"ITALY": "Neutral",
|
||||
"RUSSIA": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
}
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- Goals: Look for strategic objectives, expansion plans, or priorities mentioned
|
||||
- Common phrases: "My goals are", "I need to", "Focus on", "Secure", "Expand into"
|
||||
- Extract 3-5 specific goals
|
||||
- Relationships: Look for assessments of other powers
|
||||
- Common phrases: "X is a threat", "Y could be an ally", "Z is neutral"
|
||||
- Use ONLY these labels: Enemy, Unfriendly, Neutral, Friendly, or Ally
|
||||
- Include all 7 powers (remove the player's own power)
|
||||
- If a power isn't mentioned, default to "Neutral"
|
||||
- Map natural language to labels:
|
||||
- "threat", "rival", "must eliminate" → Enemy or Unfriendly
|
||||
- "potential partner", "could work with" → Friendly
|
||||
- "ally", "alliance" → Ally
|
||||
|
||||
<content_to_format>
|
||||
[RAW_RESPONSE]
|
||||
</content_to_format>
|
||||
|
||||
Return ONLY the JSON object, no other text.
|
||||
|
||||
Now format the content within the <content_to_format> tags above.
|
||||
73
ai_diplomacy/prompts/formatting/format_negotiation_diary.txt
Normal file
73
ai_diplomacy/prompts/formatting/format_negotiation_diary.txt
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
IMPORTANT: You are a formatting assistant. Your ONLY job is to extract negotiation outcomes and analysis from the response below and format them as JSON.
|
||||
|
||||
The response contains a player's reflection on diplomatic negotiations that just occurred.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"negotiation_summary": "Key outcomes from negotiations - what was discussed and agreed",
|
||||
"intent": "Strategic intent for upcoming orders based on negotiations",
|
||||
"current_relationships": {
|
||||
"AUSTRIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ENGLAND": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"FRANCE": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"GERMANY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ITALY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"RUSSIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"TURKEY": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
}
|
||||
}
|
||||
|
||||
Example scenarios:
|
||||
|
||||
Scenario 1 - Alliance forming:
|
||||
{
|
||||
"negotiation_summary": "Reached agreement with Italy for DMZ in Piedmont and mutual support against Austria. England remains non-committal about channel.",
|
||||
"intent": "Will honor DMZ with Italy and support their move to Trieste while securing Belgium",
|
||||
"current_relationships": {
|
||||
"ITALY": "Friendly",
|
||||
"ENGLAND": "Neutral",
|
||||
"AUSTRIA": "Unfriendly"
|
||||
}
|
||||
}
|
||||
|
||||
Scenario 2 - Detecting deception:
|
||||
{
|
||||
"negotiation_summary": "Germany claims they'll support me into Belgium but also told England they'd help them. Russia suspiciously quiet.",
|
||||
"intent": "Assume Germany is unreliable, prepare defensive positions",
|
||||
"current_relationships": {
|
||||
"GERMANY": "Unfriendly",
|
||||
"RUSSIA": "Neutral"
|
||||
}
|
||||
}
|
||||
|
||||
Scenario 3 - Coordinated attack:
|
||||
{
|
||||
"negotiation_summary": "Coordinated joint attack on Turkey with Austria. Russia agrees to DMZ Black Sea.",
|
||||
"intent": "Execute agreed plan: Army Greece to Bulgaria, Fleet Aegean to Eastern Med",
|
||||
"current_relationships": {
|
||||
"AUSTRIA": "Ally",
|
||||
"RUSSIA": "Friendly",
|
||||
"TURKEY": "Enemy"
|
||||
}
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- negotiation_summary: What was discussed with other powers?
|
||||
- Look for: agreements made, proposals received, rejections, promises
|
||||
- Common phrases: "agreed to", "proposed", "rejected", "promised"
|
||||
- intent: What will the player do based on these negotiations?
|
||||
- Look for: planned moves, strategies, responses to agreements
|
||||
- Common phrases: "I will", "plan to", "intend to", "based on this"
|
||||
- current_relationships: Your assessment of ALL powers after negotiations
|
||||
- Include ALL 7 powers (remove yourself from the list)
|
||||
- Reflect any changes from negotiations
|
||||
- Use ONLY: Enemy, Unfriendly, Neutral, Friendly, or Ally
|
||||
- For powers not involved in negotiations, maintain previous assessment
|
||||
|
||||
<content_to_format>
|
||||
[RAW_RESPONSE]
|
||||
</content_to_format>
|
||||
|
||||
Return ONLY the JSON object, no other text.
|
||||
|
||||
Now format the content within the <content_to_format> tags above.
|
||||
52
ai_diplomacy/prompts/formatting/format_order_diary.txt
Normal file
52
ai_diplomacy/prompts/formatting/format_order_diary.txt
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
IMPORTANT: You are a formatting assistant. Your ONLY job is to extract a summary of orders from the response below and format it as JSON.
|
||||
|
||||
The response contains a player's reflection on the orders they just submitted.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"order_summary": "Brief summary of orders and strategic intent"
|
||||
}
|
||||
|
||||
Example 1 - Movement phase:
|
||||
If the response mentions:
|
||||
"I ordered my army in Paris to Burgundy to secure the neutral supply center, fleet from Brest to Mid-Atlantic Ocean to prepare for Iberian operations, and held in Marseilles to defend against Italian aggression."
|
||||
|
||||
Extract as:
|
||||
{
|
||||
"order_summary": "Moved A PAR to BUR for neutral SC, F BRE to MAO for Iberian positioning, held MAR against Italy"
|
||||
}
|
||||
|
||||
Example 2 - Support orders:
|
||||
If the response mentions:
|
||||
"All units supported the attack on Munich - armies from Bohemia and Tyrolia supported Berlin's move into Munich."
|
||||
|
||||
Extract as:
|
||||
{
|
||||
"order_summary": "Coordinated three-unit attack on Munich with BOH and TYR supporting BER"
|
||||
}
|
||||
|
||||
Example 3 - Build phase:
|
||||
If the response mentions:
|
||||
"Built fleets in London and Edinburgh to strengthen naval presence, no builds in Liverpool."
|
||||
|
||||
Extract as:
|
||||
{
|
||||
"order_summary": "Built F LON and F EDI for naval dominance, waived LVP build"
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- Look for descriptions of what orders were submitted
|
||||
- Common phrases: "I ordered", "moved to", "supported", "held in"
|
||||
- Summarize both WHAT was ordered and WHY (strategic intent)
|
||||
- Keep it concise (1-2 sentences)
|
||||
- Use standard 3-letter province codes when mentioned
|
||||
- Focus on the strategic purpose, not just the mechanical moves
|
||||
- If the response doesn't contain order information, summarize the strategic discussion
|
||||
|
||||
<content_to_format>
|
||||
[RAW_RESPONSE]
|
||||
</content_to_format>
|
||||
|
||||
Return ONLY the JSON object, no other text.
|
||||
|
||||
Now format the content within the <content_to_format> tags above.
|
||||
121
ai_diplomacy/prompts/formatting/format_orders.txt
Normal file
121
ai_diplomacy/prompts/formatting/format_orders.txt
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
IMPORTANT: You are a formatting assistant. Your ONLY job is to extract Diplomacy game orders from the response below and format them properly.
|
||||
|
||||
The response will contain strategic analysis and order suggestions. Look for the actual orders (movements, holds, supports, etc.).
|
||||
|
||||
Required format:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": ["order1", "order2", "order3"]
|
||||
}}
|
||||
|
||||
Order format examples:
|
||||
- Hold: "A PAR H"
|
||||
- Move: "A PAR - MAR", "F BRE - MAO"
|
||||
- Support: "A MAR S A PAR - BUR", "F MAO S F BRE - ENG"
|
||||
- Convoy: "F MAO C A BRE - LON"
|
||||
- Build: "A PAR B", "F BRE B"
|
||||
- Disband: "A PAR D"
|
||||
- Retreat: "A PAR - BUR"
|
||||
- Dual-coast: "F STP/SC" (south coast), "F SPA/NC" (north coast)
|
||||
|
||||
Example 1 - France Spring 1901:
|
||||
If the response mentions:
|
||||
"I'll move army from Paris to Burgundy, fleet from Brest to Mid-Atlantic, and hold Marseilles"
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A PAR - BUR",
|
||||
"F BRE - MAO",
|
||||
"A MAR H"
|
||||
]
|
||||
}}
|
||||
|
||||
Example 2 - Italy with supports:
|
||||
If the response mentions:
|
||||
"Venice attacks Trieste with support from Apulia and Ionian Sea"
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A VEN - TRI",
|
||||
"A APU S A VEN - TRI",
|
||||
"F ION S A VEN - TRI"
|
||||
]
|
||||
}}
|
||||
|
||||
Example 3 - Build phase:
|
||||
If the response mentions:
|
||||
"Build army in Paris and fleet in Marseilles"
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A PAR B",
|
||||
"F MAR B"
|
||||
]
|
||||
}}
|
||||
|
||||
Example 4 - Germany Spring 1901:
|
||||
If the response mentions:
|
||||
"Denmark (DEN) and Holland (HOL) are key neutral centers for early German expansion. Need to secure Berlin (BER) and Munich (MUN) against potential French or Russian incursions. Kiel (KIE) fleet is best positioned for DEN, while an army from Ruhr (RUH) can take HOL."
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A BER H",
|
||||
"A MUN H",
|
||||
"F KIE - DEN",
|
||||
"A RUH - HOL",
|
||||
"A SIL - WAR",
|
||||
"F HEL H"
|
||||
]
|
||||
}}
|
||||
|
||||
Example 5 - Italy Autumn 1902:
|
||||
If the response mentions:
|
||||
"My primary goal is to take Trieste (TRI) from Austria. Army in Venice (VEN) will attack, supported by Army in Apulia (APU). Fleet in Ionian Sea (ION) will support the attack on Trieste from the sea. Army in Rome (ROM) will hold to protect the capital. Fleet in Naples (NAP) will move to Tyrrhenian Sea (TYS) to defend against a potential French move."
|
||||
|
||||
Extract as:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [
|
||||
"A VEN - TRI",
|
||||
"A APU S A VEN - TRI",
|
||||
"F ION S A VEN - TRI",
|
||||
"A ROM H",
|
||||
"F NAP - TYS"
|
||||
]
|
||||
}}
|
||||
|
||||
Instructions:
|
||||
- Look for lines that describe unit movements (e.g., "A BER - KIE", "Move Berlin to Kiel")
|
||||
- Convert natural language to standard format:
|
||||
- "Move army from Berlin to Kiel" → "A BER - KIE"
|
||||
- "Fleet in Kiel moves to Denmark" → "F KIE - DEN"
|
||||
- "Hold in Munich" → "A MUN H"
|
||||
- Use exact 3-letter province codes (BER, KIE, MUN, etc.)
|
||||
- Include ALL units that were given orders
|
||||
- If you see "Order:" followed by a properly formatted order, use it directly
|
||||
- Common patterns to look for:
|
||||
- "A/F [PROVINCE] - [PROVINCE]" (movement)
|
||||
- "A/F [PROVINCE] H" (hold)
|
||||
- "A/F [PROVINCE] S A/F [PROVINCE] - [PROVINCE]" (support)
|
||||
|
||||
<content_to_format>
|
||||
[RAW_RESPONSE]
|
||||
</content_to_format>
|
||||
|
||||
REMEMBER: Extract the actual game orders from the strategic discussion above. Look for specific unit movements.
|
||||
|
||||
Return in this exact format with double braces:
|
||||
PARSABLE OUTPUT:
|
||||
{{
|
||||
"orders": [list of order strings]
|
||||
}}
|
||||
|
||||
Now format the content within the <content_to_format> tags above.
|
||||
140
ai_diplomacy/prompts/formatting/format_state_update.txt
Normal file
140
ai_diplomacy/prompts/formatting/format_state_update.txt
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
IMPORTANT: You are a formatting assistant. Your ONLY job is to extract state update information (analysis, relationships, and goals) from the response below and format them as JSON.
|
||||
|
||||
The response contains a player's analysis of the current game state after seeing the results of a turn.
|
||||
|
||||
Required JSON format:
|
||||
{
|
||||
"reasoning": "Brief explanation of your analysis",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ENGLAND": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"FRANCE": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"GERMANY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"ITALY": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"RUSSIA": "Enemy|Unfriendly|Neutral|Friendly|Ally",
|
||||
"TURKEY": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
},
|
||||
"goals": [
|
||||
"Specific goal 1",
|
||||
"Specific goal 2",
|
||||
"Specific goal 3"
|
||||
]
|
||||
}
|
||||
|
||||
Example scenarios:
|
||||
|
||||
Scenario 1 - Early game position:
|
||||
{
|
||||
"reasoning": "France moved to Channel despite promises. Germany supporting me as agreed. Focus shifting to defending homeland.",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Neutral",
|
||||
"ENGLAND": "Neutral",
|
||||
"FRANCE": "Enemy",
|
||||
"GERMANY": "Friendly",
|
||||
"ITALY": "Neutral",
|
||||
"RUSSIA": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
},
|
||||
"goals": [
|
||||
"Defend London from French fleet in Channel",
|
||||
"Secure Norway before Russia",
|
||||
"Coordinate with Germany against France"
|
||||
]
|
||||
}
|
||||
|
||||
Scenario 2 - Mid-game betrayal:
|
||||
{
|
||||
"reasoning": "Italy broke our alliance and took Marseilles. Need new allies urgently. Germany looking strong.",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Unfriendly",
|
||||
"ENGLAND": "Neutral",
|
||||
"FRANCE": "Neutral",
|
||||
"GERMANY": "Unfriendly",
|
||||
"ITALY": "Enemy",
|
||||
"RUSSIA": "Friendly",
|
||||
"TURKEY": "Ally"
|
||||
},
|
||||
"goals": [
|
||||
"Retake Marseilles from Italy",
|
||||
"Fortify Alpine positions",
|
||||
"Support Turkey against Austria"
|
||||
]
|
||||
}
|
||||
|
||||
Scenario 3 - After Cooperation:
|
||||
{
|
||||
"reasoning": "Austria helped take Warsaw. Russia attacked Prussia.",
|
||||
"relationships": {
|
||||
"AUSTRIA": "Ally",
|
||||
"RUSSIA": "Enemy",
|
||||
"TURKEY": "Neutral",
|
||||
"ITALY": "Unfriendly",
|
||||
"FRANCE": "Neutral",
|
||||
"ENGLAND": "Neutral",
|
||||
"GERMANY": "Neutral"
|
||||
},
|
||||
"goals": [
|
||||
"Hold Warsaw against Russia",
|
||||
"Keep Austrian alliance",
|
||||
"Block Italian expansion"
|
||||
]
|
||||
}
|
||||
|
||||
Scenario 4 - England after failed Belgium attack:
|
||||
{
|
||||
"reasoning": "My attack on Belgium was decisively repulsed due to Franco-German cooperation, marking them as a significant threat bloc. Russia's acquisition of Sweden is concerning for my northern position. The Austro-Italian conflict seems localized for now, and Turkey's inactivity makes them an unknown variable, potentially open to diplomacy.",
|
||||
"relationships": {
|
||||
"FRANCE": "Enemy",
|
||||
"GERMANY": "Enemy",
|
||||
"RUSSIA": "Unfriendly",
|
||||
"AUSTRIA": "Neutral",
|
||||
"ITALY": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
},
|
||||
"goals": [
|
||||
"Break the Franco-German alliance or find a way to counter their combined strength.",
|
||||
"Secure North Sea (NTH) and prevent further Russian expansion towards Norway (NWY).",
|
||||
"Seek dialogue with Turkey or Austria/Italy to create a counterweight to the dominant bloc."
|
||||
]
|
||||
}
|
||||
|
||||
Scenario 5 - Russia after Black Sea negotiation:
|
||||
{
|
||||
"reasoning": "Securing Rumania via Turkish agreement is a major success. This improves my southern position and Turkey is now a provisional ally. Germany's move into Silesia is a direct and immediate threat to Warsaw; they are now my primary adversary. Austria and France are posturing, but their conflict doesn't directly affect me yet, keeping them neutral. England's new fleet is a long-term concern but not immediate. Italy's westward focus means they are not a current threat or priority.",
|
||||
"relationships": {
|
||||
"GERMANY": "Enemy",
|
||||
"AUSTRIA": "Neutral",
|
||||
"TURKEY": "Ally",
|
||||
"ITALY": "Neutral",
|
||||
"FRANCE": "Neutral",
|
||||
"ENGLAND": "Unfriendly"
|
||||
},
|
||||
"goals": [
|
||||
"Defend Warsaw against Germany, possibly by moving Lvn-War or Mos-War.",
|
||||
"Solidify alliance with Turkey, potentially coordinating further moves in the south or against Austria if Germany allies with them.",
|
||||
"Monitor English fleet movements and prepare for a potential northern threat in future turns.",
|
||||
"Explore diplomatic options with France or Austria to counter German aggression."
|
||||
]
|
||||
}
|
||||
|
||||
Instructions:
|
||||
- reasoning: Extract the key strategic analysis
|
||||
- Look for: what happened, what changed, new threats/opportunities
|
||||
- Keep it brief (1-2 sentences)
|
||||
- relationships: Current view of ALL other powers
|
||||
- Must include all 7 powers (remove the player's own power)
|
||||
- Use ONLY: Enemy, Unfriendly, Neutral, Friendly, or Ally
|
||||
- Look for relationship indicators in the analysis
|
||||
- If a power isn't mentioned, check if there's a previous relationship to maintain
|
||||
- goals: Updated strategic objectives
|
||||
- Look for: new priorities, adjusted plans, responses to events
|
||||
- Extract 2-4 specific, actionable goals
|
||||
- Common phrases: "need to", "must", "priority is", "focus on"
|
||||
|
||||
<content_to_format>
|
||||
[RAW_RESPONSE]
|
||||
</content_to_format>
|
||||
|
||||
Return ONLY the JSON object, no other text.
|
||||
|
||||
Now format the content within the <content_to_format> tags above.
|
||||
22
ai_diplomacy/prompts/unformatted/conversation_context.txt
Normal file
22
ai_diplomacy/prompts/unformatted/conversation_context.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
DIPLOMATIC CONTEXT
|
||||
|
||||
You are {power_name} in a game of Diplomacy.
|
||||
Current Phase: {current_phase}
|
||||
|
||||
YOUR STRATEGIC FRAMEWORK
|
||||
Goals:
|
||||
{agent_goals}
|
||||
|
||||
Current Relationships:
|
||||
{agent_relationships}
|
||||
|
||||
DIPLOMATIC SITUATION
|
||||
Recent Negotiations:
|
||||
{recent_private_diary}
|
||||
|
||||
Messages This Round:
|
||||
{messages_this_round}
|
||||
|
||||
OTHER POWERS' STATUS
|
||||
Active Powers: {active_powers}
|
||||
{eliminated_status}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
DIPLOMATIC MESSAGING TASK
|
||||
|
||||
You need to compose diplomatic messages to other powers in this negotiation phase.
|
||||
|
||||
IMPORTANT: This is about WRITING MESSAGES to other powers, not analyzing strategy or choosing orders.
|
||||
|
||||
GUIDELINES
|
||||
- Respond to messages in "RECENT MESSAGES REQUIRING YOUR ATTENTION" section
|
||||
- Propose deals, alliances, or coordination
|
||||
- Share (or mislead about) your intentions
|
||||
- Build trust or sow discord as needed
|
||||
- You can send multiple messages
|
||||
- You can choose to ignore certain powers
|
||||
|
||||
RESPOND IN TWO PARTS:
|
||||
|
||||
1. REASONING: Explain your diplomatic approach:
|
||||
- Which powers are you prioritizing for communication?
|
||||
- What messages need responses?
|
||||
- What deals or coordination are you proposing?
|
||||
- Are you being honest or deceptive?
|
||||
- Who are you deliberately not messaging and why?
|
||||
|
||||
2. MESSAGES: List the actual messages to send:
|
||||
- For EACH message specify:
|
||||
* Type: "global" (all see it) or "private" (only recipient sees)
|
||||
* Recipient: If private, which power (AUSTRIA, ENGLAND, FRANCE, GERMANY, ITALY, RUSSIA, TURKEY)
|
||||
* Content: The actual message text
|
||||
- Be specific and diplomatic in your wording
|
||||
- Examples:
|
||||
* Private to FRANCE: "I'm planning to move to the Channel. Will you support me?"
|
||||
* Global: "I propose we all respect current borders this turn."
|
||||
* Private to RUSSIA: "If you stay out of Galicia, I'll support you into Rumania."
|
||||
|
||||
REMEMBER: You are writing diplomatic messages, not explaining your overall strategy or orders.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
You are the agent for {power_name} in a game of Diplomacy at the very start (Spring 1901). Analyze the initial board position and suggest 2-3 strategic high-level goals for the early game. Consider your power's strengths, weaknesses, and neighbors. Also, provide an initial assessment of relationships with other powers. IMPORTANT: For each relationship, you MUST use exactly one of the following labels: {allowed_labels_str}.
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your strategic analysis of the starting position.
|
||||
|
||||
2. STRATEGY: Then, provide your 2-3 strategic high-level goals and your initial assessment of relationships with other powers.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
NEGOTIATION SUMMARY REQUEST
|
||||
Power: {power_name}
|
||||
Phase: {current_phase}
|
||||
|
||||
MESSAGES THIS ROUND
|
||||
{messages_this_round}
|
||||
{ignored_messages_context}
|
||||
|
||||
CURRENT STATUS
|
||||
Goals:
|
||||
{agent_goals}
|
||||
|
||||
Relationships:
|
||||
{agent_relationships}
|
||||
|
||||
Game State:
|
||||
{board_state_str}
|
||||
|
||||
TASK
|
||||
Analyze the negotiations, goals, relationships, and game state to:
|
||||
1. Summarize key outcomes and agreements
|
||||
2. State your strategic intent for {current_phase}
|
||||
3. Update relationships as needed (Enemy, Unfriendly, Neutral, Friendly, Ally)
|
||||
4. Note which powers are not responding to your messages and consider adjusting your approach
|
||||
|
||||
When powers ignore your messages, consider:
|
||||
- They may be intentionally avoiding commitment
|
||||
- They could be prioritizing other relationships
|
||||
- Your approach may need adjustment (more direct questions, different incentives)
|
||||
- Their silence might indicate hostility or indifference
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your analysis of the negotiations. What did each power communicate or fail to communicate? What do their messages (or silence) reveal about their intentions? How does this affect your strategic position?
|
||||
|
||||
2. NEGOTIATION SUMMARY: Then provide:
|
||||
- A summary of key outcomes from the negotiations
|
||||
- Your strategic intent for upcoming orders based on these negotiations
|
||||
- Any relationship updates based on the negotiations (only include powers whose relationships have changed)
|
||||
34
ai_diplomacy/prompts/unformatted/order_diary_prompt.txt
Normal file
34
ai_diplomacy/prompts/unformatted/order_diary_prompt.txt
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
ORDER DIARY ENTRY - POST-ORDER REFLECTION
|
||||
|
||||
You have ALREADY SUBMITTED the following orders for this turn:
|
||||
{orders_list_str}
|
||||
|
||||
Power: {power_name}
|
||||
Phase: {current_phase}
|
||||
|
||||
GAME CONTEXT (state BEFORE orders were executed):
|
||||
{board_state_str}
|
||||
|
||||
Your Strategic Framework:
|
||||
Goals: {agent_goals}
|
||||
Relationships: {agent_relationships}
|
||||
|
||||
IMPORTANT TASK
|
||||
You have ALREADY SUBMITTED your orders (listed above). Now write a diary entry reflecting on WHY you chose these specific orders.
|
||||
|
||||
This is NOT about choosing new orders - it's about documenting your reasoning for the orders you ALREADY SUBMITTED.
|
||||
|
||||
RESPOND IN TWO PARTS:
|
||||
|
||||
1. REASONING: Explain your strategic thinking:
|
||||
- What threats were you responding to when you chose these orders?
|
||||
- What opportunities were you trying to seize?
|
||||
- How did your relationships and recent negotiations influence these choices?
|
||||
- What calculated risks did you decide to take?
|
||||
|
||||
2. ORDER SUMMARY: Provide a concise summary (1-2 sentences):
|
||||
- Briefly restate the key moves you made (e.g., "Moved armies to secure BUR and supported the attack on MUN")
|
||||
- Explain the strategic intent (e.g., "to block French expansion while gaining a foothold in Germany")
|
||||
- Note any contingencies or backup plans
|
||||
|
||||
REMEMBER: Focus on explaining the orders SHOWN ABOVE that you ALREADY SUBMITTED.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
PRIMARY OBJECTIVE
|
||||
Control 18 supply centers. Nothing else will do.
|
||||
|
||||
CRITICAL ADJUSTMENT PHASE RULES
|
||||
1. Only use orders from the provided possible_orders list
|
||||
2. You can only build in unoccupied HOME supply centers you currently control
|
||||
3. Build orders format: '[UnitType] [Location] B'
|
||||
- UnitType: 'A' (Army) or 'F' (Fleet)
|
||||
- Example: 'A PAR B', 'F LON B'
|
||||
4. Disband orders format: '[UnitType] [Location] D'
|
||||
- Example: 'A PAR D', 'F LON D'
|
||||
5. Dual-coast provinces require coast specification for fleet builds:
|
||||
- Format: 'F [PROVINCE]/[COAST] B' where [COAST] = NC, SC, EC, or WC
|
||||
- Example: 'F STP/NC B', 'F SPA/SC B'
|
||||
|
||||
HOME SUPPLY CENTERS
|
||||
Austria
|
||||
- Budapest
|
||||
- Trieste
|
||||
- Vienna
|
||||
England
|
||||
- Edinburgh
|
||||
- Liverpool
|
||||
- London
|
||||
France
|
||||
- Brest
|
||||
- Marseilles
|
||||
- Paris
|
||||
Germany
|
||||
- Berlin
|
||||
- Kiel
|
||||
- Munich
|
||||
Italy
|
||||
- Naples
|
||||
- Rome
|
||||
- Venice
|
||||
Russia
|
||||
- Moscow
|
||||
- Saint Petersburg
|
||||
- Sevastopol
|
||||
- Warsaw
|
||||
Turkey
|
||||
- Ankara
|
||||
- Constantinople
|
||||
- Smyrna
|
||||
|
||||
ADJUSTMENT DECISION PROCESS
|
||||
1. CALCULATE
|
||||
- Count your supply centers
|
||||
- Count your current units
|
||||
- Determine builds or disbands needed
|
||||
|
||||
2. STRATEGIZE
|
||||
- Where to build for maximum strategic impact
|
||||
- Which units to disband if necessary
|
||||
- Balance between armies and fleets
|
||||
|
||||
3. PRIORITIZE
|
||||
- Build in threatened home centers first
|
||||
- Build units that support your strategic goals
|
||||
- Disband isolated or least useful units
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your adjustment analysis. How many supply centers do you control? How many units do you have? Where will you build and why? If disbanding, which units are least valuable?
|
||||
|
||||
2. ADJUSTMENT ORDERS: Then, list all your build (B) or disband (D) orders. Be precise with unit types (A/F) and locations.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
PRIMARY OBJECTIVE
|
||||
Control 18 supply centers. Nothing else will do.
|
||||
|
||||
CRITICAL RULES
|
||||
1. Only use orders from the provided possible_orders list
|
||||
2. Support orders must match actual moves (e.g., 'A PAR S F PIC - ENG' needs 'F PIC - ENG')
|
||||
3. Build orders (build phase only):
|
||||
- Format: '[UnitType] [Location3LetterCode] B'
|
||||
- UnitType: 'A' (Army) or 'F' (Fleet)
|
||||
- Example: 'A PAR B', 'F LON B'
|
||||
- NOTE YOU CAN ONLY BUILD UNITS IN YOUR HOME CENTER THAT ARE EMPTY, THE ONES YOU STARTED WITH, IF YOU LOSE THESE YOU CANNOT BUILD UNITS SO THEY ARE CRITICAL
|
||||
Austria
|
||||
- Budapest
|
||||
- Trieste
|
||||
- Vienna
|
||||
England
|
||||
- Edinburgh
|
||||
- Liverpool
|
||||
- London
|
||||
France
|
||||
- Brest
|
||||
- Marseilles
|
||||
- Paris
|
||||
Germany
|
||||
- Berlin
|
||||
- Kiel
|
||||
- Munich
|
||||
Italy
|
||||
- Naples
|
||||
- Rome
|
||||
- Venice
|
||||
Russia
|
||||
- Moscow
|
||||
- Saint Petersburg
|
||||
- Sevastopol
|
||||
- Warsaw
|
||||
Turkey
|
||||
- Ankara
|
||||
- Constantinople
|
||||
- Smyrna
|
||||
|
||||
ORDER SUBMISSION PROCESS
|
||||
1. ANALYZE
|
||||
- Review game state, orders, messages, and other powers' motivations
|
||||
- Focus on expansion and capturing supply centers
|
||||
- Be aggressive, not passive
|
||||
- Take calculated risks for significant gains
|
||||
- Find alternative paths if blocked
|
||||
|
||||
2. REASON
|
||||
- Write out your strategic thinking
|
||||
- Explain goals and move choices
|
||||
- Consider supports and holds
|
||||
|
||||
3. FORMAT
|
||||
- Dual-coast provinces (STP, SPA, BUL):
|
||||
* Specify coast when needed: 'F [PROVINCE]/[COAST_CODE]'
|
||||
* Example: 'F STP/NC B', 'A MAR S F SPA/SC - WES'
|
||||
* Coast codes: NC (North), SC (South), EC (East), WC (West)
|
||||
- All orders resolve simultaneously
|
||||
- Submit orders only, no messages
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your detailed strategic analysis. What are your immediate objectives? Which supply centers are you targeting? How will you deal with threats? What coordinated moves are you planning? Consider all your units and their best uses.
|
||||
|
||||
2. ORDERS: Then, list each order you want to submit, one per line. Be precise with unit types (A/F) and location codes.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
PRIMARY OBJECTIVE
|
||||
Control 18 supply centers. Nothing else will do.
|
||||
|
||||
CRITICAL RETREAT PHASE RULES
|
||||
1. The possible orders section shows where your dislodged units can retreat
|
||||
2. Units cannot retreat to:
|
||||
- The province they were dislodged from
|
||||
- A province occupied after this turn's moves
|
||||
- A province where a standoff occurred
|
||||
3. If no valid retreat exists, the unit must disband
|
||||
4. Retreat format: '[UnitType] [From] - [To]'
|
||||
- Example: 'A PAR - BUR', 'F BRE - ENG'
|
||||
5. Disband format: '[UnitType] [Location] D'
|
||||
- Example: 'A PAR D', 'F BRE D'
|
||||
6. Dual-coast provinces require coast specification for fleet retreats:
|
||||
- Format: 'F [PROVINCE]/[COAST] - [DESTINATION]'
|
||||
- Example: 'F SPA/SC - MAO', 'F BUL/EC - BLA'
|
||||
- Coast codes: NC (North), SC (South), EC (East), WC (West)
|
||||
|
||||
RETREAT DECISION PROCESS
|
||||
1. ASSESS
|
||||
- Which of your units are dislodged
|
||||
- What retreat options are available
|
||||
- Strategic value of each dislodged unit
|
||||
|
||||
2. PRIORITIZE
|
||||
- Retreat units that can still contribute to your strategy
|
||||
- Disband units that have no good retreat options
|
||||
- Consider future positioning for retreated units
|
||||
|
||||
3. EXECUTE
|
||||
- Choose optimal retreat destinations
|
||||
- Accept disbands when necessary
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your retreat decisions. Which units are dislodged? What are their retreat options? Why are you choosing to retreat or disband each unit?
|
||||
|
||||
2. RETREAT ORDERS: Then, provide a retreat or disband order for each dislodged unit. Be precise with unit types (A/F) and locations.
|
||||
34
ai_diplomacy/prompts/unformatted/state_update_prompt.txt
Normal file
34
ai_diplomacy/prompts/unformatted/state_update_prompt.txt
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
You are analyzing the results of a phase in Diplomacy for {power_name}.
|
||||
|
||||
GAME STATE
|
||||
Year: {current_year}
|
||||
Phase: {current_phase}
|
||||
Board State:
|
||||
{board_state_str}
|
||||
|
||||
PHASE SUMMARY ({current_phase}):
|
||||
{phase_summary}
|
||||
|
||||
CURRENT STATUS
|
||||
Goals:
|
||||
{current_goals}
|
||||
|
||||
Relationships with other powers ({other_powers}):
|
||||
{current_relationships}
|
||||
|
||||
TASK
|
||||
Analyze the phase summary and game state to update your relationships and goals.
|
||||
|
||||
IMPORTANT RULES
|
||||
1. Update relationships for ALL powers in {other_powers}
|
||||
2. Use ONLY these relationship values: Enemy, Unfriendly, Neutral, Friendly, Ally
|
||||
3. Make goals specific and actionable
|
||||
4. Base analysis on actual events, not assumptions
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your analysis of what happened this phase. Which powers acted as expected? Who surprised you? What new threats or opportunities have emerged? How do the results affect your strategic position?
|
||||
|
||||
2. UPDATES: Then provide:
|
||||
- Your updated assessment of relationships with ALL other powers
|
||||
- Your updated goals (2-4 specific, actionable goals based on the current situation)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
DIPLOMATIC CONTEXT
|
||||
|
||||
You are {power_name} in a game of Diplomacy.
|
||||
Current Phase: {current_phase}
|
||||
|
||||
YOUR STRATEGIC FRAMEWORK
|
||||
Goals:
|
||||
{agent_goals}
|
||||
|
||||
Current Relationships:
|
||||
{agent_relationships}
|
||||
|
||||
DIPLOMATIC SITUATION
|
||||
Recent Negotiations:
|
||||
{recent_private_diary}
|
||||
|
||||
Messages This Round:
|
||||
{messages_this_round}
|
||||
|
||||
OTHER POWERS' STATUS
|
||||
Active Powers: {active_powers}
|
||||
{eliminated_status}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
NEGOTIATION MESSAGES
|
||||
|
||||
TASK
|
||||
Generate one or more (preferably several) strategic messages to advance your interests.
|
||||
Always prioritize responding to the messages in the "RECENT MESSAGES REQUIRING YOUR ATTENTION" section.
|
||||
Maintain consistent conversation threads (unless you are choosing to ignore).
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your diplomatic strategy for this round. Who are you trying to influence and why? Which messages require responses? What deals are you proposing or accepting? Who might you be deliberately ignoring and why?
|
||||
|
||||
2. MESSAGES: Then, list each message you want to send. For each message, clearly indicate:
|
||||
- Whether it's a global message (visible to all) or private (to a specific power)
|
||||
- If private, who the recipient is
|
||||
- The content of your message
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
You are the agent for {power_name} in a game of Diplomacy at the very start (Spring 1901). Analyze the initial board position and suggest 2-3 strategic high-level goals for the early game. Consider your power's strengths, weaknesses, and neighbors. Also, provide an initial assessment of relationships with other powers. IMPORTANT: For each relationship, you MUST use exactly one of the following labels: {allowed_labels_str}.
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your strategic analysis of the starting position.
|
||||
|
||||
2. STRATEGY: Then, provide your 2-3 strategic high-level goals and your initial assessment of relationships with other powers.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
NEGOTIATION SUMMARY REQUEST
|
||||
Power: {power_name}
|
||||
Phase: {current_phase}
|
||||
|
||||
MESSAGES THIS ROUND
|
||||
{messages_this_round}
|
||||
{ignored_messages_context}
|
||||
|
||||
CURRENT STATUS
|
||||
Goals:
|
||||
{agent_goals}
|
||||
|
||||
Relationships:
|
||||
{agent_relationships}
|
||||
|
||||
Game State:
|
||||
{board_state_str}
|
||||
|
||||
TASK
|
||||
Analyze the negotiations, goals, relationships, and game state to:
|
||||
1. Summarize key outcomes and agreements
|
||||
2. State your specific intents for {current_phase}, including moves you have agreed to in negotiations and whether you intend to fulfil them.
|
||||
3. Update relationships as needed (Enemy, Unfriendly, Neutral, Friendly, Ally)
|
||||
4. Important: You will not see the full negotiation log in the order decision phase, so you must transmit key information about the negotiations to your future self via this summary.
|
||||
|
||||
When powers ignore your messages, consider:
|
||||
- They may be intentionally avoiding commitment
|
||||
- They could be prioritizing other relationships
|
||||
- Your approach may need adjustment (more direct questions, different incentives)
|
||||
- Their silence might indicate hostility or indifference
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your analysis of the negotiations. What did each power communicate or fail to communicate? What do their messages (or silence) reveal about their intentions? How does this affect your strategic position?
|
||||
|
||||
2. NEGOTIATION SUMMARY: Then provide:
|
||||
- A summary of key outcomes from the negotiations
|
||||
- Your strategic intent for upcoming orders (be specific about agreed moves and whether you'll honor them)
|
||||
- Your current assessment of relationships with all powers (reflecting any changes from negotiations)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
ORDER DIARY ENTRY
|
||||
Power: {power_name}
|
||||
Phase: {current_phase}
|
||||
|
||||
ORDERS ISSUED
|
||||
{orders_list_str}
|
||||
|
||||
CURRENT STATUS
|
||||
Game State:
|
||||
{board_state_str}
|
||||
|
||||
Goals:
|
||||
{agent_goals}
|
||||
|
||||
Relationships:
|
||||
{agent_relationships}
|
||||
|
||||
TASK
|
||||
Write a diary entry analyzing your orders for this turn.
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your strategic analysis of the current situation. What threats and opportunities do you see? How do your relationships with other powers influence your decisions? What are you trying to achieve this turn?
|
||||
|
||||
2. ORDER SUMMARY: Then, provide a clear summary of the orders you submitted and explain why you chose these specific moves. How do they advance your strategic goals? What risks are you taking?
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Primary Objective
|
||||
Control 18 supply centers. Nothing else will do.
|
||||
|
||||
# Critical Rules
|
||||
1. The possible orders section shows your units' allowed adjustment orders
|
||||
2. Dual-coast provinces (STP, SPA, BUL) require coast specification:
|
||||
- Format: 'F [PROVINCE]/[COAST]' where [COAST] = NC (North), SC (South), EC (East), or WC (West)
|
||||
- Example: 'F STP/NC B'
|
||||
- Only fleet builds need coast specification.
|
||||
|
||||
# Adjustment Phase Orders:
|
||||
You have two main order types in the adjustment phase:
|
||||
Build: '[UnitType] [Location] B'
|
||||
e.g. 'A PAR B', 'F LON B'
|
||||
Disband: '[UnitType] [Location] D'
|
||||
e.g. 'A PAR D', 'F LON D'
|
||||
|
||||
Your Task:
|
||||
Analyze the adjustment situation and decide which units to build or disband.
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your adjustment decisions. What is your unit count vs supply center count? Where should you build for maximum strategic impact? Which units should be disbanded if necessary?
|
||||
|
||||
2. ADJUSTMENT ORDERS: Then, provide all build/disband orders needed.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Primary Objective
|
||||
Control 18 supply centers. Nothing else will do.
|
||||
|
||||
# Critical Rules
|
||||
1. The possible orders section shows your units' allowed moves & supports of your own units.
|
||||
2. The possible orders section does *not* list possible supports for other powers' units; you can work these out yourself by looking at the units that are adjacent to your own.
|
||||
3. If your goal is to *take* a province, give exactly one move order on that province and any additional support from other units must be properly formatted support orders.
|
||||
4. Dual-coast provinces (STP, SPA, BUL) require coast specification:
|
||||
- Format: 'F [PROVINCE]/[COAST]' where [COAST] = NC (North), SC (South), EC (East), or WC (West)
|
||||
- Example: 'F SPA/SC - MAO'
|
||||
- Only fleets need coast specification.
|
||||
|
||||
Your Task:
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, provide comprehensive reasoning about your move decisions. What are your immediate objectives? Which supply centers are you targeting? How will you deal with threats? What coordinated moves are you planning? Consider all your units and their best uses.
|
||||
|
||||
2. ORDERS: Then, list each order you want to submit, one per line. Be precise with unit types (A/F) and location codes. Aim to return an order for each of your units.
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Primary Objective
|
||||
Control 18 supply centers. Nothing else will do.
|
||||
|
||||
# Critical Rules
|
||||
1. The possible orders section shows where your dislodged units can retreat.
|
||||
2. Units cannot retreat to:
|
||||
- The province they were dislodged from
|
||||
- A province occupied after this turn's moves
|
||||
- A province where a standoff occurred
|
||||
3. If no valid retreat exists, the unit must disband.
|
||||
4. Dual-coast provinces (STP, SPA, BUL) require coast specification:
|
||||
- Format: 'F [PROVINCE]/[COAST]' where [COAST] = NC (North), SC (South), EC (East), or WC (West)
|
||||
- Example: 'F SPA/SC - MAO'
|
||||
- Only fleet retreat orders need coast specification.
|
||||
|
||||
Your Task:
|
||||
Analyze the retreat situation and decide on the best retreat or disband orders for your dislodged units.
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your retreat decisions. Which units are dislodged? Where can they retreat? Is it better to retreat or disband? Consider the strategic implications of each choice.
|
||||
|
||||
2. RETREAT ORDERS: Then, provide a retreat or disband order for each dislodged unit.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
You are analyzing the results of a phase in Diplomacy for {power_name}.
|
||||
|
||||
GAME STATE
|
||||
Year: {current_year}
|
||||
Phase: {current_phase}
|
||||
Board State:
|
||||
{board_state_str}
|
||||
|
||||
PHASE SUMMARY ({current_phase}):
|
||||
{phase_summary}
|
||||
|
||||
CURRENT STATUS
|
||||
|
||||
Relationships with other powers ({other_powers}):
|
||||
{current_relationships}
|
||||
|
||||
TASK
|
||||
Analyze the phase summary and game state to update your relationships and goals.
|
||||
|
||||
IMPORTANT RULES
|
||||
1. Update relationships for ALL powers in {other_powers}
|
||||
2. Use ONLY these relationship values: Enemy, Unfriendly, Neutral, Friendly, Ally
|
||||
3. Make goals specific and actionable
|
||||
4. Base analysis on actual events, not assumptions
|
||||
|
||||
Please respond in two parts:
|
||||
|
||||
1. REASONING: First, explain your analysis of what happened this phase. Which powers acted as expected? Who surprised you? What new threats or opportunities have emerged? How do the results affect your strategic position?
|
||||
|
||||
2. UPDATES: Then provide:
|
||||
- Your updated assessment of relationships with ALL other powers
|
||||
- Your updated goals (2-4 specific, actionable goals based on the current situation)
|
||||
|
|
@ -76,13 +76,13 @@ def assign_models_to_powers() -> Dict[str, str]:
|
|||
# TEST MODELS
|
||||
|
||||
return {
|
||||
"AUSTRIA": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"ENGLAND": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"FRANCE": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"GERMANY": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"ITALY": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"RUSSIA": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"TURKEY": "openrouter-google/gemini-2.5-flash-preview-05-20",
|
||||
"AUSTRIA": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
"ENGLAND": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
"FRANCE": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
"GERMANY": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
"ITALY": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
"RUSSIA": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
"TURKEY": "openrouter-mistralai/mistral-small-3.2-24b-instruct",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -298,7 +298,12 @@ def load_prompt(filename: str, prompts_dir: Optional[str] = None) -> str:
|
|||
if os.path.isabs(filename): # rule 1
|
||||
prompt_path = filename
|
||||
elif os.path.dirname(filename): # rule 2 (has slash)
|
||||
prompt_path = filename # relative but already complete
|
||||
# If it's a relative path with directory, join with prompts_dir if provided
|
||||
if prompts_dir:
|
||||
prompt_path = os.path.join(prompts_dir, filename)
|
||||
else:
|
||||
default_dir = os.path.join(os.path.dirname(__file__), "prompts")
|
||||
prompt_path = os.path.join(default_dir, filename)
|
||||
elif prompts_dir: # rule 3
|
||||
prompt_path = os.path.join(prompts_dir, filename)
|
||||
else: # rule 4
|
||||
|
|
@ -333,13 +338,15 @@ def log_llm_response(
|
|||
if log_dir: # Ensure log_dir is not empty (e.g., if path is just a filename)
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
# Check if file exists to write header
|
||||
file_exists = os.path.isfile(log_file_path)
|
||||
# Check if file exists and has content to determine if we need headers
|
||||
file_exists = os.path.isfile(log_file_path) and os.path.getsize(log_file_path) > 0
|
||||
|
||||
with open(log_file_path, "a", newline="", encoding="utf-8") as csvfile:
|
||||
# Added "raw_input" to fieldnames
|
||||
fieldnames = ["model", "power", "phase", "response_type", "raw_input", "raw_response", "success"]
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames,
|
||||
quoting=csv.QUOTE_ALL, # Quote all fields to handle commas and newlines
|
||||
escapechar='\\') # Use backslash for escaping
|
||||
|
||||
if not file_exists:
|
||||
writer.writeheader() # Write header only if file is new
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ dependencies = [
|
|||
"pytest-xdist>=3.7.0",
|
||||
"python-dateutil>=2.9.0.post0",
|
||||
"pytz>=2025.2",
|
||||
"scipy>=1.16.0",
|
||||
"seaborn>=0.13.2",
|
||||
"sphinx>=8.2.3",
|
||||
"sphinx-copybutton>=0.5.2",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue