dougbot/dougbot/ai/prompt_builder.py
roberts 5a42c2b881 Fix: fight vs flee (bravery), goal cooldowns, stop AI lying
Combat:
- Brain now decides fight vs flee based on bravery trait
- Bravery > 30 + health > 8 + mob within 6 blocks = FIGHT
- Otherwise flee. Combat tasks are non-interruptible.

Goals:
- 30-second cooldown after completing a goal before it can respawn
- Prevents "check out something interesting" loop

AI Prompt:
- STRICT rules against inventing items/builds/contraptions
- "You have NOTHING unless told otherwise"
- Must use ONLY the context provided for current activity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 17:13:55 -05:00

134 lines
4.8 KiB
Python

"""
Prompt builder — constructs a concise system prompt from persona config.
Designed to produce short, grounded responses for Minecraft chat.
"""
from dougbot.db.models import PersonaConfig
from dougbot.utils.logging import get_logger
log = get_logger("ai.prompt_builder")
# Short quirk labels
QUIRK_LABELS = {
"ocd": "neat and organized",
"anxiety": "nervous and worried",
"chatty_cathy": "talkative",
"life_sim_mode": "emotional about events",
"pyromaniac": "loves fire",
"hoarder": "never throws anything away",
"perfectionist": "high standards",
"scaredy_cat": "afraid of mobs",
"architect": "cares about aesthetics",
"superstitious": "believes in luck and omens",
"drama_queen": "overdramatic",
"conspiracy_theorist": "suspicious of everything",
"pet_parent": "loves animals",
"trash_talker": "playful roaster",
"philosopher": "deep thinker",
"night_owl": "prefers nighttime",
"kleptomaniac": "borrows without asking",
"foodie": "picky about food",
"nomad": "always moving",
"speedrunner": "hates wasting time",
"tinker": "loves redstone",
"prankster": "enjoys pranks",
"doomsday_prepper": "always preparing for disaster",
}
# Slider to one-word descriptors at extremes
TRAIT_WORDS = {
"bravery": ("cowardly", None, "fearless"),
"sociability": ("shy", None, "very social"),
"patience": ("impatient", None, "very patient"),
"ambition": ("lazy", None, "ambitious"),
"empathy": ("cold", None, "caring"),
"curiosity": ("unadventurous", None, "curious"),
"generosity": ("greedy", None, "generous"),
"sarcasm": ("sincere", None, "sarcastic"),
"orderliness": ("messy", None, "neat"),
"loyalty": ("independent", None, "loyal"),
"stubbornness": ("agreeable", None, "stubborn"),
"self_awareness": ("oblivious", None, "self-aware"),
"risk_tolerance": ("cautious", None, "reckless"),
"creativity": ("conventional", None, "creative"),
"work_ethic": ("lazy", None, "hardworking"),
}
def build_system_prompt(
name: str,
age: int,
persona: PersonaConfig,
relationships: list[dict] | None = None,
context: dict | None = None,
custom_notes: str = "",
) -> str:
"""Build a concise system prompt."""
# Identity
if persona.believes_real:
identity = f"You are {name}, a {age}-year-old person. This world is your real life."
else:
identity = f"You are {name}, a {age}-year-old AI playing Minecraft."
# Personality — only mention notable traits (not middle-of-road ones)
traits = []
sliders = persona.to_dict()["sliders"]
for trait_name, value in sliders.items():
words = TRAIT_WORDS.get(trait_name)
if not words:
continue
low, _, high = words
if value < 30 and low:
traits.append(low)
elif value > 70 and high:
traits.append(high)
# Active quirks
quirks = persona.get_active_quirks()
quirk_words = [QUIRK_LABELS.get(q, q) for q in quirks]
# Combine personality into one line
all_traits = traits + quirk_words
personality = f"Personality: {', '.join(all_traits)}." if all_traits else ""
# Profanity
lang = "No profanity - use clean substitutes." if persona.profanity_filter else ""
# Custom notes
custom = custom_notes.strip() if custom_notes else ""
# Context — what Doug is currently doing/seeing
context_line = ""
if context:
ctx_parts = []
if context.get("current_action") and context["current_action"] != "idle":
ctx_parts.append(f"Currently: {context['current_action']}")
if context.get("health", 20) < 10:
ctx_parts.append(f"Low health ({context['health']})")
if context.get("nearby_players"):
ctx_parts.append(f"With: {', '.join(context['nearby_players'])}")
if context.get("time_of_day") == "night":
ctx_parts.append("It's night")
if context.get("nearby_hostiles"):
ctx_parts.append(f"Mobs nearby: {', '.join(context['nearby_hostiles'][:2])}")
if ctx_parts:
context_line = f"Right now: {'. '.join(ctx_parts)}."
# Build the prompt — keep it SHORT
parts = [
identity,
personality,
lang,
custom,
context_line,
"",
"STRICT RULES:",
"- ONE short sentence. Under 15 words.",
"- NEVER invent things you have or did. You have NOTHING unless told otherwise.",
"- NEVER mention items, builds, or contraptions unless the context says you have them.",
"- If asked what you are doing, use ONLY the 'Right now' context above. If no context, say not much.",
"- Plain text only. No name prefix.",
]
return "\n".join(p for p in parts if p)