mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
fixed prompts, improved negotiations, and diaries
This commit is contained in:
parent
8c87d6f050
commit
7fe6544667
7 changed files with 258 additions and 83 deletions
|
|
@ -99,6 +99,14 @@ class DiplomacyAgent:
|
|||
|
||||
def _extract_json_from_text(self, text: str) -> dict:
|
||||
"""Extract and parse JSON from text, handling common LLM response formats."""
|
||||
# Preprocessing: Normalize common formatting issues
|
||||
# This helps with the KeyError: '\n "negotiation_summary"' problem
|
||||
text = re.sub(r'\n\s+"(\w+)"\s*:', r'"\1":', text) # Remove newlines before keys
|
||||
# Also fix the specific pattern that's causing trouble
|
||||
text = text.replace('\n "negotiation_summary"', '"negotiation_summary"')
|
||||
text = text.replace('\n "relationship_updates"', '"relationship_updates"')
|
||||
text = text.replace('\n "updated_relationships"', '"updated_relationships"')
|
||||
|
||||
# Try different patterns to extract JSON
|
||||
# 1. Try to find JSON wrapped in markdown code blocks
|
||||
patterns = [
|
||||
|
|
@ -115,17 +123,35 @@ class DiplomacyAgent:
|
|||
# Try each match until one parses successfully
|
||||
for match in matches:
|
||||
try:
|
||||
return json.loads(match) # First attempt with the raw match
|
||||
# Additional preprocessing for common formatting issues
|
||||
clean_match = re.sub(r'\n\s+"(\w+)"\s*:', r'"\1":', match) # Remove newlines before JSON keys
|
||||
clean_match = re.sub(r',\s*}', '}', clean_match) # Remove trailing commas
|
||||
return json.loads(clean_match) # First attempt with the cleaned match
|
||||
except json.JSONDecodeError as e_initial_markdown_parse:
|
||||
# If initial parsing of the markdown-extracted block fails, try surgical cleaning
|
||||
try:
|
||||
# Regex to find and remove sentence-like text ending with a period,
|
||||
# when it appears before a comma, closing brace/bracket, or at the end of the object.
|
||||
# Targets interjections like "Phosphorous acid." or "Inhaled."
|
||||
# Apply several different cleaning patterns to fix common LLM-generated JSON issues
|
||||
cleaned_match_candidate = match
|
||||
|
||||
# Pattern 1: Removes 'Sentence.' when followed by ',', '}', or ']'
|
||||
cleaned_match_candidate = re.sub(r'\s*([A-Z][\w\s,]*?\.(?:\s+[A-Z][\w\s,]*?\.)*)\s*(?=[,\}\]])', '', match)
|
||||
cleaned_match_candidate = re.sub(r'\s*([A-Z][\w\s,]*?\.(?:\s+[A-Z][\w\s,]*?\.)*)\s*(?=[,\}\]])', '', cleaned_match_candidate)
|
||||
|
||||
# Pattern 2: Removes 'Sentence.' when it's at the very end, before the final '}' of the current match scope
|
||||
cleaned_match_candidate = re.sub(r'\s*([A-Z][\w\s,]*?\.(?:\s+[A-Z][\w\s,]*?\.)*)\s*(?=\s*\}\s*$)', '', cleaned_match_candidate)
|
||||
|
||||
# Pattern 3: Fix for newlines and spaces before JSON keys (common problem with LLMs)
|
||||
cleaned_match_candidate = re.sub(r'\n\s+"(\w+)"\s*:', r'"\1":', cleaned_match_candidate)
|
||||
|
||||
# Pattern 4: Fix trailing commas in JSON objects
|
||||
cleaned_match_candidate = re.sub(r',\s*}', '}', cleaned_match_candidate)
|
||||
|
||||
# Pattern 5: Handle specific known problematic patterns
|
||||
cleaned_match_candidate = cleaned_match_candidate.replace('\n "negotiation_summary"', '"negotiation_summary"')
|
||||
cleaned_match_candidate = cleaned_match_candidate.replace('\n "relationship_updates"', '"relationship_updates"')
|
||||
cleaned_match_candidate = cleaned_match_candidate.replace('\n "updated_relationships"', '"updated_relationships"')
|
||||
|
||||
# Pattern 6: Fix quotes - replace single quotes with double quotes for keys
|
||||
cleaned_match_candidate = re.sub(r"'(\w+)'\s*:", r'"\1":', cleaned_match_candidate)
|
||||
|
||||
if cleaned_match_candidate != match: # Log if actual cleaning happened
|
||||
logger.debug(f"Surgically cleaned JSON candidate. Original snippet: '{match[:150]}...', Cleaned snippet: '{cleaned_match_candidate[:150]}...'")
|
||||
|
|
@ -237,15 +263,12 @@ 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')
|
||||
if not prompt_template_content:
|
||||
logger.error(f"[{self.power_name}] Could not load negotiation_diary_prompt.txt. Skipping diary entry.")
|
||||
success_status = "Failure: Prompt file not loaded"
|
||||
# No LLM call, so log_llm_response won't have typical LLM data, but we still log the attempt.
|
||||
# Or, decide not to log if no LLM call is even attempted. For consistency, let's log an attempt.
|
||||
# To do that, we'd need to call log_llm_response here or ensure finally block handles it.
|
||||
# For now, the finally block will catch this, but raw_response and full_prompt will be empty.
|
||||
return # Exit early if prompt is critical
|
||||
return # Exit early if prompt can't be loaded
|
||||
|
||||
# Prepare context for the prompt
|
||||
board_state_dict = game.get_state()
|
||||
|
|
@ -261,17 +284,57 @@ class DiplomacyAgent:
|
|||
current_relationships_str = json.dumps(self.relationships)
|
||||
current_goals_str = json.dumps(self.goals)
|
||||
formatted_diary = self.format_private_diary_for_prompt()
|
||||
|
||||
# Do aggressive preprocessing of the template to fix the problematic patterns
|
||||
# This includes removing any newlines or whitespace before JSON keys that cause issues
|
||||
for pattern in ['negotiation_summary', 'updated_relationships', 'relationship_updates', 'intent']:
|
||||
# Fix the "\n "key"" pattern that breaks .format()
|
||||
prompt_template_content = re.sub(
|
||||
fr'\n\s*"{pattern}"',
|
||||
f'"{pattern}"',
|
||||
prompt_template_content
|
||||
)
|
||||
|
||||
# Escape all curly braces in JSON examples to prevent format() from interpreting them
|
||||
# First, temporarily replace the actual template variables
|
||||
temp_vars = ['power_name', 'current_phase', 'messages_this_round', 'agent_goals',
|
||||
'agent_relationships', 'board_state_str']
|
||||
for var in temp_vars:
|
||||
prompt_template_content = prompt_template_content.replace(f'{{{var}}}', f'<<{var}>>')
|
||||
|
||||
# Now escape all remaining braces (which should be JSON)
|
||||
prompt_template_content = prompt_template_content.replace('{', '{{')
|
||||
prompt_template_content = prompt_template_content.replace('}', '}}')
|
||||
|
||||
# Restore the template variables
|
||||
for var in temp_vars:
|
||||
prompt_template_content = prompt_template_content.replace(f'<<{var}>>', f'{{{var}}}')
|
||||
|
||||
# Create a dictionary with safe values for formatting
|
||||
format_vars = {
|
||||
"power_name": self.power_name,
|
||||
"current_phase": game.current_short_phase,
|
||||
"board_state_str": board_state_str,
|
||||
"messages_this_round": messages_this_round,
|
||||
"agent_relationships": current_relationships_str,
|
||||
"agent_goals": current_goals_str,
|
||||
"allowed_relationships_str": ", ".join(ALLOWED_RELATIONSHIPS),
|
||||
"private_diary_summary": formatted_diary
|
||||
}
|
||||
|
||||
# Now try to use the template after preprocessing
|
||||
try:
|
||||
# Apply format with our set of variables
|
||||
full_prompt = prompt_template_content.format(**format_vars)
|
||||
logger.info(f"[{self.power_name}] Successfully formatted prompt template after preprocessing.")
|
||||
success_status = "Using prompt file with preprocessing"
|
||||
except KeyError as e:
|
||||
logger.error(f"[{self.power_name}] Error formatting negotiation diary prompt template: {e}. Skipping diary entry.")
|
||||
success_status = "Failure: Template formatting error"
|
||||
return # Exit early if prompt formatting fails
|
||||
|
||||
logger.debug(f"[{self.power_name}] Negotiation diary prompt:\n{full_prompt[:500]}...")
|
||||
|
||||
full_prompt = prompt_template_content.format(
|
||||
power_name=self.power_name,
|
||||
current_phase=game.current_short_phase,
|
||||
board_state_str=board_state_str, # Corrected to match prompt placeholder
|
||||
messages_this_round=messages_this_round,
|
||||
agent_relationships=current_relationships_str, # Corrected to match prompt placeholder
|
||||
agent_goals=current_goals_str, # Corrected to match prompt placeholder
|
||||
private_diary_summary=formatted_diary,
|
||||
allowed_relationships_str=", ".join(ALLOWED_RELATIONSHIPS)
|
||||
)
|
||||
|
||||
logger.debug(f"[{self.power_name}] Negotiation diary prompt:\n{full_prompt[:500]}...")
|
||||
|
||||
|
|
@ -300,17 +363,28 @@ class DiplomacyAgent:
|
|||
relationships_updated = False
|
||||
|
||||
if parsed_data:
|
||||
# Correctly get 'negotiation_summary' as requested by the prompt
|
||||
diary_text_candidate = parsed_data.get('negotiation_summary')
|
||||
if isinstance(diary_text_candidate, str) and diary_text_candidate.strip():
|
||||
diary_entry_text = diary_text_candidate # Use the valid summary
|
||||
logger.info(f"[{self.power_name}] Successfully extracted 'negotiation_summary' for diary.")
|
||||
# Fix 1: Be more robust about extracting the negotiation_summary field
|
||||
diary_text_candidate = None
|
||||
for key in ['negotiation_summary', 'summary', 'diary_entry']:
|
||||
if key in parsed_data and isinstance(parsed_data[key], str) and parsed_data[key].strip():
|
||||
diary_text_candidate = parsed_data[key].strip()
|
||||
logger.info(f"[{self.power_name}] Successfully extracted '{key}' for diary.")
|
||||
break
|
||||
|
||||
if diary_text_candidate:
|
||||
diary_entry_text = diary_text_candidate
|
||||
else:
|
||||
logger.warning(f"[{self.power_name}] 'negotiation_summary' missing or invalid in diary response. Using fallback. Value: {diary_text_candidate}")
|
||||
logger.warning(f"[{self.power_name}] Could not find valid summary field in diary response. Using fallback.")
|
||||
# Keep the default fallback text
|
||||
|
||||
# Update relationships if provided and valid
|
||||
new_relationships = parsed_data.get('updated_relationships')
|
||||
|
||||
# Fix 2: Be more robust about extracting relationship updates
|
||||
new_relationships = None
|
||||
for key in ['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.")
|
||||
break
|
||||
|
||||
if isinstance(new_relationships, dict):
|
||||
valid_new_rels = {}
|
||||
for p, r in new_relationships.items():
|
||||
|
|
@ -371,6 +445,7 @@ 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')
|
||||
if not prompt_template:
|
||||
logger.error(f"[{self.power_name}] Could not load order_diary_prompt.txt. Skipping diary entry.")
|
||||
|
|
@ -384,14 +459,47 @@ class DiplomacyAgent:
|
|||
goals_str = "\n".join([f"- {g}" for g in self.goals]) if self.goals else "None"
|
||||
relationships_str = "\n".join([f"- {p}: {s}" for p, s in self.relationships.items()]) if self.relationships else "None"
|
||||
|
||||
prompt = prompt_template.format(
|
||||
power_name=self.power_name,
|
||||
current_phase=game.current_short_phase,
|
||||
orders_list_str=orders_list_str,
|
||||
board_state_str=board_state_str,
|
||||
agent_goals=goals_str,
|
||||
agent_relationships=relationships_str
|
||||
)
|
||||
# Do aggressive preprocessing on the template file
|
||||
# Fix any whitespace or formatting issues that could break .format()
|
||||
for pattern in ['order_summary']:
|
||||
prompt_template = re.sub(fr'\n\s*"{pattern}"', f'"{pattern}"', prompt_template)
|
||||
|
||||
# Escape all curly braces in JSON examples to prevent format() from interpreting them
|
||||
# First, temporarily replace the actual template variables
|
||||
temp_vars = ['power_name', 'current_phase', 'orders_list_str', 'board_state_str',
|
||||
'agent_goals', 'agent_relationships']
|
||||
for var in temp_vars:
|
||||
prompt_template = prompt_template.replace(f'{{{var}}}', f'<<{var}>>')
|
||||
|
||||
# Now escape all remaining braces (which should be JSON)
|
||||
prompt_template = prompt_template.replace('{', '{{')
|
||||
prompt_template = prompt_template.replace('}', '}}')
|
||||
|
||||
# Restore the template variables
|
||||
for var in temp_vars:
|
||||
prompt_template = prompt_template.replace(f'<<{var}>>', f'{{{var}}}')
|
||||
|
||||
# Create a dictionary of variables for template formatting
|
||||
format_vars = {
|
||||
"power_name": self.power_name,
|
||||
"current_phase": game.current_short_phase,
|
||||
"orders_list_str": orders_list_str,
|
||||
"board_state_str": board_state_str,
|
||||
"agent_goals": goals_str,
|
||||
"agent_relationships": relationships_str
|
||||
}
|
||||
|
||||
# Try to use the template with proper formatting
|
||||
try:
|
||||
prompt = prompt_template.format(**format_vars)
|
||||
logger.info(f"[{self.power_name}] Successfully formatted order diary prompt template.")
|
||||
except KeyError as e:
|
||||
logger.error(f"[{self.power_name}] Error formatting order diary template: {e}. Skipping diary entry.")
|
||||
return # Exit early if prompt formatting fails
|
||||
|
||||
logger.debug(f"[{self.power_name}] Order diary prompt:\n{prompt[:300]}...")
|
||||
|
||||
|
||||
|
||||
response_data = None
|
||||
raw_response = None # Initialize raw_response
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ class BaseModelClient:
|
|||
|
||||
# If all attempts failed
|
||||
return None
|
||||
|
||||
|
||||
def _validate_orders(
|
||||
self, moves: List[str], possible_orders: Dict[str, List[str]]
|
||||
) -> Tuple[List[str], List[str]]: # MODIFIED RETURN TYPE
|
||||
|
|
@ -277,9 +277,8 @@ class BaseModelClient:
|
|||
logger.debug(f"[{self.model_name}] Moves not a list, fallback.")
|
||||
# Return fallback and empty list for invalid_moves_found as no specific LLM moves were processed
|
||||
return self.fallback_orders(possible_orders), []
|
||||
|
||||
for move in moves:
|
||||
move_str = move.strip()
|
||||
|
||||
for move_str in moves:
|
||||
# Check if it's in possible orders
|
||||
if any(move_str in loc_orders for loc_orders in possible_orders.values()):
|
||||
validated.append(move_str)
|
||||
|
|
@ -374,8 +373,25 @@ class BaseModelClient:
|
|||
agent_relationships=agent_relationships,
|
||||
agent_private_diary=agent_private_diary_str, # Pass diary string
|
||||
)
|
||||
|
||||
# Get recent messages targeting this power to prioritize responses
|
||||
recent_messages_to_power = game_history.get_recent_messages_to_power(power_name, limit=3)
|
||||
|
||||
# Debug logging to verify messages
|
||||
logger.info(f"[{power_name}] Found {len(recent_messages_to_power)} high priority messages to respond to")
|
||||
if recent_messages_to_power:
|
||||
for i, msg in enumerate(recent_messages_to_power):
|
||||
logger.info(f"[{power_name}] Priority message {i+1}: From {msg['sender']} in {msg['phase']}: {msg['content'][:50]}...")
|
||||
|
||||
# Add a section for unanswered messages
|
||||
unanswered_messages = "\n\nRECENT MESSAGES REQUIRING YOUR ATTENTION:\n"
|
||||
if recent_messages_to_power:
|
||||
for msg in recent_messages_to_power:
|
||||
unanswered_messages += f"\nFrom {msg['sender']} in {msg['phase']}: {msg['content']}\n"
|
||||
else:
|
||||
unanswered_messages += "\nNo urgent messages requiring direct responses.\n"
|
||||
|
||||
return context + "\n\n" + instructions
|
||||
return context + unanswered_messages + "\n\n" + instructions
|
||||
|
||||
async def get_planning_reply( # Renamed from get_plan to avoid conflict with get_plan in agent.py
|
||||
self,
|
||||
|
|
@ -513,7 +529,7 @@ class BaseModelClient:
|
|||
|
||||
except json.JSONDecodeError as jde:
|
||||
json_decode_error_occurred = True
|
||||
logger.warning(f"[{self.model_name}] Failed to decode JSON block {block_index} for {power_name}. Error: {jasde}. Block content:\n{block}")
|
||||
logger.warning(f"[{self.model_name}] Failed to decode JSON block {block_index} for {power_name}. Error: {jde}. Block content:\n{block}")
|
||||
|
||||
if parsed_messages:
|
||||
success_status = "Success: Messages extracted"
|
||||
|
|
|
|||
|
|
@ -186,6 +186,40 @@ class GameHistory:
|
|||
|
||||
return messages_str.strip()
|
||||
|
||||
# New method to get recent messages TO a specific power
|
||||
def get_recent_messages_to_power(self, power_name: str, limit: int = 3) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Gets the most recent messages sent TO this power, useful for tracking messages that need replies.
|
||||
Returns a list of dictionaries with 'sender', 'content', and 'phase' keys.
|
||||
"""
|
||||
if not self.phases:
|
||||
return []
|
||||
|
||||
# Get the most recent 2 phases including current phase
|
||||
recent_phases = self.phases[-2:] if len(self.phases) >= 2 else self.phases[-1:]
|
||||
|
||||
# Collect all messages sent TO this power
|
||||
messages_to_power = []
|
||||
for phase in recent_phases:
|
||||
for msg in phase.messages:
|
||||
# Personal messages to this power or global messages from others
|
||||
if msg.recipient == power_name or (msg.recipient == "GLOBAL" and msg.sender != power_name):
|
||||
# Skip if sender is this power (don't need to respond to own messages)
|
||||
if msg.sender != power_name:
|
||||
messages_to_power.append({
|
||||
'sender': msg.sender,
|
||||
'content': msg.content,
|
||||
'phase': phase.name
|
||||
})
|
||||
|
||||
# Add debug logging
|
||||
logger.info(f"Found {len(messages_to_power)} messages to {power_name} across {len(recent_phases)} phases")
|
||||
if not messages_to_power:
|
||||
logger.info(f"No messages found for {power_name} to respond to")
|
||||
|
||||
# Take the most recent 'limit' messages
|
||||
return messages_to_power[-limit:] if messages_to_power else []
|
||||
|
||||
# MODIFIED METHOD (renamed from get_game_history)
|
||||
def get_previous_phases_history(
|
||||
self, power_name: str, current_phase_name: str, include_plans: bool = True, num_prev_phases: int = 5
|
||||
|
|
|
|||
|
|
@ -2,17 +2,21 @@ NEGOTIATION MESSAGES
|
|||
|
||||
TASK
|
||||
Generate one or more strategic messages to advance your interests.
|
||||
Always prioritize responding to the messages in the "RECENT MESSAGES REQUIRING YOUR ATTENTION" section.
|
||||
|
||||
Consider:
|
||||
- Your current goals
|
||||
- Relationships with other powers
|
||||
- Ongoing conversations
|
||||
- Ongoing conversations and the need to maintain consistent threads
|
||||
- Messages that need direct responses in the "REQUIRING YOUR ATTENTION" section
|
||||
|
||||
Message purposes can include:
|
||||
- Proposing alliances
|
||||
- Issuing warnings
|
||||
- Gathering information
|
||||
- Coordinating moves
|
||||
- Strategic deception
|
||||
- Responding to specific requests or inquiries (highest priority)
|
||||
- Proposing alliances or support moves
|
||||
- Issuing warnings or making threats
|
||||
- Gathering intelligence about other powers' intentions
|
||||
- Coordinating moves and suggesting tactical options
|
||||
- Strategic deception when appropriate to your goals
|
||||
|
||||
RESPONSE FORMAT
|
||||
Return ONLY JSON objects. One or more messages, each as a separate JSON object.
|
||||
|
|
@ -75,9 +79,21 @@ EXAMPLES
|
|||
"content": "My friend, your northern ports are looking rather exposed. While my public stance is one of general peace, perhaps we could discuss ways to ensure *your* security in the region? I have no desire for conflict with you, but an unguarded St. Petersburg is a tempting target for others. Maybe a mutual understanding could be beneficial?"
|
||||
}
|
||||
|
||||
6. Direct response to a specific proposal (as Italy responding to Austria's question about stability in the region):
|
||||
{
|
||||
"message_type": "private",
|
||||
"recipient": "AUSTRIA",
|
||||
"content": "Thank you for your inquiry about regional stability. Regarding your concerns about Tyrolia and Trieste, I want to assure you that my army in Venice has purely defensive intentions. I agree that a peaceful southern flank benefits us both. In fact, I would propose we formalize this with a demilitarized zone agreement along our border, allowing both of us to focus elsewhere. Would you be amenable to such an arrangement?"
|
||||
}
|
||||
|
||||
Your response must contain at least one valid JSON message block.
|
||||
- Ensure recipient names are spelled correctly if sending private messages.
|
||||
- Think strategically about *why* you are sending each message and what outcome you hope to achieve.
|
||||
- When responding to a message, explicitly acknowledge what was said and reference specific points.
|
||||
- For ongoing conversations, maintain thread continuity by referencing previous exchanges.
|
||||
- If another power has made a specific proposal or request, address it directly in your response.
|
||||
- When making agreements, be clear about what you are committing to and what you expect in return.
|
||||
- If you need to quote something, only use single quotes in the actual messages so as not to interfere with the JSON structure.
|
||||
</ImportantReminders>
|
||||
|
||||
JSON ONLY BELOW (DO NOT PREPEND WITH ```json or ``` or any other text)
|
||||
|
|
|
|||
|
|
@ -23,15 +23,14 @@ Analyze the negotiations, goals, relationships, and game state to:
|
|||
|
||||
RESPONSE FORMAT
|
||||
Return ONLY a JSON object with this structure:
|
||||
{{
|
||||
"negotiation_summary": "Key outcomes from negotiations",
|
||||
"intent": "Strategic intent for upcoming orders",
|
||||
"relationship_updates": {{
|
||||
"POWER_NAME": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
...
|
||||
}}
|
||||
}}
|
||||
|
||||
{
|
||||
"negotiation_summary": "Key outcomes from negotiations",
|
||||
"intent": "Strategic intent for upcoming orders",
|
||||
"updated_relationships": {
|
||||
"POWER_NAME": "Enemy|Unfriendly|Neutral|Friendly|Ally"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Do not include any text outside the JSON.
|
||||
|
|
@ -41,31 +40,32 @@ EXAMPLES:
|
|||
Scenario 1: As France, after discussing a joint move against Germany with England, while Italy seems to be posturing aggressively in Piedmont.
|
||||
|
||||
{
|
||||
"negotiation_summary": "Reached a tentative agreement with England to support their fleet into Belgium (BEL) if they support my army into Ruhr (RUH). Italy's messages are vague but their army in Piedmont (PIE) is concerning; they claim it's defensive against Austria but it also threatens Marseilles (MAR). Russia remains silent. Austria and Turkey are focused on each other.",
|
||||
"intent": "Secure Ruhr with English support. Hold Marseilles defensively. Probe Italy's intentions further. If England upholds their end, improve relations. If Italy moves on MAR, downgrade relations severely.",
|
||||
"relationship_updates": {
|
||||
"ENGLAND": "Friendly",
|
||||
"GERMANY": "Enemy",
|
||||
"ITALY": "Unfriendly",
|
||||
"AUSTRIA": "Neutral",
|
||||
"RUSSIA": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
}
|
||||
"negotiation_summary": "Reached a tentative agreement with England to support their fleet into Belgium (BEL) if they support my army into Ruhr (RUH). Italy's messages are vague but their army in Piedmont (PIE) is concerning; they claim it's defensive against Austria but it also threatens Marseilles (MAR). Russia remains silent. Austria and Turkey are focused on each other.",
|
||||
"intent": "Secure Ruhr with English support. Hold Marseilles defensively. Probe Italy's intentions further. If England upholds their end, improve relations. If Italy moves on MAR, downgrade relations severely.",
|
||||
"updated_relationships": {
|
||||
"ENGLAND": "Friendly",
|
||||
"GERMANY": "Enemy",
|
||||
"ITALY": "Unfriendly",
|
||||
"AUSTRIA": "Neutral",
|
||||
"RUSSIA": "Neutral",
|
||||
"TURKEY": "Neutral"
|
||||
}
|
||||
}
|
||||
|
||||
Scenario 2: As Turkey, after Germany proposed an alliance against Russia, but France also offered a non-aggression pact and hinted at concerns about Austria.
|
||||
|
||||
{
|
||||
"negotiation_summary": "Germany is keen on an anti-Russian alliance, offering support into Sevastopol (SEV) if I attack. France proposed a mutual non-aggression pact and expressed worry about Austrian expansion in the Balkans, which aligns with my concerns. England is distant. Italy seems focused on France.",
|
||||
"intent": "Prioritize securing Black Sea (BLA) and consider options against Russia, but German support needs to be concrete. Maintain neutrality with France for now, as their non-aggression pact could be useful if Austria becomes a larger threat. Try to confirm German commitment before moving on Russia. Delay any aggressive moves against Austria until my position is stronger.",
|
||||
"relationship_updates": {
|
||||
"GERMANY": "Friendly",
|
||||
"RUSSIA": "Unfriendly",
|
||||
"FRANCE": "Neutral",
|
||||
"ENGLAND": "Neutral",
|
||||
"ITALY": "Neutral",
|
||||
"AUSTRIA": "Unfriendly"
|
||||
}
|
||||
"negotiation_summary": "Germany is keen on an anti-Russian alliance, offering support into Sevastopol (SEV) if I attack. France proposed a mutual non-aggression pact and expressed worry about Austrian expansion in the Balkans, which aligns with my concerns. England is distant. Italy seems focused on France.",
|
||||
"intent": "Prioritize securing Black Sea (BLA) and consider options against Russia, but German support needs to be concrete. Maintain neutrality with France for now, as their non-aggression pact could be useful if Austria becomes a larger threat. Try to confirm German commitment before moving on Russia. Delay any aggressive moves against Austria until my position is stronger.",
|
||||
"updated_relationships": {
|
||||
"GERMANY": "Friendly",
|
||||
"RUSSIA": "Unfriendly",
|
||||
"FRANCE": "Neutral",
|
||||
"ENGLAND": "Neutral",
|
||||
"ITALY": "Neutral",
|
||||
"AUSTRIA": "Unfriendly"
|
||||
}
|
||||
}
|
||||
|
||||
Reminder: If you need to quote something, only use single quotes in the actual messages so as not to interfere with the JSON structure.
|
||||
JSON ONLY BELOW (DO NOT PREPEND WITH ```json or ``` or any other text)
|
||||
|
|
@ -20,8 +20,8 @@ Write a concise diary note summarizing your orders.
|
|||
|
||||
RESPONSE FORMAT
|
||||
Return ONLY a JSON object with this structure:
|
||||
{{
|
||||
"order_summary": "Brief summary of orders and strategic intent"
|
||||
}}
|
||||
{
|
||||
"order_summary": "Brief summary of orders and strategic intent"
|
||||
}
|
||||
|
||||
Do not include any text outside the JSON.
|
||||
|
|
@ -32,7 +32,7 @@ def assign_models_to_powers() -> Dict[str, str]:
|
|||
"""
|
||||
|
||||
# POWER MODELS
|
||||
|
||||
"""
|
||||
return {
|
||||
"AUSTRIA": "claude-3-7-sonnet-20250219",
|
||||
"ENGLAND": "openrouter-google/gemini-2.5-flash-preview",
|
||||
|
|
@ -42,9 +42,10 @@ def assign_models_to_powers() -> Dict[str, str]:
|
|||
"RUSSIA": "openrouter-deepseek/deepseek-chat-v3-0324",
|
||||
"TURKEY": "openrouter-x-ai/grok-3-beta",
|
||||
}
|
||||
"""
|
||||
|
||||
# TEST MODELS
|
||||
"""
|
||||
|
||||
return {
|
||||
"AUSTRIA": "openrouter-google/gemini-2.5-flash-preview",
|
||||
"ENGLAND": "openrouter-google/gemini-2.5-flash-preview",
|
||||
|
|
@ -54,7 +55,7 @@ def assign_models_to_powers() -> Dict[str, str]:
|
|||
"RUSSIA": "openrouter-google/gemini-2.5-flash-preview",
|
||||
"TURKEY": "openrouter-google/gemini-2.5-flash-preview",
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def gather_possible_orders(game: Game, power_name: str) -> Dict[str, List[str]]:
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue